diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 32ab526344..e0822c348e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,5 +2,16 @@ | ------------- | --- | JIRA Ticket | | Versions | +| Edition | + +#### Checklist + +- [ ] Text renders correctly +- [ ] Text has been checked with vale +- [ ] Description metadata is up to date +- [ ] Redirects cover removed/moved pages +- [ ] Code samples are working +- [ ] PHP code samples have been fixed with PHP CS fixer +- [ ] Added link to this PR in relevant JIRA ticket or code PR diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000000..ddf10fd43b --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,62 @@ +name: 'Build & test documentation' + +on: + push: + branches: + - master + - '[0-9]+.[0-9]+' + pull_request: ~ + +jobs: + build: + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # added or changed files to the repository. + contents: write + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install php-cs-fixer + run: composer require friendsofphp/php-cs-fixer --dev + - name: Run PHP CS Fixer + run: ./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php -v --show-progress=dots + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: PHP CS Fixes + - name: Install dependencies + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Run build + run: | + mkdocs build --strict + + vale-check: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v3 + - name: Get Vale.sh configs + env: + TOKEN: ${{ secrets.EZROBOT_PAT }} + run: | + curl -H "Authorization: token $TOKEN" -L https://github.com/ibexa/vale-styles/archive/refs/heads/main.zip -o vale.zip + unzip vale.zip + rm vale.zip + mv vale-styles-main/* vale-styles-main/.vale.ini . + - name: Run Vale.sh + uses: errata-ai/vale-action@reviewdog + with: + reporter: github-check + filter_mode: added diff --git a/.gitignore b/.gitignore index 9dab47b6ca..9106d700f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ /vendor/ **/.DS_Store +__pycache__/* +/site/ +**/.idea/ +.php-cs-fixer.cache +composer.lock diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000000..38b8df8e41 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,27 @@ +withRules([ + 'header_comment' => false, +]); + +return $configFactory + ->buildConfig() + ->setFinder( + PhpCsFixer\Finder::create() + ->in( + array_filter([ + __DIR__ . '/code_samples', + __DIR__ . '/tests', + ], 'is_dir') + ) + ->files()->name('*.php') + ); diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 6e09b5df18..0000000000 --- a/.php_cs +++ /dev/null @@ -1,14 +0,0 @@ -setFinder( - PhpCsFixer\Finder::create() - ->in(__DIR__ . '/tests') - ->files()->name('*.php') - ) -; diff --git a/.readthedocs.yml b/.readthedocs.yml index c37daa1f14..d161ec9b53 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -2,10 +2,14 @@ version: 2 mkdocs: configuration: mkdocs.yml - fail_on_warning: false + fail_on_warning: true + +build: + os: ubuntu-20.04 + tools: + python: "3.11" python: - version: 3.6 install: - requirements: pip_require.txt - requirements: requirements.txt diff --git a/README.md b/README.md index 95399c2f6d..0024898a97 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,55 @@ -# eZ Platform Developer Documentation +# Ibexa DXP Developer Documentation -This repository is the source for the [developer documentation for eZ Platform](https://doc.ezplatform.com), -an open source CMS based on the Symfony Full Stack Framework in PHP. +This repository is the source for the [developer documentation for Ibexa](https://doc.ibexa.co/en/latest), +a digital experience platform that is based on the Symfony Full Stack Framework in PHP. # Resources -1. eZ Platform Developer Hub: https://ezplatform.com/ -1. eZ Platform Repository: https://github.com/ezsystems/ezplatform -1. Open JIRA board: https://jira.ez.no/ -1. Ibexa Website: https://ibexa.co/ -1. User documentation: https://doc.ezplatform.com/projects/userguide/en/latest/ -1. Personalization Solution: https://doc.ezplatform.com/projects/ezpersonalization/en/latest/ +1. Ibexa DXP Developer Hub: https://developers.ibexa.co +1. Ibexa DXP Repository: https://github.com/ibexa/oss +1. Open JIRA board: https://issues.ibexa.co +1. Ibexa Website: https://ibexa.co +1. User documentation: https://doc.ibexa.co/projects/userguide +## How to contribute -## How to Contribute -https://doc.ezplatform.com/en/master/community_resources/contributing/#contribute-to-documentation +To contribute to the documentation, you can open a PR in this repository. -Invite yourself to the community Slack and join `#documentation-contrib` +If you'd like to see Ibexa DXP in your language, you can [contribute to the translations](https://doc.ibexa.co/en/latest/resources/contributing/contribute_translations/). -https://ez-community-on-slack.herokuapp.com/ +### Contribute to API reference + +The REST API Reference is located in the `docs/api/rest_api/rest_api_reference/rest_api_reference.html` +file, which is generated automatically by the RAML2HTML tool. +It is based on `*.raml` files located in the `docs/api/rest_api/rest_api_reference/input` directory that you can edit in your editor/IDE. + +After you modify relevant files in the input folder, you can generate an HTML file from repository root (this step can also be performed by one of the Tech Writers during PR review): + +`php tools/raml2html/raml2html.php build --non-standard-http-methods=COPY,MOVE,PUBLISH,SWAP -t default -o docs/api/rest_api/rest_api_reference/output/ docs/api/rest_api/rest_api_reference/input/ez.raml` + +In case of errors, look for mistakes in the RAML file, for example, double apostrophes. +Move `rest_api_reference.html` from the output folder to `docs/api/rest_api/rest_api_reference/` root. + +See `tools/raml2html/README.md` for more information. + +## Build and preview documentation + +To build and preview your changes locally, you need to install Python along with its package manager (`pip`). +Other required tools will be installed by using the following command: + +```bash +pip install -r requirements.txt +``` + +Then you can run: + +```bash +mkdocs serve +``` + +After a short while your documentation should be reachable at http://localhost:8000. If it isn't, check the output +of the command. ## Where to View -https://doc.ezplatform.com + +https://doc.ibexa.co diff --git a/code_samples/api/migration/src/Command/MigrationCommand.php b/code_samples/api/migration/src/Command/MigrationCommand.php new file mode 100644 index 0000000000..bad63281e5 --- /dev/null +++ b/code_samples/api/migration/src/Command/MigrationCommand.php @@ -0,0 +1,38 @@ +migrationService->add( + new Migration( + 'new_migration.yaml', + $string_with_migration_content + ) + ); + + foreach ($this->migrationService->listMigrations() as $migration) { + $output->writeln($migration->getName()); + } + + $migration_name = $this->migrationService->listMigrations()[0]->getName(); + $my_migration = $this->migrationService->findOneByName($migration_name); + + $this->migrationService->executeOne($my_migration); + $this->migrationService->executeAll('admin'); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/config/packages/views.yaml b/code_samples/api/public_php_api/config/packages/views.yaml new file mode 100644 index 0000000000..bfa64ed74b --- /dev/null +++ b/code_samples/api/public_php_api/config/packages/views.yaml @@ -0,0 +1,12 @@ +ezplatform: + system: + site: + content_view: + full: + folder: + controller: App\Controller\CustomController::showContentAction +# controller: App\Controller\PaginationController::showContentAction +# controller: App\Controller\CustomFilterController::showChildrenAction +# template: custom_filter.html.twig + match: + Identifier\ContentType: [folder] diff --git a/code_samples/api/public_php_api/src/Command/AddLanguageCommand.php b/code_samples/api/public_php_api/src/Command/AddLanguageCommand.php new file mode 100644 index 0000000000..b3a82e7055 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/AddLanguageCommand.php @@ -0,0 +1,52 @@ +languageService = $languageService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:add_language'); + } + + protected function configure() + { + $this->setDescription('Lists available languages and add Polish.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $languageList = $this->languageService->loadLanguages(); + + foreach ($languageList as $language) { + $output->writeln($language->languageCode . ': ' . $language->name); + } + + $languageCreateStruct = $this->languageService->newLanguageCreateStruct(); + $languageCreateStruct->languageCode = 'pol-PL'; + $languageCreateStruct->name = 'Polish'; + $this->languageService->createLanguage($languageCreateStruct); + $output->writeln('Added language Polish with language code pol-PL.'); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php b/code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php new file mode 100644 index 0000000000..c871c8921b --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php @@ -0,0 +1,63 @@ +contentService = $contentService; + $this->locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:add_location'); + } + + protected function configure() + { + $this + ->setDescription('Add a Location to Content item and hides it.') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'Content ID'), + new InputArgument('parentLocationId', InputArgument::REQUIRED, 'Parent Location ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $parentLocationId = $input->getArgument('parentLocationId'); + $contentId = $input->getArgument('contentId'); + + $locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocationId); + + $locationCreateStruct->priority = 500; + $locationCreateStruct->hidden = true; + + $contentInfo = $this->contentService->loadContentInfo($contentId); + $newLocation = $this->locationService->createLocation($contentInfo, $locationCreateStruct); + + $output->writeln('Added hidden location ' . $newLocation->id . ' to Content item: ' . $contentInfo->name); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/BookmarkCommand.php b/code_samples/api/public_php_api/src/Command/BookmarkCommand.php new file mode 100644 index 0000000000..8bee391331 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/BookmarkCommand.php @@ -0,0 +1,59 @@ +bookmarkService = $bookmarkService; + $this->locationService = $locationService; + parent::__construct('doc:bookmark'); + } + + protected function configure() + { + $this + ->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, 'Location id'), + ]) + ->addOption('delete', 'd', InputOption::VALUE_NONE, 'Delete the created bookmark?', null); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $locationId = $input->getArgument('locationId'); + $location = $this->locationService->loadLocation($locationId); + + $this->bookmarkService->createBookmark($location); + + $output->writeln('Added bookmark to ' . $location->getContentInfo()->name); + + $bookmarkList = $this->bookmarkService->loadBookmarks(); + + $output->writeln('Total bookmarks: ' . $bookmarkList->totalCount); + + foreach ($bookmarkList->items as $bookmark) { + $output->writeln($bookmark->getContentInfo()->name); + } + + if ($input->getOption('delete')) { + $this->bookmarkService->deleteBookmark($location); + $output->writeln('Deleted bookmark from ' . $location->getContentInfo()->name); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/BrowseLocationsCommand.php b/code_samples/api/public_php_api/src/Command/BrowseLocationsCommand.php new file mode 100644 index 0000000000..8ede6dfc07 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/BrowseLocationsCommand.php @@ -0,0 +1,50 @@ +locationService = $locationService; + parent::__construct('doc:browse_locations'); + } + + protected function configure() + { + $this + ->setDescription('Lists all descendants of the Location') + ->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, 'Location ID to browse from'), + ]); + } + + private function browseLocation(Location $location, OutputInterface $output, $depth = 0) + { + $output->writeln($location->contentInfo->name); + + $children = $this->locationService->loadLocationChildren($location); + foreach ($children->locations as $child) { + $this->browseLocation($child, $output, $depth + 1); + } + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $locationId = $input->getArgument('locationId'); + + $location = $this->locationService->loadLocation($locationId); + $this->browseLocation($location, $output); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/CalendarCommand.php b/code_samples/api/public_php_api/src/Command/CalendarCommand.php new file mode 100644 index 0000000000..0b0f1bc3a8 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/CalendarCommand.php @@ -0,0 +1,67 @@ +permissionResolver = $permissionResolver; + $this->userService = $userService; + $this->calendarService = $calendarService; + parent::__construct('doc:calendar'); + } + + public function configure(): void + { + $this->setDescription('Lists Calendar event in the provided time range and reschedules them.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $dateFrom = new \DateTimeImmutable('2021-12-01T10:00:00+00:00'); + $dateTo = new \DateTimeImmutable('2021-12-31T10:0:00+00:00'); + $dateRange = new Calendar\DateRange($dateFrom, $dateTo); + + $eventQuery = new Calendar\EventQuery($dateRange, 10); + + $eventList = $this->calendarService->getEvents($eventQuery); + + foreach ($eventList as $event) { + $output->writeln($event->getName() . '; date: ' . $event->getDateTime()->format('T Y-m-d H:i:s')); + } + + $eventCollection = $eventList->getEvents(); + $output->writeln('First event: ' . $eventCollection->first()->getName() . '; date: ' . $eventCollection->first()->getDateTime()->format('T Y-m-d H:i:s')); + + $newCollection = $eventCollection->slice(3, 5); + foreach ($newCollection as $event) { + $output->writeln('New collection: ' . $event->getName() . '; date: ' . $event->getDateTime()->format('T Y-m-d H:i:s')); + } + + $newDate = new \DateTimeImmutable('2021-12-06T13:00:00+00:00'); + $context = new RescheduleEventActionContext($eventCollection, $newDate); + + $this->calendarService->executeAction($context); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/CreateContentCommand.php b/code_samples/api/public_php_api/src/Command/CreateContentCommand.php new file mode 100644 index 0000000000..4c5e8c332e --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/CreateContentCommand.php @@ -0,0 +1,75 @@ +contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:create_content'); + } + + protected function configure() + { + $this + ->setDefinition([ + new InputArgument('parentLocationId', InputArgument::REQUIRED, 'Parent Location ID'), + new InputArgument('contentType', InputArgument::REQUIRED, 'Identifier of a Content type with a Name and Description Field'), + new InputArgument('name', InputArgument::REQUIRED, 'Content for the Name field'), + ]) + ->addOption('publish', 'p', InputOption::VALUE_NONE, 'Do you want to publish the Content item?'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $parentLocationId = $input->getArgument('parentLocationId'); + $contentTypeIdentifier = $input->getArgument('contentType'); + $name = $input->getArgument('name'); + + $contentType = $this->contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', $name); + + $locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocationId); + + $draft = $this->contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + + $output->writeln('Created a draft of ' . $contentType->getName() . ' with name ' . $draft->getName()); + + if ($input->getOption('publish')) { + $content = $this->contentService->publishVersion($draft->versionInfo); + $output->writeln('Published Content item ' . $content->getName()); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php b/code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php new file mode 100644 index 0000000000..542b2e6fe4 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php @@ -0,0 +1,101 @@ +contentTypeService = $contentTypeService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:create_content_type'); + } + + protected function configure() + { + $this->setDefinition([ + new InputArgument('identifier', InputArgument::REQUIRED, 'Content Type identifier'), + new InputArgument('group_identifier', InputArgument::REQUIRED, 'Content Type group identifier'), + new InputArgument('copy_identifier', InputArgument::OPTIONAL, 'Identifier of the CT copy'), + ]) + ->addOption('copy', 'c', InputOption::VALUE_NONE, 'Do you want to make a copy the Content Type?'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $groupIdentifier = $input->getArgument('group_identifier'); + $contentTypeIdentifier = $input->getArgument('identifier'); + if ($input->getArgument('copy_identifier')) { + $copyIdentifier = $input->getArgument('copy_identifier'); + } + + try { + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroupByIdentifier($groupIdentifier); + } catch (\eZ\Publish\API\Repository\Exceptions\NotFoundException $e) { + $output->writeln("Content Type group with identifier $groupIdentifier not found"); + + return self::FAILURE; + } + + $contentTypeCreateStruct = $this->contentTypeService->newContentTypeCreateStruct($contentTypeIdentifier); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->nameSchema = ''; + + $contentTypeCreateStruct->names = [ + 'eng-GB' => $contentTypeIdentifier, + ]; + + $titleFieldCreateStruct = $this->contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $titleFieldCreateStruct->names = ['eng-GB' => 'Name']; + $titleFieldCreateStruct->descriptions = ['eng-GB' => 'The name']; + $titleFieldCreateStruct->fieldGroup = 'content'; + $titleFieldCreateStruct->position = 10; + $titleFieldCreateStruct->isTranslatable = true; + $titleFieldCreateStruct->isRequired = true; + $titleFieldCreateStruct->isSearchable = true; + + $contentTypeCreateStruct->addFieldDefinition($titleFieldCreateStruct); + + $contentTypeDraft = $this->contentTypeService->createContentType( + $contentTypeCreateStruct, + [$contentTypeGroup] + ); + + $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); + $output->writeln("Content type '$contentTypeIdentifier' with ID $contentTypeDraft->id created"); + + if ($input->getOption('copy')) { + $contentTypeToCopy = $this->contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); + + $copy = $this->contentTypeService->copyContentType($contentTypeToCopy); + $copyDraft = $this->contentTypeService->createContentTypeDraft($copy); + $copyUpdateStruct = $this->contentTypeService->newContentTypeUpdateStruct(); + $copyUpdateStruct->identifier = $copyIdentifier; + $copyUpdateStruct->names = ['eng-GB' => $copyIdentifier]; + $this->contentTypeService->updateContentTypeDraft($copyDraft, $copyUpdateStruct); + $this->contentTypeService->publishContentTypeDraft($copyDraft); + $output->writeln('Copy of the new CT created with identifier ' . $copyIdentifier); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/CreateImageCommand.php b/code_samples/api/public_php_api/src/Command/CreateImageCommand.php new file mode 100644 index 0000000000..61f6dec2bf --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/CreateImageCommand.php @@ -0,0 +1,83 @@ +contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:create_image'); + } + + protected function configure() + { + $this + ->setDefinition([ + new InputArgument('name', InputArgument::REQUIRED, 'Content for the Name field'), + new InputArgument('file', InputArgument::REQUIRED, 'Content for the Image field'), + ]) + ->addOption('publish', 'p', InputOption::VALUE_NONE, 'Do you want to publish the Content item?'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $name = $input->getArgument('name'); + $file = $input->getArgument('file'); + $publish = $input->getOption('publish'); + + $contentType = $this->contentTypeService->loadContentTypeByIdentifier('image'); + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreateStruct->setField('name', $name); + $imageValue = new \eZ\Publish\Core\FieldType\Image\Value( + [ + 'path' => $file, + 'fileSize' => filesize($file), + 'fileName' => basename($file), + 'alternativeText' => $name, + ] + ); + $contentCreateStruct->setField('image', $imageValue); + + $locationCreateStruct = $this->locationService->newLocationCreateStruct(51); + + $draft = $this->contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + + $output->writeln('Created a draft of ' . $contentType->getName() . ' with name ' . $draft->getName()); + + if ($publish == true) { + $content = $this->contentService->publishVersion($draft->versionInfo); + $output->writeln('Published Content item ' . $content->getName()); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/DeleteContentCommand.php b/code_samples/api/public_php_api/src/Command/DeleteContentCommand.php new file mode 100644 index 0000000000..7cf4c665f5 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/DeleteContentCommand.php @@ -0,0 +1,51 @@ +locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:delete_content'); + } + + protected function configure() + { + $this->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, 'Location to delete'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $locationId = $input->getArgument('locationId'); + + $location = $this->locationService->loadLocation($locationId); + + $this->locationService->deleteLocation($location); + + $output->writeln('Location ' . $locationId . ' deleted.'); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FilterCommand.php b/code_samples/api/public_php_api/src/Command/FilterCommand.php new file mode 100644 index 0000000000..14b88e3ec0 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FilterCommand.php @@ -0,0 +1,52 @@ +contentService = $contentService; + parent::__construct('doc:filter'); + } + + public function configure() + { + $this->setDescription('Returns children of the provided Location, sorted by name in descending order.'); + $this->setDefinition([ + new InputArgument('parentLocationId', InputArgument::REQUIRED, 'ID of the parent Location'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $parentLocationId = (int)$input->getArgument('parentLocationId'); + + $filter = new Filter(); + $filter + ->withCriterion(new Criterion\ParentLocationId($parentLocationId)) + ->withSortClause(new SortClause\ContentName(Query::SORT_DESC)); + + $result = $this->contentService->find($filter, []); + + $output->writeln('Found ' . $result->getTotalCount() . ' items'); + + foreach ($result as $content) { + $output->writeln($content->getName()); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FilterLocationCommand.php b/code_samples/api/public_php_api/src/Command/FilterLocationCommand.php new file mode 100644 index 0000000000..3492652adc --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FilterLocationCommand.php @@ -0,0 +1,52 @@ +locationService = $locationService; + parent::__construct('doc:filter_location'); + } + + public function configure() + { + $this->setDescription('Returns children of the provided Location, sorted by name in descending order.'); + $this->setDefinition([ + new InputArgument('parentLocationId', InputArgument::REQUIRED, 'ID of the parent Location'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $parentLocationId = (int)$input->getArgument('parentLocationId'); + + $filter = new Filter(); + $filter + ->withCriterion(new Criterion\ParentLocationId($parentLocationId)) + ->withSortClause(new SortClause\ContentName(Query::SORT_DESC)); + + $result = $this->locationService->find($filter, []); + + $output->writeln('Found ' . $result->totalCount . ' items'); + + foreach ($result as $content) { + $output->writeln($content->getContent()->getName()); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FindComplexCommand.php b/code_samples/api/public_php_api/src/Command/FindComplexCommand.php new file mode 100644 index 0000000000..aa2f48c717 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FindComplexCommand.php @@ -0,0 +1,69 @@ +searchService = $searchService; + $this->locationService = $locationService; + parent::__construct('doc:find_complex'); + } + + protected function configure() + { + $this + ->setDescription('Lists content belonging to the provided Content Type.') + ->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, ''), + new InputArgument('contentTypeIdentifier', InputArgument::REQUIRED, 'Content Type identifier'), + new InputArgument('text', InputArgument::REQUIRED, ''), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $locationId = $input->getArgument('locationId'); + $contentTypeIdentifier = $input->getArgument('contentTypeIdentifier'); + $text = $input->getArgument('text'); + + $query = new LocationQuery(); + + $query->query = new Criterion\LogicalAnd([ + new Criterion\Subtree($this->locationService->loadLocation($locationId)->pathString), + new Criterion\ContentTypeIdentifier($contentTypeIdentifier), + new Criterion\FullText($text), + new Criterion\LogicalNot( + new Criterion\SectionIdentifier('Media') + ), + ]); + + $query->sortClauses = [ + new SortClause\DatePublished(LocationQuery::SORT_ASC), + new SortClause\ContentName(LocationQuery::SORT_DESC), + ]; + + $result = $this->searchService->findContentInfo($query); + $output->writeln('Found ' . $result->totalCount . ' items'); + foreach ($result->searchHits as $searchHit) { + $output->writeln($searchHit->valueObject->name); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FindContentCommand.php b/code_samples/api/public_php_api/src/Command/FindContentCommand.php new file mode 100644 index 0000000000..94025b09ec --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FindContentCommand.php @@ -0,0 +1,48 @@ +searchService = $searchService; + parent::__construct('doc:find_content'); + } + + protected function configure() + { + $this + ->setDescription('Lists content belonging to the provided Content Type.') + ->setDefinition([ + new InputArgument('contentTypeIdentifier', InputArgument::REQUIRED, 'Content Type identifier'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $contentTypeIdentifier = $input->getArgument('contentTypeIdentifier'); + + $query = new LocationQuery(); + $query->filter = new Criterion\ContentTypeIdentifier($contentTypeIdentifier); + + $result = $this->searchService->findContentInfo($query); + + $output->writeln('Found ' . $result->totalCount . ' items'); + foreach ($result->searchHits as $searchHit) { + $output->writeln($searchHit->valueObject->name); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FindInTrashCommand.php b/code_samples/api/public_php_api/src/Command/FindInTrashCommand.php new file mode 100644 index 0000000000..281a10b867 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FindInTrashCommand.php @@ -0,0 +1,45 @@ +trashService = $trashService; + parent::__construct('doc:find_in_trash'); + } + + protected function configure() + { + $this + ->setDescription('Lists content in Trash belonging to the provided Content Type.') + ->setDefinition([ + new InputArgument('contentTypeId', InputArgument::REQUIRED, 'Content Type ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $contentTypeId = $input->getArgument('contentTypeId'); + + $query = new Query(); + + $query->filter = new Query\Criterion\ContentTypeId($contentTypeId); + $results = $this->trashService->findTrashItems($query); + foreach ($results->items as $trashedLocation) { + $output->writeln($trashedLocation->getContentInfo()->name); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FindUrlCommand.php b/code_samples/api/public_php_api/src/Command/FindUrlCommand.php new file mode 100644 index 0000000000..3d1751211d --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FindUrlCommand.php @@ -0,0 +1,64 @@ +urlService = $URLService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:find_url'); + } + + protected function configure() + { + $this + ->setDescription('Finds all valid URLs in the provided Section.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $query = new URLQuery(); + + $query->filter = new Criterion\LogicalAnd( + [ + new Criterion\SectionIdentifier(['standard']), + new Criterion\Validity(true), + ] + ); + $query->sortClauses = [ + new SortClause\URL(SortClause::SORT_DESC), + ]; + $query->offset = 0; + $query->limit = 25; + + $results = $this->urlService->findUrls($query); + + foreach ($results->items as $result) { + $output->writeln($result->url); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php b/code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php new file mode 100644 index 0000000000..af8feb12ae --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php @@ -0,0 +1,56 @@ +searchService = $searchService; + parent::__construct('doc:find_with_aggregation'); + } + + protected function configure() + { + $this + ->setDescription('Counts content per Content Type and the value of Selection Field.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $query = new LocationQuery(); + $query->query = new Criterion\ParentLocationId(2); + + $query->aggregations[] = new ContentTypeTermAggregation('content_type'); + $query->aggregations[] = new SelectionTermAggregation('selection', 'blog_post', 'topic'); + + $results = $this->searchService->findContentInfo($query); + + $contentByType = $results->aggregations->get('content_type'); + $contentBySelection = $results->aggregations->get('selection'); + + $query->aggregations[0]->setLimit(5); + $query->aggregations[0]->setMinCount(10); + + foreach ($contentByType as $contentType => $count) { + $output->writeln($contentType->getName() . ': ' . $count); + } + + foreach ($contentBySelection as $selection => $count) { + $output->writeln($selection . ': ' . $count); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php b/code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php new file mode 100644 index 0000000000..e91bfd8fec --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php @@ -0,0 +1,72 @@ +userService = $userService; + $this->permissionResolver = $permissionResolver; + $this->formSubmissionService = $formSubmissionService; + $this->contentService = $contentService; + + parent::__construct('doc:form-submission'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $content = $this->contentService->loadContent(143); + $contentInfo = $content->contentInfo; + + $formValue = $content->getFieldValue('form', 'eng-GB')->getFormValue(); + $data = [ + ['identifier' => 'single_line', 'name' => 'Line', 'value' => 'The name'], + ['identifier' => 'number', 'name' => 'Number', 'value' => 123], + ['identifier' => 'checkbox', 'name' => 'Checkbox', 'value' => 0], + ]; + + $this->formSubmissionService->create( + $contentInfo, + 'eng-GB', + $formValue, + $data + ); + + $submissions = $this->formSubmissionService->loadByContent($contentInfo); + + $output->writeln('Total number of submissions: ' . $submissions->getTotalCount()); + foreach ($submissions as $sub) { + $output->write($sub->getId() . '. submitted on '); + $output->write($sub->getCreated()->format('Y-m-d H:i:s') . ' by '); + $output->writeln($this->userService->loadUser($sub->getUserId())->getName()); + foreach ($sub->getValues() as $value) { + $output->writeln('- ' . $value->getIdentifier() . ': ' . $value->getDisplayValue()); + } + } + + $submission = $this->formSubmissionService->loadById(29); + $this->formSubmissionService->delete($submission); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/HideLocationCommand.php b/code_samples/api/public_php_api/src/Command/HideLocationCommand.php new file mode 100644 index 0000000000..8c49dddf51 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/HideLocationCommand.php @@ -0,0 +1,55 @@ +locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:hide'); + } + + protected function configure() + { + $this + ->setDescription('Hides and reveals again selected Location.') + ->setDefinition([ + new InputArgument('location_id', InputArgument::REQUIRED, 'Location ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $locationId = $input->getArgument('location_id'); + + $location = $this->locationService->loadLocation($locationId); + + $this->locationService->hideLocation($location); + $output->writeln('Location hidden: ' . $locationId); + + $this->locationService->unhideLocation($location); + $output->writeln('Location revealed: ' . $locationId); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/MoveContentCommand.php b/code_samples/api/public_php_api/src/Command/MoveContentCommand.php new file mode 100644 index 0000000000..98af4127b7 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/MoveContentCommand.php @@ -0,0 +1,54 @@ +locationService = $locationService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:move_content'); + } + + protected function configure() + { + $this + ->setDescription('Moves the selected Location with its subtree.') + ->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, 'Location to copy'), + new InputArgument('targetLocationId', InputArgument::REQUIRED, 'Target to copy or move to'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $locationId = $input->getArgument('locationId'); + $targetLocationId = $input->getArgument('targetLocationId'); + + $sourceLocation = $this->locationService->loadLocation($locationId); + $targetLocation = $this->locationService->loadLocation($targetLocationId); + $this->locationService->moveSubtree($sourceLocation, $targetLocation); + $output->writeln('Location ' . $locationId . ' moved to ' . $targetLocationId . ' with its subtree.'); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/ObjectStateCommand.php b/code_samples/api/public_php_api/src/Command/ObjectStateCommand.php new file mode 100644 index 0000000000..57731997a8 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/ObjectStateCommand.php @@ -0,0 +1,88 @@ +contentService = $contentService; + $this->userService = $userService; + $this->objectStateService = $objectStateService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:object_state'); + } + + protected function configure() + { + $this + ->setDescription('Creates OS group with provided States and assigned the Lock OS to provided Content item') + ->setDefinition([ + new InputArgument('objectStateGroupIdentifier', InputArgument::REQUIRED, 'Identifier of new OG group to create'), + new InputArgument('objectStateIdentifier', InputArgument::REQUIRED, 'Identifier(s) of a new Object State'), + new InputArgument('contentID', InputArgument::OPTIONAL, 'Content ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $objectStateGroup = $this->objectStateService->loadObjectStateGroupByIdentifier('ez_lock'); + $objectState = $this->objectStateService->loadObjectStateByIdentifier($objectStateGroup, 'locked'); + + $output->writeln($objectStateGroup->getName()); + $output->writeln($objectState->getName()); + + $objectStateGroupIdentifier = $input->getArgument('objectStateGroupIdentifier'); + $objectStateIdentifierList = explode(',', $input->getArgument('objectStateIdentifier')); + + $objectStateGroupStruct = $this->objectStateService->newObjectStateGroupCreateStruct($objectStateGroupIdentifier); + $objectStateGroupStruct->defaultLanguageCode = 'eng-GB'; + $objectStateGroupStruct->names = ['eng-GB' => $objectStateGroupIdentifier]; + $newObjectStateGroup = $this->objectStateService->createObjectStateGroup($objectStateGroupStruct); + + foreach ($objectStateIdentifierList as $objectStateIdentifier) { + $stateStruct = $this->objectStateService->newObjectStateCreateStruct($objectStateIdentifier); + $stateStruct->defaultLanguageCode = 'eng-GB'; + $stateStruct->names = ['eng-GB' => $objectStateIdentifier]; + $this->objectStateService->createObjectState($newObjectStateGroup, $stateStruct); + } + + $output->writeln('Created new Object state group ' . $newObjectStateGroup->identifier . ' with Object states:'); + foreach ($this->objectStateService->loadObjectStates($newObjectStateGroup) as $objectState) { + $output->writeln('* ' . $objectState->identifier); + } + + if ($input->getArgument('contentID')) { + $contentId = $input->getArgument('contentID'); + $objectStateToAssign = $objectStateIdentifierList[0]; + $contentInfo = $this->contentService->loadContentInfo($contentId); + $objectStateGroup = $this->objectStateService->loadObjectStateGroupByIdentifier($objectStateGroupIdentifier); + $objectState = $this->objectStateService->loadObjectStateByIdentifier($objectStateGroup, $objectStateToAssign); + + $this->objectStateService->setContentState($contentInfo, $objectStateGroup, $objectState); + $output->writeln('Content ' . $contentInfo->name . ' assigned state ' . $objectState->getName()); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/SectionCommand.php b/code_samples/api/public_php_api/src/Command/SectionCommand.php new file mode 100644 index 0000000000..56b3d2d6a7 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/SectionCommand.php @@ -0,0 +1,90 @@ +sectionService = $sectionService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + $this->searchService = $searchService; + $this->contentService = $contentService; + parent::__construct('doc:section'); + } + + protected function configure() + { + $this + ->setDescription('Creates new section and adds selected Content item to it.') + ->setDefinition([ + new InputArgument('sectionName', InputArgument::REQUIRED, 'Name of the new Section'), + new InputArgument('sectionIdentifier', InputArgument::REQUIRED, 'Identifier of the new Section'), + new InputArgument('contentId', InputArgument::REQUIRED, 'Content id'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $sectionName = $input->getArgument('sectionName'); + $sectionIdentifier = $input->getArgument('sectionIdentifier'); + $contentId = $input->getArgument('contentId'); + + $sectionCreateStruct = $this->sectionService->newSectionCreateStruct(); + $sectionCreateStruct->name = $sectionName; + $sectionCreateStruct->identifier = $sectionIdentifier; + $this->sectionService->createSection($sectionCreateStruct); + $output->writeln('Created new Section ' . $sectionName); + + $section = $this->sectionService->loadSectionByIdentifier($sectionIdentifier); + $contentInfo = $this->contentService->loadContentInfo($contentId); + $this->sectionService->assignSection($contentInfo, $section); + $output->writeln('Content ' . $contentInfo->name . ' assigned to Section ' . $sectionName); + + $query = new LocationQuery(); + $query->filter = new Criterion\SectionId([ + $section->id, + ]); + + $result = $this->searchService->findContentInfo($query); + + $output->writeln(( + $this->sectionService->isSectionUsed($section) + ? 'This section is in use.' + : 'This section is not in use.' + )); + $output->writeln('Content in this section: ' . $result->totalCount); + + foreach ($result->searchHits as $searchResult) { + $output->writeln('* ' . $searchResult->valueObject->name); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/SegmentCommand.php b/code_samples/api/public_php_api/src/Command/SegmentCommand.php new file mode 100644 index 0000000000..656a78d641 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/SegmentCommand.php @@ -0,0 +1,75 @@ +segmentationService = $segmentationService; + $this->permissionResolver = $permissionResolver; + $this->userService = $userService; + parent::__construct('doc:segment'); + } + + protected function configure() + { + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $segmentGroupCreateStruct = new SegmentGroupCreateStruct([ + 'name' => 'Custom Group', + 'identifier' => 'custom_group', + 'createSegments' => [], + ]); + + $newSegmentGroup = $this->segmentationService->createSegmentGroup($segmentGroupCreateStruct); + + $segmentCreateStruct = new SegmentCreateStruct([ + 'name' => 'Segment 1', + 'identifier' => 'segment_1', + 'group' => $newSegmentGroup, + ]); + + $newSegment = $this->segmentationService->createSegment($segmentCreateStruct); + + $segmentGroup = $this->segmentationService->loadSegmentGroupByIdentifier('custom_group'); + + $segments = $this->segmentationService->loadSegmentsAssignedToGroup($segmentGroup); + + foreach ($segments as $segment) { + $output->writeln('Segment ID: ' . $segment->id . ', name: ' . $segment->name); + } + + $segment = $this->segmentationService->loadSegmentByIdentifier('segment_1'); + + $this->segmentationService->assignUserToSegment($user, $segment); + + $output->writeln(( + $this->segmentationService->isUserAssignedToSegment($user, $segment) + ? 'The user is assigned to the segment.' + : 'The user is not assigned to the segment.' + )); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/SetMainLocationCommand.php b/code_samples/api/public_php_api/src/Command/SetMainLocationCommand.php new file mode 100644 index 0000000000..fa56cd0214 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/SetMainLocationCommand.php @@ -0,0 +1,58 @@ +contentService = $contentService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:set_main_location'); + } + + protected function configure() + { + $this + ->setDescription('Set a Location as Content item\'s main') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'The Content ID'), + new InputArgument('locationId', InputArgument::REQUIRED, 'One of the Locations of the Content'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $contentId = $input->getArgument('contentId'); + $locationId = $input->getArgument('locationId'); + + $contentInfo = $this->contentService->loadContentInfo($contentId); + + $contentUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); + $contentUpdateStruct->mainLocationId = $locationId; + + $this->contentService->updateContentMetadata($contentInfo, $contentUpdateStruct); + + $output->writeln('Location ' . $locationId . ' is now the main Location for ' . $contentInfo->name); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/TranslateContentCommand.php b/code_samples/api/public_php_api/src/Command/TranslateContentCommand.php new file mode 100644 index 0000000000..1d336e7890 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/TranslateContentCommand.php @@ -0,0 +1,69 @@ +contentService = $contentService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:translate_content'); + } + + protected function configure() + { + $this + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'ID of content to be updated'), + new InputArgument('language', InputArgument::REQUIRED, 'Language to add'), + new InputArgument('nameInNewLanguage', InputArgument::REQUIRED, 'Content name in new language'), + new InputArgument('secondaryLanguage', InputArgument::OPTIONAL, 'Secondary language to add'), + new InputArgument('nameInSecondaryLanguage', InputArgument::OPTIONAL, 'Content name in secondary language'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $contentId = $input->getArgument('contentId'); + $language = $input->getArgument('language'); + $newName = $input->getArgument('nameInNewLanguage'); + $secondaryLanguage = $input->getArgument('secondaryLanguage'); + $nameInSecondaryLanguage = $input->getArgument('nameInSecondaryLanguage'); + + $contentInfo = $this->contentService->loadContentInfo($contentId); + $contentDraft = $this->contentService->createContentDraft($contentInfo); + + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->initialLanguageCode = $language; + $contentUpdateStruct->setField('name', $newName); + + if ($nameInSecondaryLanguage !== null) { + $contentUpdateStruct->setField('name', $nameInSecondaryLanguage, $secondaryLanguage); + } + + $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($contentDraft->versionInfo); + $output->writeln('Translated ' . $contentInfo->name . ' to ' . $language . ' as ' . $newName); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/TrashContentCommand.php b/code_samples/api/public_php_api/src/Command/TrashContentCommand.php new file mode 100644 index 0000000000..9b90f67a5c --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/TrashContentCommand.php @@ -0,0 +1,71 @@ +locationService = $locationService; + $this->userService = $userService; + $this->trashService = $trashService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:trash_content'); + } + + protected function configure() + { + $this->setDefinition([ + new InputArgument('locationId', InputArgument::REQUIRED, 'Location to trash'), + new InputArgument('newParentId', InputArgument::OPTIONAL, 'New Location to restore under'), + ]) + ->addOption('restore', 'r', InputOption::VALUE_NONE, 'Do you want to restore the Content item?'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $locationId = $input->getArgument('locationId'); + if ($input->getArgument('newParentId')) { + $newParentId = $input->getArgument('newParentId'); + } + + $location = $this->locationService->loadLocation($locationId); + + $this->trashService->trash($location); + $output->writeln('Location ' . $locationId . ' moved to trash.'); + + if ($input->getOption('restore')) { + if ($input->getArgument('newParentId')) { + $newParent = $this->locationService->loadLocation($newParentId); + } else { + $newParent = null; + } + $trashItem = $this->trashService->loadTrashItem($locationId); + $this->trashService->recover($trashItem, $newParent); + $output->writeln('Restored from trash.'); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/UpdateContentCommand.php b/code_samples/api/public_php_api/src/Command/UpdateContentCommand.php new file mode 100644 index 0000000000..e7418e0764 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/UpdateContentCommand.php @@ -0,0 +1,61 @@ +contentService = $contentService; + $this->userService = $userService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:update_content'); + } + + protected function configure() + { + $this + ->setDescription('Update provided Content item with a new name') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'Content ID'), + new InputArgument('newName', InputArgument::REQUIRED, 'New name for the updated Content item'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $contentId = $input->getArgument('contentId'); + $newName = $input->getArgument('newName'); + + $contentInfo = $this->contentService->loadContentInfo($contentId); + $contentDraft = $this->contentService->createContentDraft($contentInfo); + + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->initialLanguageCode = 'eng-GB'; + $contentUpdateStruct->setField('name', $newName); + + $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($contentDraft->versionInfo); + + $output->writeln('Content item ' . $contentId . ' updated with new name: ' . $newName); + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/ViewContentCommand.php b/code_samples/api/public_php_api/src/Command/ViewContentCommand.php new file mode 100644 index 0000000000..ac619413c3 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/ViewContentCommand.php @@ -0,0 +1,56 @@ +contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->fieldTypeService = $fieldTypeService; + parent::__construct('doc:view_content'); + } + + protected function configure() + { + $this + ->setDescription('Output Field values on provided Content item.') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'Location ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $contentId = $input->getArgument('contentId'); + + $content = $this->contentService->loadContent($contentId); + $contentType = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId); + + foreach ($contentType->fieldDefinitions as $fieldDefinition) { + $output->writeln('Field: ' . $fieldDefinition->identifier); + $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->fieldTypeIdentifier); + $field = $content->getFieldValue($fieldDefinition->identifier); + $valueHash = $fieldType->toHash($field); + $output->writeln('Value:'); + $output->writeln($valueHash); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php b/code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php new file mode 100644 index 0000000000..46cb74d157 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php @@ -0,0 +1,123 @@ +contentService = $contentService; + $this->locationService = $locationService; + $this->urlAliasService = $urlAliasService; + $this->userService = $userService; + $this->objectStateService = $objectStateService; + $this->permissionResolver = $permissionResolver; + parent::__construct('doc:view_metadata'); + } + + protected function configure() + { + $this + ->setDescription('Output various metadata about a Content item.') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'An existing content ID'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $user = $this->userService->loadUserByLogin('admin'); + $this->permissionResolver->setCurrentUserReference($user); + + $contentId = $input->getArgument('contentId'); + + // Metadata + $contentInfo = $this->contentService->loadContentInfo($contentId); + + $output->writeln("Name: $contentInfo->name"); + $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d')); + $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d')); + $output->writeln("RemoteId: $contentInfo->remoteId"); + $output->writeln("Main Language: $contentInfo->mainLanguageCode"); + $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No')); + + // Locations + $locations = $this->locationService->loadLocations($contentInfo); + + foreach ($locations as $location) { + $output->writeln('Location: ' . $location->pathString); + $urlAlias = $this->urlAliasService->reverseLookup($location); + $output->writeln('URL alias: ' . $urlAlias->path); + } + + // Content Type + $content = $this->contentService->loadContent($contentId); + $output->writeln('Content Type: ' . $content->getContentType()->getName()); + + // Versions + $versionInfos = $this->contentService->loadVersions($contentInfo); + foreach ($versionInfos as $versionInfo) { + $output->write("Version $versionInfo->versionNo"); + $output->write(' by ' . $versionInfo->getCreator()->getName()); + $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name); + } + + $versionInfoArray = $this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_ARCHIVED); + if (count($versionInfoArray)) { + $output->writeln('Archived versions:'); + foreach ($versionInfoArray as $versionInfo) { + $creator = $this->userService->loadUser($versionInfo->creatorId); + $output->write("Version $versionInfo->versionNo"); + $output->write(' by ' . $creator->contentInfo->name); + $output->writeln(' in ' . $versionInfo->initialLanguageCode); + } + } + + // Relations + $versionInfo = $this->contentService->loadVersionInfo($contentInfo); + $relations = $this->contentService->loadRelations($versionInfo); + foreach ($relations as $relation) { + $name = $relation->destinationContentInfo->name; + $output->writeln('Relation to content ' . $name); + } + + // Owner + $output->writeln('Owner: ' . $contentInfo->getOwner()->getName()); + + // Section + $output->writeln('Section: ' . $contentInfo->getSection()->name); + + // Object states + $stateGroups = $this->objectStateService->loadObjectStateGroups(); + foreach ($stateGroups as $stateGroup) { + $state = $this->objectStateService->getContentState($contentInfo, $stateGroup); + $output->writeln("Object state: $state->identifier"); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Command/WorkflowCommand.php b/code_samples/api/public_php_api/src/Command/WorkflowCommand.php new file mode 100644 index 0000000000..7b300dbc99 --- /dev/null +++ b/code_samples/api/public_php_api/src/Command/WorkflowCommand.php @@ -0,0 +1,68 @@ +contentService = $contentService; + $this->workflowService = $workflowService; + $this->workflowRegistry = $workflowRegistry; + parent::__construct('doc:workflow'); + } + + protected function configure() + { + $this + ->setDescription('Starts content in the selected workflow and makes the provided transition.') + ->setDefinition([ + new InputArgument('contentId', InputArgument::REQUIRED, 'Content ID'), + new InputArgument('workflowName', InputArgument::REQUIRED, 'Workflow identifier'), + new InputArgument('transitionName', InputArgument::REQUIRED, 'Transition name'), + ]); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $contentId = $input->getArgument('contentId'); + $workflowName = $input->getArgument('workflowName'); + $transitionName = $input->getArgument('transitionName'); + + $content = $this->contentService->loadContent($contentId); + + $supportedWorkflows = $this->workflowRegistry->getSupportedWorkflows($content); + foreach ($supportedWorkflows as $supportedWorkflow) { + $output->writeln('Supports workflow: ' . $supportedWorkflow->getName()); + } + + $this->workflowService->start($content, $workflowName); + $workflowMetadata = $this->workflowService->loadWorkflowMetadataForContent($content, $workflowName); + + foreach ($workflowMetadata->markings as $marking) { + $output->writeln($content->getName() . ' is in stage ' . $marking->name . ' in workflow ' . $workflowMetadata->workflow->getName()); + } + + if ($this->workflowService->can($workflowMetadata, $transitionName)) { + $workflow = $this->workflowRegistry->getWorkflow($workflowName); + $workflow->apply($workflowMetadata->content, $transitionName, ['Please review']); + $output->writeln('Moved ' . $content->getName() . ' through transition ' . $transitionName); + } + + return self::SUCCESS; + } +} diff --git a/code_samples/api/public_php_api/src/Controller/CustomController.php b/code_samples/api/public_php_api/src/Controller/CustomController.php new file mode 100644 index 0000000000..48c976534e --- /dev/null +++ b/code_samples/api/public_php_api/src/Controller/CustomController.php @@ -0,0 +1,34 @@ +searchService = $searchService; + } + + public function showContentAction($locationId) + { + $query = new LocationQuery(); + $query->filter = new Criterion\ParentLocationId($locationId); + + $results = $this->searchService->findContentInfo($query); + $items = []; + foreach ($results->searchHits as $searchHit) { + $items[] = $searchHit; + } + + return $this->render('custom.html.twig', [ + 'items' => $items, + ]); + } +} diff --git a/code_samples/api/public_php_api/src/Controller/CustomFilterController.php b/code_samples/api/public_php_api/src/Controller/CustomFilterController.php new file mode 100644 index 0000000000..69f6a7559a --- /dev/null +++ b/code_samples/api/public_php_api/src/Controller/CustomFilterController.php @@ -0,0 +1,34 @@ +contentService = $contentService; + } + + public function showChildrenAction(ContentView $view): ContentView + { + $filter = new Filter(); + $filter + ->withCriterion(new ParentLocationId($view->getLocation()->id)); + + $view->setParameters( + [ + 'items' => $this->contentService->find($filter), + ] + ); + + return $view; + } +} diff --git a/code_samples/api/public_php_api/src/Controller/PaginationController.php b/code_samples/api/public_php_api/src/Controller/PaginationController.php new file mode 100644 index 0000000000..310cb374ad --- /dev/null +++ b/code_samples/api/public_php_api/src/Controller/PaginationController.php @@ -0,0 +1,44 @@ +searchService = $searchService; + } + + public function showContentAction(Request $request, $locationId) + { + $query = new LocationQuery(); + $query->filter = new Criterion\ParentLocationId($locationId); + + $pager = new Pagerfanta( + new ContentSearchAdapter($query, $this->searchService) + ); + $pager->setMaxPerPage(3); + $pager->setCurrentPage($request->get('page', 1)); + + $pager->getMaxScore(); + $pager->getTime(); + + return $this->render( + 'custom_pagination.html.twig', + [ + 'totalItemCount' => $pager->getNbResults(), + 'pagerItems' => $pager, + ] + ); + } +} diff --git a/code_samples/api/public_php_api/templates/custom.html.twig b/code_samples/api/public_php_api/templates/custom.html.twig new file mode 100644 index 0000000000..4c2c6755ef --- /dev/null +++ b/code_samples/api/public_php_api/templates/custom.html.twig @@ -0,0 +1,3 @@ +{% for item in items %} +

{{ item.valueObject.name }}

+{% endfor %} diff --git a/code_samples/api/public_php_api/templates/custom_filter.html.twig b/code_samples/api/public_php_api/templates/custom_filter.html.twig new file mode 100644 index 0000000000..916de8609a --- /dev/null +++ b/code_samples/api/public_php_api/templates/custom_filter.html.twig @@ -0,0 +1,3 @@ +{% for item in items %} +

{{ item.name }}

+{% endfor %} diff --git a/code_samples/api/public_php_api/templates/custom_pagination.html.twig b/code_samples/api/public_php_api/templates/custom_pagination.html.twig new file mode 100644 index 0000000000..4fe11087cc --- /dev/null +++ b/code_samples/api/public_php_api/templates/custom_pagination.html.twig @@ -0,0 +1,7 @@ +{% for item in pagerItems %} +

{{ ez_content_name(item) }}

+{% endfor %} + +{% if pagerItems.haveToPaginate() %} + {{ pagerfanta( pagerItems, 'ez') }} +{% endif %} diff --git a/code_samples/api/rest_api/config/routes.yaml b/code_samples/api/rest_api/config/routes.yaml new file mode 100644 index 0000000000..89b56f313d --- /dev/null +++ b/code_samples/api/rest_api/config/routes.yaml @@ -0,0 +1,8 @@ +app.rest: + resource: routes_rest.yaml + prefix: '%ezpublish_rest.path_prefix%' + +app.rest.options: + resource: routes_rest.yaml + prefix: '%ezpublish_rest.path_prefix%' + type: rest_options diff --git a/code_samples/api/rest_api/config/routes_rest.yaml b/code_samples/api/rest_api/config/routes_rest.yaml new file mode 100644 index 0000000000..967abc848a --- /dev/null +++ b/code_samples/api/rest_api/config/routes_rest.yaml @@ -0,0 +1,6 @@ +app.rest.greeting: + path: '/greet' + controller: App\Rest\Controller\DefaultController::helloWorld + methods: [GET,POST] + defaults: + csrf_protection: false diff --git a/code_samples/api/rest_api/config/services.yaml b/code_samples/api/rest_api/config/services.yaml new file mode 100644 index 0000000000..7ff39d6a0f --- /dev/null +++ b/code_samples/api/rest_api/config/services.yaml @@ -0,0 +1,52 @@ +parameters: + app.rest.output.visitor.xml.regexps: ['(^application/app\.api\.[A-Za-z]+\+xml$)'] + app.rest.output.visitor.json.regexps: ['(^application/app\.api\.[A-Za-z]+\+json$)'] + +services: + + app.rest.output.visitor.xml: + class: '%ezpublish_rest.output.visitor.class%' + arguments: + - '@ezpublish_rest.output.generator.xml' + - '@App\Rest\Output\ValueObjectVisitorDispatcher' + tags: + - { name: ezpublish_rest.output.visitor, regexps: app.rest.output.visitor.xml.regexps, priority: 20 } + + app.rest.output.visitor.json: + class: '%ezpublish_rest.output.visitor.class%' + arguments: + - '@ezpublish_rest.output.generator.json' + - '@App\Rest\Output\ValueObjectVisitorDispatcher' + tags: + - { name: ezpublish_rest.output.visitor, regexps: app.rest.output.visitor.json.regexps, priority: 20 } + + App\Rest\Output\ValueObjectVisitorDispatcher: + class: App\Rest\Output\ValueObjectVisitorDispatcher + arguments: + - !tagged_iterator { tag: 'app.rest.output.value_object.visitor', index_by: 'type' } + - '@ezpublish_rest.output.value_object_visitor.dispatcher' + + App\Rest\ValueObjectVisitor\RestLocation: + class: App\Rest\ValueObjectVisitor\RestLocation + parent: ezpublish_rest.output.value_object_visitor.base + arguments: + $urlAliasService: '@ezpublish.api.service.url_alias' + tags: + - { name: app.rest.output.value_object.visitor, type: EzSystems\EzPlatformRest\Server\Values\RestLocation } + + App\Rest\Controller\: + resource: '../src/Rest/Controller/' + parent: ezpublish_rest.controller.base + autowire: true + autoconfigure: true + tags: [ 'controller.service_arguments' ] + + App\Rest\ValueObjectVisitor\Greeting: + parent: ezpublish_rest.output.value_object_visitor.base + tags: + - { name: ezpublish_rest.output.value_object_visitor, type: App\Rest\Values\Greeting } + + App\Rest\InputParser\GreetingInput: + parent: ezpublish_rest.input.parser + tags: + - { name: ezpublish_rest.input.parser, mediaType: application/vnd.ez.api.GreetingInput } diff --git a/code_samples/api/rest_api/create_image.json.php b/code_samples/api/rest_api/create_image.json.php new file mode 100644 index 0000000000..a680113d0b --- /dev/null +++ b/code_samples/api/rest_api/create_image.json.php @@ -0,0 +1,165 @@ + []\n"; + exit(1); +} + +if (!is_file($argv[1])) { + echo "{$argv[1]} doesn't exist or is not a file.\n"; + exit(2); +} + +// URL to Ibexa DXP installation and its REST API +$host = 'api.example.com'; +$scheme = 'https'; +$api = '/api/ezp/v2'; +$baseUrl = "{$scheme}://{$host}{$api}"; + +// User credentials +$username = 'admin'; +$password = 'publish'; + +// Targets +$contentTypeId = 5; // "Image" +$parentLocationPath = '1/43/51'; // "Media > Images" +$sectionId = 3; // "Media" + +// Request payload +$data = [ + 'ContentCreate' => [ + 'ContentType' => [ + '_href' => "$api/content/types/$contentTypeId", + ], + 'mainLanguageCode' => 'eng-GB', + 'LocationCreate' => [ + 'ParentLocation' => [ + '_href' => "$api/content/locations/$parentLocationPath", + ], + 'sortField' => 'PATH', + 'sortOrder' => 'ASC', + ], + 'Section' => [ + '_href' => "$api/content/sections/$sectionId", + ], + 'fields' => [ + 'field' => [ + [ + 'fieldDefinitionIdentifier' => 'name', + 'fieldValue' => $argv[2] ?? basename($argv[1]), + ], + [ + 'fieldDefinitionIdentifier' => 'image', + 'fieldValue' => [ + // Original file name + 'fileName' => basename($argv[1]), + // File size in bytes + 'fileSize' => filesize($argv[1]), + // File content must be encoded as Base64 + 'data' => base64_encode(file_get_contents($argv[1])), + ], + ], + ], + ], + ], +]; + +$curl = curl_init(); +$responseHeaders = []; + +curl_setopt_array($curl, [ + CURLOPT_USERPWD => "$username:$password", + CURLOPT_URL => "$baseUrl/content/objects", + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($data), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/vnd.ez.api.ContentCreate+json', + 'Accept: application/vnd.ez.api.ContentInfo+json', + ], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADERFUNCTION => static function ($curl, $header) { + global $responseHeaders; + $responseHeaders[] = $header; + + return strlen($header); + }, +]); + +$response = curl_exec($curl); + +if ($error = curl_error($curl)) { + echo "cURL error: $error\n"; + curl_close($curl); + exit(3); +} + +if (201 !== $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) { + $response = json_decode($response, true); + if (is_array($response) && array_key_exists('ErrorMessage', $response)) { + echo "Server error: {$response['ErrorMessage']['errorCode']} {$response['ErrorMessage']['errorMessage']}\n"; + echo "\t{$response['ErrorMessage']['errorDescription']}\n"; + curl_close($curl); + exit(4); + } + $error = $responseHeaders[0] ?? $responseCode; + echo "Server error: $error\n"; + curl_close($curl); + exit(5); +} + +$response = json_decode($response, true); + +if (!(array_key_exists('Content', $response) && array_key_exists('_id', $response['Content']))) { + echo "Response error: Unexpected response structure\n"; + curl_close($curl); + exit(6); +} + +$contentId = $response['Content']['_id']; + +curl_reset($curl); +$responseHeaders = []; + +curl_setopt_array($curl, [ + CURLOPT_USERPWD => "$username:$password", + CURLOPT_URL => "$baseUrl/content/objects/$contentId/versions/1", + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => [ + 'X-HTTP-Method-Override: PUBLISH', + 'Accept: application/json', + ], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADERFUNCTION => static function ($curl, $header) { + global $responseHeaders; + $responseHeaders[] = $header; + + return strlen($header); + }, +]); + +$response = curl_exec($curl); + +if ($error = curl_error($curl)) { + echo "cURL error: $error\n"; + curl_close($curl); + exit(7); +} + +if (204 !== $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) { + $response = json_decode($response, true); + if (is_array($response) && array_key_exists('ErrorMessage', $response)) { + echo "Server error: {$response['ErrorMessage']['errorCode']} {$response['ErrorMessage']['errorMessage']}\n"; + echo "\t{$response['ErrorMessage']['errorDescription']}\n"; + curl_close($curl); + exit(8); + } + $error = $responseHeaders[0] ?? $responseCode; + echo "Server error: $error\n"; + exit(9); +} + +echo "Success: Image Content item created with ID $contentId and published\n"; + +curl_close($curl); +exit(0); diff --git a/code_samples/api/rest_api/create_image.xml.php b/code_samples/api/rest_api/create_image.xml.php new file mode 100644 index 0000000000..1f2975b38e --- /dev/null +++ b/code_samples/api/rest_api/create_image.xml.php @@ -0,0 +1,159 @@ + []\n"; + exit(1); +} + +if (!is_file($argv[1])) { + echo "{$argv[1]} doesn't exist or is not a file.\n"; + exit(2); +} + +// URL to Ibexa DXP installation and its REST API +$host = 'api.example.com'; +$scheme = 'https'; +$api = '/api/ezp/v2'; +$baseUrl = "{$scheme}://{$host}{$api}"; + +// User credentials +$username = 'admin'; +$password = 'publish'; + +// Targets +$contentTypeId = 5; // "Image" +$parentLocationPath = '1/43/51'; // "Media > Images" +$sectionId = 3; // "Media" + +$fileName = basename($argv[1]); +$fileSize = filesize($argv[1]); +$fileContent = base64_encode(file_get_contents($argv[1])); +$name = $argv[2] ?? $fileName; + +// Request payload +$data = << + + + eng-GB + + + PATH + ASC + +
+ + + name + $name + + + caption + +

$name

]]> + + + + image + + $fileName + $fileSize + + + + +
+XML; + +$curl = curl_init(); +$responseHeaders = []; +$doc = new DOMDocument(); + +curl_setopt_array($curl, [ + CURLOPT_USERPWD => "$username:$password", + CURLOPT_URL => "$baseUrl/content/objects", + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $data, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/vnd.ez.api.ContentCreate+xml', + 'Accept: application/vnd.ez.api.ContentInfo+xml', + ], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADERFUNCTION => static function ($curl, $header) { + global $responseHeaders; + $responseHeaders[] = $header; + + return strlen($header); + }, +]); + +$response = curl_exec($curl); + +if ($error = curl_error($curl)) { + echo "cURL error: $error\n"; + curl_close($curl); + exit(3); +} + +if (201 !== $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) { + if (!empty($response) && $doc->loadXML($response) && 'ErrorMessage' === $doc->firstChild->nodeName) { + echo "Server error: {$doc->getElementsByTagName('errorCode')->item(0)->nodeValue} {$doc->getElementsByTagName('errorMessage')->item(0)->nodeValue}\n"; + echo "\t{$doc->getElementsByTagName('errorDescription')->item(0)->nodeValue}\n"; + curl_close($curl); + exit(4); + } + $error = $responseHeaders[0] ?? $responseCode; + echo "Server error: $error\n"; + curl_close($curl); + exit(5); +} + +$doc->loadXML($response); + +if ('Content' !== $doc->firstChild->nodeName || !$doc->firstChild->hasAttribute('id')) { + echo "Response error: Unexpected response structure\n"; + curl_close($curl); + exit(6); +} + +$contentId = $doc->firstChild->getAttribute('id'); + +curl_reset($curl); +$responseHeaders = []; + +curl_setopt_array($curl, [ + CURLOPT_USERPWD => "$username:$password", + CURLOPT_URL => "$baseUrl/content/objects/$contentId/versions/1", + CURLOPT_POST => true, + CURLOPT_HTTPHEADER => [ + 'X-HTTP-Method-Override: PUBLISH', + 'Accept: application/json', + ], + CURLOPT_RETURNTRANSFER => true, +]); + +$response = curl_exec($curl); + +if ($error = curl_error($curl)) { + echo "cURL error: $error\n"; + curl_close($curl); + exit(7); +} + +if (204 !== $responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE)) { + if (!empty($response) && $doc->loadXML($response) && 'ErrorMessage' === $doc->firstChild->nodeName) { + echo "Server error: {$doc->getElementsByTagName('errorCode')->item(0)->nodeValue} {{$doc->getElementsByTagName('errorMessage')->item(0)->nodeValue}\n"; + echo "\t{$doc->getElementsByTagName('errorDescription')->item(0)->nodeValue}\n"; + curl_close($curl); + exit(8); + } + $error = $responseHeaders[0] ?? $responseCode; + echo "Server error: $error\n"; + exit(9); +} + +echo "Success: Image Content item created with ID $contentId and published\n"; + +curl_close($curl); +exit(0); diff --git a/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php new file mode 100644 index 0000000000..dd520717f6 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/Controller/DefaultController.php @@ -0,0 +1,25 @@ +getMethod()) { + return $this->inputDispatcher->parse( + new Message( + ['Content-Type' => $request->headers->get('Content-Type')], + $request->getContent() + ) + ); + } + + return new Greeting(); + } +} diff --git a/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php b/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php new file mode 100644 index 0000000000..66c51c5083 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php @@ -0,0 +1,20 @@ +visitors = []; + foreach ($visitors as $type => $visitor) { + $this->visitors[$type] = $visitor; + } + $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; + } + + public function setOutputVisitor(Visitor $outputVisitor) + { + $this->outputVisitor = $outputVisitor; + $this->valueObjectVisitorDispatcher->setOutputVisitor($outputVisitor); + } + + public function setOutputGenerator(Generator $outputGenerator) + { + $this->outputGenerator = $outputGenerator; + $this->valueObjectVisitorDispatcher->setOutputGenerator($outputGenerator); + } + + public function visit($data) + { + $className = get_class($data); + if (isset($this->visitors[$className])) { + return $this->visitors[$className]->visit($this->outputVisitor, $this->outputGenerator, $data); + } + + return $this->valueObjectVisitorDispatcher->visit($data); + } +} diff --git a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php new file mode 100644 index 0000000000..c192c291fc --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php @@ -0,0 +1,21 @@ +setHeader('Content-Type', $generator->getMediaType('Greeting')); + $generator->startObjectElement('Greeting'); + $generator->attribute('href', $this->router->generate('app.rest.greeting')); + $generator->valueElement('Salutation', $data->salutation); + $generator->valueElement('Recipient', $data->recipient); + $generator->valueElement('Sentence', "{$data->salutation} {$data->recipient}"); + $generator->endObjectElement('Greeting'); + } +} diff --git a/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php new file mode 100644 index 0000000000..9a29cd79e7 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php @@ -0,0 +1,45 @@ +urlAliasService = $urlAliasService; + } + + public function visit(Visitor $visitor, Generator $generator, $data) + { + // Not using $generator->startObjectElement to not have the XML Generator adding its own media-type attribute with the default vendor + $generator->startHashElement('Location'); + $generator->attribute( + 'media-type', + 'application/app.api.Location+' . strtolower((new \ReflectionClass($generator))->getShortName()) + ); + $generator->attribute( + 'href', + $this->router->generate( + 'ezpublish_rest_loadLocation', + ['locationPath' => trim($data->location->pathString, '/')] + ) + ); + parent::visit($visitor, $generator, $data); + $visitor->visitValueObject(new URLAliasRefList(array_merge( + $this->urlAliasService->listLocationAliases($data->location, false), + $this->urlAliasService->listLocationAliases($data->location, true) + ), $this->router->generate( + 'ezpublish_rest_listLocationURLAliases', + ['locationPath' => trim($data->location->pathString, '/')] + ))); + $generator->endHashElement('Location'); + } +} diff --git a/code_samples/api/rest_api/src/Rest/Values/Greeting.php b/code_samples/api/rest_api/src/Rest/Values/Greeting.php new file mode 100644 index 0000000000..81a126b786 --- /dev/null +++ b/code_samples/api/rest_api/src/Rest/Values/Greeting.php @@ -0,0 +1,16 @@ +salutation = $salutation; + $this->recipient = $recipient; + } +} diff --git a/code_samples/back_office/calendar/config/custom_services.yaml b/code_samples/back_office/calendar/config/custom_services.yaml new file mode 100644 index 0000000000..e5f5ef1559 --- /dev/null +++ b/code_samples/back_office/calendar/config/custom_services.yaml @@ -0,0 +1,16 @@ +services: + App\Calendar\Holidays\EventType: + arguments: + $actions: [ ] + tags: + - { name: ezplatform.calendar.event_type } + + App\Calendar\Holidays\EventSourceFactory: + arguments: + $eventType: '@App\Calendar\Holidays\EventType' + + App\Calendar\Holidays\EventSource: + class: EzSystems\EzPlatformCalendar\Calendar\EventSource\InMemoryEventSource + factory: [ '@App\Calendar\Holidays\EventSourceFactory','createEventSource' ] + tags: + - { name: ezplatform.calendar.event_source } diff --git a/code_samples/back_office/calendar/config/packages/calendar.yaml b/code_samples/back_office/calendar/config/packages/calendar.yaml new file mode 100644 index 0000000000..3dfdb71804 --- /dev/null +++ b/code_samples/back_office/calendar/config/packages/calendar.yaml @@ -0,0 +1,10 @@ +ezplatform: + system: + admin_group: + calendar: + event_types: + future_publication: + color: '#47BEDB' + actions: + reschedule: + icon: '/bundles/ibexaplatformicons/img/all-icons.svg#notice' diff --git a/code_samples/back_office/calendar/src/Calendar/Holidays/Event.php b/code_samples/back_office/calendar/src/Calendar/Holidays/Event.php new file mode 100644 index 0000000000..fe0da268ec --- /dev/null +++ b/code_samples/back_office/calendar/src/Calendar/Holidays/Event.php @@ -0,0 +1,14 @@ +eventType = $eventType; + } + + public function createEventSource(): EventSourceInterface + { + $eventCollectionArray = []; + $eventCollectionArray[] = $this->createEvent('April Fools', new DateTime('2022-04-01')); + + $items = json_decode(file_get_contents(__DIR__ . \DIRECTORY_SEPARATOR . 'holidays.json'), true); + foreach ($items as $item) { + $eventCollectionArray[] = $this->createEvent($item['name'], new DateTime($item['date'])); + } + + $collection = new EventCollection($eventCollectionArray); + + return new InMemoryEventSource($collection); + } + + private function createEvent(string $id, DateTimeInterface $dateTime): Event + { + return new Event($id, $dateTime, $this->eventType); + } +} diff --git a/code_samples/back_office/calendar/src/Calendar/Holidays/EventType.php b/code_samples/back_office/calendar/src/Calendar/Holidays/EventType.php new file mode 100644 index 0000000000..a37dfe0792 --- /dev/null +++ b/code_samples/back_office/calendar/src/Calendar/Holidays/EventType.php @@ -0,0 +1,39 @@ +actions = new EventActionCollection($actions); + } + + public function getTypeIdentifier(): string + { + return self::EVENT_TYPE_IDENTIFIER; + } + + public function getTypeLabel(): string + { + return 'Holidays'; + } + + public function getEventName(Event $event): string + { + return $event->getId(); + } + + public function getActions(): EventActionCollection + { + return $this->actions; + } +} diff --git a/code_samples/back_office/calendar/src/Calendar/Holidays/holidays.json b/code_samples/back_office/calendar/src/Calendar/Holidays/holidays.json new file mode 100644 index 0000000000..c82cd52518 --- /dev/null +++ b/code_samples/back_office/calendar/src/Calendar/Holidays/holidays.json @@ -0,0 +1,10 @@ +[ + { + "date": "2022-01-01", + "name": "New Year Day" + }, + { + "date": "2021-12-25", + "name": "Christmas Day" + } +] diff --git a/code_samples/back_office/dashboard/article_tab/config/custom_services.yaml b/code_samples/back_office/dashboard/article_tab/config/custom_services.yaml new file mode 100644 index 0000000000..95ea317b8c --- /dev/null +++ b/code_samples/back_office/dashboard/article_tab/config/custom_services.yaml @@ -0,0 +1,7 @@ +services: + App\Tab\Dashboard\Everyone\EveryoneArticleTab: + autowire: true + autoconfigure: true + public: false + tags: + - { name: ezplatform.tab, group: dashboard-everyone } diff --git a/code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php b/code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php new file mode 100644 index 0000000000..73435dbad3 --- /dev/null +++ b/code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php @@ -0,0 +1,75 @@ +pagerContentToDataMapper = $pagerContentToDataMapper; + $this->searchService = $searchService; + } + + public function getIdentifier(): string + { + return 'everyone-article'; + } + + public function getName(): string + { + return 'Articles'; + } + + public function getOrder(): int + { + return 300; + } + + public function renderView(array $parameters): string + { + $page = 1; + $limit = 10; + + $query = new LocationQuery(); + + $query->sortClauses = [new SortClause\DateModified(LocationQuery::SORT_DESC)]; + $query->query = new Criterion\LogicalAnd([ + new Criterion\ContentTypeIdentifier('article'), + ]); + + $pager = new Pagerfanta( + new ContentSearchAdapter( + $query, + $this->searchService + ) + ); + $pager->setMaxPerPage($limit); + $pager->setCurrentPage($page); + + return $this->twig->render('@ezdesign/ui/dashboard/tab/all_content.html.twig', [ + 'data' => $this->pagerContentToDataMapper->map($pager), + ]); + } +} diff --git a/code_samples/back_office/image_editor/assets/random_dot/random-dot-stem.js b/code_samples/back_office/image_editor/assets/random_dot/random-dot-stem.js new file mode 100644 index 0000000000..6c7ce3795a --- /dev/null +++ b/code_samples/back_office/image_editor/assets/random_dot/random-dot-stem.js @@ -0,0 +1,31 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; + +const IDENTIFIER = 'dot'; + +const Dot = () => { + return ( +
+ +
+ ); +}; + +Dot.propTypes = {}; + +Dot.defaultProps = {}; + +export default Dot; + +window.eZ.addConfig( + 'imageEditor.actions.dot', // The ID ("dot") must match the one from the configuration yaml file + { + label: 'Dot', + component: Dot, + icon: window.eZ.helpers.icon.getIconPath('radio-button'), // Path to an icon that will be displayed in the UI + identifier: IDENTIFIER, // The identifier must match the one from the configuration yaml file + }, + true +); diff --git a/code_samples/back_office/image_editor/assets/random_dot/random-dot.js b/code_samples/back_office/image_editor/assets/random_dot/random-dot.js new file mode 100644 index 0000000000..cb6ac9ce0a --- /dev/null +++ b/code_samples/back_office/image_editor/assets/random_dot/random-dot.js @@ -0,0 +1,67 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; + +import { + CanvasContext, + ImageHistoryContext, + AdditionalDataContext, +} from '../../vendor/ibexa/image-editor/src/bundle/ui-dev/src/modules/image-editor/image.editor.modules'; + +const IDENTIFIER = 'dot'; + +const Dot = () => { + const [canvas, setCanvas] = useContext(CanvasContext); + const [imageHistory, dispatchImageHistoryAction] = useContext(ImageHistoryContext); + const [additionalData, setAdditionalData] = useContext(AdditionalDataContext); + const saveInHistory = () => { + const newImage = new Image(); + + newImage.onload = () => { + dispatchImageHistoryAction({ type: 'ADD_TO_HISTORY', image: newImage, additionalData }); + }; + + newImage.src = canvas.current.toDataURL(); + }; + const drawDot = () => { + const ctx = canvas.current.getContext('2d'); + const positionX = Math.random() * canvas.current.width; + const positionY = Math.random() * canvas.current.height; + + ctx.save(); + + ctx.fillStyle = '#000000'; + + ctx.beginPath(); + ctx.arc(positionX, positionY, 20, 0, Math.PI * 2, true); + ctx.fill(); + + ctx.restore(); + + saveInHistory(); + }; + + return ( +
+ +
+ ); +}; + +Dot.propTypes = {}; + +Dot.defaultProps = {}; + +export default Dot; + +window.eZ.addConfig( + 'imageEditor.actions.dot', + { + label: 'Dot', + component: Dot, + icon: window.eZ.helpers.icon.getIconPath('radio-button'), + identifier: IDENTIFIER, + }, + true +); diff --git a/code_samples/back_office/image_editor/config/packages/image_editor.yaml b/code_samples/back_office/image_editor/config/packages/image_editor.yaml new file mode 100644 index 0000000000..62244752e1 --- /dev/null +++ b/code_samples/back_office/image_editor/config/packages/image_editor.yaml @@ -0,0 +1,40 @@ +ezplatform: + system: + my_site: + image_editor: + action_groups: + default: + id: default + label: Default + actions: + crop: + id: crop + priority: 1 + visible: true + buttons: + 1-1: + label: 1:1 + ratio: + x: 1 + y: 1 + 3-4: + label: 3:4 + ratio: + x: 3 + y: 4 + 4-3: + label: 4:3 + ratio: + x: 4 + y: 3 + 16-9: + label: 16:9 + ratio: + x: 16 + y: 9 + custom: + label: Custom + dot: + id: dot + priority: 50 + image_quality: 0.8 diff --git a/code_samples/back_office/image_editor/config/webpack.config.js b/code_samples/back_office/image_editor/config/webpack.config.js new file mode 100644 index 0000000000..f9ce1604f0 --- /dev/null +++ b/code_samples/back_office/image_editor/config/webpack.config.js @@ -0,0 +1,50 @@ +const Encore = require('@symfony/webpack-encore'); +const path = require('path'); +const getEzConfig = require('./ez.webpack.config.js'); +const eZConfigManager = require('./ez.webpack.config.manager.js'); +const eZConfig = getEzConfig(Encore); +const customConfigs = require('./ez.webpack.custom.configs.js'); + +Encore.reset(); +Encore + .setOutputPath('public/build/') + .setPublicPath('/build') + .enableStimulusBridge('./assets/controllers.json') + .enableSassLoader() + .enableReactPreset() + .enableSingleRuntimeChunk() + .copyFiles({ + from: './assets/images', + to: 'images/[path][name].[ext]', + pattern: /\.(png|svg)$/ + }) + .configureBabel((config) => { + config.plugins.push('@babel/plugin-proposal-class-properties'); + }) + + // enables @babel/preset-env polyfills + .configureBabelPresetEnv((config) => { + config.useBuiltIns = 'usage'; + config.corejs = 3; + }) +; + +// Welcome page stylesheets +Encore.addEntry('welcome_page', [ + path.resolve(__dirname, './assets/scss/welcome-page.scss'), +]); + +Encore.addEntry('app', './assets/app.js'); + +// Image Editor Dot Action +eZConfigManager.add({ + eZConfig, + entryName: 'ezplatform-admin-ui-layout-js', + newItems: [ path.resolve(__dirname, './assets/random_dot/random-dot.js'), ], +}); + +const projectConfig = Encore.getWebpackConfig(); +module.exports = [ eZConfig, ...customConfigs, projectConfig ]; + +// uncomment this line if you've commented-out the above lines +// module.exports = [ eZConfig, ...customConfigs ]; diff --git a/code_samples/back_office/image_editor/src/AdditionalInformation.php b/code_samples/back_office/image_editor/src/AdditionalInformation.php new file mode 100644 index 0000000000..158a805c4a --- /dev/null +++ b/code_samples/back_office/image_editor/src/AdditionalInformation.php @@ -0,0 +1,17 @@ + [ + 'width' => '100', + 'height' => '200', + 'alternativeText' => 'test', + 'mime' => 'image/png', + 'id' => 1, + 'fileName' => 'image.png', + 'additionalData' => [ + 'focalPointX' => 50, + 'focalPointY' => 100, + 'author' => 'John Smith', + ], + ], + ]), diff --git a/code_samples/back_office/images/config/default_settings.yaml b/code_samples/back_office/images/config/default_settings.yaml new file mode 100644 index 0000000000..c084dbe5ea --- /dev/null +++ b/code_samples/back_office/images/config/default_settings.yaml @@ -0,0 +1,3 @@ +parameters: + ezplatform.content_view.image_embed_content_types_identifiers: [image, photo, banner] + ezplatform.default_view_templates.content.embed_image: content/view/embed/image.html.twig diff --git a/code_samples/back_office/images/config/packages/images_basic.yaml b/code_samples/back_office/images/config/packages/images_basic.yaml new file mode 100644 index 0000000000..fb1342ffe7 --- /dev/null +++ b/code_samples/back_office/images/config/packages/images_basic.yaml @@ -0,0 +1,9 @@ +ezplatform: + image_placeholder: + default: + provider: generic + options: + fontpath: '%kernel.project_dir%/src/Resources/font/font.ttf' + background: '#EEEEEE' + foreground: '#FF0000' + text: 'MISSING IMAGE %%width%%x%%height%%' diff --git a/code_samples/back_office/images/config/packages/images_live.yaml b/code_samples/back_office/images/config/packages/images_live.yaml new file mode 100644 index 0000000000..b7cf28f73d --- /dev/null +++ b/code_samples/back_office/images/config/packages/images_live.yaml @@ -0,0 +1,6 @@ +ezplatform: + image_placeholder: + default: + provider: remote + options: + url_pattern: 'http://example.com/var/site/storage/%%id%%' diff --git a/code_samples/back_office/images/config/packages/images_remote.yaml b/code_samples/back_office/images/config/packages/images_remote.yaml new file mode 100644 index 0000000000..c4388ef66a --- /dev/null +++ b/code_samples/back_office/images/config/packages/images_remote.yaml @@ -0,0 +1,6 @@ +ezplatform: + image_placeholder: + default: + provider: remote + options: + url_pattern: 'https://placekitten.com/%%width%%/%%height%%' diff --git a/code_samples/back_office/images/config/routes.yaml b/code_samples/back_office/images/config/routes.yaml new file mode 100644 index 0000000000..c499401933 --- /dev/null +++ b/code_samples/back_office/images/config/routes.yaml @@ -0,0 +1,3 @@ +app.svg_download: + path: /asset/download/{contentId}/{fieldIdentifier}/{filename} + defaults: { _controller: app.controller.content.svg:downloadSvgAction } diff --git a/code_samples/back_office/images/config/services.yaml b/code_samples/back_office/images/config/services.yaml new file mode 100644 index 0000000000..a0a0d3f3b7 --- /dev/null +++ b/code_samples/back_office/images/config/services.yaml @@ -0,0 +1,8 @@ +services: + # ... + App\Controller\SvgController: + public: true + arguments: + - '@ezpublish.api.service.content' + - '@ezpublish.fieldType.ezbinaryfile.io_service' + - '@ezpublish.translation_helper' diff --git a/code_samples/back_office/images/src/PlaceholderProvider.php b/code_samples/back_office/images/src/PlaceholderProvider.php new file mode 100644 index 0000000000..1275066d8f --- /dev/null +++ b/code_samples/back_office/images/src/PlaceholderProvider.php @@ -0,0 +1,18 @@ +contentService = $contentService; + $this->ioService = $ioService; + $this->translationHelper = $translationHelper; + } + + /** + * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + */ + public function downloadSvgAction( + int $contentId, + string $fieldIdentifier, + string $filename, + Request $request + ): Response { + $version = null; + + if ($request->query->has('version')) { + $version = $request->query->get('version'); + } + + $content = $this->contentService->loadContent($contentId, null, $version); + $language = $request->query->has('inLanguage') ? $request->query->get('inLanguage') : null; + $field = $this->translationHelper->getTranslatedField($content, $fieldIdentifier, $language); + + if (!$field instanceof Field) { + throw new InvalidArgumentException( + sprintf( + "%s field not present in content %d '%s'", + $fieldIdentifier, + $content->contentInfo->id, + $content->contentInfo->name + ) + ); + } + + $binaryFile = $this->ioService->loadBinaryFile($field->value->id); + $response = new Response($this->ioService->getFileContents($binaryFile)); + $disposition = $response->headers->makeDisposition( + ResponseHeaderBag::DISPOSITION_INLINE, + $filename + ); + + $response->headers->set('Content-Disposition', $disposition); + $response->headers->set('Content-Type', self::CONTENT_TYPE_HEADER); + + return $response; + } +} diff --git a/code_samples/back_office/images/src/SvgExtension.php b/code_samples/back_office/images/src/SvgExtension.php new file mode 100644 index 0000000000..604f2b9078 --- /dev/null +++ b/code_samples/back_office/images/src/SvgExtension.php @@ -0,0 +1,45 @@ +router = $router; + } + + /** + * @return \Twig\TwigFunction[] + */ + public function getFunctions() + { + return [ + new TwigFunction('ibexa_svg_link', [ + $this, + 'generateLink', + ]), + ]; + } + + public function generateLink(int $contentId, string $fieldIdentifier, string $filename): string + { + return $this->router->generate('app.svg_download', [ + 'contentId' => $contentId, + 'fieldIdentifier' => $fieldIdentifier, + 'filename' => $filename, + ]); + } +} diff --git a/code_samples/back_office/images/templates/svg_helper.html.twig b/code_samples/back_office/images/templates/svg_helper.html.twig new file mode 100644 index 0000000000..552c6e0787 --- /dev/null +++ b/code_samples/back_office/images/templates/svg_helper.html.twig @@ -0,0 +1,3 @@ +{% set svgField = ez_field(content, 'file') %} + + diff --git a/code_samples/back_office/menu/content_list/config/routes.yaml b/code_samples/back_office/menu/content_list/config/routes.yaml new file mode 100644 index 0000000000..db49568918 --- /dev/null +++ b/code_samples/back_office/menu/content_list/config/routes.yaml @@ -0,0 +1,9 @@ +#index: +# path: / +# controller: App\Controller\DefaultController::index + +all_content_list.list: + path: /all_content_list/{page} + defaults: + page: 1 + _controller: App\Controller\AllContentListController::listAction diff --git a/code_samples/back_office/menu/content_list/src/Controller/AllContentListController.php b/code_samples/back_office/menu/content_list/src/Controller/AllContentListController.php new file mode 100644 index 0000000000..76e1547ffa --- /dev/null +++ b/code_samples/back_office/menu/content_list/src/Controller/AllContentListController.php @@ -0,0 +1,46 @@ +searchService = $searchService; + $this->contentTypeService = $contentTypeService; + } + + public function listAction($page = 1) + { + $query = new LocationQuery(); + + $criterions = [ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + ]; + + $query->query = new Criterion\LogicalAnd($criterions); + + $paginator = new Pagerfanta( + new LocationSearchAdapter($query, $this->searchService) + ); + $paginator->setMaxPerPage(8); + $paginator->setCurrentPage($page); + + return $this->render('list/all_content_list.html.twig', [ + 'totalCount' => $paginator->getNbResults(), + 'articles' => $paginator, + ]); + } +} diff --git a/code_samples/back_office/menu/content_list/src/EventSubscriber/MyMenuSubscriber.php b/code_samples/back_office/menu/content_list/src/EventSubscriber/MyMenuSubscriber.php new file mode 100644 index 0000000000..d3bc43341d --- /dev/null +++ b/code_samples/back_office/menu/content_list/src/EventSubscriber/MyMenuSubscriber.php @@ -0,0 +1,30 @@ + ['onMenuConfigure', 0], + ]; + } + + public function onMenuConfigure(ConfigureMenuEvent $event) + { + $menu = $event->getMenu(); + + $menu[MainMenuBuilder::ITEM_CONTENT]->addChild( + 'all_content_list', + [ + 'label' => 'Content List', + 'route' => 'all_content_list.list', + ] + ); + } +} diff --git a/code_samples/back_office/menu/content_list/templates/list/all_content_list.html.twig b/code_samples/back_office/menu/content_list/templates/list/all_content_list.html.twig new file mode 100644 index 0000000000..b399a633ad --- /dev/null +++ b/code_samples/back_office/menu/content_list/templates/list/all_content_list.html.twig @@ -0,0 +1,45 @@ +{% extends '@ezdesign/ui/layout.html.twig' %} + +{% block title %}{{ 'Content List'|trans }}{% endblock %} + +{%- block breadcrumbs -%} + {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ + { value: 'url.list'|trans|desc('Content List') } + ]} %} +{%- endblock -%} + +{%- block page_title -%} + {% include '@ezdesign/ui/page_title.html.twig' with { + title: 'url.list'|trans|desc('Content List'), + icon_name: 'article' + } %} +{%- endblock -%} + +{%- block content -%} +
+
+
{{ "Content List"|trans }}
+
+ + + + + + + + + + + {% for article in articles %} + + + + + + + {% endfor %} + +
{{ 'Content name'|trans }}{{ 'Content Type'|trans }}{{ 'Modified'|trans }}{{ 'Published'|trans }}
{{ ez_content_name(article.contentInfo) }}{{ article.contentInfo.contentTypeId }}{{ article.contentInfo.modificationDate|ez_full_datetime }}{{ article.contentInfo.publishedDate|ez_full_datetime }}
+ {{ pagerfanta(articles, 'ez') }} +
+{%- endblock -%} diff --git a/code_samples/back_office/menu/content_list_edit_button/src/Controller/AllContentListController.php b/code_samples/back_office/menu/content_list_edit_button/src/Controller/AllContentListController.php new file mode 100644 index 0000000000..23db1be4c3 --- /dev/null +++ b/code_samples/back_office/menu/content_list_edit_button/src/Controller/AllContentListController.php @@ -0,0 +1,52 @@ +searchService = $searchService; + $this->contentTypeService = $contentTypeService; + $this->formFactory = $formFactory; + } + + public function listAction($page = 1) + { + $query = new LocationQuery(); + $criterions = [ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + ]; + $query->query = new Criterion\LogicalAnd($criterions); + $paginator = new Pagerfanta( + new LocationSearchAdapter($query, $this->searchService) + ); + $paginator->setMaxPerPage(8); + $paginator->setCurrentPage($page); + $editForm = $this->formFactory->contentEdit(); + + return $this->render('list/all_content_list.html.twig', [ + 'totalCount' => $paginator->getNbResults(), + 'articles' => $paginator, + 'form_edit' => $editForm->createView(), + ]); + } +} diff --git a/code_samples/back_office/menu/content_list_edit_button/templates/list/all_content_list.html.twig b/code_samples/back_office/menu/content_list_edit_button/templates/list/all_content_list.html.twig new file mode 100644 index 0000000000..70c2192a1e --- /dev/null +++ b/code_samples/back_office/menu/content_list_edit_button/templates/list/all_content_list.html.twig @@ -0,0 +1,69 @@ +{% extends '@ezdesign/ui/layout.html.twig' %} + +{% block title %}{{ 'Content List'|trans }}{% endblock %} + +{%- block breadcrumbs -%} + {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ + { value: 'url.list'|trans|desc('Content List') } + ]} %} +{%- endblock -%} + +{%- block page_title -%} + {% include '@ezdesign/ui/page_title.html.twig' with { + title: 'url.list'|trans|desc('Content List'), + icon_name: 'article' + } %} +{%- endblock -%} + +{%- block content -%} +
+
+
{{ "Content List"|trans }}
+
+ + + + + + + + + + + + {% for article in articles %} + + + + + + + + {% endfor %} + +
{{ 'Content name'|trans }}{{ 'Content Type'|trans }}{{ 'Modified'|trans }}{{ 'Published'|trans }}{{ 'Edit'|trans }}
{{ ez_content_name(article.contentInfo) }}{{ article.contentInfo.contentTypeId }}{{ article.contentInfo.modificationDate|ez_full_datetime }}{{ article.contentInfo.publishedDate|ez_full_datetime }} + +
+ {{ pagerfanta(articles, 'ez') }} +
+ {{ form_start(form_edit, { + 'action': path('ezplatform.content.edit'), + 'attr': + { 'class': 'ez-edit-content-form'} + }) }} + {{ form_widget(form_edit.language, {'attr': {'hidden': 'hidden', 'class': 'language-input'}}) }} + {{ form_end(form_edit) }} + {% include '@ezdesign/content/modal/version_conflict.html.twig' %} +{%- endblock -%} + +{% block javascripts %} + {{ encore_entry_script_tags('ezplatform-admin-ui-dashboard-js', null, 'ezplatform') }} +{%- endblock -%} diff --git a/code_samples/back_office/menu/filtered_content_list/config/routes.yaml b/code_samples/back_office/menu/filtered_content_list/config/routes.yaml new file mode 100644 index 0000000000..3e30d08409 --- /dev/null +++ b/code_samples/back_office/menu/filtered_content_list/config/routes.yaml @@ -0,0 +1,10 @@ +#index: +# path: / +# controller: App\Controller\DefaultController::index + +all_content_list.list: + path: /all_content_list/{page}/{contentTypeIdentifier} + defaults: + page: 1 + contentTypeIdentifier: false + _controller: App\Controller\AllContentListController::listAction diff --git a/code_samples/back_office/menu/filtered_content_list/src/Controller/AllContentListController.php b/code_samples/back_office/menu/filtered_content_list/src/Controller/AllContentListController.php new file mode 100644 index 0000000000..97de737744 --- /dev/null +++ b/code_samples/back_office/menu/filtered_content_list/src/Controller/AllContentListController.php @@ -0,0 +1,57 @@ +searchService = $searchService; + $this->contentTypeService = $contentTypeService; + } + + public function listAction($contentTypeIdentifier = false, $page = 1) + { + $query = new LocationQuery(); + + $criterions = [ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + ]; + + if ($contentTypeIdentifier) { + $criterions[] = new Criterion\ContentTypeIdentifier($contentTypeIdentifier); + } + + $query->query = new Criterion\LogicalAnd($criterions); + + $paginator = new Pagerfanta( + new LocationSearchAdapter($query, $this->searchService) + ); + $paginator->setMaxPerPage(8); + $paginator->setCurrentPage($page); + + $contentTypes = []; + $contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); + foreach ($contentTypeGroups as $group) { + $contentTypes[$group->identifier] = $this->contentTypeService->loadContentTypes($group); + } + + return $this->render('list/all_content_list.html.twig', [ + 'totalCount' => $paginator->getNbResults(), + 'articles' => $paginator, + 'contentTypes' => $contentTypes, + ]); + } +} diff --git a/code_samples/back_office/menu/filtered_content_list/templates/list/all_content_list.html.twig b/code_samples/back_office/menu/filtered_content_list/templates/list/all_content_list.html.twig new file mode 100644 index 0000000000..1e81d57355 --- /dev/null +++ b/code_samples/back_office/menu/filtered_content_list/templates/list/all_content_list.html.twig @@ -0,0 +1,60 @@ +{% extends '@ezdesign/ui/layout.html.twig' %} + +{% block title %}{{ 'Content List'|trans }}{% endblock %} + +{%- block breadcrumbs -%} + {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ + { value: 'url.list'|trans|desc('Content List') } + ]} %} +{%- endblock -%} + +{%- block page_title -%} + {% include '@ezdesign/ui/page_title.html.twig' with { + title: 'url.list'|trans|desc('Content List'), + icon_name: 'article' + } %} +{%- endblock -%} + +{%- block content -%} +
+
+ +
+
+
{{ "Content List"|trans }}
+
+ + + + + + + + + + + {% for article in articles %} + + + + + + + {% endfor %} + +
{{ 'Content name'|trans }}{{ 'Content Type'|trans }}{{ 'Modified'|trans }}{{ 'Published'|trans }}
{{ ez_content_name(article.contentInfo) }}{{ article.contentInfo.contentTypeId }}{{ article.contentInfo.modificationDate|ez_full_datetime }}{{ article.contentInfo.publishedDate|ez_full_datetime }}
+ {{ pagerfanta(articles, 'ez') }} +
+{%- endblock -%} diff --git a/code_samples/back_office/menu/menu_item/config/custom_routes.yaml b/code_samples/back_office/menu/menu_item/config/custom_routes.yaml new file mode 100644 index 0000000000..0b4c67890a --- /dev/null +++ b/code_samples/back_office/menu/menu_item/config/custom_routes.yaml @@ -0,0 +1,6 @@ +all_content_list.list: + path: /all_content_list/{page}/{contentTypeIdentifier} + defaults: + page: 1 + contentTypeIdentifier: false + _controller: App\Controller\AllContentListController::listAction diff --git a/code_samples/back_office/menu/menu_item/src/Controller/AllContentListController.php b/code_samples/back_office/menu/menu_item/src/Controller/AllContentListController.php new file mode 100644 index 0000000000..b7f5201631 --- /dev/null +++ b/code_samples/back_office/menu/menu_item/src/Controller/AllContentListController.php @@ -0,0 +1,63 @@ +searchService = $searchService; + $this->contentTypeService = $contentTypeService; + $this->formFactory = $formFactory; + } + + public function listAction($contentTypeIdentifier = false, $page = 1) + { + $query = new LocationQuery(); + + $criterions = [ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + ]; + + if ($contentTypeIdentifier) { + $criterions[] = new Criterion\ContentTypeIdentifier($contentTypeIdentifier); + } + + $query->query = new Criterion\LogicalAnd($criterions); + + $paginator = new Pagerfanta( + new LocationSearchAdapter($query, $this->searchService) + ); + $paginator->setMaxPerPage(8); + $paginator->setCurrentPage($page); + $editForm = $this->formFactory->contentEdit(); + + $contentTypes = []; + $contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); + foreach ($contentTypeGroups as $group) { + $contentTypes[$group->identifier] = $this->contentTypeService->loadContentTypes($group); + } + + return $this->render('list/all_content_list.html.twig', [ + 'totalCount' => $paginator->getNbResults(), + 'articles' => $paginator, + 'form_edit' => $editForm->createView(), + 'contentTypes' => $contentTypes, + ]); + } +} diff --git a/code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php b/code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php new file mode 100644 index 0000000000..c0a2231a3c --- /dev/null +++ b/code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php @@ -0,0 +1,47 @@ + ['onMainMenuConfigure', 0], + ConfigureMenuEvent::CONTENT_SIDEBAR_RIGHT => ['onContentSidebarConfigure', 0], + ]; + } + + public function onMainMenuConfigure(ConfigureMenuEvent $event) + { + $menu = $event->getMenu(); + + $menu[MainMenuBuilder::ITEM_CONTENT]->addChild( + 'all_content_list', + [ + 'label' => 'Content List', + 'route' => 'all_content_list.list', + 'attributes' => [ + 'class' => 'custom-menu-item', + ], + 'linkAttributes' => [ + 'class' => 'custom-menu-item-link', + ], + ] + ); + } + + public function onContentSidebarConfigure(ConfigureMenuEvent $event) + { + $menu = $event->getMenu(); + + $menu->removeChild('content__sidebar_right__copy_subtree'); + + $menu->getChild('content__sidebar_right__create') + ->setExtra('icon_path', '/bundles/ibexaplatformicons/img/all-icons.svg#notice'); + } +} diff --git a/code_samples/back_office/menu/menu_item/templates/list/all_content_list.html.twig b/code_samples/back_office/menu/menu_item/templates/list/all_content_list.html.twig new file mode 100644 index 0000000000..e1e9710fe7 --- /dev/null +++ b/code_samples/back_office/menu/menu_item/templates/list/all_content_list.html.twig @@ -0,0 +1,84 @@ +{% extends '@ezdesign/ui/layout.html.twig' %} + +{% block title %}{{ 'Content List'|trans }}{% endblock %} + +{%- block breadcrumbs -%} + {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ + { value: 'url.list'|trans|desc('Content List') } + ]} %} +{%- endblock -%} + +{%- block page_title -%} + {% include '@ezdesign/ui/page_title.html.twig' with { + title: 'url.list'|trans|desc('Content List'), + icon_name: 'article' + } %} +{%- endblock -%} + +{%- block content -%} +
+
+ +
+
+
{{ "Content List"|trans }}
+
+ + + + + + + + + + + + {% for article in articles %} + + + + + + + + {% endfor %} + +
{{ 'Content name'|trans }}{{ 'Content Type'|trans }}{{ 'Modified'|trans }}{{ 'Published'|trans }}{{ 'Edit'|trans }}
{{ ez_content_name(article.contentInfo) }}{{ article.contentInfo.contentTypeId }}{{ article.contentInfo.modificationDate|ez_full_datetime }}{{ article.contentInfo.publishedDate|ez_full_datetime }} + +
+ {{ pagerfanta(articles, 'ez') }} +
+ {{ form_start(form_edit, { + 'action': path('ezplatform.content.edit'), + 'attr': + { 'class': 'ez-edit-content-form'} + }) }} + {{ form_widget(form_edit.language, {'attr': {'hidden': 'hidden', 'class': 'language-input'}}) }} + {{ form_end(form_edit) }} + {% include '@ezdesign/content/modal/version_conflict.html.twig' %} +{%- endblock -%} + +{% block javascripts %} + {{ encore_entry_script_tags('ezplatform-admin-ui-dashboard-js', null, 'ezplatform') }} +{%- endblock -%} diff --git a/code_samples/back_office/online_editor/assets/js/online_editor/buttons/date.js b/code_samples/back_office/online_editor/assets/js/online_editor/buttons/date.js new file mode 100644 index 0000000000..330b4a20c9 --- /dev/null +++ b/code_samples/back_office/online_editor/assets/js/online_editor/buttons/date.js @@ -0,0 +1,41 @@ +import PropTypes from 'prop-types'; +import AlloyEditor from 'alloyeditor'; +import EzButton + from '../../../../vendor/ezsystems/ezplatform-richtext/src/bundle/Resources/public/js/OnlineEditor/buttons/base/ez-button.js'; + +export default class BtnDate extends EzButton { + static get key() { + return 'date'; + } + + insertDate(data) { + this.execCommand(data); + } + + render() { + const title = 'Date'; + + return ( + + ); + } +} + +AlloyEditor.Buttons[BtnDate.key] = AlloyEditor.BtnDate = BtnDate; +eZ.addConfig('ezAlloyEditor.BtnDate', BtnDate); + +BtnDate.propTypes = { + command: PropTypes.string, +}; + +BtnDate.defaultProps = { + command: 'InsertDate', +}; diff --git a/code_samples/back_office/online_editor/assets/js/online_editor/buttons/hr.js b/code_samples/back_office/online_editor/assets/js/online_editor/buttons/hr.js new file mode 100644 index 0000000000..74c7f0a057 --- /dev/null +++ b/code_samples/back_office/online_editor/assets/js/online_editor/buttons/hr.js @@ -0,0 +1,48 @@ +import PropTypes from 'prop-types'; +import AlloyEditor from 'alloyeditor'; +import EzButton + from '../../../../vendor/ezsystems/ezplatform-richtext/src/bundle/Resources/public/js/OnlineEditor/buttons/base/ez-button.js'; + +export default class EzBtnHr extends EzButton { + static get key() { + return 'hr'; + } + + addHr() { + this.execCommand({ + tagName: 'hr', + }); + } + + render() { + const title = "Hr"; + return ( + + ); + } +} + +AlloyEditor.Buttons[EzBtnHr.key] = AlloyEditor.EzBtnHr = EzBtnHr; + +const eZ = (window.eZ = window.eZ || {}); + +eZ.ezAlloyEditor = eZ.ezAlloyEditor || {}; +eZ.ezAlloyEditor.ezBtnHr = EzBtnHr; + +EzBtnHr.propTypes = { + command: PropTypes.string, + modifiesSelection: PropTypes.bool, +}; + +EzBtnHr.defaultProps = { + command: 'eZAddContent', + modifiesSelection: true, +}; diff --git a/code_samples/back_office/online_editor/assets/js/online_editor/plugins/date.js b/code_samples/back_office/online_editor/assets/js/online_editor/plugins/date.js new file mode 100644 index 0000000000..d75bf3364d --- /dev/null +++ b/code_samples/back_office/online_editor/assets/js/online_editor/plugins/date.js @@ -0,0 +1,16 @@ +(function (global) { + if (CKEDITOR.plugins.get('date')) { + return; + } + + const InsertDate = { + exec: function (editor) { + const now = new Date(); + editor.insertHtml( now.toString() ); + }, + }; + + global.CKEDITOR.plugins.add('date', { + init: (editor) => editor.addCommand('InsertDate', InsertDate), + }); +})(window); diff --git a/code_samples/back_office/online_editor/config/packages/custom_buttons.yaml b/code_samples/back_office/online_editor/config/packages/custom_buttons.yaml new file mode 100644 index 0000000000..8f82e1d650 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_buttons.yaml @@ -0,0 +1,16 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + toolbars: + heading: + buttons: + ezanchor: + priority: 100 + ezembedinline: + visible: false + ezadd: + buttons: + hr: + priority: 50 diff --git a/code_samples/back_office/online_editor/config/packages/custom_classes.yaml b/code_samples/back_office/online_editor/config/packages/custom_classes.yaml new file mode 100644 index 0000000000..6c90888a70 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_classes.yaml @@ -0,0 +1,11 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + classes: + paragraph: + choices: [regular, special, tip_box, warning_box] + default_value: regular + required: false + multiple: false diff --git a/code_samples/back_office/online_editor/config/packages/custom_data_attributes.yaml b/code_samples/back_office/online_editor/config/packages/custom_data_attributes.yaml new file mode 100644 index 0000000000..d969aaabd8 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_data_attributes.yaml @@ -0,0 +1,16 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + attributes: + heading: + custom-attribute: + type: boolean + default_value: false + another-attribute: + type: choice + choices: [attr1, attr2] + default_value: attr2 + required: false + multiple: true diff --git a/code_samples/back_office/online_editor/config/packages/custom_plugin.yaml b/code_samples/back_office/online_editor/config/packages/custom_plugin.yaml new file mode 100644 index 0000000000..6a5b7d5ff7 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_plugin.yaml @@ -0,0 +1,13 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + toolbars: + paragraph: + buttons: + date: + priority: 0 +ezrichtext: + alloy_editor: + extra_plugins: [date] diff --git a/code_samples/back_office/online_editor/config/packages/custom_styles.yaml b/code_samples/back_office/online_editor/config/packages/custom_styles.yaml new file mode 100644 index 0000000000..f5a04710f7 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_styles.yaml @@ -0,0 +1,14 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_styles: [highlighted_block, highlighted_word] +ezrichtext: + custom_styles: + highlighted_word: + template: '@ezdesign/field_type/ezrichtext/custom_style/highlighted_word.html.twig' + inline: true + highlighted_block: + template: '@ezdesign/field_type/ezrichtext/custom_style/highlighted_block.html.twig' + inline: false diff --git a/code_samples/back_office/online_editor/config/packages/custom_styles_highlight.yaml b/code_samples/back_office/online_editor/config/packages/custom_styles_highlight.yaml new file mode 100644 index 0000000000..00902c0445 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_styles_highlight.yaml @@ -0,0 +1,11 @@ +ezpublish: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_styles: [highlight] +ezrichtext: + custom_styles: + highlight: + template: field_type/ezrichtext/custom_style/highlight.html.twig + inline: true diff --git a/code_samples/back_office/online_editor/config/packages/custom_styles_note_box.yaml b/code_samples/back_office/online_editor/config/packages/custom_styles_note_box.yaml new file mode 100644 index 0000000000..342231aa64 --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/custom_styles_note_box.yaml @@ -0,0 +1,10 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_styles: [note_box] +ezrichtext: + custom_styles: + note_box: + template: field_type/ezrichtext/custom_style/note_box.html.twig diff --git a/code_samples/back_office/online_editor/config/packages/ezplatform_page_fieldtype.yaml b/code_samples/back_office/online_editor/config/packages/ezplatform_page_fieldtype.yaml new file mode 100644 index 0000000000..821d42fdff --- /dev/null +++ b/code_samples/back_office/online_editor/config/packages/ezplatform_page_fieldtype.yaml @@ -0,0 +1,15 @@ +ezplatform_page_fieldtype: + blocks: + my_block: + name: My Richtext Block + thumbnail: assets/images/blocks/richtext_block_icon.svg + configuration_template: blocks/my_block/config.html.twig + views: + default: + template: blocks/my_block/default.html.twig + name: My block view + priority: -255 + attributes: + content: + name: Content + type: richtext diff --git a/code_samples/back_office/online_editor/custom_tags/acronym/config/packages/custom_tags.yaml b/code_samples/back_office/online_editor/custom_tags/acronym/config/packages/custom_tags.yaml new file mode 100644 index 0000000000..470a9ce154 --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/acronym/config/packages/custom_tags.yaml @@ -0,0 +1,30 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_tags: [acronym] + toolbars: + text: + buttons: + acronym: + priority: 5 + acronym: + buttons: + ezmoveup: + priority: 40 + ezmovedown: + priority: 30 + ezcustomtagedit: + priority: 20 + ezblockremove: + priority: 10 +ezrichtext: + custom_tags: + acronym: + template: '@ezdesign/field_type/ezrichtext/custom_tags/acronym.html.twig' + icon: '/bundles/ezplatformadminui/img/ez-icons.svg#edit' + is_inline: true + attributes: + explanation: + type: string diff --git a/code_samples/back_office/online_editor/custom_tags/acronym/templates/themes/standard/field_type/ezrichtext/custom_tags/acronym.html.twig b/code_samples/back_office/online_editor/custom_tags/acronym/templates/themes/standard/field_type/ezrichtext/custom_tags/acronym.html.twig new file mode 100644 index 0000000000..555b7dc18f --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/acronym/templates/themes/standard/field_type/ezrichtext/custom_tags/acronym.html.twig @@ -0,0 +1 @@ +{{ content }} diff --git a/code_samples/back_office/online_editor/custom_tags/acronym/translations/custom_tags.en.yaml b/code_samples/back_office/online_editor/custom_tags/acronym/translations/custom_tags.en.yaml new file mode 100644 index 0000000000..e302b20424 --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/acronym/translations/custom_tags.en.yaml @@ -0,0 +1,2 @@ +ezrichtext.custom_tags.acronym.label: 'Acronym' +ezrichtext.custom_tags.acronym.attributes.explanation.label: 'Explanation' diff --git a/code_samples/back_office/online_editor/custom_tags/factbox/config/packages/custom_tags.yaml b/code_samples/back_office/online_editor/custom_tags/factbox/config/packages/custom_tags.yaml new file mode 100644 index 0000000000..c6d4601820 --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/factbox/config/packages/custom_tags.yaml @@ -0,0 +1,35 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_tags: [factbox] + toolbars: + ezadd: + buttons: + factbox: + priority: 5 + factbox: + buttons: + ezmoveup: + priority: 40 + ezmovedown: + priority: 30 + ezcustomtagedit: + priority: 20 + ezblockremove: + priority: 10 +ezrichtext: + custom_tags: + factbox: + template: '@ezdesign/field_type/ezrichtext/custom_tags/factbox.html.twig' + icon: '/bundles/ezplatformadminui/img/ez-icons.svg#information' + attributes: + name: + type: string + required: true + style: + type: choice + required: true + default_value: light + choices: [light, dark] diff --git a/code_samples/back_office/online_editor/custom_tags/factbox/templates/themes/standard/field_type/ezrichtext/custom_tags/factbox.html.twig b/code_samples/back_office/online_editor/custom_tags/factbox/templates/themes/standard/field_type/ezrichtext/custom_tags/factbox.html.twig new file mode 100644 index 0000000000..6edef7e82c --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/factbox/templates/themes/standard/field_type/ezrichtext/custom_tags/factbox.html.twig @@ -0,0 +1,6 @@ +
+

{{ params.name }}

+
+ {{ content|raw }} +
+
diff --git a/code_samples/back_office/online_editor/custom_tags/factbox/translations/custom_tags.en.yaml b/code_samples/back_office/online_editor/custom_tags/factbox/translations/custom_tags.en.yaml new file mode 100644 index 0000000000..8c503b5dcf --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/factbox/translations/custom_tags.en.yaml @@ -0,0 +1,3 @@ +ezrichtext.custom_tags.factbox.label: 'Factbox' +ezrichtext.custom_tags.factbox.attributes.name.label: 'Name' +ezrichtext.custom_tags.factbox.attributes.style.label: 'Style' diff --git a/code_samples/back_office/online_editor/custom_tags/linktag/config/packages/custom_tags.yaml b/code_samples/back_office/online_editor/custom_tags/linktag/config/packages/custom_tags.yaml new file mode 100644 index 0000000000..12ca7b1e40 --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/linktag/config/packages/custom_tags.yaml @@ -0,0 +1,41 @@ +ezplatform: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_tags: [linktag] + toolbars: + text: + buttons: + linktag: + priority: 5 + linktag: + buttons: + ezmoveup: + priority: 40 + ezmovedown: + priority: 30 + ezcustomtagedit: + priority: 20 + ezblockremove: + priority: 10 +ezrichtext: + custom_tags: + linktag: + template: '@ezdesign/field_type/ezrichtext/custom_tags/linktag.html.twig' + icon: '/bundles/ezplatformadminui/img/ez-icons.svg#link' + is_inline: true + attributes: + title: + type: string + required: false + description: + type: string + required: false + color: + type: choice + required: false + choices: [Red, Blue, Green] + url: + type: link + required: false diff --git a/code_samples/back_office/online_editor/custom_tags/linktag/templates/themes/standard/field_type/ezrichtext/custom_tags/linktag.html.twig b/code_samples/back_office/online_editor/custom_tags/linktag/templates/themes/standard/field_type/ezrichtext/custom_tags/linktag.html.twig new file mode 100644 index 0000000000..562a7d7e2c --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/linktag/templates/themes/standard/field_type/ezrichtext/custom_tags/linktag.html.twig @@ -0,0 +1,4 @@ +

Custom link

+{% for attr_name, attr_value in params %} +
{{ attr_name }}: {{ attr_value }}
+{% endfor %} diff --git a/code_samples/back_office/online_editor/custom_tags/linktag/translations/custom_tags.en.yaml b/code_samples/back_office/online_editor/custom_tags/linktag/translations/custom_tags.en.yaml new file mode 100644 index 0000000000..083f90183b --- /dev/null +++ b/code_samples/back_office/online_editor/custom_tags/linktag/translations/custom_tags.en.yaml @@ -0,0 +1,5 @@ +ezrichtext.custom_tags.linktag.label: 'Link Tag' +ezrichtext.custom_tags.linktag.attributes.title.label: 'Title' +ezrichtext.custom_tags.linktag.attributes.description.label: 'Description' +ezrichtext.custom_tags.linktag.attributes.color.label: 'Color' +ezrichtext.custom_tags.linktag.attributes.url.label: 'URL' diff --git a/code_samples/back_office/online_editor/src/event/subscriber/RichTextBlockSubscriber.php b/code_samples/back_office/online_editor/src/event/subscriber/RichTextBlockSubscriber.php new file mode 100644 index 0000000000..12c043cd03 --- /dev/null +++ b/code_samples/back_office/online_editor/src/event/subscriber/RichTextBlockSubscriber.php @@ -0,0 +1,53 @@ +domDocumentFactory = $domDocumentFactory; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array + { + return [ + BlockRenderEvents::getBlockPreRenderEventName('my_block') => 'onBlockPreRender', + ]; + } + + /** + * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event + */ + public function onBlockPreRender(PreRenderEvent $event): void + { + $renderRequest = $event->getRenderRequest(); + if (!$renderRequest instanceof TwigRenderRequest) { + return; + } + $parameters = $renderRequest->getParameters(); + $parameters['document'] = null; + $xml = $event->getBlockValue()->getAttribute('content')->getValue(); + if (!empty($xml)) { + $parameters['document'] = $this->domDocumentFactory->loadXMLString($xml); + } + $renderRequest->setParameters($parameters); + } +} diff --git a/code_samples/back_office/online_editor/translations/custom_styles.en.yaml b/code_samples/back_office/online_editor/translations/custom_styles.en.yaml new file mode 100644 index 0000000000..227d8cda2f --- /dev/null +++ b/code_samples/back_office/online_editor/translations/custom_styles.en.yaml @@ -0,0 +1,2 @@ +ezrichtext.custom_styles.highlighted_block.label: Highlighted block +ezrichtext.custom_styles.highlighted_word.label: Highlighted word diff --git a/code_samples/back_office/online_editor/translations/linktag.en.yaml b/code_samples/back_office/online_editor/translations/linktag.en.yaml new file mode 100644 index 0000000000..85cd993ef3 --- /dev/null +++ b/code_samples/back_office/online_editor/translations/linktag.en.yaml @@ -0,0 +1,5 @@ +ezrichtext.custom_tags.linktag.label: 'Link Tag' +ezrichtext.custom_tags.linktag.attributes.attrTitle.label: 'Title' +ezrichtext.custom_tags.linktag.attributes.attrDesc.label: 'Description' +ezrichtext.custom_tags.linktag.attributes.attrColor.label: 'Color' +ezrichtext.custom_tags.linktag.attributes.attrUrl.label: 'URL' diff --git a/code_samples/back_office/settings/config/custom_services.yaml b/code_samples/back_office/settings/config/custom_services.yaml new file mode 100644 index 0000000000..082ac8e965 --- /dev/null +++ b/code_samples/back_office/settings/config/custom_services.yaml @@ -0,0 +1,5 @@ +services: + App\Setting\Unit: + tags: + - { name: ezplatform.admin_ui.user_setting.value, identifier: unit, priority: 50 } + - { name: ezplatform.admin_ui.user_setting.form_mapper, identifier: unit } diff --git a/code_samples/back_office/settings/config/packages/user_settings.yaml b/code_samples/back_office/settings/config/packages/user_settings.yaml new file mode 100644 index 0000000000..58d9be501e --- /dev/null +++ b/code_samples/back_office/settings/config/packages/user_settings.yaml @@ -0,0 +1,9 @@ +ezplatform: + system: + admin_group: + user_settings_update_view: + full: + unit: + template: User/Setting/update_unit.html.twig + match: + Identifier: [ unit ] diff --git a/code_samples/back_office/settings/src/Setting/Unit.php b/code_samples/back_office/settings/src/Setting/Unit.php new file mode 100644 index 0000000000..10f2572e9c --- /dev/null +++ b/code_samples/back_office/settings/src/Setting/Unit.php @@ -0,0 +1,65 @@ + self::METRIC_OPTION, + 'Imperial' => self::IMPERIAL_OPTION, + ]; + + return $formBuilder->create( + 'value', + ChoiceType::class, + [ + 'multiple' => false, + 'required' => true, + 'label' => $this->getDescription(), + 'choices' => $choices, + ] + ); + } +} diff --git a/code_samples/back_office/settings/templates/User/Setting/update_unit.html.twig b/code_samples/back_office/settings/templates/User/Setting/update_unit.html.twig new file mode 100644 index 0000000000..de423be60c --- /dev/null +++ b/code_samples/back_office/settings/templates/User/Setting/update_unit.html.twig @@ -0,0 +1,9 @@ +{% extends '@ezdesign/account/settings/update.html.twig' %} + +{% block form %} + {{ parent() }} + +{% endblock %} diff --git a/code_samples/back_office/udw/assets/css/image.css b/code_samples/back_office/udw/assets/css/image.css new file mode 100644 index 0000000000..ba904dea6e --- /dev/null +++ b/code_samples/back_office/udw/assets/css/image.css @@ -0,0 +1,43 @@ +.c-image { + width: 300px; + height: 200px; + background: #fff; + transition: box-shadow 0.3s ease-in-out; + position: relative; + cursor: pointer; + display: flex; +} + +.c-image:before { + content: attr(data-title); + display: flex; + background: rgba(0, 0, 0, 0.75); + color: #fff; + width: 300px; + align-items: center; + justify-content: center; + font-weight: 700; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + opacity: 0; + padding: 1rem; + transition: opacity 0.3s ease-in-out; + overflow: hidden; +} + +.c-image:hover:before, +.c-image:focus:before { + opacity: 1; +} + +.c-image__thumb { + display: block; + max-width: 300px; + max-height: 200px; + width: auto; + height: auto; + margin: auto; +} \ No newline at end of file diff --git a/code_samples/back_office/udw/assets/css/image.list.css b/code_samples/back_office/udw/assets/css/image.list.css new file mode 100644 index 0000000000..ac0192dd53 --- /dev/null +++ b/code_samples/back_office/udw/assets/css/image.list.css @@ -0,0 +1,59 @@ +.c-images-list { + display: grid; + grid-template-areas: 'prev list next'; + grid-template-columns: 32px 1fr 32px; + grid-gap: 16px; + overflow: hidden; +} + +.c-images-list__items-wrapper { + overflow: hidden; + max-width: 1564px; +} + +[class*='c-images-list__btn--'] { + display: flex; + align-items: center; + justify-content: center; + background: #f15a10; + transition: background 0.2s ease-in-out, opacity 0.2s ease-in-out; +} + +[class*='c-images-list__btn--']:focus, +[class*='c-images-list__btn--']:hover { + background: #ab3f0a; +} + +[class*='c-images-list__btn--'][disabled], +[class*='c-images-list__btn--'][disabled]:focus, +[class*='c-images-list__btn--'][disabled]:hover { + background: #f15a10; + opacity: 0.5; +} + +[class*='c-images-list__btn--'] .ez-icon { + fill: #fff; +} + +.c-images-list__btn--prev { + grid-area: prev; +} + +.c-images-list__btn--next { + grid-area: next; +} + +.c-images-list__items { + grid-area: list; + display: flex; + flex-wrap: nowrap; + transition: transform 0.3s ease-in-out; +} + +.c-images-list__items .c-image { + flex: 0 0 300px; +} + +.c-images-list__items .c-image + .c-image { + margin-left: 1rem; +} diff --git a/code_samples/back_office/udw/assets/js/image-tab/components/image.js b/code_samples/back_office/udw/assets/js/image-tab/components/image.js new file mode 100644 index 0000000000..09fd71c264 --- /dev/null +++ b/code_samples/back_office/udw/assets/js/image-tab/components/image.js @@ -0,0 +1,33 @@ +import React, { useState, useEffect } from 'react'; +import { loadImageContent } from '../services/images.service'; + +const Image = ({ restInfo, location }) => { + const [content, setContent] = useState(null); + const updateVersionInfoState = (response) => { + + setContent(response.View.Result.searchHits.searchHit[0].value.Content); + }; + let src = + + 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI0MHB4IiBoZWlnaHQ9IjQwcHgiIHZpZXdCb3g9IjAgMCA0MCA0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNDE0MjE7IiB4PSIwcHgiIHk9IjBweCI+CiAgICA8ZGVmcz4KICAgICAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWwogICAgICAgICAgICBALXdlYmtpdC1rZXlmcmFtZXMgc3BpbiB7CiAgICAgICAgICAgICAgZnJvbSB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoLTM1OWRlZykKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgQGtleWZyYW1lcyBzcGluIHsKICAgICAgICAgICAgICBmcm9tIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKC0zNTlkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHN2ZyB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybS1vcmlnaW46IDUwJSA1MCU7CiAgICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogc3BpbiAxLjVzIGxpbmVhciBpbmZpbml0ZTsKICAgICAgICAgICAgICAgIC13ZWJraXQtYmFja2ZhY2UtdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgICAgICAgICAgYW5pbWF0aW9uOiBzcGluIDEuNXMgbGluZWFyIGluZmluaXRlOwogICAgICAgICAgICB9CiAgICAgICAgXV0+PC9zdHlsZT4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJvdXRlciI+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwwQzIyLjIwNTgsMCAyMy45OTM5LDEuNzg4MTMgMjMuOTkzOSwzLjk5MzlDMjMuOTkzOSw2LjE5OTY4IDIyLjIwNTgsNy45ODc4MSAyMCw3Ljk4NzgxQzE3Ljc5NDIsNy45ODc4MSAxNi4wMDYxLDYuMTk5NjggMTYuMDA2MSwzLjk5MzlDMTYuMDA2MSwxLjc4ODEzIDE3Ljc5NDIsMCAyMCwwWiIgc3R5bGU9ImZpbGw6YmxhY2s7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44NTc4Niw1Ljg1Nzg2QzcuNDE3NTgsNC4yOTgxNSA5Ljk0NjM4LDQuMjk4MTUgMTEuNTA2MSw1Ljg1Nzg2QzEzLjA2NTgsNy40MTc1OCAxMy4wNjU4LDkuOTQ2MzggMTEuNTA2MSwxMS41MDYxQzkuOTQ2MzgsMTMuMDY1OCA3LjQxNzU4LDEzLjA2NTggNS44NTc4NiwxMS41MDYxQzQuMjk4MTUsOS45NDYzOCA0LjI5ODE1LDcuNDE3NTggNS44NTc4Niw1Ljg1Nzg2WiIgc3R5bGU9ImZpbGw6cmdiKDIxMCwyMTAsMjEwKTsiLz4KICAgICAgICA8L2c+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwzMi4wMTIyQzIyLjIwNTgsMzIuMDEyMiAyMy45OTM5LDMzLjgwMDMgMjMuOTkzOSwzNi4wMDYxQzIzLjk5MzksMzguMjExOSAyMi4yMDU4LDQwIDIwLDQwQzE3Ljc5NDIsNDAgMTYuMDA2MSwzOC4yMTE5IDE2LjAwNjEsMzYuMDA2MUMxNi4wMDYxLDMzLjgwMDMgMTcuNzk0MiwzMi4wMTIyIDIwLDMyLjAxMjJaIiBzdHlsZT0iZmlsbDpyZ2IoMTMwLDEzMCwxMzApOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksMjguNDkzOUMzMC4wNTM2LDI2LjkzNDIgMzIuNTgyNCwyNi45MzQyIDM0LjE0MjEsMjguNDkzOUMzNS43MDE5LDMwLjA1MzYgMzUuNzAxOSwzMi41ODI0IDM0LjE0MjEsMzQuMTQyMUMzMi41ODI0LDM1LjcwMTkgMzAuMDUzNiwzNS43MDE5IDI4LjQ5MzksMzQuMTQyMUMyNi45MzQyLDMyLjU4MjQgMjYuOTM0MiwzMC4wNTM2IDI4LjQ5MzksMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxMDEsMTAxLDEwMSk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMy45OTM5LDE2LjAwNjFDNi4xOTk2OCwxNi4wMDYxIDcuOTg3ODEsMTcuNzk0MiA3Ljk4NzgxLDIwQzcuOTg3ODEsMjIuMjA1OCA2LjE5OTY4LDIzLjk5MzkgMy45OTM5LDIzLjk5MzlDMS43ODgxMywyMy45OTM5IDAsMjIuMjA1OCAwLDIwQzAsMTcuNzk0MiAxLjc4ODEzLDE2LjAwNjEgMy45OTM5LDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoMTg3LDE4NywxODcpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTUuODU3ODYsMjguNDkzOUM3LjQxNzU4LDI2LjkzNDIgOS45NDYzOCwyNi45MzQyIDExLjUwNjEsMjguNDkzOUMxMy4wNjU4LDMwLjA1MzYgMTMuMDY1OCwzMi41ODI0IDExLjUwNjEsMzQuMTQyMUM5Ljk0NjM4LDM1LjcwMTkgNy40MTc1OCwzNS43MDE5IDUuODU3ODYsMzQuMTQyMUM0LjI5ODE1LDMyLjU4MjQgNC4yOTgxNSwzMC4wNTM2IDUuODU3ODYsMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxNjQsMTY0LDE2NCk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMzYuMDA2MSwxNi4wMDYxQzM4LjIxMTksMTYuMDA2MSA0MCwxNy43OTQyIDQwLDIwQzQwLDIyLjIwNTggMzguMjExOSwyMy45OTM5IDM2LjAwNjEsMjMuOTkzOUMzMy44MDAzLDIzLjk5MzkgMzIuMDEyMiwyMi4yMDU4IDMyLjAxMjIsMjBDMzIuMDEyMiwxNy43OTQyIDMzLjgwMDMsMTYuMDA2MSAzNi4wMDYxLDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoNzQsNzQsNzQpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksNS44NTc4NkMzMC4wNTM2LDQuMjk4MTUgMzIuNTgyNCw0LjI5ODE1IDM0LjE0MjEsNS44NTc4NkMzNS43MDE5LDcuNDE3NTggMzUuNzAxOSw5Ljk0NjM4IDM0LjE0MjEsMTEuNTA2MUMzMi41ODI0LDEzLjA2NTggMzAuMDUzNiwxMy4wNjU4IDI4LjQ5MzksMTEuNTA2MUMyNi45MzQyLDkuOTQ2MzggMjYuOTM0Miw3LjQxNzU4IDI4LjQ5MzksNS44NTc4NloiIHN0eWxlPSJmaWxsOnJnYig1MCw1MCw1MCk7Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K'; + let alt = 'Loading meta data ...'; + + useEffect(() => { + loadImageContent({ ...restInfo, contentId: location.ContentInfo.Content._id }, updateVersionInfoState); + }, []); + + if (content) { + const imageField = content.CurrentVersion.Version.Fields.field.find((field) => field.fieldTypeIdentifier === 'ezimage').fieldValue; + + src = imageField.uri; + alt = imageField.fileName; + } + + return ( +
console.log(data)}> + {alt} +
+ ); +}; + +export default Image; \ No newline at end of file diff --git a/code_samples/back_office/udw/assets/js/image-tab/components/images.list.js b/code_samples/back_office/udw/assets/js/image-tab/components/images.list.js new file mode 100644 index 0000000000..0de3cb279d --- /dev/null +++ b/code_samples/back_office/udw/assets/js/image-tab/components/images.list.js @@ -0,0 +1,99 @@ +import React, { useState, useContext, useEffect } from 'react'; +import Image from './image'; +import { getImages } from '../services/images.service'; + +import { RestInfoContext } from '../../../../vendor/ezsystems/ezplatform-admin-ui/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module'; + +const ImagesList = () => { + const [images, setImages] = useState([]); + const [page, setPage] = useState(0); + const [itemsPerPage, setItemPerPage] = useState(5); + const [maxPageIndex, setMaxPageIndex] = useState(0); + const restInfo = useContext(RestInfoContext); + const updateImagesState = (response) => { + const images = response.View.Result.searchHits.searchHit.map((item) => item.value.Location); + const modulo = images.length % itemsPerPage; + const maxPageIndex = modulo ? (images.length - modulo) / itemsPerPage : images.length / itemsPerPage - 1; + + setImages(images); + setMaxPageIndex(maxPageIndex); + }; + const showPrevPage = () => { + const prevPage = page > 0 ? page - 1 : 0; + + setPage(prevPage); + }; + const showNextPage = () => { + const nextPage = maxPageIndex > page ? page + 1 : maxPageIndex; + + setPage(nextPage); + }; + const renderItems = () => { + const attrs = { + className: 'c-images-list__items', + style: { + transform: `translate3d(-${page * itemsPerPage * 316}px, 0, 0)`, + }, + }; + + return ( +
+
+ {images.map((imageLocation) => ( + + ))} +
+
+ ); + }; + const renderPrevBtn = () => { + const attrs = { + className: 'c-images-list__btn--prev', + onClick: showPrevPage, + }; + + if (page <= 0) { + attrs.disabled = true; + } + + return ( +
+ + + +
+ ); + }; + const renderNextBtn = () => { + const attrs = { + className: 'c-images-list__btn--next', + onClick: showNextPage, + }; + + if (page >= maxPageIndex) { + attrs.disabled = true; + } + + return ( +
+ + + +
+ ); + }; + + useEffect(() => { + getImages(restInfo, updateImagesState); + }, []); + + return ( +
+ {renderPrevBtn()} + {renderItems()} + {renderNextBtn()} +
+ ); +}; + +export default ImagesList; \ No newline at end of file diff --git a/code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js b/code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js new file mode 100644 index 0000000000..cc92b05bb0 --- /dev/null +++ b/code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js @@ -0,0 +1,28 @@ +import React, { useContext } from 'react'; + + import Tab from '../../../vendor/ezsystems/ezplatform-admin-ui/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab'; + import ImagesList from './components/images.list'; + + const ImageTabModule = () => { + return ( +
+ + + +
+ ); + }; +eZ.addConfig( + 'adminUiConfig.universalDiscoveryWidget.tabs', + [ + { + id: 'image', + component: ImageTabModule, + label: 'Image', + icon: '/bundles/ezplatformadminui/img/ez-icons.svg#image', + }, + ], + true +); + +export default ImageTabModule; \ No newline at end of file diff --git a/code_samples/back_office/udw/assets/js/image-tab/services/images.service.js b/code_samples/back_office/udw/assets/js/image-tab/services/images.service.js new file mode 100644 index 0000000000..d80ee509c3 --- /dev/null +++ b/code_samples/back_office/udw/assets/js/image-tab/services/images.service.js @@ -0,0 +1,69 @@ +const handleRequestResponse = (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + + return response.json(); +}; + +export const getImages = ({ token, siteaccess, contentId }, callback) => { + const body = JSON.stringify({ + ViewInput: { + identifier: 'images', + public: false, + LocationQuery: { + Criteria: {}, + FacetBuilders: {}, + SortClauses: {}, + Filter: { ContentTypeIdCriterion: 5 }, + }, + }, + }); + const request = new Request('/api/ezp/v2/views', { + method: 'POST', + headers: { + Accept: 'application/vnd.ez.api.View+json; version=1.1', + 'Content-Type': 'application/vnd.ez.api.ViewInput+json; version=1.1', + 'X-Siteaccess': siteaccess, + 'X-CSRF-Token': token, + }, + body, + mode: 'cors', + }); + + fetch(request) + .then(handleRequestResponse) + .then(callback) + .catch((error) => console.log('error:load:images', error)); +}; + +export const loadImageContent = ({ token, siteaccess, contentId }, callback) => { + const body = JSON.stringify({ + ViewInput: { + identifier: `image-content-${contentId}`, + public: false, + ContentQuery: { + Criteria: {}, + FacetBuilders: {}, + SortClauses: {}, + Filter: { ContentIdCriterion: contentId }, + }, + }, + }); + const request = new Request('/api/ezp/v2/views', { + method: 'POST', + headers: { + Accept: 'application/vnd.ez.api.View+json; version=1.1', + 'Content-Type': 'application/vnd.ez.api.ViewInput+json; version=1.1', + 'X-Siteaccess': siteaccess, + 'X-CSRF-Token': token, + }, + body, + mode: 'cors', + }); + + fetch(request) + .then(handleRequestResponse) + .then(callback) + .catch((error) => console.log('error:load:images', error)); +}; \ No newline at end of file diff --git a/code_samples/data_migration/config/custom_services.yaml b/code_samples/data_migration/config/custom_services.yaml new file mode 100644 index 0000000000..ccbf2c68f1 --- /dev/null +++ b/code_samples/data_migration/config/custom_services.yaml @@ -0,0 +1,26 @@ +services: + App\Migrations\Action\AssignSectionDenormalizer: + autoconfigure: false + tags: + - { name: 'ibexa.migrations.serializer.normalizer' } + + App\Migrations\Action\AssignSectionExecutor: + tags: + - { name: 'ibexa.migrations.executor.action.content', key: !php/const App\Migrations\Action\AssignSection::TYPE } + + App\Migrations\Matcher\SectionIdentifierNormalizer: + tags: + - { name: 'ibexa.migrations.serializer.normalizer' } + + App\Migrations\Matcher\SectionIdentifierGenerator: + tags: + - { name: 'ibexa.migrations.generator.criterion_generator.content' } + + App\Migrations\Step\ReplaceNameStepNormalizer: + tags: + - 'ibexa.migrations.serializer.step_normalizer' + - 'ibexa.migrations.serializer.normalizer' + + App\Migrations\Step\ReplaceNameStepExecutor: + tags: + - 'ibexa.migrations.step_executor' diff --git a/code_samples/data_migration/examples/repeatable_step.yaml b/code_samples/data_migration/examples/repeatable_step.yaml new file mode 100644 index 0000000000..b43a6a75e7 --- /dev/null +++ b/code_samples/data_migration/examples/repeatable_step.yaml @@ -0,0 +1,19 @@ +- + type: repeatable + mode: create + iterations: 5 + steps: + - type: content + mode: create + metadata: + contentType: folder + mainTranslation: eng-GB + location: + parentLocationId: 2 + fields: + - fieldDefIdentifier: name + languageCode: eng-GB + value: 'Folder ###SSS i SSS###' + - fieldDefIdentifier: short_name + languageCode: eng-GB + value: '### faker().name() ###' diff --git a/code_samples/data_migration/src/Migrations/Action/AssignSection.php b/code_samples/data_migration/src/Migrations/Action/AssignSection.php new file mode 100644 index 0000000000..0b9b9cafcb --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Action/AssignSection.php @@ -0,0 +1,33 @@ +sectionIdentifier = $sectionIdentifier; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->sectionIdentifier; + } + + public function getSupportedType(): string + { + return self::TYPE; + } +} diff --git a/code_samples/data_migration/src/Migrations/Action/AssignSectionDenormalizer.php b/code_samples/data_migration/src/Migrations/Action/AssignSectionDenormalizer.php new file mode 100644 index 0000000000..cefe28e8db --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Action/AssignSectionDenormalizer.php @@ -0,0 +1,31 @@ + $data + * @param string $type + * @param string|null $format + * @param array $context + * + * @return \App\Migrations\Action\AssignSection + */ + public function denormalize($data, string $type, string $format = null, array $context = []): AssignSection + { + Assert::keyExists($data, 'value'); + + return new AssignSection($data['value']); + } +} diff --git a/code_samples/data_migration/src/Migrations/Action/AssignSectionExecutor.php b/code_samples/data_migration/src/Migrations/Action/AssignSectionExecutor.php new file mode 100644 index 0000000000..a36f96c2e2 --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Action/AssignSectionExecutor.php @@ -0,0 +1,37 @@ +sectionService = $sectionService; + $this->contentService = $contentService; + } + + /** + * @param \App\Migrations\Action\AssignSection $action + * @param \eZ\Publish\API\Repository\Values\Content\Content $content + */ + public function handle(ValueObject\Step\Action $action, APIValueObject $content): void + { + $contentInfo = $this->contentService->loadContentInfo($content->id); + $section = $this->sectionService->loadSectionByIdentifier($action->getValue()); + $this->sectionService->assignSection($contentInfo, $section); + } +} diff --git a/code_samples/data_migration/src/Migrations/Matcher/SectionIdentifierGenerator.php b/code_samples/data_migration/src/Migrations/Matcher/SectionIdentifierGenerator.php new file mode 100644 index 0000000000..e6efcaf576 --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Matcher/SectionIdentifierGenerator.php @@ -0,0 +1,21 @@ + $data + * @param array $context + */ + protected function createCriterion(array $data, string $type, ?string $format, array $context): FilteringCriterion + { + Assert::keyExists($data, 'value'); + + return new Criterion\SectionIdentifier($data['value']); + } + + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof Criterion\SectionIdentifier; + } +} diff --git a/code_samples/data_migration/src/Migrations/Step/ReplaceNameStep.php b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStep.php new file mode 100644 index 0000000000..6e22a3dcf0 --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStep.php @@ -0,0 +1,22 @@ +replacement = $replacement ?? 'New Company Name'; + } + + public function getReplacement(): string + { + return $this->replacement; + } +} diff --git a/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepExecutor.php b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepExecutor.php new file mode 100644 index 0000000000..44675118c9 --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepExecutor.php @@ -0,0 +1,61 @@ +contentService = $contentService; + } + + protected function doHandle(StepInterface $step): void + { + assert($step instanceof ReplaceNameStep); + + $contentItems = $this->contentService->find(new Filter()); + + foreach ($contentItems as $contentItem) { + $struct = $this->contentService->newContentUpdateStruct(); + + foreach ($contentItem->getFields() as $field) { + if ($field->fieldTypeIdentifier !== 'ezstring') { + continue; + } + + if ($field->fieldDefIdentifier === 'identifier') { + continue; + } + + if (str_contains($field->value, 'Company Name')) { + $newValue = str_replace('Company Name', $step->getReplacement(), $field->value); + $struct->setField($field->fieldDefIdentifier, new Value($newValue)); + } + } + + try { + $content = $this->contentService->createContentDraft($contentItem->contentInfo); + $content = $this->contentService->updateContent($content->getVersionInfo(), $struct); + $this->contentService->publishVersion($content->getVersionInfo()); + } catch (\Throwable $e) { + // Ignore + } + } + } + + public function canHandle(StepInterface $step): bool + { + return $step instanceof ReplaceNameStep; + } +} diff --git a/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepNormalizer.php b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepNormalizer.php new file mode 100644 index 0000000000..2e4b52e920 --- /dev/null +++ b/code_samples/data_migration/src/Migrations/Step/ReplaceNameStepNormalizer.php @@ -0,0 +1,50 @@ + + */ +final class ReplaceNameStepNormalizer extends AbstractStepNormalizer +{ + protected function normalizeStep( + StepInterface $object, + string $format = null, + array $context = [] + ): array { + assert($object instanceof ReplaceNameStep); + + return [ + 'replacement' => $object->getReplacement(), + ]; + } + + protected function denormalizeStep( + $data, + string $type, + string $format, + array $context = [] + ): ReplaceNameStep { + return new ReplaceNameStep($data['replacement'] ?? null); + } + + public function getHandledClassType(): string + { + return ReplaceNameStep::class; + } + + public function getType(): string + { + return 'company_name'; + } + + public function getMode(): string + { + return 'replace'; + } +} diff --git a/code_samples/field_types/2dpoint_ft/config/packages/field_templates.yaml b/code_samples/field_types/2dpoint_ft/config/packages/field_templates.yaml new file mode 100644 index 0000000000..342e47303b --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/config/packages/field_templates.yaml @@ -0,0 +1,7 @@ +ezplatform: + system: + default: + field_templates: + - { template: 'point2d_field.html.twig', priority: 0 } + fielddefinition_edit_templates: + - { template: 'point2d_field_type_definition.html.twig', priority: 0 } diff --git a/code_samples/field_types/2dpoint_ft/config/services.yaml b/code_samples/field_types/2dpoint_ft/config/services.yaml new file mode 100644 index 0000000000..f3ad4b2244 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/config/services.yaml @@ -0,0 +1,46 @@ +# This file is the entry point to configure your own services. +# Files in the packages/ subdirectory configure your dependencies. + +# Put parameters here that don't need to change on each machine where the app is deployed +# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration +parameters: + google_recaptcha_site_key: '%env(GOOGLE_RECAPTCHA_SITE_KEY)%' + +services: + # default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: '../src/' + exclude: + - '../src/DependencyInjection/' + - '../src/Entity/' + - '../src/Kernel.php' + - '../src/Tests/' + + # controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + App\Controller\: + resource: '../src/Controller/' + tags: ['controller.service_arguments'] + + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones + + App\FieldType\Point2D\Type: + tags: + - { name: ezplatform.field_type, alias: point2d } + - { name: ezplatform.field_type.form_mapper.value, fieldType: point2d } + - { name: ezplatform.field_type.form_mapper.definition, fieldType: point2d } + + App\Serializer\Point2D\ValueNormalizer: + tags: + - { name: serializer.normalizer } + + App\Serializer\Point2D\ValueDenormalizer: + tags: + - { name: serializer.denormalizer } diff --git a/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php b/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php new file mode 100644 index 0000000000..c9a199600f --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php @@ -0,0 +1,47 @@ + [ + 'type' => 'string', + 'default' => '(%x%, %y%)', + ], + ]; + } + + public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) + { + $definition = $data->fieldDefinition; + $fieldForm->add('value', Point2DType::class, [ + 'required' => $definition->isRequired, + 'label' => $definition->getName(), + ]); + } + + public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data): void + { + $fieldDefinitionForm->add('fieldSettings', Point2DSettingsType::class, [ + 'label' => false, + ]); + } +} diff --git a/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Value.php b/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Value.php new file mode 100644 index 0000000000..de6e03fb42 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Value.php @@ -0,0 +1,57 @@ +x = $coords[0]; + $this->y = $coords[1]; + } + } + + public function getX(): ?float + { + return $this->x; + } + + public function setX(?float $x): void + { + $this->x = $x; + } + + public function getY(): ?float + { + return $this->y; + } + + public function setY(?float $y): void + { + $this->y = $y; + } + + public function __toString() + { + return "({$this->x}, {$this->y})"; + } +} diff --git a/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DSettingsType.php b/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DSettingsType.php new file mode 100644 index 0000000000..5c93733402 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DSettingsType.php @@ -0,0 +1,16 @@ +add('format', TextType::class); + } +} diff --git a/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DType.php b/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DType.php new file mode 100644 index 0000000000..1f4227ae7a --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DType.php @@ -0,0 +1,26 @@ +add('x', NumberType::class); + $builder->add('y', NumberType::class); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Value::class, + ]); + } +} diff --git a/code_samples/field_types/2dpoint_ft/src/Serializer/Point2D/ValueDenormalizer.php b/code_samples/field_types/2dpoint_ft/src/Serializer/Point2D/ValueDenormalizer.php new file mode 100644 index 0000000000..38b6526fde --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/src/Serializer/Point2D/ValueDenormalizer.php @@ -0,0 +1,25 @@ +getX(), + $object->getY(), + ]; + } + + public function supportsNormalization($data, string $format = null) + { + return $data instanceof Value; + } +} diff --git a/code_samples/field_types/2dpoint_ft/steps/step_1/Value.php b/code_samples/field_types/2dpoint_ft/steps/step_1/Value.php new file mode 100644 index 0000000000..2ab70e0480 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/steps/step_1/Value.php @@ -0,0 +1,46 @@ +x = $x; + $this->y = $y; + } + + public function getX(): ?float + { + return $this->x; + } + + public function setX(?float $x): void + { + $this->x = $x; + } + + public function getY(): ?float + { + return $this->y; + } + + public function setY(?float $y): void + { + $this->y = $y; + } + + public function __toString() + { + return "({$this->x}, {$this->y})"; + } +} diff --git a/code_samples/field_types/2dpoint_ft/steps/step_2/Type.php b/code_samples/field_types/2dpoint_ft/steps/step_2/Type.php new file mode 100644 index 0000000000..bdd6940744 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/steps/step_2/Type.php @@ -0,0 +1,14 @@ +add('x', NumberType::class); + $builder->add('y', NumberType::class); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Value::class, + ]); + } +} diff --git a/code_samples/field_types/2dpoint_ft/steps/step_3/Type.php b/code_samples/field_types/2dpoint_ft/steps/step_3/Type.php new file mode 100644 index 0000000000..b74d88b241 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/steps/step_3/Type.php @@ -0,0 +1,27 @@ +fieldDefinition; + $fieldForm->add('value', Point2DType::class, [ + 'required' => $definition->isRequired, + 'label' => $definition->getName(), + ]); + } +} diff --git a/code_samples/field_types/2dpoint_ft/steps/step_4/point2d_field.html.twig b/code_samples/field_types/2dpoint_ft/steps/step_4/point2d_field.html.twig new file mode 100644 index 0000000000..83f401e9a1 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/steps/step_4/point2d_field.html.twig @@ -0,0 +1,3 @@ +{% block point2d_field %} + ({{ field.value.getX() }}, {{ field.value.getY() }}) +{% endblock %} diff --git a/code_samples/field_types/2dpoint_ft/steps/step_6/Type.php b/code_samples/field_types/2dpoint_ft/steps/step_6/Type.php new file mode 100644 index 0000000000..1af766f77c --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/steps/step_6/Type.php @@ -0,0 +1,36 @@ + [ + 'type' => 'string', + 'default' => '(%x%, %y%)', + ], + ]; + } + + public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) + { + $definition = $data->fieldDefinition; + $fieldForm->add('value', Point2DType::class, [ + 'required' => $definition->isRequired, + 'label' => $definition->getName(), + ]); + } +} diff --git a/code_samples/field_types/2dpoint_ft/templates/point2d_field.html.twig b/code_samples/field_types/2dpoint_ft/templates/point2d_field.html.twig new file mode 100644 index 0000000000..02af5535d0 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/templates/point2d_field.html.twig @@ -0,0 +1,6 @@ +{% block point2d_field %} + {{ fieldSettings.format|replace({ + '%x%': field.value.x, + '%y%': field.value.y + }) }} +{% endblock %} \ No newline at end of file diff --git a/code_samples/field_types/2dpoint_ft/templates/point2d_field_type_definition.html.twig b/code_samples/field_types/2dpoint_ft/templates/point2d_field_type_definition.html.twig new file mode 100644 index 0000000000..6630478765 --- /dev/null +++ b/code_samples/field_types/2dpoint_ft/templates/point2d_field_type_definition.html.twig @@ -0,0 +1,7 @@ +{% block point2d_field_definition_edit %} +
+ {{- form_label(form.fieldSettings.format) -}} + {{- form_errors(form.fieldSettings.format) -}} + {{- form_widget(form.fieldSettings.format) -}} +
+{% endblock %} \ No newline at end of file diff --git a/code_samples/field_types/generic_ft/config/custom_services.yaml b/code_samples/field_types/generic_ft/config/custom_services.yaml new file mode 100644 index 0000000000..d5e7b0854c --- /dev/null +++ b/code_samples/field_types/generic_ft/config/custom_services.yaml @@ -0,0 +1,14 @@ +services: + App\FieldType\HelloWorld\Type: + public: true + tags: + - { name: ezplatform.field_type, alias: hello_world } + - { name: ezplatform.field_type.form_mapper.value, fieldType: hello_world } + + App\FieldType\HelloWorld\Comparison\Comparable: + tags: + - { name: ezplatform.field_type.comparable, alias: hello_world } + + App\FieldType\HelloWorld\Comparison\HelloWorldComparisonEngine: + tags: + - { name: ezplatform.field_type.comparable.engine, supported_type: App\FieldType\HelloWorld\Comparison\Value } diff --git a/code_samples/field_types/generic_ft/config/packages/field_templates.yaml b/code_samples/field_types/generic_ft/config/packages/field_templates.yaml new file mode 100644 index 0000000000..f58eebbfb2 --- /dev/null +++ b/code_samples/field_types/generic_ft/config/packages/field_templates.yaml @@ -0,0 +1,7 @@ +ezplatform: + system: + default: + field_templates: + - { template: 'field_type.html.twig', priority: 0 } + field_comparison_templates: + - { template: 'field_type_comparison.html.twig', priority: 10 } diff --git a/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Comparable.php b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Comparable.php new file mode 100644 index 0000000000..3549ea4003 --- /dev/null +++ b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Comparable.php @@ -0,0 +1,22 @@ + new StringComparisonValue([ + 'value' => $value->getName(), + ]), + ]); + } +} diff --git a/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonEngine.php b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonEngine.php new file mode 100644 index 0000000000..dc8d3289e4 --- /dev/null +++ b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonEngine.php @@ -0,0 +1,37 @@ +stringValueComparisonEngine = $stringValueComparisonEngine; + } + + /** + * @param \EzSystems\EzPlatformVersionComparison\FieldType\TextLine\Value $comparisonDataA + * @param \EzSystems\EzPlatformVersionComparison\FieldType\TextLine\Value $comparisonDataB + */ + public function compareFieldsTypeValues(FieldTypeComparisonValue $comparisonDataA, FieldTypeComparisonValue $comparisonDataB): ComparisonResult + { + return new HelloWorldComparisonResult( + $this->stringValueComparisonEngine->compareValues($comparisonDataA->name, $comparisonDataB->name) + ); + } + + public function shouldRunComparison(FieldTypeComparisonValue $comparisonDataA, FieldTypeComparisonValue $comparisonDataB): bool + { + return $comparisonDataA->name->value !== $comparisonDataB->name->value; + } +} diff --git a/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonResult.php b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonResult.php new file mode 100644 index 0000000000..a6be170a59 --- /dev/null +++ b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonResult.php @@ -0,0 +1,29 @@ +stringDiff = $stringDiff; + } + + public function getHelloWorldDiff(): StringComparisonResult + { + return $this->stringDiff; + } + + public function isChanged(): bool + { + return $this->stringDiff->isChanged(); + } +} diff --git a/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Value.php b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Value.php new file mode 100644 index 0000000000..dcfcdfcf45 --- /dev/null +++ b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Value.php @@ -0,0 +1,13 @@ +fieldDefinition; + + $fieldForm->add('value', HelloWorldType::class, [ + 'required' => $definition->isRequired, + 'label' => $definition->getName(), + ]); + } +} diff --git a/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Value.php b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Value.php new file mode 100644 index 0000000000..14576bce33 --- /dev/null +++ b/code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Value.php @@ -0,0 +1,29 @@ +name; + } + + public function setName(?string $name): void + { + $this->name = $name; + } + + public function __toString() + { + return "Hello {$this->name}!"; + } +} diff --git a/code_samples/field_types/generic_ft/src/Form/Type/HelloWorldType.php b/code_samples/field_types/generic_ft/src/Form/Type/HelloWorldType.php new file mode 100644 index 0000000000..6a0cbf5e9b --- /dev/null +++ b/code_samples/field_types/generic_ft/src/Form/Type/HelloWorldType.php @@ -0,0 +1,26 @@ +add('name', TextType::class); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Value::class, + ]); + } +} diff --git a/code_samples/field_types/generic_ft/templates/field_type.html.twig b/code_samples/field_types/generic_ft/templates/field_type.html.twig new file mode 100644 index 0000000000..40ce50b3a6 --- /dev/null +++ b/code_samples/field_types/generic_ft/templates/field_type.html.twig @@ -0,0 +1,3 @@ +{% block hello_world_field %} + Hello {{ field.value.getName() }}! +{% endblock %} diff --git a/code_samples/field_types/generic_ft/templates/field_type_comparison.html.twig b/code_samples/field_types/generic_ft/templates/field_type_comparison.html.twig new file mode 100644 index 0000000000..6e0c866fb2 --- /dev/null +++ b/code_samples/field_types/generic_ft/templates/field_type_comparison.html.twig @@ -0,0 +1,13 @@ +{% extends '@ezdesign/version_comparison/comparison_result_blocks.html.twig' %} + +{% block hello_world_field_comparison %} + {% apply spaceless %} + + {% with { + 'comparison_result': comparison_result.getHelloWorldDiff() + } %} + {{ block('string_diff_render') }} + {% endwith %} + + {% endapply %} +{% endblock %} diff --git a/code_samples/forms/custom_form_attribute/config/custom_services.yaml b/code_samples/forms/custom_form_attribute/config/custom_services.yaml new file mode 100644 index 0000000000..cb454e66e6 --- /dev/null +++ b/code_samples/forms/custom_form_attribute/config/custom_services.yaml @@ -0,0 +1,21 @@ + App\FormBuilder\FieldType\Field\Mapper\CheckboxWithRichtextDescriptionFieldMapper: + arguments: + $fieldIdentifier: checkbox_with_richtext_description + $formType: 'App\FormBuilder\Form\Type\CheckboxWithRichtextDescriptionType' + tags: + - { name: ezplatform.form_builder.field_mapper } + + ezplatform.form_builder.attribute_form_type_mapper.richtext_description: + class: EzSystems\EzPlatformFormBuilder\Form\Mapper\FieldAttribute\GenericFieldAttributeTypeMapper + arguments: + $formTypeClass: App\FormBuilder\Form\Type\FieldAttribute\AttributeRichtextDescriptionType + $typeIdentifier: 'richtext_description' + tags: + - { name: ezplatform.form_builder.attribute_form_type_mapper } + + App\FormBuilder\FormSubmission\Converter\RichtextDescriptionFieldSubmissionConverter: + arguments: + $typeIdentifier: 'checkbox_with_richtext_description' + $twig: '@twig' + tags: + - { name: ezplatform.form_builder.field_submission_converter } \ No newline at end of file diff --git a/code_samples/forms/custom_form_attribute/config/packages/form_attribute_config.yaml b/code_samples/forms/custom_form_attribute/config/packages/form_attribute_config.yaml new file mode 100644 index 0000000000..02c480a11f --- /dev/null +++ b/code_samples/forms/custom_form_attribute/config/packages/form_attribute_config.yaml @@ -0,0 +1,18 @@ +ez_platform_form_builder: + fields: + checkbox_with_richtext_description: + name: Checkbox with Text + category: Default + thumbnail: '/bundles/ezplatformadminui/img/ez-icons.svg#input-line' + attributes: + label: + name: Label + type: string + validators: + not_blank: + message: You must provide label of the field + richtext_description: + name: 'Description' + type: 'richtext_description' + validators: + required: ~ \ No newline at end of file diff --git a/code_samples/forms/custom_form_attribute/src/FormBuilder/FieldType/Field/Mapper/CheckboxWithRichtextDescriptionFieldMapper.php b/code_samples/forms/custom_form_attribute/src/FormBuilder/FieldType/Field/Mapper/CheckboxWithRichtextDescriptionFieldMapper.php new file mode 100644 index 0000000000..cfe11e2cf2 --- /dev/null +++ b/code_samples/forms/custom_form_attribute/src/FormBuilder/FieldType/Field/Mapper/CheckboxWithRichtextDescriptionFieldMapper.php @@ -0,0 +1,21 @@ +getAttributeValue('label'); + $options['richtext_description'] = $field->getAttributeValue('richtext_description'); + + return $options; + } +} diff --git a/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/CheckboxWithRichtextDescriptionType.php b/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/CheckboxWithRichtextDescriptionType.php new file mode 100644 index 0000000000..4ce8f2f28c --- /dev/null +++ b/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/CheckboxWithRichtextDescriptionType.php @@ -0,0 +1,44 @@ +setDefaults([ + 'richtext_description' => '', + ]); + $resolver->setAllowedTypes('richtext_description', ['null', 'string']); + } + + public function buildView(FormView $view, FormInterface $form, array $options): void + { + // pass the Dom object of the richtext doc to the template + $dom = new \DOMDocument(); + $dom->loadXML($options['richtext_description']); + $view->vars['richtextDescription'] = $dom; + } +} diff --git a/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/FieldAttribute/AttributeRichtextDescriptionType.php b/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/FieldAttribute/AttributeRichtextDescriptionType.php new file mode 100644 index 0000000000..4b40e6fd9c --- /dev/null +++ b/code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/FieldAttribute/AttributeRichtextDescriptionType.php @@ -0,0 +1,25 @@ + { + const richtextContainer = ezDataSource.querySelector(".ez-data-source__richtext"); + const alloyEditor = richtext.init(richtextContainer); + }); +})(window, window.document, window.eZ, window.jQuery); \ No newline at end of file diff --git a/code_samples/forms/custom_form_attribute/webpack.config.js b/code_samples/forms/custom_form_attribute/webpack.config.js new file mode 100644 index 0000000000..77ca59086d --- /dev/null +++ b/code_samples/forms/custom_form_attribute/webpack.config.js @@ -0,0 +1,51 @@ +const Encore = require('@symfony/webpack-encore'); +const path = require('path'); +const getEzConfig = require('./ez.webpack.config.js'); +const eZConfigManager = require('./ez.webpack.config.manager.js'); +const eZConfig = getEzConfig(Encore); +const customConfigs = require('./ez.webpack.custom.configs.js'); + +Encore.reset(); +Encore + .setOutputPath('public/build/') + .setPublicPath('/build') + .enableStimulusBridge('./assets/controllers.json') + .enableSassLoader() + .enableReactPreset() + .enableSingleRuntimeChunk() + .copyFiles({ + from: './assets/images', + to: 'images/[path][name].[ext]', + pattern: /\.(png|svg)$/ + }) + .configureBabel((config) => { + config.plugins.push('@babel/plugin-proposal-class-properties'); + }) + + // enables @babel/preset-env polyfills + .configureBabelPresetEnv((config) => { + config.useBuiltIns = 'usage'; + config.corejs = 3; + }) +; + +// Welcome page stylesheets +Encore.addEntry('welcome_page', [ + path.resolve(__dirname, './assets/scss/welcome-page.scss'), +]); + +Encore.addEntry('app', './assets/app.js'); + +Encore.addEntry('formbuilder-richtext-checkbox-js', [ + path.resolve(__dirname, './src/Resources/public/js/formbuilder-richtext-checkbox.js') +]); +// .autoProvidejQuery(); + +const projectConfig = Encore.getWebpackConfig(); + +projectConfig.name = 'app'; + +module.exports = [ eZConfig, ...customConfigs, projectConfig ]; + +// uncomment this line if you've commented-out the above lines +// module.exports = [ eZConfig, ...customConfigs ]; \ No newline at end of file diff --git a/code_samples/forms/custom_form_field/config/custom_services.yaml b/code_samples/forms/custom_form_field/config/custom_services.yaml new file mode 100644 index 0000000000..80654ce89e --- /dev/null +++ b/code_samples/forms/custom_form_field/config/custom_services.yaml @@ -0,0 +1,11 @@ +services: + App\FormBuilder\Field\Mapper\CountryFieldMapper: + arguments: + $fieldIdentifier: country + $formType: Symfony\Component\Form\Extension\Core\Type\CountryType + tags: + - { name: ezplatform.form_builder.field_mapper } + App\EventSubscriber\FormFieldDefinitionSubscriber: + public: true + tags: + - kernel.event_subscriber diff --git a/code_samples/forms/custom_form_field/config/packages/form_builder.yaml b/code_samples/forms/custom_form_field/config/packages/form_builder.yaml new file mode 100644 index 0000000000..cf8791ee8b --- /dev/null +++ b/code_samples/forms/custom_form_field/config/packages/form_builder.yaml @@ -0,0 +1,18 @@ +ez_platform_form_builder: + fields: + country: + name: Country + category: Custom form fields + thumbnail: '/bundles/ibexaplatformicons/img/all-icons.svg#places' + attributes: + label: + name: Display label + type: string + validators: + not_blank: + message: You must provide a label for the field + help: + name: Help text + type: string + validators: + required: ~ diff --git a/code_samples/forms/custom_form_field/src/EventSubscriber/FormFieldDefinitionSubscriber.php b/code_samples/forms/custom_form_field/src/EventSubscriber/FormFieldDefinitionSubscriber.php new file mode 100644 index 0000000000..11fc8dfc04 --- /dev/null +++ b/code_samples/forms/custom_form_field/src/EventSubscriber/FormFieldDefinitionSubscriber.php @@ -0,0 +1,29 @@ + 'onSingleLineFieldDefinition', + ]; + } + + public function onSingleLineFieldDefinition(FieldDefinitionEvent $event): void + { + $isReadOnlyAttribute = new FieldAttributeDefinitionBuilder(); + $isReadOnlyAttribute->setIdentifier('custom'); + $isReadOnlyAttribute->setName('Custom attribute'); + $isReadOnlyAttribute->setType('string'); + + $definitionBuilder = $event->getDefinitionBuilder(); + $definitionBuilder->addAttribute($isReadOnlyAttribute->buildDefinition()); + } +} diff --git a/code_samples/forms/custom_form_field/src/FormBuilder/Field/Mapper/CountryFieldMapper.php b/code_samples/forms/custom_form_field/src/FormBuilder/Field/Mapper/CountryFieldMapper.php new file mode 100644 index 0000000000..ad8f374d02 --- /dev/null +++ b/code_samples/forms/custom_form_field/src/FormBuilder/Field/Mapper/CountryFieldMapper.php @@ -0,0 +1,18 @@ +getAttributeValue('label'); + $options['help'] = $field->getAttributeValue('help'); + + return $options; + } +} diff --git a/code_samples/front/add_design/config/packages/views.yaml b/code_samples/front/add_design/config/packages/views.yaml new file mode 100644 index 0000000000..774c0155b5 --- /dev/null +++ b/code_samples/front/add_design/config/packages/views.yaml @@ -0,0 +1,19 @@ +ezdesign: + design_list: + summersale: [summersale, standard] + +ezplatform: + system: + site_group: + content_view: + full: + article: + template: '@ezdesign/full/article.html.twig' + match: + Identifier\ContentType: [ article ] + campaign: + languages: [eng-GB] + design: summersale + site: + languages: [eng-GB] + design: standard diff --git a/code_samples/front/add_design/templates/themes/standard/full/article.html.twig b/code_samples/front/add_design/templates/themes/standard/full/article.html.twig new file mode 100644 index 0000000000..8caff9c415 --- /dev/null +++ b/code_samples/front/add_design/templates/themes/standard/full/article.html.twig @@ -0,0 +1,4 @@ +{% extends '@ezdesign/pagelayout.html.twig' %} + +{% block content %} +{% endblock %} diff --git a/code_samples/front/add_design/templates/themes/standard/pagelayout.html.twig b/code_samples/front/add_design/templates/themes/standard/pagelayout.html.twig new file mode 100644 index 0000000000..a8c7a329fb --- /dev/null +++ b/code_samples/front/add_design/templates/themes/standard/pagelayout.html.twig @@ -0,0 +1,32 @@ + + + + + + {% if content is defined and title is not defined %} + {% set title = ez_content_name( content ) %} + {% endif %} + {{ title|default( 'Home' ) }} + + {% if content is defined and content.contentInfo.mainLocationId %} + + {% endif %} + + {% block stylesheets %} + {{ encore_entry_link_tags('app') }} + {% endblock %} + + + +{% include '@ezdesign/parts/header.html.twig' %} + +{% block content %} +{% endblock %} + +{% include '@ezdesign/parts/footer.html.twig' %} + +{% block javascripts %} + {{ encore_entry_script_tags('app') }} +{% endblock %} + + diff --git a/code_samples/front/add_design/templates/themes/standard/parts/footer.html.twig b/code_samples/front/add_design/templates/themes/standard/parts/footer.html.twig new file mode 100644 index 0000000000..a4515eae6a --- /dev/null +++ b/code_samples/front/add_design/templates/themes/standard/parts/footer.html.twig @@ -0,0 +1 @@ +
Copyright Acme SA
diff --git a/code_samples/front/add_design/templates/themes/standard/parts/header.html.twig b/code_samples/front/add_design/templates/themes/standard/parts/header.html.twig new file mode 100644 index 0000000000..32d4427078 --- /dev/null +++ b/code_samples/front/add_design/templates/themes/standard/parts/header.html.twig @@ -0,0 +1 @@ + diff --git a/code_samples/front/add_design/templates/themes/summersale/parts/header.html.twig b/code_samples/front/add_design/templates/themes/summersale/parts/header.html.twig new file mode 100644 index 0000000000..41a454f349 --- /dev/null +++ b/code_samples/front/add_design/templates/themes/summersale/parts/header.html.twig @@ -0,0 +1 @@ + diff --git a/code_samples/front/custom_query_type/config/packages/views.yaml b/code_samples/front/custom_query_type/config/packages/views.yaml new file mode 100644 index 0000000000..58468e9ec5 --- /dev/null +++ b/code_samples/front/custom_query_type/config/packages/views.yaml @@ -0,0 +1,22 @@ +ezdesign: + design_list: + my_design: [ my_theme ] + +ezplatform: + system: + site_group: + design: my_design + content_view: + full: + latest: + controller: ez_query::locationQueryAction + template: '@ezdesign/full/latest.html.twig' + match: + Identifier\ContentType: "latest" + params: + query: + query_type: LatestContent + parameters: + contentType: [article, blog_post] + assign_results_to: latest + diff --git a/code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php b/code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php new file mode 100644 index 0000000000..8544502c7f --- /dev/null +++ b/code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php @@ -0,0 +1,36 @@ + new Query\Criterion\LogicalAnd($criteria), + 'sortClauses' => [ + new Query\SortClause\DatePublished(Query::SORT_DESC), + ], + 'limit' => isset($parameters['limit']) ? $parameters['limit'] : 10, + ]); + } + + public function getSupportedParameters() + { + return ['contentType', 'limit']; + } +} diff --git a/code_samples/front/custom_query_type/src/QueryType/OptionsBasedLatestContentQueryType.php b/code_samples/front/custom_query_type/src/QueryType/OptionsBasedLatestContentQueryType.php new file mode 100644 index 0000000000..c064ce8d0b --- /dev/null +++ b/code_samples/front/custom_query_type/src/QueryType/OptionsBasedLatestContentQueryType.php @@ -0,0 +1,41 @@ + new Query\Criterion\LogicalAnd($criteria), + 'sortClauses' => [ + new Query\SortClause\DatePublished(Query::SORT_DESC), + ], + 'limit' => isset($parameters['limit']) ? $parameters['limit'] : 10, + ]); + } + + protected function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefined(['contentType', 'limit']); + $resolver->setAllowedTypes('contentType', 'array'); + $resolver->setAllowedTypes('limit', 'int'); + $resolver->setDefault('limit', 10); + } +} diff --git a/code_samples/front/embed_content/config/packages/views.yaml b/code_samples/front/embed_content/config/packages/views.yaml new file mode 100644 index 0000000000..9d0cac252d --- /dev/null +++ b/code_samples/front/embed_content/config/packages/views.yaml @@ -0,0 +1,30 @@ +ezdesign: + design_list: + my_design: [ my_theme ] + +ezplatform: + system: + site_group: + design: my_design + content_view: + full: + blog_post: + controller: ez_query::contentQueryAction + template: '@ezdesign/full/blog_post.html.twig' + params: + query: + query_type: 'Siblings' + parameters: + content: '@=content' + limit: 3 + sort: 'date_published desc' + assign_results_to: items + match: + Identifier\ContentType: blog_post + article: + controller: App\Controller\RelationController::showContentAction + template: '@ezdesign/full/article.html.twig' + params: + accepted_content_types: [ 'article', 'test_target', 'test_source' ] + match: + Identifier\ContentType: article diff --git a/code_samples/front/embed_content/src/Controller/RelationController.php b/code_samples/front/embed_content/src/Controller/RelationController.php new file mode 100644 index 0000000000..8a11032654 --- /dev/null +++ b/code_samples/front/embed_content/src/Controller/RelationController.php @@ -0,0 +1,44 @@ +contentService = $contentService; + $this->locationService = $locationService; + } + + public function showContentAction(View $view, $locationId) + { + $acceptedContentTypes = $view->getParameter('accepted_content_types'); + + $location = $this->locationService->loadLocation($locationId); + $contentInfo = $location->getContentInfo(); + $versionInfo = $this->contentService->loadVersionInfo($contentInfo); + $relations = $this->contentService->loadRelations($versionInfo); + + $items = []; + + foreach ($relations as $relation) { + if (in_array($relation->getDestinationContentInfo()->getContentType()->identifier, $acceptedContentTypes)) { + $items[] = $this->contentService->loadContentByContentInfo($relation->getDestinationContentInfo()); + } + } + + $view->addParameters([ + 'items' => $items, + ]); + + return $view; + } +} diff --git a/code_samples/front/embed_content/templates/themes/my_theme/full/article.html.twig b/code_samples/front/embed_content/templates/themes/my_theme/full/article.html.twig new file mode 100644 index 0000000000..480502c75f --- /dev/null +++ b/code_samples/front/embed_content/templates/themes/my_theme/full/article.html.twig @@ -0,0 +1,8 @@ +{% block content %} +

{{ ez_content_name(content) }}

+
    + {% for item in items %} + {{ ez_render(item, {'viewType': 'embed'} ) }} + {% endfor %} +
+{% endblock %} diff --git a/code_samples/front/embed_content/templates/themes/my_theme/full/blog_post.html.twig b/code_samples/front/embed_content/templates/themes/my_theme/full/blog_post.html.twig new file mode 100644 index 0000000000..6ea7fcd78b --- /dev/null +++ b/code_samples/front/embed_content/templates/themes/my_theme/full/blog_post.html.twig @@ -0,0 +1,4 @@ +{{ ez_content_name(content) }} +{% for item in items.searchHits %} + {{ ez_render(item.valueObject, {'viewType': 'line'}) }} +{% endfor %} diff --git a/code_samples/front/layouts/breadcrumbs/src/Controller/BreadcrumbController.php b/code_samples/front/layouts/breadcrumbs/src/Controller/BreadcrumbController.php new file mode 100644 index 0000000000..1248302252 --- /dev/null +++ b/code_samples/front/layouts/breadcrumbs/src/Controller/BreadcrumbController.php @@ -0,0 +1,41 @@ +locationService = $locationService; + $this->searchService = $searchService; + } + + public function showBreadcrumbsAction($locationId) + { + $query = new LocationQuery(); + $query->query = new Criterion\Ancestor([$this->locationService->loadLocation($locationId)->pathString]); + + $results = $this->searchService->findLocations($query); + $breadcrumbs = []; + foreach ($results->searchHits as $searchHit) { + $breadcrumbs[] = $searchHit; + } + + return $this->render( + '@ezdesign/parts/breadcrumbs.html.twig', + [ + 'breadcrumbs' => $breadcrumbs, + ] + ); + } +} diff --git a/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/pagelayout.html.twig b/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/pagelayout.html.twig new file mode 100644 index 0000000000..54ad26fb5a --- /dev/null +++ b/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/pagelayout.html.twig @@ -0,0 +1,11 @@ +{{ render( + controller( + "App\\Controller\\BreadcrumbController::showBreadcrumbsAction", + { + 'locationId': locationId, + } + ) +) }} + +{% block content %} +{% endblock %} diff --git a/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/parts/breadcrumbs.html.twig b/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/parts/breadcrumbs.html.twig new file mode 100644 index 0000000000..2f02d01c4f --- /dev/null +++ b/code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/parts/breadcrumbs.html.twig @@ -0,0 +1,8 @@ +{% for breadcrumb in breadcrumbs %} + {% if not loop.first %} -> {% endif %} + {% if not loop.last %} + {{ breadcrumb.valueObject.contentInfo.name }} + {% else %} + {{ breadcrumb.valueObject.contentInfo.name }} + {% endif %} +{% endfor %} diff --git a/code_samples/front/layouts/menu/src/Menu/MenuBuilder.php b/code_samples/front/layouts/menu/src/Menu/MenuBuilder.php new file mode 100644 index 0000000000..f95bc9bd97 --- /dev/null +++ b/code_samples/front/layouts/menu/src/Menu/MenuBuilder.php @@ -0,0 +1,30 @@ +factory = $factory; + } + + public function buildMenu() + { + $menu = $this->factory->createItem('root'); + + $menu->addChild('Home', ['route' => 'ez_urlalias', 'routeParameters' => [ + 'locationId' => 2, + ]]); + $menu->addChild('Blog', ['route' => 'ez_urlalias', 'routeParameters' => [ + 'locationId' => 67, + ]]); + $menu->addChild('Search', ['route' => 'ezplatform.search']); + + return $menu; + } +} diff --git a/code_samples/front/layouts/menu/src/QueryType/MenuQueryType.php b/code_samples/front/layouts/menu/src/QueryType/MenuQueryType.php new file mode 100644 index 0000000000..e3458ffd73 --- /dev/null +++ b/code_samples/front/layouts/menu/src/QueryType/MenuQueryType.php @@ -0,0 +1,37 @@ + $criteria, + 'sortClauses' => [ + new SortClause\Location\Priority(LocationQuery::SORT_ASC), + ], + ]; + + return new LocationQuery($options); + } + + public static function getName() + { + return 'Menu'; + } + + public function getSupportedParameters() + { + return []; + } +} diff --git a/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout.html.twig b/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout.html.twig new file mode 100644 index 0000000000..01957da941 --- /dev/null +++ b/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout.html.twig @@ -0,0 +1,12 @@ +{{ ez_render_content_query({ + 'query': { + 'query_type': 'Menu', + 'assign_results_to': 'menuItems' + }, + 'template': '@ezdesign/pagelayout_menu.html.twig', +}) }} + +{{ knp_menu_render('root') }} + +{% block content %} +{% endblock %} diff --git a/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout_menu.html.twig b/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout_menu.html.twig new file mode 100644 index 0000000000..e3c1abb366 --- /dev/null +++ b/code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout_menu.html.twig @@ -0,0 +1,5 @@ +{% if menuItems is defined and menuItems is not empty %} + {% for item in menuItems %} +
  • {{ ez_content_name(item.valueObject.contentInfo) }}
  • + {% endfor %} +{% endif %} diff --git a/code_samples/front/list_content/config/packages/views.yaml b/code_samples/front/list_content/config/packages/views.yaml new file mode 100644 index 0000000000..c3a8d09b9c --- /dev/null +++ b/code_samples/front/list_content/config/packages/views.yaml @@ -0,0 +1,28 @@ +ezdesign: + design_list: + my_design: [ my_theme ] + +ezplatform: + system: + site_group: + design: my_design + content_view: + full: + folder: + controller: ez_query::contentQueryAction + template: '@ezdesign/full/folder.html.twig' + params: + query: + query_type: 'Children' + parameters: + content: '@=content' + assign_results_to: items + limit: 3 + match: + Identifier\ContentType: folder + content_query_field: + blog: + template: '@ezdesign/content_query/blog_posts.html.twig' + match: + Identifier\ContentType: blog + '@EzSystems\EzPlatformQueryFieldType\eZ\ContentView\FieldDefinitionIdentifierMatcher': query diff --git a/code_samples/front/list_content/templates/themes/my_theme/full/blog_post.html.twig b/code_samples/front/list_content/templates/themes/my_theme/full/blog_post.html.twig new file mode 100644 index 0000000000..61d111c61e --- /dev/null +++ b/code_samples/front/list_content/templates/themes/my_theme/full/blog_post.html.twig @@ -0,0 +1,3 @@ +{% for item in items.searchHits %} + {{ ez_render(item.valueObject, {'viewType': 'line'}) }} +{% endfor %} diff --git a/code_samples/front/list_content/templates/themes/my_theme/full/folder.html.twig b/code_samples/front/list_content/templates/themes/my_theme/full/folder.html.twig new file mode 100644 index 0000000000..61d111c61e --- /dev/null +++ b/code_samples/front/list_content/templates/themes/my_theme/full/folder.html.twig @@ -0,0 +1,3 @@ +{% for item in items.searchHits %} + {{ ez_render(item.valueObject, {'viewType': 'line'}) }} +{% endfor %} diff --git a/code_samples/front/query_pagination/config/packages/views.yaml b/code_samples/front/query_pagination/config/packages/views.yaml new file mode 100644 index 0000000000..52705e49e5 --- /dev/null +++ b/code_samples/front/query_pagination/config/packages/views.yaml @@ -0,0 +1,22 @@ +ezdesign: + design_list: + my_design: [ my_theme ] + +ezplatform: + system: + site_group: + design: my_design + content_view: + full: + folder: + controller: ez_query::pagingQueryAction + template: '@ezdesign/full/folder.html.twig' + params: + query: + query_type: 'Children' + parameters: + content: '@=content' + assign_results_to: items + limit: 3 + match: + Identifier\ContentType: folder diff --git a/code_samples/front/query_pagination/templates/themes/my_theme/full/folder.html.twig b/code_samples/front/query_pagination/templates/themes/my_theme/full/folder.html.twig new file mode 100644 index 0000000000..c5b0e51f2a --- /dev/null +++ b/code_samples/front/query_pagination/templates/themes/my_theme/full/folder.html.twig @@ -0,0 +1,8 @@ +{% for item in items %} + {{ ez_render(item.valueObject) }} +{% endfor %} + +{{ pagerfanta(items, 'ez', { + 'routeName': 'ez_urlalias', + 'routeParams': {'location': location } +}) }} diff --git a/code_samples/front/render_content/config/packages/views.yaml b/code_samples/front/render_content/config/packages/views.yaml new file mode 100644 index 0000000000..92642dea4d --- /dev/null +++ b/code_samples/front/render_content/config/packages/views.yaml @@ -0,0 +1,33 @@ +ezdesign: + design_list: + my_design: [my_theme] + +ezplatform: + system: + site_group: + pagelayout: '@ezdesign/pagelayout.html.twig' + design: my_design + content_view: + full: + article: + template: '@ezdesign/full/article.html.twig' + match: + Identifier\ContentType: article + blog_post: + template: '@ezdesign/full/blog_post.html.twig' + controller: App\Controller\BlogController::showBlogPostAction + match: + Identifier\ContentType: [blog_post] + terms: + template: '@ezdesign/full/terms_and_conditions.html.twig' + match: + Id\Content: 144 + line: + article: + template: '@ezdesign/line/article.html.twig' + match: + Identifier\ContentType: [article] + params: + custom_variable_per_view: 'variable_value' + twig_variables: + custom_variable: 'variable_value' diff --git a/code_samples/front/render_content/templates/themes/my_theme/fields/author.html.twig b/code_samples/front/render_content/templates/themes/my_theme/fields/author.html.twig new file mode 100644 index 0000000000..1554b2dd53 --- /dev/null +++ b/code_samples/front/render_content/templates/themes/my_theme/fields/author.html.twig @@ -0,0 +1,7 @@ +{% block ezauthor_field %} +{% if field.value.authors|length() > 0 %} + {% for author in field.value.authors %} + {{ author.name }} + {% endfor %} +{% endif %} +{% endblock %} diff --git a/code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig b/code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig new file mode 100644 index 0000000000..7cde90dd7b --- /dev/null +++ b/code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig @@ -0,0 +1,23 @@ +{% extends '@ezdesign/pagelayout.html.twig' %} + +{% block content %} +

    {{ ez_content_name(content) }}

    + +{{ content.contentInfo.publishedDate|ez_full_datetime }} + +{{ ez_render_field(content, 'intro') }} + +{{ ez_render_field(content, 'body', { + 'attr': { + class: 'article-body' + } +}) }} + +{{ ez_render_field(content, 'author', { + 'template': '@ezdesign/fields/author.html.twig' +}) }} + +{{ my_variable }} + +{{ another_variable }} +{% endblock %} diff --git a/code_samples/front/render_content/templates/themes/my_theme/pagelayout.html.twig b/code_samples/front/render_content/templates/themes/my_theme/pagelayout.html.twig new file mode 100644 index 0000000000..f1a0a3edad --- /dev/null +++ b/code_samples/front/render_content/templates/themes/my_theme/pagelayout.html.twig @@ -0,0 +1,5 @@ +{{ encore_entry_link_tags('style') }} + +{% block content %} +{% endblock %} +
    diff --git a/code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml b/code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml new file mode 100644 index 0000000000..28f0e04bda --- /dev/null +++ b/code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml @@ -0,0 +1,19 @@ +ezplatform_page_fieldtype: + layouts: + sidebar: + identifier: sidebar + name: Right sidebar + description: Main section with sidebar on the right + thumbnail: /assets/images/layouts/sidebar.png + template: '@ezdesign/layouts/sidebar.html.twig' + zones: + first: + name: First zone + second: + name: Second zone + blocks: + contentlist: + views: + custom: + template: '@ezdesign/blocks/contentlist.html.twig' + name: Custom content list diff --git a/code_samples/front/render_page/config/packages/views.yaml b/code_samples/front/render_page/config/packages/views.yaml new file mode 100644 index 0000000000..c8b27c1425 --- /dev/null +++ b/code_samples/front/render_page/config/packages/views.yaml @@ -0,0 +1,8 @@ +ezdesign: + design_list: + my_design: [my_theme] + +ezplatform: + system: + site_group: + design: my_design diff --git a/code_samples/front/render_page/templates/themes/my_theme/blocks/contentlist.html.twig b/code_samples/front/render_page/templates/themes/my_theme/blocks/contentlist.html.twig new file mode 100644 index 0000000000..09512b9544 --- /dev/null +++ b/code_samples/front/render_page/templates/themes/my_theme/blocks/contentlist.html.twig @@ -0,0 +1,12 @@ +
    +

    {{ parentName }}

    + {% if contentArray|length > 0 %} +
    + {% for content in contentArray %} + + {% endfor %} +
    + {% endif %} +
    diff --git a/code_samples/front/render_page/templates/themes/my_theme/layouts/sidebar.html.twig b/code_samples/front/render_page/templates/themes/my_theme/layouts/sidebar.html.twig new file mode 100644 index 0000000000..fe93d21b69 --- /dev/null +++ b/code_samples/front/render_page/templates/themes/my_theme/layouts/sidebar.html.twig @@ -0,0 +1,32 @@ +
    +
    + {% if zones[0].blocks %} + {% for block in zones[0].blocks %} +
    + {{ render_esi(controller('EzSystems\\EzPlatformPageFieldTypeBundle\\Controller\\BlockController::renderAction', { + 'contentId': contentInfo.id, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo, + 'languageCode': field.languageCode + })) + }} +
    + {% endfor %} + {% endif %} +
    +
    + {% if zones[1].blocks %} + {% for block in zones[1].blocks %} +
    + {{ render_esi(controller('EzSystems\\EzPlatformPageFieldTypeBundle\\Controller\\BlockController::renderAction', { + 'contentId': contentInfo.id, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo, + 'languageCode': field.languageCode + })) + }} +
    + {% endfor %} + {% endif %} +
    +
    diff --git a/code_samples/front/shop/embed_product/config/packages/views.yaml b/code_samples/front/shop/embed_product/config/packages/views.yaml new file mode 100644 index 0000000000..c599993788 --- /dev/null +++ b/code_samples/front/shop/embed_product/config/packages/views.yaml @@ -0,0 +1,23 @@ +ezdesign: + design_list: + my_design: [my_theme, eshop_base_theme, eshop_ui_base_theme, transaction_theme, checkout_base_theme] + templates_theme_paths: + eshop_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views' + eshop_ui_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-shop-ui/src/bundle/Resources/views/themes/standard' + checkout_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-checkout/src/bundle/Resources/views/themes/standard/' + transaction_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-transaction/src/Siso/Bundle/ShopFrontendBundle/Resources/views/themes/standard/' + +ezplatform: + system: + site: + design: my_design + content_view: + embed: + product_card: + template: '@ezdesign/embed/product_card.html.twig' + match: + Identifier\ContentType: 'ses_product' diff --git a/code_samples/front/shop/embed_product/templates/themes/my_theme/Catalog/Subrequests/product.html.twig b/code_samples/front/shop/embed_product/templates/themes/my_theme/Catalog/Subrequests/product.html.twig new file mode 100644 index 0000000000..c948a331df --- /dev/null +++ b/code_samples/front/shop/embed_product/templates/themes/my_theme/Catalog/Subrequests/product.html.twig @@ -0,0 +1,5 @@ +
    +
    + {{ ses_render_price(catalogElement, catalogElement.price) }} +
    +
    diff --git a/code_samples/front/shop/embed_product/templates/themes/my_theme/embed/product_card.html.twig b/code_samples/front/shop/embed_product/templates/themes/my_theme/embed/product_card.html.twig new file mode 100644 index 0000000000..f44d0f0b8c --- /dev/null +++ b/code_samples/front/shop/embed_product/templates/themes/my_theme/embed/product_card.html.twig @@ -0,0 +1,27 @@ + diff --git a/code_samples/front/shop/override_navigation/config/packages/design.yaml b/code_samples/front/shop/override_navigation/config/packages/design.yaml new file mode 100644 index 0000000000..2c37b1e9f7 --- /dev/null +++ b/code_samples/front/shop/override_navigation/config/packages/design.yaml @@ -0,0 +1,12 @@ +ezdesign: + design_list: + my_design: [my_theme, eshop_base_theme, eshop_ui_base_theme, transaction_theme, checkout_base_theme] + templates_theme_paths: + eshop_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views' + eshop_ui_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-shop-ui/src/bundle/Resources/views/themes/standard' + checkout_base_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-checkout/src/bundle/Resources/views/themes/base_theme/' + transaction_theme: + - '%kernel.project_dir%/vendor/ezsystems/ezcommerce-transaction/src/Siso/Bundle/ShopFrontendBundle/Resources/views/themes/base_theme/' diff --git a/code_samples/front/shop/override_navigation/templates/themes/my_theme/Navigation/left_menu.html.twig b/code_samples/front/shop/override_navigation/templates/themes/my_theme/Navigation/left_menu.html.twig new file mode 100644 index 0000000000..06deea51dc --- /dev/null +++ b/code_samples/front/shop/override_navigation/templates/themes/my_theme/Navigation/left_menu.html.twig @@ -0,0 +1,10 @@ +{{ fos_httpcache_tag('siso_menu') }} + diff --git a/code_samples/front/shop/render_product/templates/themes/my_theme/Catalog/product_layout.html.twig b/code_samples/front/shop/render_product/templates/themes/my_theme/Catalog/product_layout.html.twig new file mode 100644 index 0000000000..7e5aec1916 --- /dev/null +++ b/code_samples/front/shop/render_product/templates/themes/my_theme/Catalog/product_layout.html.twig @@ -0,0 +1,32 @@ +{% extends '@ezdesign/pagelayout_front.html.twig' %} + +{% block all_content %} +
    +
    +
    +

    {{ catalogElement.name }}

    + + {% if catalogElement.sku is defined %} +
    {{ 'product.sku'|trans({'%sku%': catalogElement.sku})|desc('SKU: %sku%') }}
    + {% endif %} + + + + {% if catalogElement.shortDescription %} +
    {{ catalogElement.shortDescription|raw }}
    + {% endif %} + + {% if catalogElement.longDescription %} +
    {{ catalogElement.longDescription|raw }}
    + {% endif %} + +
    +
    + {% block sidebar %}{% endblock %} +
    +
    +{% endblock %} + +{% block javascripts %} + {{ parent() }} +{% endblock %} diff --git a/code_samples/front/view_matcher/config/packages/views.yaml b/code_samples/front/view_matcher/config/packages/views.yaml new file mode 100644 index 0000000000..7733cd4472 --- /dev/null +++ b/code_samples/front/view_matcher/config/packages/views.yaml @@ -0,0 +1,10 @@ +ezplatform: + system: + site_group: + content_view: + full: + editor_articles: + template: '@ezdesign/full/featured_article.html.twig' + match: + Identifier\ContentType: article + \App\View\Matcher\Owner: [johndoe, janedoe] diff --git a/code_samples/front/view_matcher/src/View/Matcher/Owner.php b/code_samples/front/view_matcher/src/View/Matcher/Owner.php new file mode 100644 index 0000000000..923513f501 --- /dev/null +++ b/code_samples/front/view_matcher/src/View/Matcher/Owner.php @@ -0,0 +1,49 @@ +hasOwner($location->getContentInfo()); + } + + public function matchContentInfo(ContentInfo $contentInfo) + { + return $this->hasOwner($contentInfo); + } + + public function match(View $view): ?bool + { + $location = null; + + if ($view instanceof LocationValueView) { + return $this->matchLocation($view->getLocation()); + } + + if ($view instanceof ContentValueView) { + return $this->matchContentInfo($view->getContent()->contentInfo); + } + + return false; + } + + private function hasOwner(ContentInfo $contentInfo): bool + { + $owner = $this->getRepository()->getUserService()->loadUser($contentInfo->ownerId); + + if (\array_key_exists($owner->login, $this->values)) { + return true; + } + + return false; + } +} diff --git a/code_samples/page/custom_attribute/config/packages/help_messages.yaml b/code_samples/page/custom_attribute/config/packages/help_messages.yaml new file mode 100644 index 0000000000..a27b7fe165 --- /dev/null +++ b/code_samples/page/custom_attribute/config/packages/help_messages.yaml @@ -0,0 +1,36 @@ +ezplatform_page_fieldtype: + blocks: + slider: + category: default + thumbnail: '/bundles/ibexaadminui/img/ibexa-icons.svg' + views: + default: { name: 'Default block layout', template: 'themes/blocks/slider.html.twig', priority: -255 } + attributes: + group: + name: Group name + type: nested_attribute + options: + help: + text: 'Root class text' + html: true # true|false + attr: + class: 'root-class-1 root-class-2' + attributes: + integer: + name: Age + type: integer + validators: + not_blank: + message: 'Provide a value' + options: + help: + text: 'Nested attribute text' + html: true + attr: + class: 'nested-1 nested-2' + string: + name: Name + type: string + validators: + not_blank: + message: 'Provide a value' \ No newline at end of file diff --git a/code_samples/page/custom_attribute/config/packages/page_blocks.yaml b/code_samples/page/custom_attribute/config/packages/page_blocks.yaml new file mode 100644 index 0000000000..2180aa3587 --- /dev/null +++ b/code_samples/page/custom_attribute/config/packages/page_blocks.yaml @@ -0,0 +1,15 @@ +ezplatform_page_fieldtype: + blocks: + my_block: + name: MyBlock + category: default + thumbnail: /bundles/ibexaplatformicons/img/all-icons.svg#edit + views: + default: + name: Default block layout + template: my_block.html.twig + priority: -255 + attributes: + my_string_attribute: + type: my_string + name: MyString diff --git a/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeMapper.php b/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeMapper.php new file mode 100644 index 0000000000..3f2faabae4 --- /dev/null +++ b/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeMapper.php @@ -0,0 +1,36 @@ +create( + 'value', + MyStringAttributeType::class, + [ + 'constraints' => $constraints, + ] + ); + } +} diff --git a/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeType.php b/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeType.php new file mode 100644 index 0000000000..9a9fe33d03 --- /dev/null +++ b/code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeType.php @@ -0,0 +1,19 @@ +context->buildViolation($constraint->message) + ->setParameter('{{ string }}', $value) + ->addViolation(); + } + } +} diff --git a/code_samples/page/custom_page_block/config/packages/nested_attribute.yaml b/code_samples/page/custom_page_block/config/packages/nested_attribute.yaml new file mode 100644 index 0000000000..2693e4fdb4 --- /dev/null +++ b/code_samples/page/custom_page_block/config/packages/nested_attribute.yaml @@ -0,0 +1,26 @@ +ezplatform_page_fieldtype: + blocks: + block_name: + category: default + thumbnail: 'path/icons.svg' + views: + default: { name: 'Default block layout', template: 'template.html.twig', priority: -255 } + attributes: + group: + name: Group name + type: nested_attribute + options: + attributes: + attribute_1: + name: Name 1 + type: type + validators: + not_blank: + message: 'Provide a value' + attribute_2: + name: Name 2 + type: type + multiple: true + validators: + not_blank: + message: 'Provide a value' \ No newline at end of file diff --git a/code_samples/page/custom_page_block/config/packages/page_blocks.yaml b/code_samples/page/custom_page_block/config/packages/page_blocks.yaml new file mode 100644 index 0000000000..f823e3da02 --- /dev/null +++ b/code_samples/page/custom_page_block/config/packages/page_blocks.yaml @@ -0,0 +1,39 @@ +ezplatform_page_fieldtype: + blocks: + event: + name: Event Block + category: Custom + thumbnail: /bundles/ibexaplatformicons/img/all-icons.svg#date + configuration_template: '@ezdesign/blocks/event/config.html.twig' + views: + default: + template: '@ezdesign/blocks/event/template.html.twig' + name: Default view + priority: -255 + featured: + template: '@ezdesign/blocks/event/featured_template.html.twig' + name: Featured view + priority: 50 + attributes: + name: + type: text + name: Event name + validators: + not_blank: + message: Please provide a name + category: + type: select + name: Select a category + value: visual + options: + multiple: true + choices: + 'Music': music + 'Visual arts': visual + 'Sports': sports + event: + type: embed + name: Event + validators: + not_blank: + message: Please select an event diff --git a/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/config.html.twig b/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/config.html.twig new file mode 100644 index 0000000000..ae4b619279 --- /dev/null +++ b/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/config.html.twig @@ -0,0 +1,18 @@ +{% extends '@EzPlatformPageBuilder/page_builder/block/config.html.twig' %} + +{% block basic_tab_content %} +
    + {{ form_row(form.name) }} + {% if attributes_per_category['default'] is defined %} +
      + {% for identifier in attributes_per_category['default'] %} + {% block config_entry %} +
    1. + {{ form_row(form.attributes[identifier]) }} +
    2. + {% endblock %} + {% endfor %} +
    + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/featured_template.html.twig b/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/featured_template.html.twig new file mode 100644 index 0000000000..261062557e --- /dev/null +++ b/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/featured_template.html.twig @@ -0,0 +1,6 @@ +

    {{ name }}

    +

    {{ category }}

    +{{ render(controller('ez_content::viewAction', { + 'contentId': event, + 'viewType': 'embed' +})) }} diff --git a/material/__init__.py b/code_samples/page/custom_page_block/templates/themes/standard/blocks/event/template.html.twig similarity index 100% rename from material/__init__.py rename to code_samples/page/custom_page_block/templates/themes/standard/blocks/event/template.html.twig diff --git a/code_samples/page/page_listener/config/packages/page_blocks.yaml b/code_samples/page/page_listener/config/packages/page_blocks.yaml new file mode 100644 index 0000000000..5194a1f2cb --- /dev/null +++ b/code_samples/page/page_listener/config/packages/page_blocks.yaml @@ -0,0 +1,15 @@ +ezplatform_page_fieldtype: + blocks: + my_block: + name: My Block + category: default + thumbnail: images/thumbnails/my_block.svg + views: + default: + name: Default block layout + template: my_block.html.twig + priority: -255 + attributes: + my_text_attribute: + type: text + name: My text attribute diff --git a/code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php b/code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php new file mode 100644 index 0000000000..22ef493001 --- /dev/null +++ b/code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php @@ -0,0 +1,28 @@ + 'onBlockPreRender', + ]; + } + + public function onBlockPreRender(PreRenderEvent $event) + { + $renderRequest = $event->getRenderRequest(); + + $parameters = $event->getRenderRequest()->getParameters(); + + $parameters['my_parameter'] = 'parameter_value'; + + $renderRequest->setParameters($parameters); + } +} diff --git a/code_samples/page/page_listener/templates/my_block.html.twig b/code_samples/page/page_listener/templates/my_block.html.twig new file mode 100644 index 0000000000..903e7c4622 --- /dev/null +++ b/code_samples/page/page_listener/templates/my_block.html.twig @@ -0,0 +1,3 @@ +
    + {{ my_parameter }} +
    diff --git a/code_samples/search/elasticsearch/config/aggregation_services.yaml b/code_samples/search/elasticsearch/config/aggregation_services.yaml new file mode 100644 index 0000000000..744678d615 --- /dev/null +++ b/code_samples/search/elasticsearch/config/aggregation_services.yaml @@ -0,0 +1,10 @@ +services: + App\Query\Aggregation\Elasticsearch\PriorityAggregationVisitor: + tags: + - { name: ezplatform.search.elasticsearch.query.location.aggregation_visitor } + - { name: ezplatform.search.elasticsearch.query.content.aggregation_visitor } + + App\Query\Aggregation\Elasticsearch\PriorityAggregationResultExtractor: + tags: + - { name: ezplatform.search.elasticsearch.query.location.aggregation_result_extractor } + - { name: ezplatform.search.elasticsearch.query.content.aggregation_result_extractor } diff --git a/code_samples/search/elasticsearch/config/criterion_services.yaml b/code_samples/search/elasticsearch/config/criterion_services.yaml new file mode 100644 index 0000000000..f0f907a00c --- /dev/null +++ b/code_samples/search/elasticsearch/config/criterion_services.yaml @@ -0,0 +1,5 @@ +services: + App\Query\Criterion\Elasticsearch\CameraManufacturerVisitor: + tags: + - { name: ezpublish.search.elasticsearch.query.content.criterion_visitor } + - { name: ezpublish.search.elasticsearch.query.location.criterion_visitor } diff --git a/code_samples/search/elasticsearch/config/group_resolver_services.yaml b/code_samples/search/elasticsearch/config/group_resolver_services.yaml new file mode 100644 index 0000000000..b56e3ad3de --- /dev/null +++ b/code_samples/search/elasticsearch/config/group_resolver_services.yaml @@ -0,0 +1,4 @@ +services: + App\GroupResolver\ContentTypeGroupGroupResolver: + arguments: + $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' diff --git a/code_samples/search/elasticsearch/config/packages/elasticsearch.yaml b/code_samples/search/elasticsearch/config/packages/elasticsearch.yaml new file mode 100644 index 0000000000..dc73899009 --- /dev/null +++ b/code_samples/search/elasticsearch/config/packages/elasticsearch.yaml @@ -0,0 +1,2 @@ +ezplatform_elastic_search_engine: + document_group_resolver: 'App\GroupResolver\ContentTypeGroupGroupResolver' diff --git a/code_samples/search/elasticsearch/config/sort_clause_services.yaml b/code_samples/search/elasticsearch/config/sort_clause_services.yaml new file mode 100644 index 0000000000..efaf049bca --- /dev/null +++ b/code_samples/search/elasticsearch/config/sort_clause_services.yaml @@ -0,0 +1,5 @@ +services: + App\Query\SortClause\Elasticsearch\ScoreVisitor: + tags: + - { name: ezpublish.search.elasticsearch.query.content.sort_clause_visitor } + - { name: ezpublish.search.elasticsearch.query.location.sort_clause_visitor } diff --git a/code_samples/search/elasticsearch/src/GroupResolver/ContentTypeGroupGroupResolver.php b/code_samples/search/elasticsearch/src/GroupResolver/ContentTypeGroupGroupResolver.php new file mode 100644 index 0000000000..3ad8cd1b3e --- /dev/null +++ b/code_samples/search/elasticsearch/src/GroupResolver/ContentTypeGroupGroupResolver.php @@ -0,0 +1,26 @@ +contentTypeHandler = $contentTypeHandler; + } + + public function resolveDocumentGroup(BaseDocument $document): string + { + $index = $this->contentTypeHandler->load($document->contentTypeId)->groupIds[0]; + + return (string)$index; + } +} diff --git a/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationResultExtractor.php b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationResultExtractor.php new file mode 100644 index 0000000000..5d84585601 --- /dev/null +++ b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationResultExtractor.php @@ -0,0 +1,32 @@ +getName(), $entries); + } +} diff --git a/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationVisitor.php b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationVisitor.php new file mode 100644 index 0000000000..c7f419e9b1 --- /dev/null +++ b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationVisitor.php @@ -0,0 +1,51 @@ +getRanges() as $range) { + if ($range->getFrom() !== null && $range->getTo() !== null) { + $ranges[] = [ + 'from' => $range->getFrom(), + 'to' => $range->getTo(), + ]; + } elseif ($range->getFrom() === null && $range->getTo() !== null) { + $ranges[] = [ + 'to' => $range->getTo(), + ]; + } elseif ($range->getFrom() !== null && $range->getTo() === null) { + $ranges[] = [ + 'from' => $range->getFrom(), + ]; + } else { + // invalid range + } + } + + return [ + 'range' => [ + 'field' => 'priority_i', + 'ranges' => $ranges, + ], + ]; + } +} diff --git a/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityRangeAggregation.php b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityRangeAggregation.php new file mode 100644 index 0000000000..a86aa3cabf --- /dev/null +++ b/code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityRangeAggregation.php @@ -0,0 +1,12 @@ + [ + 'exif_camera_manufacturer_id' => (array)$criterion->value, + ], + ]; + } +} diff --git a/code_samples/search/elasticsearch/src/Query/SortClause/Elasticsearch/ScoreSortClause.php b/code_samples/search/elasticsearch/src/Query/SortClause/Elasticsearch/ScoreSortClause.php new file mode 100644 index 0000000000..c5ff5c43ec --- /dev/null +++ b/code_samples/search/elasticsearch/src/Query/SortClause/Elasticsearch/ScoreSortClause.php @@ -0,0 +1,16 @@ +direction === Query::SORT_ASC ? 'asc' : 'desc'; + + return [ + '_score' => [ + 'order' => $order, + ], + ]; + } +} diff --git a/code_samples/search/solr/config/aggregation_services.yaml b/code_samples/search/solr/config/aggregation_services.yaml new file mode 100644 index 0000000000..11ef49fbd9 --- /dev/null +++ b/code_samples/search/solr/config/aggregation_services.yaml @@ -0,0 +1,10 @@ +services: + App\Query\Aggregation\Solr\PriorityRangeAggregationVisitor: + tags: + - { name: ezplatform.search.solr.query.location.aggregation_visitor } + - { name: ezplatform.search.solr.query.content.aggregation_visitor } + + App\Query\Aggregation\Solr\PriorityAggregationResultExtractor: + tags: + - { name: ezplatform.search.solr.query.location.aggregation_result_extractor } + - { name: ezplatform.search.solr.query.content.aggregation_result_extractor } diff --git a/code_samples/search/solr/config/criterion_services.yaml b/code_samples/search/solr/config/criterion_services.yaml new file mode 100644 index 0000000000..135dee59e7 --- /dev/null +++ b/code_samples/search/solr/config/criterion_services.yaml @@ -0,0 +1,5 @@ +services: + App\Query\Criterion\Solr\CameraManufacturerVisitor: + tags: + - { name: ezpublish.search.solr.query.content.criterion_visitor } + - { name: ezpublish.search.solr.query.location.criterion_visitor } diff --git a/code_samples/search/solr/config/field_mapper_services.yaml b/code_samples/search/solr/config/field_mapper_services.yaml new file mode 100644 index 0000000000..ba7bbef776 --- /dev/null +++ b/code_samples/search/solr/config/field_mapper_services.yaml @@ -0,0 +1,7 @@ +services: + App\Search\Mapper\WebinarEventTitleFulltextFieldMapper: + arguments: + - '@ezpublish.spi.persistence.content_handler' + - '@ezpublish.spi.persistence.location_handler' + tags: + - {name: ezpublish.search.solr.field_mapper.content} diff --git a/code_samples/search/solr/config/sort_clause_services.yaml b/code_samples/search/solr/config/sort_clause_services.yaml new file mode 100644 index 0000000000..4a9c6d9f9e --- /dev/null +++ b/code_samples/search/solr/config/sort_clause_services.yaml @@ -0,0 +1,5 @@ +services: + App\Query\SortClause\Solr\ScoreVisitor: + tags: + - { name: ezpublish.search.solr.query.content.sort_clause_visitor } + - { name: ezpublish.search.solr.query.location.sort_clause_visitor } diff --git a/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityAggregationResultExtractor.php b/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityAggregationResultExtractor.php new file mode 100644 index 0000000000..f20cb3ce9f --- /dev/null +++ b/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityAggregationResultExtractor.php @@ -0,0 +1,41 @@ + $bucket) { + if ($key === 'count' || strpos($key, '_') === false) { + continue; + } + [$from, $to] = explode('_', $key, 2); + $entries[] = new RangeAggregationResultEntry( + new Range( + $from !== '*' ? $from : null, + $to !== '*' ? $to : null + ), + $bucket->count + ); + } + + return new RangeAggregationResult($aggregation->getName(), $entries); + } +} diff --git a/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityRangeAggregation.php b/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityRangeAggregation.php new file mode 100644 index 0000000000..7f2d0810b5 --- /dev/null +++ b/code_samples/search/solr/src/Query/Aggregation/Solr/PriorityRangeAggregation.php @@ -0,0 +1,12 @@ +getRanges() as $range) { + $from = $this->formatRangeValue($range->getFrom()); + $to = $this->formatRangeValue($range->getTo()); + $rangeFacets["${from}_${to}"] = [ + 'type' => 'query', + 'q' => sprintf('priority_i:[%s TO %s}', $from, $to), + ]; + } + + return [ + 'type' => 'query', + 'q' => '*:*', + 'facet' => $rangeFacets, + ]; + } + + private function formatRangeValue($value): string + { + if ($value === null) { + return '*'; + } + + return (string)$value; + } +} diff --git a/code_samples/search/solr/src/Query/Criterion/Solr/CameraManufacturerCriterion.php b/code_samples/search/solr/src/Query/Criterion/Solr/CameraManufacturerCriterion.php new file mode 100644 index 0000000000..91c32cd940 --- /dev/null +++ b/code_samples/search/solr/src/Query/Criterion/Solr/CameraManufacturerCriterion.php @@ -0,0 +1,36 @@ +escapeQuote($value) . '"'; + }, + $criterion->value + ); + + return '(' . implode(' OR ', $expressions) . ')'; + } +} diff --git a/code_samples/search/solr/src/Query/SortClause/Solr/ScoreSortClause.php b/code_samples/search/solr/src/Query/SortClause/Solr/ScoreSortClause.php new file mode 100644 index 0000000000..4e7b7ca11f --- /dev/null +++ b/code_samples/search/solr/src/Query/SortClause/Solr/ScoreSortClause.php @@ -0,0 +1,16 @@ +getDirection($sortClause); + } +} diff --git a/code_samples/search/solr/src/Search/FieldMapper/WebinarEventTitleFulltextFieldMapper.php b/code_samples/search/solr/src/Search/FieldMapper/WebinarEventTitleFulltextFieldMapper.php new file mode 100644 index 0000000000..a50759986b --- /dev/null +++ b/code_samples/search/solr/src/Search/FieldMapper/WebinarEventTitleFulltextFieldMapper.php @@ -0,0 +1,56 @@ +contentHandler = $contentHandler; + $this->locationHandler = $locationHandler; + } + + public function accept(Content $content) + { + // ContentType with ID 42 is webinar event + return $content->versionInfo->contentInfo->contentTypeId == 42; + } + + public function mapFields(Content $content) + { + $mainLocationId = $content->versionInfo->contentInfo->mainLocationId; + $location = $this->locationHandler->load($mainLocationId); + $parentLocation = $this->locationHandler->load($location->parentId); + $parentContentInfo = $this->contentHandler->loadContentInfo($parentLocation->contentId); + + return [ + new Search\Field( + 'fulltext', + $parentContentInfo->name, + new Search\FieldType\FullTextField() + ), + ]; + } +} diff --git a/code_samples/templates/bundles/EzPlatformFormBuilderBundle/fields/config/form_fields.html.twig b/code_samples/templates/bundles/EzPlatformFormBuilderBundle/fields/config/form_fields.html.twig new file mode 100644 index 0000000000..f3d037e9d4 --- /dev/null +++ b/code_samples/templates/bundles/EzPlatformFormBuilderBundle/fields/config/form_fields.html.twig @@ -0,0 +1,32 @@ +{% extends "@!EzPlatformFormBuilder/fields/config/form_fields.html.twig" %} + +{% block _field_configuration_attributes_richtext_description_row %} +
    +
    +
    +
    +
    +
    + {% set udw_context = { + 'languageCode': 'en', + } %} + + {{ form_errors(form) }} + {{ form_widget(form, {'attr': {'class': 'vidar-textarea d-none'}}) }} + + +
    + + {{ form_rest(form) }} + +
    + {{ encore_entry_link_tags('ezplatform-richtext-onlineeditor-css', null, 'ezplatform') }} + + {{ encore_entry_script_tags('ezplatform-richtext-onlineeditor-js', null, 'ezplatform') }} + {{ encore_entry_script_tags('formbuilder-richtext-checkbox-js') }} +
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/code_samples/templates/formtheme/formbuilder_checkbox_with_richtext_description.html.twig b/code_samples/templates/formtheme/formbuilder_checkbox_with_richtext_description.html.twig new file mode 100644 index 0000000000..c0589177c4 --- /dev/null +++ b/code_samples/templates/formtheme/formbuilder_checkbox_with_richtext_description.html.twig @@ -0,0 +1,6 @@ +{% block checkbox_with_richtext_description_row %} + {{ form_label(form)}} + {{ form_errors(form) }} + {{ form_widget(form) }} + {{ form.vars.richtextDescription|ez_richtext_to_html5() }} +{% endblock %} \ No newline at end of file diff --git a/code_samples/user_management/oauth_google/config/packages/knpu_oauth2_client.yaml b/code_samples/user_management/oauth_google/config/packages/knpu_oauth2_client.yaml new file mode 100644 index 0000000000..b4777fabff --- /dev/null +++ b/code_samples/user_management/oauth_google/config/packages/knpu_oauth2_client.yaml @@ -0,0 +1,11 @@ +knpu_oauth2_client: + clients: + # Configure your clients as described here: https://github.com/knpuniversity/oauth2-client-bundle#configuration + google: + type: google + client_id: '%env(OAUTH_GOOGLE_CLIENT_ID)%' + client_secret: '%env(OAUTH_GOOGLE_CLIENT_SECRET)%' + # Redirect route: + redirect_route: ibexa.oauth2.check + redirect_params: + identifier: google diff --git a/code_samples/user_management/oauth_google/config/packages/oauth.yaml b/code_samples/user_management/oauth_google/config/packages/oauth.yaml new file mode 100644 index 0000000000..056810ffa3 --- /dev/null +++ b/code_samples/user_management/oauth_google/config/packages/oauth.yaml @@ -0,0 +1,6 @@ +ezplatform: + system: + admin: + oauth2: + enabled: true + clients: ['google'] diff --git a/code_samples/user_management/oauth_google/config/packages/security.yaml b/code_samples/user_management/oauth_google/config/packages/security.yaml new file mode 100644 index 0000000000..3c0e2fa35e --- /dev/null +++ b/code_samples/user_management/oauth_google/config/packages/security.yaml @@ -0,0 +1,46 @@ +# To get started with security, check out the documentation: +# https://symfony.com/doc/current/security.html +security: + # https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded + providers: + ezplatform: + id: ezpublish.security.user_provider + #! in_memory: + #! memory: ~ + + firewalls: + # disables authentication for assets and the profiler, adapt it according to your needs + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + + ezpublish_forgot_password: + pattern: /user/(forgot-password|reset-password) + security: false + + oauth2_connect: + pattern: /oauth2/connect/* + security: false + + ezpublish_front: + pattern: ^/ + user_checker: eZ\Publish\Core\MVC\Symfony\Security\UserChecker + anonymous: ~ + ezpublish_rest_session: ~ + guard: + authenticators: + - 'Ibexa\Platform\Bundle\OAuth2Client\Security\Authenticator\OAuth2Authenticator' + form_login: + require_previous_session: false + csrf_token_generator: security.csrf.token_manager + logout: ~ + + main: + anonymous: ~ + # activate different ways to authenticate + + # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate + #http_basic: ~ + + # https://symfony.com/doc/current/security/form_login_setup.html + #form_login: ~ diff --git a/code_samples/user_management/oauth_google/config/services.yaml b/code_samples/user_management/oauth_google/config/services.yaml new file mode 100644 index 0000000000..e9c74def0d --- /dev/null +++ b/code_samples/user_management/oauth_google/config/services.yaml @@ -0,0 +1,43 @@ +# This file is the entry point to configure your own services. +# Files in the packages/ subdirectory configure your dependencies. + +# Put parameters here that don't need to change on each machine where the app is deployed +# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration +parameters: + google_recaptcha_site_key: '%env(GOOGLE_RECAPTCHA_SITE_KEY)%' + +services: + # default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: '../src/' + exclude: + - '../src/DependencyInjection/' + - '../src/Entity/' + - '../src/Kernel.php' + - '../src/Tests/' + + # controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + App\Controller\: + resource: '../src/Controller/' + tags: ['controller.service_arguments'] + + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones + + App\OAuth\GoogleResourceOwnerMapper: + tags: + - { name: ibexa.oauth2_client.resource_owner_mapper, identifier: google } + + app.components.oauth2_login: + parent: EzSystems\EzPlatformAdminUi\Component\TwigComponent + arguments: + $template: '@@ezdesign/account/login/oauth2_login.html.twig' + tags: + - { name: ezplatform.admin_ui.component, group: login-form-after } diff --git a/code_samples/user_management/oauth_google/src/OAuth/GoogleResourceOwnerMapper.php b/code_samples/user_management/oauth_google/src/OAuth/GoogleResourceOwnerMapper.php new file mode 100644 index 0000000000..ee33690ce8 --- /dev/null +++ b/code_samples/user_management/oauth_google/src/OAuth/GoogleResourceOwnerMapper.php @@ -0,0 +1,108 @@ +userService = $userService; + $this->languageResolver = $languageResolver; + $this->contentTypeIdentifier = $contentTypeIdentifier; + $this->parentGroupRemoteId = $parentGroupRemoteId; + } + + /** + * @param \League\OAuth2\Client\Provider\GoogleUser $resourceOwner + */ + protected function loadUser( + ResourceOwnerInterface $resourceOwner, + UserProviderInterface $userProvider + ): ?UserInterface { + return $userProvider->loadUserByUsername($this->getUsername($resourceOwner)); + } + + /** + * @param \League\OAuth2\Client\Provider\GoogleUser $resourceOwner + */ + protected function createUser( + ResourceOwnerInterface $resourceOwner, + UserProviderInterface $userProvider + ): ?UserInterface { + $userCreateStruct = $this->userService->newOAuth2UserCreateStruct( + $this->getUsername($resourceOwner), + $resourceOwner->getEmail(), + $this->getMainLanguageCode(), + $this->getOAuth2UserContentType($this->repository) + ); + + $userCreateStruct->setField('first_name', $resourceOwner->getFirstName()); + $userCreateStruct->setField('last_name', $resourceOwner->getLastName()); + + $parentGroups = []; + if ($this->parentGroupRemoteId !== null) { + $parentGroups[] = $this->userService->loadUserGroupByRemoteId($this->parentGroupRemoteId); + } + + $this->userService->createUser($userCreateStruct, $parentGroups); + + return $userProvider->loadUserByUsername($this->getUsername($resourceOwner)); + } + + private function getOAuth2UserContentType(Repository $repository): ?ContentType + { + if ($this->contentTypeIdentifier !== null) { + $contentTypeService = $repository->getContentTypeService(); + + return $contentTypeService->loadContentTypeByIdentifier( + $this->contentTypeIdentifier + ); + } + + return null; + } + + private function getMainLanguageCode(): string + { + // Get first prioritized language for current scope + return $this->languageResolver->getPrioritizedLanguages()[0]; + } + + private function getUsername(GoogleUser $resourceOwner): string + { + return self::PROVIDER_PREFIX . $resourceOwner->getId(); + } +} diff --git a/code_samples/user_management/oauth_google/templates/themes/admin/account/login/oauth2_login.html.twig b/code_samples/user_management/oauth_google/templates/themes/admin/account/login/oauth2_login.html.twig new file mode 100644 index 0000000000..9ce4972e5c --- /dev/null +++ b/code_samples/user_management/oauth_google/templates/themes/admin/account/login/oauth2_login.html.twig @@ -0,0 +1,10 @@ +
    + +
    diff --git a/code_samples/workflow/custom_workflow/config/custom_services.yaml b/code_samples/workflow/custom_workflow/config/custom_services.yaml new file mode 100644 index 0000000000..497c8dd0a0 --- /dev/null +++ b/code_samples/workflow/custom_workflow/config/custom_services.yaml @@ -0,0 +1,7 @@ +services: + App\EventListener\LegalTransitionListener: + tags: + - { name: ezplatform.workflow.action_listener } + App\EventListener\ApprovedTransitionListener: + tags: + - { name: ezplatform.workflow.action_listener } diff --git a/code_samples/workflow/custom_workflow/config/packages/workflows.yaml b/code_samples/workflow/custom_workflow/config/packages/workflows.yaml new file mode 100644 index 0000000000..98e4aa8dcd --- /dev/null +++ b/code_samples/workflow/custom_workflow/config/packages/workflows.yaml @@ -0,0 +1,66 @@ +ezplatform: + system: + default: + workflows: + custom_workflow: + name: Custom Workflow + matchers: + content_type: [article, folder] + content_status: [draft] + stages: + draft: + label: Draft + color: '#f15a10' + legal: + label: Legal + color: '#5a10f1' + actions: + notify_reviewer: ~ + done: + label: Done + color: '#301203' + last_stage: true + initial_stage: draft + transitions: + to_legal: + from: [draft] + to: [legal] + label: To legal + color: '#8888ba' + icon: '/bundles/ibexaplatformicons/img/all-icons.svg#notice' + reviewers: + required: true + actions: + legal_transition_action: + data: + message: "Sent to the legal department" + validate: true + back_to_draft: + reverse: to_legal + label: Back to draft + color: '#cb8888' + icon: '/bundles/ibexaplatformicons/img/all-icons.svg#back' + approved_by_legal: + from: [legal] + to: [done] + label: Approved by legal + color: '#88ad88' + icon: '/bundles/ibexaplatformicons/img/all-icons.svg#checkbox' + actions: + publish: ~ + approved_transition_action: + condition: + - result.legal_transition_action == true + done: + from: [draft] + to: [done] + label: Done + color: '#88ad88' + icon: '/bundles/ibexaplatformicons/img/all-icons.svg#checkbox' + actions: + publish: ~ + validate: true + quick_review: + name: Quick Review + matchers: + content_type: [] diff --git a/code_samples/workflow/custom_workflow/src/EventListener/ApprovedTransitionListener.php b/code_samples/workflow/custom_workflow/src/EventListener/ApprovedTransitionListener.php new file mode 100644 index 0000000000..f51e4d86cc --- /dev/null +++ b/code_samples/workflow/custom_workflow/src/EventListener/ApprovedTransitionListener.php @@ -0,0 +1,36 @@ +notificationHandler = $notificationHandler; + } + + public function getIdentifier(): string + { + return 'approved_transition_action'; + } + + public function onWorkflowEvent(TransitionEvent $event): void + { + $context = $event->getContext(); + $message = $context['message']; + + $this->notificationHandler->info( + $message, + [], + 'domain' + ); + } +} diff --git a/code_samples/workflow/custom_workflow/src/EventListener/LegalTransitionListener.php b/code_samples/workflow/custom_workflow/src/EventListener/LegalTransitionListener.php new file mode 100644 index 0000000000..79bc30eae4 --- /dev/null +++ b/code_samples/workflow/custom_workflow/src/EventListener/LegalTransitionListener.php @@ -0,0 +1,38 @@ +notificationHandler = $notificationHandler; + } + + public function getIdentifier(): string + { + return 'legal_transition_action'; + } + + public function onWorkflowEvent(TransitionEvent $event): void + { + $metadata = $this->getActionMetadata($event->getWorkflow(), $event->getTransition()); + $message = $metadata['data']['message']; + + $this->notificationHandler->info( + $message, + [], + 'domain' + ); + + $this->setResult($event, true); + } +} diff --git a/composer.json b/composer.json index 6c2aee8746..61c366ddea 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,11 @@ "symfony/property-access": "^5.0" }, "require-dev": { - "ezsystems/ezplatform-code-style": "^0.1.0", - "friendsofphp/php-cs-fixer": "^2.16.0" + "ibexa/code-style": "^1.0", + "friendsofphp/php-cs-fixer": "^3.9" + }, + "scripts": { + "fix-cs": "php-cs-fixer fix --config=.php-cs-fixer.php -v --show-progress=dots", + "check-cs": "@fix-cs --dry-run" } } diff --git a/docs/api/commerce_api/business_api/baseoperation/addproducts.md b/docs/api/commerce_api/business_api/baseoperation/addproducts.md deleted file mode 100644 index 6527b4e2fd..0000000000 --- a/docs/api/commerce_api/business_api/baseoperation/addproducts.md +++ /dev/null @@ -1,145 +0,0 @@ -# addProducts - -`basket.add_products` adds one or more products to the basket. - -Exceptions: - -- `\InvalidArgumentException` - thrown if variant without `variantCode` is chosen - -## Example - -``` php -$outputGetBasket = $this->getBusinessApi()->call('basket.get_basket', $inputGetBasket); -//clear all messages for each request -$outputGetBasket->basket->clearAllMessages(); -$itemData = new ItemData( - array( - 'quantity' => '1', - 'isVariant' => '', - 'variantCode' => '', - 'sku' => '1000', - ) -); -/** @var InputAddItemToBasket $inputAddItemToBasket */ -$inputAddItemToBasket = new InputAddItemToBasket( - array( - 'itemData' => $itemData, - 'basket' => $outputGetBasket->basket, - ) -); -try { - $outputGetBasket = $this->getBusinessApi()->call('basket.add_products', $inputAddItemToBasket); -} catch (\InvalidArgumentException $e) { - // .... -} -$message = $this->getBasketMessage($outputGetBasket->basket); - -``` - -## Input parameters - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\InputValueObjects; - -class AddItemToBasket extends ValueObject -{ - - /** - * @var \Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\AddItemToBasket\ItemData - */ - protected $itemData; - /** - * @var \Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket $basket - */ - protected $basket; - /** - * @var array - */ - protected $checkProperties = array( - array( - 'name' => 'itemData', - 'mandatory' => true, - 'type' => '\Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\AddItemToBasket\ItemData', - 'isObject' => true, - ), - array( - 'name' => 'basket', - 'mandatory' => true, - 'type' => '\Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket', - 'isObject' => true - ) -} -``` - -``` php -namespace Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\AddItemToBasket; - -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; - -/** - * This class bundles all data, which is necessary for a basket item line. - * - * @property-read float $quantity - * @property-read string $isVariant - * @property-read string $variantCode - * @property-read string $sku - */ -class ItemData extends ValueObject -{ - /** - * @var string - */ - protected $quantity; - /** - * @var string - */ - protected $isVariant; - /** - * @var string - */ - protected $variantCode; - /** - * @var string - */ - protected $sku; - /** - * @var array - */ - protected $checkProperties = array( - array('name' => 'quantity', 'mandatory' => true, 'type' => 'string'), - array('name' => 'isVariant', 'mandatory' => false, 'type' => 'string'), - array('name' => 'variantCode', 'mandatory' => false, 'type' => 'string'), - array('name' => 'sku', 'mandatory' => true, 'type' => 'string'), - ); -} -``` - -## Returns output - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\OutputValueObjects; -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; -/** - * Class AddItemToBasket - * - * This class is used as an output parameter for an appropriate business method - * - * @property-read \Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket $basket - */ -class AddItemToBasket extends ValueObject -{ - /** - * @var \Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket $basket - */ - protected $basket; - /** @var array $checkProperties */ - protected $checkProperties = array( - array( - 'name' => 'basket', - 'mandatory' => true, - 'type' => '\Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket', - 'isObject' => true - ) - ); -} -``` diff --git a/docs/api/commerce_api/business_api/baseoperation/baseoperation.md b/docs/api/commerce_api/business_api/baseoperation/baseoperation.md deleted file mode 100644 index 2e95772a6c..0000000000 --- a/docs/api/commerce_api/business_api/baseoperation/baseoperation.md +++ /dev/null @@ -1,66 +0,0 @@ -# BaseOperation - -The `BaseOperation` class is used as a base class for all operation services. -It is abstract and holds dependencies to services which are commonly needed by business services. - -The following services are injected into this base class. - -| Service | Base class attribute | -| -------------------------------------------------------------------- | -------------------- | -| `Silversolutions\Bundle\EshopBundle\Services\LogService` | `$logger` | -| `Silversolutions\Bundle\TranslationBundle\Services\TransService` | `$transService` | - -Service ID: `ses_eshop.business_api.base` - -## Operation basket - -This class implements business logic for basket. - -### Methods - -| Method | Parameters | Returns | Purpose | Operation identifier | -| ------ | ----------- | ---------- | -------- | -------------------- | -| [`addProducts`](addproducts.md) | `InputAddItemToBasket $operationInput` | `OutputAddItemToBasket $operationOutput` | adds products to the basket | `basket.add_products` | -| [`getBasket`](getbasket.md) | `InputGetBasket $input` | `OutputGetBasket $output` | returns current basket | `basket.get_basket` | - -### Service definition - -``` xml - - Silversolutions\Bundle\EshopBundle\Services\BusinessLayer\Operations\Basket - - - - - - - -``` - -## Operation catalog - -This class implements business logic for catalog. - -### Methods - -|Method|Parameters|Returns|Purpose|Operation identifier| -|--- |--- |--- |--- |--- | -|`loadProducts`|`InputLoadList $input`|`OutputLoadList $input`|loads products from catalog|`catalog.load_products`| - -### Service definition - -services.business_layer.xml: - -``` xml - - Silversolutions\Bundle\EshopBundle\Services\BusinessLayer\Operations\Catalog - - - - - - - -``` diff --git a/docs/api/commerce_api/business_api/baseoperation/getbasket.md b/docs/api/commerce_api/business_api/baseoperation/getbasket.md deleted file mode 100644 index fa51dc9c6c..0000000000 --- a/docs/api/commerce_api/business_api/baseoperation/getbasket.md +++ /dev/null @@ -1,78 +0,0 @@ -# getBasket - -`basket.get_basket` gets the current basket. - -## Example - -``` php -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as InputGetBasket; -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\OutputValueObjects\GetBasket as OutputGetBasket; - -/** @var InputGetBasket $inputGetBasket */ -$inputGetBasket = new InputGetBasket(array('request' => $request)); - -/** @var OutputGetBasket $outputGetBasket */ -$outputGetBasket = $this->getBusinessApi()->call('basket.get_basket', $inputGetBasket); - -$message = $this->getBasketMessage($outputGetBasket->basket); -``` - -## Input parameters - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\InputValueObjects; - -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; - -/** - * Class GetBasket - * - * This class is used as an input parameter for an appropriate business method - * - * @property-read \Symfony\Component\HttpFoundation\Request $request - */ -class GetBasket extends ValueObject -{ - /** @var \Symfony\Component\HttpFoundation\Request $request */ - protected $request; - /** @var array $checkProperties */ - protected $checkProperties = array( - array( - 'name' => 'request', - 'mandatory' => true, - 'type' => '\Symfony\Component\HttpFoundation\Request', - 'isObject' => true - ) - ); -} -``` - -## Returns output - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\OutputValueObjects; - -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; - -/** - * Class GetBasket - * - * This class is used as an output parameter for an appropriate business method - * - * @property-read \Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket $basket - */ -class GetBasket extends ValueObject -{ - /** @var Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket $basket */ - protected $basket; - /** @var array $checkProperties */ - protected $checkProperties = array( - array( - 'name' => 'basket', - 'mandatory' => true, - 'type' => '\Ibexa\Platform\Bundle\Commerce\Checkout\Entity\Basket', - 'isObject' => true - ) - ); -} -``` diff --git a/docs/api/commerce_api/business_api/baseoperation/loadproducts.md b/docs/api/commerce_api/business_api/baseoperation/loadproducts.md deleted file mode 100644 index d591f52550..0000000000 --- a/docs/api/commerce_api/business_api/baseoperation/loadproducts.md +++ /dev/null @@ -1,207 +0,0 @@ -# loadProducts - -`catalog.load_products` loads products from storage. - -## Example call to Business API - -``` php -/** @var InputLoadList $input */ -$input = new InputLoadList( - array( - 'locationId' => 136, - 'limit' => 3, - 'offset' => 3, - 'language' => 'de_DE', - 'filterType' => 'productList', - ) -); - -/** @var OutputLoadList $output */ -$output = $this->getBusinessApi()->call('catalog.load_products', $input); - -$html = $this->renderView( - 'SilversolutionsEshopBundle:Catalog:listProductNodes.html.twig', - array( - 'catalogList' => $output->catalogList, - 'params' => $data, - 'locationId' => $output->locationId, - ) -); -``` - -## Business API `loadProducts` - -Business API calls catalog service to fetch all products and passes the `filterType` as an argument. - -``` php -public function loadProducts(InputLoadList $input) - { - list($catalogList, $catalogCount) = $this->catalogService->fetchChildrenList( - $input->locationId, - self::DEFAULT_FETCH_DEPTH, - array('filterType' => $input->filterType), - $input->language, - $input->offset, - $input->limit - ); - /** @var OutputLoadList $output */ - $output = new OutputLoadList( - array( - 'catalogList' => $catalogList, - 'catalogCount' => $catalogCount, - 'locationId' => $input->locationId - ) - ); - return $output; - } -``` - -## CatalogService - -`CatalogService` provides a method for searching catalog elements using the proper data provider. -It also gets prices and total count of elements without offset or limit. - -!!! note - - `fetchChildrenList` returns an array with two elements: - - - `$catalogList` - list of catalog elements - - `$catalogCount` - total number of elements (without offset and limit) for pagination purposes - -``` php -public function fetchChildrenList($identifier, $depth, $filter, $languages = null, $offset = 0, $limit = 3) - { - /** @var CatalogListResult $catalogList */ - $catalogList = $this->dataProviderService->getDataProvider() - ->fetchChildrenList($identifier, $depth, $filter, $languages, $offset, $limit); - if ($catalogList->countChildren() > 0) { - // handle prices - /** @var $priceService \Siso\Bundle\PriceBundle\Service\PriceService */ - $priceRequest = PriceRequest::getPriceRequestByList( - $catalogList, - array(), - array('customerNumber' => $this->customerService->getCurrentCustomer()->getCustomerNumber(), - 'source' => PriceService::SOURCE_CATALOG, - ) - ); - if ($priceRequest->getRequestProductCount() > 0) { - $this->priceService->getPriceProvider()->requestPrice($priceRequest); - } - } - $catalogCount = $this->dataProviderService->getDataProvider() - ->countChildrenList($identifier, $depth, $filter, $languages); - return array($catalogList, $catalogCount); - } -``` - -## Configuration - -`catalogList` and `productList` filters are defined in the configuration file. -They are used in catalog data provider to filter and sort all elements or product lists from catalog. - -``` yaml -silver_eshop.default.ez5_catalog_data_provider.filter: - navigation: - ... - catalogList: - contentTypes: ["ses_category", "ses_product"] - sortClauses: - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\Location\\Priority" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_DESC" - productList: - contentTypes: ["ses_product"] - sortClauses: - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\Location\\Priority" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_DESC" -``` - -## Input parameters - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\InputValueObjects; - -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; - -/** - * Class LoadList - * - * This class is used as an input parameter for an appropriate business method - * - * @property-read int $locationId - * @property-read int $limit - * @property-read int $offset - * @property-read string $language - */ -class LoadList extends ValueObject -{ - /** - * @var int $locationId - */ - protected $locationId; - /** - * @var int $limit - */ - protected $limit; - /** - * @var int $offset - */ - protected $offset; - /** - * @var string $language - */ - protected $language; - /** - * @var string $filterType - */ - protected $filterType; - /** - * @var array $checkProperties - */ - protected $checkProperties = array( - array('name' => 'locationId', 'mandatory' => true, 'type' => 'int'), - array('name' => 'limit', 'mandatory' => true, 'type' => 'int'), - array('name' => 'offset', 'mandatory' => true, 'type' => 'int'), - array('name' => 'language', 'mandatory' => false, 'type' => 'string'), - array('name' => 'filterType', 'mandatory' => false, 'type' => 'string'), - ); -} -``` - -## Returns output - -``` php -namespace Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\OutputValueObjects; -use Silversolutions\Bundle\EshopBundle\Catalog\CatalogListResult; -use Silversolutions\Bundle\EshopBundle\Content\ValueObject; -/** - * Class LoadList - * - * This class is used as an output parameter for an appropriate business method - * - * @property-read int $locationId - * @property-read CatalogListResult $catalogList - * @property-read int $catalogCount - */ -class LoadList extends ValueObject -{ - /** @var int $locationId */ - protected $locationId; - /** @var CatalogListResult $catalogList */ - protected $catalogList; - /** @var int $catalogCount */ - protected $catalogCount; - /** @var array $checkProperties */ - protected $checkProperties = array( - array('name' => 'locationId', 'mandatory' => true, 'type' => 'int'), - array( - 'name' => 'catalogList', - 'mandatory' => true, - 'type' => 'Silversolutions\Bundle\EshopBundle\Catalog\CatalogListResult', - 'isObject' => true, - ), - array('name' => 'catalogCount', 'mandatory' => true, 'type' => 'int'), - ); -} -``` diff --git a/docs/api/commerce_api/business_api/business_api.md b/docs/api/commerce_api/business_api/business_api.md deleted file mode 100644 index ef591cd3b0..0000000000 --- a/docs/api/commerce_api/business_api/business_api.md +++ /dev/null @@ -1,24 +0,0 @@ -# Business API - -Business API is the layer between the application entry points (like controllers or CLI commands) and the particular services. - -![](../img/business_api_1.png) - -To access the Business API, you have to use the [Business API invocation service](businessapi_invocation_service.md). - -Business API Operations work with input/output data. -The Business API invocation service calls the Operation service with input data. -The service then returns output data. - -## Input/output data - -All data passed to Business API calls (input) must be an instance of a class that extends `ValueObject`. -This input data class must define all fields and/or structs which are needed to process the called business logic. -Business logic must not be implemented in these classes. - -All data which is returned by the Business API calls (output) must also be an instance of `ValueObject`. - -All input and output classes are located in: - -- `Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\InputValueObjects\*` -- `Silversolutions\Bundle\EshopBundle\Entities\BusinessLayer\OutputValueObjects\*` diff --git a/docs/api/commerce_api/business_api/businessapi_invocation_service.md b/docs/api/commerce_api/business_api/businessapi_invocation_service.md deleted file mode 100644 index 1fe5716744..0000000000 --- a/docs/api/commerce_api/business_api/businessapi_invocation_service.md +++ /dev/null @@ -1,116 +0,0 @@ -# Business API invocation service - -This service is the access point to the Business API and defined by a service with the ID `ses_eshop.business_api.invocation`. -Because this service acts only as a proxy, the business logic itself is handled by different operation services. - -## Calling Business API operation service - -To call Business API operation service, use the `call()` method of the Business API invocation service. - -|method|parameters|returns| -|--- |--- |--- | -|call()|`string $operationIdentifier`
    `ValueObject $operationInput`|`ValueObject $operationOutput`| - -### Creating $operationIdentifier - -The identifier of the Business API operation service consists of two parts separated by a dot. - -1. The first part is the alias of the operation service that can be found in the configuration: `services.business_layer.xml`. -1. The second part is the operation method that should be invoked. -The method name must be lowercase, separated by underscore instead of the method's camelCase notation (`get_basket` => `getBasket()`). - -For examples: - -``` -basket.get_basket -basket.add_products -catalog.load_products -``` - -## Example - -The following example shows how to use the Business API basket operation service. - -``` php -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as InputGetBasket; -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as OutputGetBasket; - -/** @var InputGetBasket $input */ -$input = new InputGetBasket(array('request' => $request)); - -/** @var OutputGetBasket $output */ -$output = $this->get('ses_eshop.business_api.invocation')->call('basket.get_basket', $input); -``` - -### Implementing a Business API operation - -To extend the Business API, you need to create a new service class. -The operations of that class contain the business logic respectively and are stored in `EshopBundle/Services/BusinessLayer/Operations/*`. - -Each operation, e.g. basket, is a service which must be tagged in the following way: - -``` xml - - - - -``` - -!!! note - - The tag `name` must always be `business_api_operation`. - - The attribute `alias` defines the first part in the operation identifier. - - Derive your new service from the [common parent class `BaseOperation`](baseoperation/baseoperation.md) - to get access to commonly needed dependencies (e.g. logging and translations). - -### Overriding an existing Business API class - -If you want to override a particular API service you need to create a new service class, which extends the original. -Within that class you can override individual methods or add new methods. -Then you need to redefine the service in the Symfony configuration with the new class and the same tag values (name and alias). - -For example: - -``` php -namespace Example\Bundle\ExtensionBundle\Services\BusinessLayer\Operations; - -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\AddItemToBasket as InputAddItemToBasket; -use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\OutputValueObjects\AddItemToBasket as OutputAddItemToBasket; - -class NewBasketApi extends Silversolutions\Bundle\EshopBundle\Services\BusinessLayer\Operations\Basket -{ - public function newBasketOperation(InputBasketOperation $operationInput) { - // process something - // .. - return new OutputBasketOperation(array('resultAttribute' => $result)); - } - - public function addProducts(InputAddItemToBasket $operationInput) - { - // override method logic - // .. - return new OutputAddItemToBasket(array('basket' => $basketObject)); - } -} -``` - -There are two ways to redefine the service configuration: - -If the original service is defined with a configuration parameter (`%` notation) in its class attribute, -you need to redefine that parameter with a fully-qualified class name. - -``` yaml -parameters: - ses_eshop.basket.business_api.class: 'Example\Bundle\ExtensionBundle\Services\BusinessLayer\Operations\NewBasketApi' -``` - -The other option is to completely redefine the original service with a new fully-qualified class name. - -``` xml - - - - -``` diff --git a/docs/api/commerce_api/field_type_reference/field_type_reference.md b/docs/api/commerce_api/field_type_reference/field_type_reference.md deleted file mode 100644 index 2cf2cd9a90..0000000000 --- a/docs/api/commerce_api/field_type_reference/field_type_reference.md +++ /dev/null @@ -1,9 +0,0 @@ -# Commerce Field Types - -[[= product_name_com =]] uses special ecommerce-related Field Types: - -- [sesselection](sesselection.md) - offers a selection using options from a YAML file -- [sesexternaldata](sesexternaldata.md) -- [sesprofiledata](sesprofiledata.md) - stores address data for a customer -- [SpecificationsType](specificationstype.md) - stores a structured list of specification data for products -- [varianttype](varianttype.md) - stores variant data for products diff --git a/docs/api/commerce_api/field_type_reference/productselection.md b/docs/api/commerce_api/field_type_reference/productselection.md deleted file mode 100644 index 62ee651cc1..0000000000 --- a/docs/api/commerce_api/field_type_reference/productselection.md +++ /dev/null @@ -1,37 +0,0 @@ -# ProductSelection - -This Field Type offers a relation feature for products. It can be used with products stored in the content model or in eContent. - -Add a new Field (Product relationlist) to a Content Type: - -![](../img/additional_ez_fieldtypes_1.png) - -In edit mode an editor can search for a product or SKU and add the product to the relation list: - -The optional attribute `note` can be used for example to display a hint on the frontend (e.g. "Offer"). - -![](../img/additional_ez_fieldtypes_2.png) - -You can access the relation list data in Twig with: - -- `sku` -- `name` -- `note` -- `image` - -For example: - -``` html+twig - - {% for product in field.value.product_list %} - - - - - {% endfor %} -
    - {% if product.image|default('') != '' %} - - {% endif %} - {{ product.name }}
    -``` diff --git a/docs/api/commerce_api/field_type_reference/sesexternaldata.md b/docs/api/commerce_api/field_type_reference/sesexternaldata.md deleted file mode 100644 index 55a748143a..0000000000 --- a/docs/api/commerce_api/field_type_reference/sesexternaldata.md +++ /dev/null @@ -1,125 +0,0 @@ -# SesExternalData - -Field Type `sesexternaldatatype` uses external storage to store data. The data must be stored in the `ses_externaldata` table. - -|Field|Type|Description| -|--- |--- |--- | -|`sku`|char(40)|Unique ID of the Product category (CatalogElement).| -|`identifier`|char(40)|ID of the Field. Constant prefix (`ses`) + lower case letters from the ERP fields
    Example: `VENDOR_NO --> ses_vendor_no`.| -|`language_code`|char(8)|Language code e.g. ger-DE.| -|`ses_field_type`|char(20)|The data type used for this data.| -|`content`|longtext|Serialized data in string format.| - -### Create the `ses_externaldata` table - -``` sql -CREATE TABLE `ses_externaldata` ( - `sku` char(40) NOT NULL, - `identifier` char(40) NOT NULL, - `language_code` char(8) NOT NULL, - `ses_field_type` char(20) NOT NULL, - `content` longtext, - PRIMARY KEY (`sku`,`identifier`,`language_code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 -``` - -!!! note - - You must create one attribute in `ses_externaldata` per language and product which contains the SKU itself. - For example: `(sku = 1122222)` - -||||||| -|---|---|---|---|---|---| -| id | sku | identifier | language_code | ses_field_type | content | -| 11087155 | 1122222 | ses_sku_erp | ger-DE | TextLineField | a:1:{s:17:"TextLineFieldHash";a:1:{s:4:"text";s:7:"1122222";}} | - -## Storing data in `ses_externaldata` - -Data that is stored in the `ses_externaldata` table must be either a simple datatype: int, float, boolean or a [Field Type](../fields_for_ecommerce_data/fields_for_ecommerce_data.md). - -### Data format - -#### Field Types - -The data content is stored in the database in serialized form using the `toHash()` method`. - -``` php -//Product with sku: 308488, NAV-Field: WERBETEXT_KURZ, content: 'Die Enten mit Kultcharakter. Als Geschenk immer wieder begehrt. Machen Sie Ihren Kunden die Freude.', ses_field_type: TextBlockField - -$blockField = new TextBlockField( - array( - 'text' => 'Die Enten mit Kultcharakter. Als Geschenk immer wieder begehrt. Machen Sie Ihren Kunden die Freude.', - ) - ); -$content = serialize($blockField->toHash()); - -$dbStatement = "INSERT INTO ses_externaldata VALUES(308488, ses_werbetext_kurz, ger-DE, TextBlockField, $content)"; -``` - -#### Simple data types - -Simple data types (int, float, bool) are stored in serialized form. - -``` php -//Product with sku: 308488, NAV-Field: GEWICHT, content: 60, ses_field_type: int - -$content = serialize(60); - -$dbStatement = "INSERT INTO ses_externaldata VALUES(308488, ses_gewicht, ger-DE, int, $content)"; -``` - -## Symfony data type - -Symfony data type is stored in: - -``` -Ibexa/Platform/Commerce/FieldTypes/FieldType/SesExternalData/* -Ibexa/Platform/Commerce/FieldTypes/Converter/SesExternalData.php -``` - -!!! note - - The member attribute `$text` of `FieldType\SesExternalData\Value` is actually an array. - Do not assign strings to this (public) attribute, because all implementations rely on the PHP type array. - -#### Configuration - -``` yaml -services: - Ibexa\Platform\Commerce\FieldTypes\Converter\SesExternalData: - arguments: - $db: '@ezpublish.persistence.connection' - $configResolver: '@ezpublish.config.resolver' - $logService: '@silver_common.logger' - tags: - - { name: ezpublish.storageEngine.legacy.converter, alias: sesexternaldata } - Ibexa\Platform\Commerce\FieldTypes\FieldType\SesExternalData\Type: - tags: - - { name: ezpublish.fieldType, alias: sesexternaldata } - - Ibexa\Platform\Commerce\FieldTypes\FieldType\SesExternalData\SearchField: - arguments: - $configResolver: '@ezpublish.config.resolver' - calls: - - [ setIndexDefinition ] - tags: - - { name: ezpublish.fieldType.indexable, alias: sesexternaldata } -``` - -## Handling of `sesexternaldata` in the content model data provider - -### Adding a new field in the Product Content Type - -You can extend the Product Content Type with the `sesexternaldata` Field. - -![](../img/additional_ez_fieldtypes_3.png) - -### Connecting to the external storage - -If the `ses_externaldata` table is filled properly with the data, you can create the connection with the appropriate product by typing the SKU. - -### Handling the fetched data - -The data from `sesexternaldata` is converted in the shop and an array of [Field Types](../fields_for_ecommerce_data/fields_for_ecommerce_data.md) or simple types is returned. - -![](../img/additional_ez_fieldtypes_6.png) diff --git a/docs/api/commerce_api/field_type_reference/sesprofiledata.md b/docs/api/commerce_api/field_type_reference/sesprofiledata.md deleted file mode 100644 index 6e5a342c9b..0000000000 --- a/docs/api/commerce_api/field_type_reference/sesprofiledata.md +++ /dev/null @@ -1,34 +0,0 @@ -# SesProfileData - -This Field Type stores [`CustomerProfileData`](../../../guide/customers/customer_api/customer_profile_data.md) in the User Content Type. - -!!! note - - `CustomerProfileData` must be stored as a serialized string in base64 format, - because it is not possible to store any special HTML characters(`<`,`>`, `""`,`''`, `&`) in text field or text area field. - -# Symfony data type - -The Symfony data type is stored in: - -``` -Ibexa/Platform/Commerce/FieldTypes/FieldType/SesProfileData/* -Ibexa/Platform/Commerce/FieldTypes/Converter/SesProfileData.php -``` - -#### Configuration - -``` yaml -services: - Ibexa\Platform\Commerce\FieldTypes\Converter\SesProfileData: - tags: - - { name: ezpublish.storageEngine.legacy.converter, alias: sesprofiledata } - Ibexa\Platform\Commerce\FieldTypes\FieldType\SesProfileData\Type: - tags: - - { name: ezpublish.fieldType, alias: sesprofiledata } -``` - -The name of the customer (taken from the contact section) can be used for lists. -To do it, use the name pattern in the Content Type definition of the User Content Type. - -`customer_profile_data` is the identifier of the Field where the profile data is stored. diff --git a/docs/api/commerce_api/field_type_reference/sesselection.md b/docs/api/commerce_api/field_type_reference/sesselection.md deleted file mode 100644 index ccfbe3df48..0000000000 --- a/docs/api/commerce_api/field_type_reference/sesselection.md +++ /dev/null @@ -1,33 +0,0 @@ -# SesSelection - -This Field Type renders a selection in the backend, so the editor can choose a value. - -Unlike the standard selection Field Type, the SesSelection Field Type configuration is located in a YAML file. -For that reason, it is possible to set up SiteAccess-specific selection Field Types. - -## Configuration - -The Field Type must be configured per attribute (for example for `ses_vat_code`): - -``` yaml -siso_core.default.sesselection.ses_vat_code: - default: VATNORMAL - translation_context: ses_vat_code - options: - VATREDUCED: 7 - VATNORMAL: 19 -``` - -The translation context is optional. - -## Change Content Type - -Add a `sesselection` Field to your Content Type. - -![](../img/additional_ez_fieldtypes_8.png) - -The identifier of the Field Type (in this example. `ses_vat_code`) must be filled as defined in the configuration above. - -When the Field Type is set up correctly, you can assign a value in the Content item: - -![](../img/additional_ez_fieldtypes_9.png) diff --git a/docs/api/commerce_api/field_type_reference/silver.module.md b/docs/api/commerce_api/field_type_reference/silver.module.md deleted file mode 100644 index 51b67d80a5..0000000000 --- a/docs/api/commerce_api/field_type_reference/silver.module.md +++ /dev/null @@ -1,37 +0,0 @@ -# silver.module - -silver.module is a special Content Type that enables you to load specific controllers using a Content item. -silver.module defines a controller path and optionally parameters for this controller. -This provides a convenient opportunity to define custom, simple and translatable URLs using a silver.module name to any controller. - -Following attributes are possible: - -|Attribute name|Description|Example value| -|--- |--- |--- | -|Name|The name of the silver.module. It is shown on the frontend and is translatable.|"My test module", results in the URL: `/my-test-module`| -|Controller|The controller which should be loaded when clicking that module. Define the controller including the fully-qualified namespace and the controller method separated by two colons (`::`). The silver.module controller handler which loads the target controller automatically validates if the controller class and action method are available or throws an exception otherwise.|`\Silversolutions\Bundle\EshopBundle\Controller\CommonController::testAction`| -|Parameters|Optional parameters (list of hashes) which are directed to the target controller. Only string key-value pairs are possible. The key value pairs are separated by a semicolon (`;`)|Parameter key: `formTypeResolver`
    Parameter value: `contact`| - -## Functionality - -`ezpublish.yml` contains a full view controller configuration for Content Type identifier `st_module`: `SilversolutionsEshopBundle:SilverModule:viewModuleLocation()`. - -Within this controller the `loadControllerAction()` is called. -This method validates the controller value defined in the silver.module Content item -(is controller class available, is controller action available), -loads the target controller action and passes the current request and optional controller parameters to the target. - -``` yaml -system: - ezdemo_site_clean_group: - location_view: - full: - silverModuleRuleset: - controller: SilversolutionsEshopBundle:SilverModule:viewModuleLocation - match: - Identifier\ContentType: [st_module] -``` - -!!! note - - The silver.module Content item is passed to the loaded controller together with the optional parameters. diff --git a/docs/api/commerce_api/field_type_reference/specificationstype.md b/docs/api/commerce_api/field_type_reference/specificationstype.md deleted file mode 100644 index 061c74bc6c..0000000000 --- a/docs/api/commerce_api/field_type_reference/specificationstype.md +++ /dev/null @@ -1,99 +0,0 @@ -# SpecificationsType - -The Field Type stores a structured list of attributes for products: - -![](../img/additional_ez_fieldtypes_10.png) - -!!! caution "Field naming" - - A Field of the SpecificationsType must have `ses_specifications` as its Field identifier. - -The data is stored in JSON format. - -``` json -[ - { - "name": "marketing", - "data": [ - { - "label": "Brand", - "value": "MG" - }, - { - "label": "Warranty", - "value": "2yrs" - } - ] - }, - { - "name": "technic", - "data": [ - { - "label": "Size", - "value": "12cm" - }, - { - "label": "color", - "value": "red" - } - ] - } -] -``` - -## Adding a unit selection - -The optional `option` attribute enables you to add a select field offering e.g. a selection of units: - -![](../img/additional_ez_fieldtypes_11.png) - -``` yaml -- - code: "technic" - label: "Technical data" - default_values: - - - label: "Width" - value: "" - options: ['mm','cm','m'] - - - label: "Heigth" - value: "" - options: ['mm','cm','m'] - - - label: "Length" - value: "" - options: ['mm','cm','m'] - - - label: "Weight" - value: "" - options: ['g','kg'] -``` - -## Adding default values - -The Field Type uses a configuration that controls which groups are offered. -You can also provide default attributes: - -``` yaml -siso_core.default.specification_groups: - - - code: "marketing" - label: "Marketing data" - default_values: - - - label: "Brand" - value: "" - - - code: "technic" - label: "Technical data" - default_values: ~ - - - code: "norms" - label: "Standards" - default_values: ~ -``` - -## Template - -`IbexaPlatformCommerceFieldTypesBundle::sesselectiontype_content_field.html.twig` diff --git a/docs/api/commerce_api/field_type_reference/varianttype.md b/docs/api/commerce_api/field_type_reference/varianttype.md deleted file mode 100644 index ed44c31c35..0000000000 --- a/docs/api/commerce_api/field_type_reference/varianttype.md +++ /dev/null @@ -1,177 +0,0 @@ -# VariantType - -The VariantType Field Type offers a user interface for editing variants. - -The Field Type offers a selection of preconfigured variant types. - -![](../img/additional_ez_fieldtypes_12.png) - -A variant type can be a one level or two level variant. The variant types can be setup in a YAML file: - -``` yaml -siso_core.default.variant_types: - - - id: size_color - type_label: "Size and Color" - variant_levels: 2 - variant_level_label_1: "Size" - variant_level_code_1: "size" - variant_level_code_2: "color" - variant_level_label_2: "Color" - - - id: size - type_label: "Size" - variant_levels: 1 - variant_level_label_1: "Size" - variant_level_code_1: "size" - - - id: color - type_label: "color" - variant_levels: 1 - variant_level_label_1: "Color" - variant_level_code_1: "color" - - - id: package - type_label: "Package" - variant_levels: 1 - variant_level_label_1: "Package size" - variant_level_code_1: "packagesize" - -``` - -Depending on the variant type, different edit forms are generated: - -![](../img/additional_ez_fieldtypes_13.png) - -The data is stored in JSON format: - -``` json -[ - { - "sku": { - "label": "Sku", - "value": "5515" - }, - "variantCode": { - "label": "Variant Code", - "value": "5515" - }, - "description": { - "label": "Description", - "value": "Alibaba Single Door Silver Refrigerator" - }, - "characteristicCode1": { - "label": "22\"", - "value": "22\"" - }, - "characteristicLabel1": { - "label": "Color", - "value": "Color" - }, - "characteristicCode2": { - "label": "Silver", - "value": "Silver" - }, - "characteristicLabel2": { - "label": "", - "value": "" - }, - "priceNet": { - "label": "Listprice net", - "value": "220" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - }, - { - "sku": { - "label": "Sku", - "value": "5515" - }, - "variantCode": { - "label": "Variant Code", - "value": "" - }, - "description": { - "label": "Description", - "value": "Alibaba Single Door Silver Refrigerator" - }, - "characteristicCode1": { - "label": "22\"", - "value": "22\"" - }, - "characteristicLabel1": { - "label": "Size", - "value": "Size" - }, - "characteristicCode2": { - "label": "Silver", - "value": "Silver" - }, - "characteristicLabel2": { - "label": "Color", - "value": "Color" - }, - "priceNet": { - "label": "Listprice net", - "value": "220" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - }, - { - "sku": { - "label": "Sku", - "value": "5515" - }, - "variantCode": { - "label": "Variant Code", - "value": "5532" - }, - "description": { - "label": "Description", - "value": "Alibaba Single Door Silver Refrigerator" - }, - "characteristicCode1": { - "label": "30\"", - "value": "30\"" - }, - "characteristicLabel1": { - "label": "Size", - "value": "Size" - }, - "characteristicCode2": { - "label": "White", - "value": "White" - }, - "characteristicLabel2": { - "label": "Color", - "value": "Color" - }, - "priceNet": { - "label": "Listprice net", - "value": "330" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - } -] -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/arrayfield.md b/docs/api/commerce_api/fields_for_ecommerce_data/arrayfield.md deleted file mode 100644 index 183768d72f..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/arrayfield.md +++ /dev/null @@ -1,16 +0,0 @@ -# ArrayField - -`ArrayField` is the representative implementation of `AbstractField` for a structured array. - -A new `ArrayField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\ImageField; - -// Usage: -$myArray = array ( - 'weight' => '10 kg', - 'color' => 'red' -); -$arrayField = new ArrayField(array('array' => $myArray)); -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/fields_for_ecommerce_data.md b/docs/api/commerce_api/fields_for_ecommerce_data/fields_for_ecommerce_data.md deleted file mode 100644 index d1db857795..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/fields_for_ecommerce_data.md +++ /dev/null @@ -1,61 +0,0 @@ -# Fields for eCommerce data - -[[= product_name_com =]] uses custom Fields to store eCommerce related data, for example for the catalog or basket. - -The shop provides a flexible way to store data using concrete instances of classes implementing `FieldInterface` and inheriting from the `AbstractField` class. -Fields are used for fixed attributes of a product/catalog and for flexible attributes (property `dataMap` in [`CatalogElement`](../../../guide/catalog/catalog_api/catalog_element.md)). - -Each instance of a concrete Field provides the following methods: - -| Method | Description | -| --------------------- | ------------------------------------------------------------------- | -| `getTypeIdentifier()` | Returns the identifier of the Field (e.g. `sesimage`) | -| `isSearchable()` | Returns true if the Field is searchable | -| `isEmptyValue()` | Returns true if the value of the Field is empty | -| `getEmptyValue()` | Returns an empty version of the Field | -| `toHash()` | Returns an associative array (hash) from the Field | -| `fromHash($hash)` | Returns a created instance of the Field by associative array (hash) | -| `toString()` | Returns the value of the Field as string | - -All `AbstractField` objects can be serialized if you use the `toHash()` and `fromHash()` methods before/after the (un)serialize method. -For example: - -``` php -//serialize -if($field instanceof AbstractField) { - $fieldValue = serialize($field->toHash()); -} -``` - -## Class diagram - -![](../img/fields_for_ecommerce_data_1.png) - -## Implemented concrete Field classes - -| Type | Used for | identifier | -| ---------------- | ------------------------------------------------------------ | -------------- | -| `TextLineField` | A simple string without HTML code | `sestextline` | -| `TextBlockField` | A rich text field containing HTML code | `sestextblock` | -| `ImageField` | An image containing a path to an image and an alternative text | `sesimage` | -| `ArrayField` | A structured array | `sesarray` | -| `PriceField` | An instance of the Price class | `sesprice` | - -## Templates - -For each concrete Field you have to provide a template to render the Field in the template. - -The templates have to be put in the `FieldTypes` folder. The name of the template has to start with the identifier of the Field, e.g.: - -- `ImageField.html.twig` -- `TextBlockField.html.twig` -- `TextLineField.html.twig` -- `PriceField.html.twig` - -The renderer provides a `$field` parameter containing the object of the given Field. - -You can call the Field from a Twig template, with `ses_render_field`: - -``` html+twig -{{ ses_render_field(catalogElement, 'longDescription')|raw }} -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/filefield.md b/docs/api/commerce_api/fields_for_ecommerce_data/filefield.md deleted file mode 100644 index c62f6ae8a5..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/filefield.md +++ /dev/null @@ -1,35 +0,0 @@ -# FileField - -`FileField` is the representative implementation of `AbstractField` for a media file in a defined storage directory. -A file is identified by a `productId`, `storagePath` and several optional parameters. - -``` php -const IDENTIFIER = 'sesfile'; -// Same as for sesimage: -public $alternativeText; -public $fileName; -public $fileSize; -public $path; -public $id; -public $mimetype; // e.g. application/pdf, -public $description; -``` - -A new `FileField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\FileField; - -$filePath = 'var/storage/4711-001.pdf'; - -$fileField = new FileField( - array( - 'alternativeText' => 'a nice product', - 'fileName' => basename($filePath), - 'fileSize' => filesize($filePath), - 'path' => $$filePath, - 'mimetype' => 'application/pdf', - 'description' => 'Something nice' - ) -); -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/imagefield.md b/docs/api/commerce_api/fields_for_ecommerce_data/imagefield.md deleted file mode 100644 index c6cf91deb2..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/imagefield.md +++ /dev/null @@ -1,21 +0,0 @@ -# ImageField - -`ImageField` is the representative implementation of `AbstractField` for an image. -An image is identified and initiated by a given path and an optional alternative text. - -A new `ImageField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\ImageField; - -// Usage: -$imagePath = 'var/storage/images/product_image.jpg'; -$imageField = new ImageField( - array( - 'alternativeText' => 'a nice product', - 'fileName' => basename($imagePath), - 'fileSize' => filesize($imagePath), - 'path' => $imagePath, - ) -); -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/pricefield.md b/docs/api/commerce_api/fields_for_ecommerce_data/pricefield.md deleted file mode 100644 index e558cbdcc8..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/pricefield.md +++ /dev/null @@ -1,32 +0,0 @@ -# PriceField - -`PriceField` is the representative implementation of `AbstractField` for a `Price`. - -A new `PriceField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\PriceField; -use Siso\Bundle\PriceBundle\Model\Price; - -// Usage: -$price = new Price( - array( - 'price' => 99.99, - 'isVatPrice' => true, - 'vatCode' => 19.0, - 'currency' => 'EUR', - 'source' => 'ERP', - ) -); -$priceField = new PriceField(array('price' => $price)); -``` - -## Rendering -See [Rendering for prices](../../../guide/pricing/price_templates.md#pricefield-rendering) to see the possibilities of outputting a `PriceField` using the `ses_render_field()` function. - -You can also render a `priceField` with a Twig function `ses_render_price()`: - -|Twig function|Parameters|Usage| -|--- |--- |--- | -|`ses_render_field()`|`$catalogElement`
    `string $fieldIdentifier`
    `array $params`|Renders `FieldInterface $fields` from `$catalogElement`, like TextBlockField, ImageField, PriceField.| -|`ses_render_price()`|`$catalogElement`
    `PriceField $priceField`
    `array $params`|Renders only `PriceField $price`.| diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/stockfield.md b/docs/api/commerce_api/fields_for_ecommerce_data/stockfield.md deleted file mode 100644 index 0bf4d34747..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/stockfield.md +++ /dev/null @@ -1,47 +0,0 @@ -# StockField - -`StockField` is the representative implementation of `AbstractField` for stock information. - -The `stockNumeric` information must be either an int or a float. - -A new `StockField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\StockField; - -// Usage: -$stockField = new StockField(array('stockNumeric' => 15)); -``` - -### StockField rendering - -The StockField can be rendered with the `ses_render_stock` [Twig helper](../../../guide/shop_templates/twig_extension.md). - -This method renders the StockField from a central template: - -`Silversolutions/Bundle/EshopBundle/Resources/views/Fieldtypes/StockField.html.twig` - -The availability is displayed depending on the given parameters. -If the StockField is not defined (e.g. ERP is not responding), no availability information for this product is displayed. - -``` html+twig -{# display availability for the basket line, pass additionally the requested quantity in order to find out if the product is available in required amount #} -{% set field = line.remoteDataMap.stock is defined ? line.remoteDataMap.stock : null %} - {{ ses_render_stock(field, { - 'outputStock': { - 'numeric': false, - 'cssClass': 'availability' - }, - 'quantity' : line.quantity - }) }} -{# display availability for the product detail page, set numeric to true, so the stock information will be displayed as a number: e.g. 54 items on Stock #} -{% set field = catalogElement.stock is defined ? catalogElement.stock : null %} - {{ ses_render_stock(field, { - 'outputStock': { - 'numeric': true, - 'cssClass': 'availability' - } - }) - }} - -``` diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/textblockfield.md b/docs/api/commerce_api/fields_for_ecommerce_data/textblockfield.md deleted file mode 100644 index f20963b652..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/textblockfield.md +++ /dev/null @@ -1,20 +0,0 @@ -# TextBlockField - -`TextBlockField` is the representative implementation of `AbstractField` for a multi-line text (or DOMDocument). - -A new `TextBlockField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\TextBlockField; - -// Usage: -$textBlockField = new TextBlockField( - array( - 'text' => 'This is the description of the product' - ) -); -``` - -!!! note - - A TextBlockField object can be reliably serialized, because it implements the magic `__sleep()` and `__wakeup()` methods. diff --git a/docs/api/commerce_api/fields_for_ecommerce_data/textlinefield.md b/docs/api/commerce_api/fields_for_ecommerce_data/textlinefield.md deleted file mode 100644 index aff0d9a3b4..0000000000 --- a/docs/api/commerce_api/fields_for_ecommerce_data/textlinefield.md +++ /dev/null @@ -1,12 +0,0 @@ -# TextLineField - -`TextLineField` is the representative implementation of `AbstractField` for a single line of text. - -A new `TextLineField` can be created using the following data: - -``` php -use Silversolutions\Bundle\EshopBundle\Content\Fields\TextLineField; - -// Usage: -$textLineField = new TextLineField(array('text' => 'This is the name of the product')); -``` diff --git a/docs/api/commerce_api/helper_services/country_service.md b/docs/api/commerce_api/helper_services/country_service.md deleted file mode 100644 index e12bdadf9b..0000000000 --- a/docs/api/commerce_api/helper_services/country_service.md +++ /dev/null @@ -1,33 +0,0 @@ -# CountryService - -`CountryService` returns a list of countries with corresponding country codes. -The country names are translated for every SiteAccess. -This list of countries is used in all forms in the shop as a list of choices. - -Because this service is aware of the form type name, it can return different countries for every form. -It can display a different list for the checkout process and a different list for the registration process. - -Service ID: `siso_tools.country` - -## Configuration - -The list of possible countries can be configured in `siso_tools.default.countries`. - -## `getCountryNames` - -The `getCountryNames` method takes the following parameters: - -- `$formTypeName = null` -- `$locale = null` - -It returns an array of country codes and names. -The country names are translated for required `Locale $locale`. If `$locale` is not set, current Locale is used. -This function can be customized depending on the given `$formTypeNam`e, e.g.: - -``` -array ( - 'AF' => 'Afghanistan', - ... - 'DE' => 'Germany' -) -``` diff --git a/docs/api/commerce_api/helper_services/ezhelperservice.md b/docs/api/commerce_api/helper_services/ezhelperservice.md deleted file mode 100644 index b8b22b915e..0000000000 --- a/docs/api/commerce_api/helper_services/ezhelperservice.md +++ /dev/null @@ -1,46 +0,0 @@ -# EzHelperService - -`Silversolutions\Bundle\ToolsBundle\Services\EzHelperService` provides helper methods for e.g. User Components or Translation Components. - -Service ID: `silver_tools.ez_helper` - -## Methods - -### General - -|Method|Parameters|Returns|Description| -|--- |--- |--- |--- | -|`clearVersions`|`ContentInfo $contentInfo`|`void`|Clears the (archived) versions of the given `$contentInfo` up to the required count of versions (set in configuration)| - -### User Components - -|Method|Parameters|Returns|Description| -|--- |--- |--- |--- | -|`getCurrentUser`|-|`\eZ\Publish\API\Repository\Values\User\User`|Returns the current user from the default context| -|`updateUser`|`User $ezUser`
    `array $fields = array()`|`\eZ\Publish\API\Repository\Values\Content\Content`|Updates User Content item| -|`updateUserCustomAttributes`|`User $ezUser`
    `array $fields = array()`|`\eZ\Publish\API\Repository\Values\Content\Content`|Updates custom User Content item information (such as `customer_number`, `customer_profile_data`, etc.)| -|`getUserByUserId`|`$userId`|`\eZ\Publish\API\Repository\Values\User\User`|Returns a User Content item by user ID| -|`createUser`|`array $userData = array()`
    `$defaultLanguage = 'eng-US'`|`\eZ\Publish\API\Repository\Values\User\User`|Creates a User Content item| -|`loginEzUser`|`\eZ\Publish\API\Repository\Values\User\User $user`
    `\Symfony\Component\HttpFoundation\Session\Session $session`
    `\Symfony\Component\HttpFoundation\Response $response`|-|Login as a User| -|`isEzUserLoggedIn`|`Request $request`|boolean|Returns true if the user is logged in| -|`getAnonymousUser`|-|`\eZ\Publish\API\Repository\Values\User\User`|Returns the anonymous user for the current context| -|`isEzUserAnonymous`|`\eZ\Publish\API\Repository\Values\User\User $user`|boolean|Returns true if the given user is anonymous| -|`findUser`|`$value`
    `$searchAttribute = 'login'`
    `$locationId = null`|`\eZ\Publish\API\Repository\Values\User\User or null`|Returns a User Content item by an attribute. Default attribute is `login`, but other, such as `email` are possible| - -### SiteAccess translation components - -|Method|Parameters|Returns|Description| -|--- |--- |--- |--- | -|`getCurrentLanguageCode()`|-|null\|string
    e.g. ger-DE|Returns the current SiteAccess language if set in the configuration, otherwise null| -|`getTranslatedNameForEzContent()`|`\eZ\Publish\API\Repository\Values\Content\Content $ezContent`|string|Returns the translated name of the Content item| -|`getTranslatedFieldForEzContent()`|`\eZ\Publish\API\Repository\Values\Content\Content $ezContent`
    `string $fieldIdentifier`|`\eZ\Publish\API\Repository\Values\Content\Field`\|null|Returns the translated Field of the Content item| -|`getUrlForSiteAccess()`|`string $urlPath`|string
    e.g. `home` -> `/de/home`|Changes the given URL to the URL for the current SiteAccess| -|`getUserConf()`|`$key`|string|Returns the parameter value from the configuration for given `$key` and namespace `self::ST_EZ_HELPER_CREATE_USER`| -|`getEzContentByLocationId()`|`$ezLocationId`|`Content`|Returns content by Location ID| -|`getEzLocationByLocationId()`|`$ezLocationId`|`Content`|Returns Location by Location ID| -|`getEzContentByLocation()`|`Location $ezLocation`|`Content`|Returns content by Location| -|`getEzContentByContentId()`|`$ezContentId`|`Content`|Returns content by content ID| -|`getEzTypeIdentifierByEzContent()`|`Content $ezContent`|string|Returns the Content Type identifier for the given Content item| -|`getEzContentFieldValue()`|`Content $ezContent`
    `$fieldIdentifier`
    `$languageCode`
    `$fieldType`|null\|mixed|Returns a Field value for the given Content item by identifier| -|`getUrlAliasByUrl()`|`$url`
    `$languageCode`|`\eZ\Publish\API\Repository\Values\Content\URLAlias`
    `@throws \eZ\Publish\API\Repository\Exceptions\NotFoundException`|Looks up the URL alias for the given URL.| -|`getUrlAliasByLocation()`|`Location $ezLocation`
    `$languageCode`|null\|`\eZ\Publish\API\Repository\Values\Content\URLAlias`|Returns the URL alias for the given Location| diff --git a/docs/api/commerce_api/helper_services/mail_logging.md b/docs/api/commerce_api/helper_services/mail_logging.md deleted file mode 100644 index 31a595027d..0000000000 --- a/docs/api/commerce_api/helper_services/mail_logging.md +++ /dev/null @@ -1,123 +0,0 @@ -# Mail logging - -Every email sent by `MailHelperService` is recorded by `siso_tools.mailer.logger`. -It is a special service of the `Monolog/Logger` class. - -For more information about logging, see [logging documentation](../../../guide/logging/logging.md). - -## MailLog entity and repository - -Emails are logged in a database. Database logging is handled by [the Doctrine logger](../../../guide/logging/logging_api.md#base-classes-of-doctrine-based-logging). - -### Siso\Bundle\ToolsBundle\Entity\MailLog - -This is the mail logging extension of `AbstractLog`. It contains the following attributes: - -- `private $requestId` - string - A value which identifies uniquely every HTTP request across all logs. -- `private $sessionId` - string - A value which identifies uniquely every client session across all logs and requests. -- `private $userId` - string - The identifier for the user (mostly eZ User object ID). -- `private $sender` - string - The sender's email address. -- `private $receiver` - string - The receiver's email address. -- `private $subject` - string - The mail subject content. -- `private $sentTimestamp` - `\DateTime` - The date and time of sending. -- `private $status` - boolean - True if the mail has been sent successfully, false otherwise. - -### Class Siso\Bundle\ToolsBundle\Service\Logging\MailDataProcessor - -This Monolog data processor replaces the original log message with the non-empty contents of `context/content_html` or `context/content_text`, in this order of precedence. - -!!! note: - - Most emails are sent with `MailHelperService`. - Error messages during the email process are only logged to the database if the email body cannot be rendered. - If sending of the email failed, the email body is logged and any error message is ignored in this logging process. - -The email log entity has no field for the content body. It is intended to log this data within the message attribute. - -### Siso\Bundle\ToolsBundle\Entity\MailOrmLogRepository - -This is the Doctrine repository class for `MailLog` entities. -It exists to comply with the Doctrine conventions and to inject it into the mail logging instance of `DoctrineHandler`. - -## Configuration - -### Container `set-tp` for mail logging - -``` xml - - Siso\Bundle\ToolsBundle\Service\Logging\MailDataProcessor - Siso\Bundle\ToolsBundle\Entity\MailOrmLogRepository - SisoToolsBundle:MailLog - - - - - - %siso_tools.mailer.logging.entity% - - - - - - - - - - - - - - - - - - - silver_eshop_mail - - - - - - - - - processRecord - - - - - - - processRecord - - - - - -``` - -### Entity table `ses_log_mail` - -The ORM is defined by annotations in `Siso\Bundle\ToolsBundle\Entity\MailLog`. - -Example of a generated MySQL table: - -``` sql -CREATE TABLE `ses_log_mail` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `log_timestamp` datetime NOT NULL, - `log_channel` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `log_level` int(11) NOT NULL, - `log_message` longtext COLLATE utf8_unicode_ci, - `request_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `session_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `user_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `sender` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `receiver` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `subject` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL, - `sent_timestamp` datetime DEFAULT NULL, - `status` tinyint(1) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -``` diff --git a/docs/api/commerce_api/helper_services/mailhelperservice.md b/docs/api/commerce_api/helper_services/mailhelperservice.md deleted file mode 100644 index 45d1b7678d..0000000000 --- a/docs/api/commerce_api/helper_services/mailhelperservice.md +++ /dev/null @@ -1,57 +0,0 @@ -# MailHelperService - -`MailHelperService` is used to create, render and send emails from [[= product_name_com =]]. - -## Interface and implementations - -| | | -| ----------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| Interface definition | `vendor/silversolutions/silver.tools/src/Siso/Bundle/ToolsBundle/Service/MailHelperServiceInterface.php` | -| Implementation class (using Swift-Mailer) | `vendor/silversolutions/silver.tools/src/Siso/Bundle/ToolsBundle/Service/SwiftMailHelperService.php` | - -## Using - -!!! note - - `$subject` is translated in the helper service. Translating it in advance is unnecessary or, in rare cases, could even cause conflicts. - -!!! note - - The `SwiftMailHelperService` uses [Mail Logging](../../../guide/logging/logging.md) to help administrators keep track of all the sent emails. - - In the Customer center the content of any template parameter named `password` is logged with a masked/removed value. - -You can send predefined plain text and/or HTML content by using `sendMail()` or `sendMailWithRenderedTemplate()`. - -``` php -$sender = 'fd@silversolutions.de'; -$recipient = 'rel@silversolutions.de'; -$subject = 'Test Mail'; -/** @var \Siso\Bundle\ToolsBundle\Service\MailHelperServiceInterface $mailerService */ -$mailerService = $container->get('siso_tools.mailer_helper'); -$attachment = '/path/to/file/to/attach.jpg'; // optional - -// using sendMail() -$mailerService->sendMail( - $sender, - $recipient, - $subject, - 'This is my plain text mail', - '

    HTML Mail

    My HTML mail', - $attachment -); - -// using sendMailWithRenderedTemplate() with Twig template paths -$mailerService->sendMailWithRenderedTemplate( - $sender, - $recipient, - $subject, - 'SilversolutionsEshopBundle:Checkout/Email:order_confirmation.txt.twig', // template for plain-text mail - 'SilversolutionsEshopBundle:Checkout/Email:order_confirmation.html.twig', // template for HTML mail - array( // optional Twig parameters - 'template_param_1' => array('test', 'array'), - 'template_param_2' => 'this is a parameter for the templates', - ), - $attachment -); -``` diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_1.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_1.png deleted file mode 100644 index 69de157840..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_1.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_10.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_10.png deleted file mode 100644 index d85cecfe78..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_10.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_11.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_11.png deleted file mode 100644 index e7ecee684f..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_11.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_12.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_12.png deleted file mode 100644 index 3d1de944a0..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_12.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_13.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_13.png deleted file mode 100644 index fb9ded305c..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_13.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_2.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_2.png deleted file mode 100644 index b7715250b3..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_2.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_3.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_3.png deleted file mode 100644 index 0dab322bba..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_3.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_6.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_6.png deleted file mode 100644 index e30a3e0ece..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_6.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_8.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_8.png deleted file mode 100644 index 41cbf1b239..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_8.png and /dev/null differ diff --git a/docs/api/commerce_api/img/additional_ez_fieldtypes_9.png b/docs/api/commerce_api/img/additional_ez_fieldtypes_9.png deleted file mode 100644 index c4c3a90551..0000000000 Binary files a/docs/api/commerce_api/img/additional_ez_fieldtypes_9.png and /dev/null differ diff --git a/docs/api/commerce_api/img/business_api_1.png b/docs/api/commerce_api/img/business_api_1.png deleted file mode 100644 index 4b1b41bfd7..0000000000 Binary files a/docs/api/commerce_api/img/business_api_1.png and /dev/null differ diff --git a/docs/api/commerce_api/img/fields_for_ecommerce_data_1.png b/docs/api/commerce_api/img/fields_for_ecommerce_data_1.png deleted file mode 100644 index bd67abd2bc..0000000000 Binary files a/docs/api/commerce_api/img/fields_for_ecommerce_data_1.png and /dev/null differ diff --git a/docs/api/commerce_api/sisorestapibundle/basket_functions.md b/docs/api/commerce_api/sisorestapibundle/basket_functions.md deleted file mode 100644 index fb3fa4a8e8..0000000000 --- a/docs/api/commerce_api/sisorestapibundle/basket_functions.md +++ /dev/null @@ -1,1159 +0,0 @@ -# Basket functions - -## getBasket - -`/api/ezp/v2/siso-rest/basket/current (GET)` - -Returns the current basket of the user with header information and order lines. - -Standard service ID: `silver_basket.basket_service` - -### Request - -empty - -??? note "Response" - - ``` - "Basket": { - "_media-type": "application\/vnd.ez.api.Basket+json", - "basketId": 107, - "originId": null, - "erpOrderId": null, - "guid": null, - "state": "new", - "type": "basket", - "erpFailCounter": null, - "erpFailErrorLog": null, - "sessionId": "haub3a7963sg9mj7vbtt2dj403", - "userId": null, - "basketName": null, - "invoiceParty": { - "_media-type": "application\/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": null - } - ], - "PostalAddress": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "Teststr. 11", - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": "Testingen", - "PostalZone": "1111", - "CountrySubentity": "Dummy", - "CountrySubentityCode": null, - "AddressLine": [], - "Country": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "NO", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application\/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": "aka_test1@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application\/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } - }, - "deliveryParty": null, - "buyerParty": { - "_media-type": "application\/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": null - } - ], - "PostalAddress": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "Teststr. 11", - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": "Testingen", - "PostalZone": "1111", - "CountrySubentity": "Dummy", - "CountrySubentityCode": null, - "AddressLine": [], - "Country": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "NO", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application\/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": "aka_test1@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application\/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } - }, - "remark": null, - "dateCreated": { - "_media-type": "application\/vnd.ez.api.DateTime+json", - "_exception": { - "Name": "eZ\\Publish\\Core\\REST\\Common\\Output\\Exceptions\\NoVisitorFoundException", - "Message": "No visitor found for DateTime!" - } - }, - "dateLastModified": { - "_media-type": "application\/vnd.ez.api.DateTime+json", - "_exception": { - "Name": "eZ\\Publish\\Core\\REST\\Common\\Output\\Exceptions\\NoVisitorFoundException", - "Message": "No visitor found for DateTime!" - } - }, - "shop": "haugenbok", - "requirePriceUpdate": false, - "totals": { - "lines": { - "_media-type": "application\/vnd.ez.api.BasketTotals+json", - "name": "", - "totalNet": null, - "totalGross": null, - "vatList": {}, - "groupType": "order", - "currency": "NOK" - }, - "additionalLines": { - "_media-type": "application\/vnd.ez.api.BasketTotals+json", - "name": "", - "totalNet": null, - "totalGross": null, - "vatList": {}, - "groupType": "order", - "currency": "NOK" - } - }, - "totalsSum": { - "_media-type": "application\/vnd.ez.api.BasketTotals+json", - "name": "", - "totalNet": null, - "totalGross": null, - "vatList": {}, - "groupType": "order", - "currency": "NOK" - }, - "currency": null, - "totalsSumNet": null, - "totalsSumGross": null, - "additionalLines": null, - "lines": [], - "dateLastPriceCalculation": { - "_media-type": "application\/vnd.ez.api.DateTime+json", - "_exception": { - "Name": "eZ\\Publish\\Core\\REST\\Common\\Output\\Exceptions\\NoVisitorFoundException", - "Message": "No visitor found for DateTime!" - } - }, - "shippingMethod": "expressDel", - "paymentMethod": "invoice", - "paymentTransactionId": null, - "confirmationEmail": null, - "salesConfirmationEmail": null, - "allProductsAvailable": true, - "dataMap": { - "voucher_1234567": "1234567" - } - } - ``` - -## readBasketList - -`/api/ezp/v2/siso-rest/basket/header/{basket_type} (GET)` - -Returns all baskets of the given type of the user with header information. - -Standard service ID: `silver_basket.basket_service` - -### Request - -empty - -??? note "Response" - - ``` - { - "BasketListResponse": { - "_media-type": "application/vnd.ez.api.BasketListResponse+json", - "basketList": [ - { - "_media-type": "application/vnd.ez.api.stdClass+json", - "basketId": 1198, - "state": "new", - "type": "basket", - "userId": 26569, - "invoiceParty": { - "_media-type": "application/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": null - } - ], - "PostalAddress": { - "_media-type": "application/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "610 N Elm DR", - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": "Beverly Hills", - "PostalZone": "90210", - "CountrySubentity": "CA", - "CountrySubentityCode": "CA", - "AddressLine": [], - "Country": { - "_media-type": "application/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "US", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": "mal@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } - }, - "deliveryParty": { - "_media-type": "application/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": null - } - ], - "PostalAddress": { - "_media-type": "application/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "610 N Elm DR", - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": "Beverly Hills", - "PostalZone": "90210", - "CountrySubentity": "CA", - "CountrySubentityCode": "CA", - "AddressLine": [], - "Country": { - "_media-type": "application/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "US", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": "mal@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } - }, - "buyerParty": { - "_media-type": "application/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application/vnd.ez.api.PartyPartyName+json", - "Name": null - } - ], - "PostalAddress": { - "_media-type": "application/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "610 N Elm DR", - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": "Beverly Hills", - "PostalZone": "90210", - "CountrySubentity": "CA", - "CountrySubentityCode": "CA", - "AddressLine": [], - "Country": { - "_media-type": "application/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "US", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": "mal@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } - }, - "remark": "Test remark", - "dateCreated": { - "_media-type": "application/vnd.ez.api.DateTime+json", - "date": "2018-07-11 11:28:42.000000", - "timezone_type": 3, - "timezone": "Europe/Berlin" - }, - "dateLastModified": { - "_media-type": "application/vnd.ez.api.DateTime+json", - "date": "2018-07-31 13:16:09.000000", - "timezone_type": 3, - "timezone": "Europe/Berlin" - }, - "totals": [ - { - "_media-type": "application/vnd.ez.api.BasketTotals+json" - }, - { - "_media-type": "application/vnd.ez.api.BasketTotals+json" - } - ], - "totalsSum": { - "_media-type": "application/vnd.ez.api.BasketTotals+json" - }, - "currency": "USD", - "totalsSumNet": 784, - "totalsSumGross": 784, - "dateLastPriceCalculation": { - "_media-type": "application/vnd.ez.api.DateTime+json", - "date": "2018-07-31 12:48:58.000000", - "timezone_type": 3, - "timezone": "Europe/Berlin" - }, - "paymentMethod": "invoice" - } - ] - } - } - ``` - -## updateBasketParty - -``` -/api/ezp/v2/siso-rest/basket/current/party/invoice (PATCH) -/api/ezp/v2/siso-rest/basket/current/party/delivery (PATCH) -/api/ezp/v2/siso-rest/basket/current/party/buyer (PATCH) -``` - -Validates party data and stores it in basket. - -Standard service ID: `siso_rest_api.basket_helper_service` (methods `validateParty` and `storePartyInBasket`) - -### Validation in standard - -!!! note - - Due to a bug in the Symfony Validation component, it is not possible to pass groups to sub-constraints of composite constraints. - Composite constraints themselves can be configured with validation groups. - This means that if the sub-constraints should be validated in different groups, - the composite (in this example `StringObject`) must be duplicated with the respective combinations of groups. - An example of this can be found in the ElectronicMail field of the Contact class below. - -``` yaml -Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\Party: - properties: - PartyName: - - Valid: ~ - PostalAddress: - - Valid: ~ - Contact: - - Valid: ~ - constraints: - - Callback: [Siso\RestApiBundle\Validator\Constraints\PartyValidator, validatePartyName] - -Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\PartyPartyName: - properties: - Name: - - NotBlank: ~ - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - Length: - min: 2 - max: 30 - minMessage: 'Please enter minimum {{ limit }} chars' - maxMessage: 'Maximum {{ limit }} chars' - -Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\PartyPostalAddress: - properties: - StreetName: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - NotBlank: ~ - - Length: - min: 2 - max: 30 - minMessage: 'Please enter minimum {{ limit }} chars' - maxMessage: 'Maximum {{ limit }} chars' - AdditionalStreetName: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - Length: - min: 2 - max: 30 - minMessage: 'Please enter minimum {{ limit }} chars' - maxMessage: 'Maximum {{ limit }} chars' - CityName: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - NotBlank: ~ - - Length: - min: 2 - max: 30 - minMessage: 'Please enter minimum {{ limit }} chars' - maxMessage: 'Maximum {{ limit }} chars' - PostalZone: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - NotBlank: ~ - - Siso\RestApiBundle\Validator\Constraints\Zip: ~ - CountrySubentity: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - Length: - min: 2 - max: 30 - minMessage: 'Please enter minimum {{ limit }} chars' - maxMessage: 'Maximum {{ limit }} chars' - Country: - - Valid: ~ - -Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\PartyPostalAddressCountry: - properties: - IdentificationCode: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - NotBlank: ~ - -Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\Contact: - properties: - ElectronicMail: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - groups: ['invoice'] - constraints: - - NotBlank: ~ - - Silversolutions\Bundle\EshopBundle\Entities\Forms\Constraints\Email: ~ - - Siso\RestApiBundle\Validator\Constraints\StringObject: - groups: ['Default'] - constraints: - - Silversolutions\Bundle\EshopBundle\Entities\Forms\Constraints\Email: ~ - Telephone: - - Siso\RestApiBundle\Validator\Constraints\StringObject: - - Silversolutions\Bundle\EshopBundle\Entities\Forms\Constraints\Phone: ~ -``` - -### Request - -``` -"PartyData": { - "_data-type": "invoice", - "_media-type": "application\/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [ - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": "Anke Test" - }, - { - "_media-type": "application\/vnd.ez.api.PartyPartyName+json", - "Name": "" - } - ], - "PostalAddress": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json", - "StreetName": "Teststr. 11", - "AdditionalStreetName": "", - "BuildingNumber": null, - "CityName": "Testingen", - "PostalZone": "1111", - "CountrySubentity": "Dummy", - "CountrySubentityCode": null, - "AddressLine": [], - "Country": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": "NO", - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application\/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": "", - "Telefax": null, - "ElectronicMail": "aka_test1@silversolutions.de", - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application\/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": { - "forceInvoice": false, - "customer_type": "private" - } -} -``` - -### Response - -Success: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -``` - -Error: - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": { - "Party": { - "PartyName": [ - { - "Name": "This value should not be blank." - } - ], - "PostalAddress": { - "StreetName": "This value should not be blank.", - "CityName": "This value should not be blank.", - "PostalZone": [ - "This value should not be blank.", - "\"\" is not a valid ZIP code. For valid ZIP code use following pattern \"1234\"." - ] - }, - "Contact": { - "ElectronicMail": "This value should not be blank." - } - } - } - } -} -``` - -## updateStoredBasket - -`/api/ezp/v2/siso-rest/basket/update-stored/{basketId} (POST)` - -Adds lines to stored basket with `basketId`. - -### Request - -``` -{ - "BasketLineData": [{ - "basketLineId": 900, - "lineNumber": 1, - "sku": "000000000000001134", - "variantCode": null, - "productType": "OrderableProductNode", - "quantity": 1, - "unit": null, - "price": 300, - "priceNet": 300, - "priceGross": 300, - "linePriceAmountNet": 300, - "linePriceAmountGross": 300, - "vat": 0, - "isIncVat": false, - "currency": "EUR", - "catalogElement": { - "groupCalc": null, - "groupOrder": null, - "remark": null, - "remoteDataMap": { - "isPriceValid": true, - "lineRemark": null, - "scaledPrices": null, - "sapLineId": 10, - "dlvDate": "2018-08-14", - "discount": 35, - "hasDeliveryDateEmpty": false, - "sapListPrice": { - "price": 300, - "currency": "EUR", - "stock": { - "variantCharacteristics": null, - "assignedLines": null - } - } - - } - } - }, - { - "basketLineId": 900, - "lineNumber": 1, - "sku": "000000000000001134", - "variantCode": null, - "productType": "OrderableProductNode", - "quantity": 1, - "unit": null, - "price": 300, - "priceNet": 300, - "priceGross": 300, - "linePriceAmountNet": 300, - "linePriceAmountGross": 300, - "vat": 0, - "isIncVat": false, - "currency": "EUR", - "catalogElement": { - "groupCalc": null, - "groupOrder": null, - "remark": null, - "remoteDataMap": { - "isPriceValid": true, - "lineRemark": null, - "scaledPrices": null, - "sapLineId": 10, - "dlvDate": "2018-08-14", - "discount": 35, - "hasDeliveryDateEmpty": false, - "sapListPrice": { - "price": 300, - "currency": "EUR", - "stock": { - "variantCharacteristics": null, - "assignedLines": null - } - } - } - } - } - ]}; -} -``` - -### Response - -Success: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": { - "_success":"" - } - } -``` - -Error: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": { - "_error":[ - "error_message" - ] - } - } -``` - - -## updateBasketShippingMethod - -`/api/ezp/v2/siso-rest/basket/current/shippingmethod (PATCH)` - -Validates and stores shipping method in the current basket. - -Standard service ID: `siso_rest_api.basket_helper_service` (methods `validateShippingMethod` and `storeShippingMethodInBasket`) - -### Validation in standard - -``` -Siso\RestApiBundle\Model\ShippingMethod: - constraints: - - Siso\RestApiBundle\Validator\Constraints\ShippingMethodConstraint: ~ -``` - -### Request - -``` -"ShippingMethodData":{ - "shippingMethod":"mail" -} -``` - -### Reponse - -Success: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -``` - -Error: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [ - "Object(Siso\\RestApiBundle\\Model\\ShippingMethod):\n The value is not valid" - ] - } -``` - -## updateBasketPaymentMethod - -`/api/ezp/v2/siso-rest/basket/current/paymentmethod (PATCH)` - -Validates and stores shipping method in the current basket. - -Standard service ID: `siso_rest_api.basket_helper_service` (methods `validatePaymentMethod` and `storePaymentMethodInBasket`) - -### Validation in standard - -``` -Siso\RestApiBundle\Model\PaymentMethod: - constraints: - - Siso\RestApiBundle\Validator\Constraints\PaymentMethodConstraint: ~ -``` - -### Request - -``` -"PaymentMethodData":{ - "paymentMethod":"invoice" -} -``` - -### Response - -Success: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -``` - -Error: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [ - "Object(Siso\\RestApiBundle\\Model\\PaymentMethod):\n The value is not valid" - ] - } -``` - -## updateBasketVoucher - -`/api/ezp/v2/siso-rest/basket/current/voucher (PATCH)` - -Validates and stores the voucher code in the current basket. If an empty field is sent, the current voucher is deleted. - -Standard service ID: `siso_rest_api.basket_helper_service` (method `validateAndStoreVoucherCode`) - -### Request - -``` -"VoucherData":{ - "voucherCode":"1234567" -} -``` - -### Response - -Success: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -``` - -Error: - -``` -"ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [ - "Object(Siso\\RestApiBundle\\Model\\Voucher):\n The value is not valid" - ] - } -``` - -## AddToBasket - -`/api/ezp/v2/siso-rest/basket/current/lines (POST)` - -Call to add one or more items to the current basket. - -### Request - -``` -{ - "BasketLineData":[ - { - "sku": "9788202544683", - "quantity": 3, - "isVariant": 0, - "variantCode": '' - }, - { - "sku":"9788210056116", - "quantity": 1, - "isVariant": 0, - "variantCode": '' - } - ] - -} -``` - -### Response - -Success: - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -} -``` - -Error: - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [ - "Product with the sku: 9788210056115 not found" - ] - } -} -``` - -## UpdateBasket - -`/api/ezp/v2/siso-rest/basket/update (POST)` - -Calls to update one or more items in the current basket. - -If the quantity is 0, the line is removed from the basket - -Currently only the following properties are updated: - -- `quantity` -- `RemoteDataMap` -- `remark` - -### Request - -``` -{ - "BasketLineData":[ - { - "_media-type": "application/vnd.ez.api.BasketLine+json", - "basketLineId": 122, - "lineNumber": 3, - "sku": "000000000000001134", - "variantCode": null, - "productType": "OrderableProductNode", - "quantity": 2, - "unit": null, - "price": 392, - "priceNet": 392, - "priceGross": 392, - "linePriceAmountNet": 392, - "linePriceAmountGross": 392, - "vat": 0, - "isIncVat": false, - "currency": "USD", - "catalogElement": { - "_media-type": "application/vnd.ez.api.OrderableProductNode+json", - "sku": "000000000000001134", - "manufacturerSku": "", - "ean": "4053913000014", - "type": null, - "isOrderable": null, - "price": { - "_media-type": "application/vnd.ez.api.PriceField+json" - }, - "customerPrice": null, - "scaledPrices": null, - "stock": null, - "shortDescription": { - "_media-type": "application/vnd.ez.api.TextBlockField+json" - }, - "longDescription": { - "_media-type": "application/vnd.ez.api.TextBlockField+json" - }, - "specifications": null, - "imageList": { - "0": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "1": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "2": { - "_media-type": "application/vnd.ez.api.ImageField+json" - } - }, - "minOrderQuantity": null, - "maxOrderQuantity": null, - "allowedQuantity": null, - "packagingUnit": null, - "unit": null, - "vatCode": "VATUNIT", - "name": "2/2-way-piston-operated angle-seat valve", - "text": "", - "image": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "path": null, - "url": "/process-and-control-valves/shut-off-valves-on-off/angle-seat-valves/pneumatic/1134", - "parentElementIdentifier": "1000015", - "identifier": "1005576", - "cacheIdentifier": null, - "checkProperties": null - }, - "groupCalc": null, - "groupOrder": null, - "remark": null, - "remoteDataMap": { - "isPriceValid": true, - "lineRemark": null, - "scaledPrices": null, - "hasDeliveryDateEmpty": true - }, - "variantCharacteristics": null, - "assignedLines": null - }, - { - "_media-type": "application/vnd.ez.api.BasketLine+json", - "basketLineId": 123, - "lineNumber": 4, - "sku": "000000000000134590", - "variantCode": null, - "productType": "OrderableProductNode", - "quantity": 3, - "unit": null, - "price": 623, - "priceNet": 623, - "priceGross": 623, - "linePriceAmountNet": 623, - "linePriceAmountGross": 623, - "vat": 0, - "isIncVat": false, - "currency": "USD", - "catalogElement": { - "_media-type": "application/vnd.ez.api.OrderableProductNode+json", - "sku": "000000000000134590", - "manufacturerSku": "", - "ean": "4053913025352", - "type": null, - "isOrderable": null, - "price": { - "_media-type": "application/vnd.ez.api.PriceField+json" - }, - "customerPrice": null, - "scaledPrices": null, - "stock": null, - "shortDescription": { - "_media-type": "application/vnd.ez.api.TextBlockField+json" - }, - "longDescription": { - "_media-type": "application/vnd.ez.api.TextBlockField+json" - }, - "specifications": null, - "imageList": { - "0": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "1": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "2": { - "_media-type": "application/vnd.ez.api.ImageField+json" - } - }, - "minOrderQuantity": null, - "maxOrderQuantity": null, - "allowedQuantity": null, - "packagingUnit": null, - "unit": null, - "vatCode": "VATUNIT", - "name": "2/2-way-solenoid valve; servo-piston", - "text": "", - "image": { - "_media-type": "application/vnd.ez.api.ImageField+json" - }, - "path": null, - "url": "/solenoid-valves/general-purpose-2-2-solenoids/134590", - "parentElementIdentifier": "1000001", - "identifier": "1006516", - "cacheIdentifier": null, - "checkProperties": null - }, - "groupCalc": null, - "groupOrder": null, - "remark": null, - "remoteDataMap": { - "isPriceValid": true, - "lineRemark": null, - "scaledPrices": null, - "hasDeliveryDateEmpty": true - }, - "variantCharacteristics": null, - "assignedLines": null - } -] - -} -``` - -### Response - -Success: - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [] - } -} -``` - -Error: - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": [ - "Product with the sku: 9788210056115 not found" - ] - } -} -``` diff --git a/docs/api/commerce_api/sisorestapibundle/checkout_functions.md b/docs/api/commerce_api/sisorestapibundle/checkout_functions.md deleted file mode 100644 index febda204e8..0000000000 --- a/docs/api/commerce_api/sisorestapibundle/checkout_functions.md +++ /dev/null @@ -1,60 +0,0 @@ -# Checkout functions - -## getBasketPaymentMethods - -`/api/ezp/v2/siso-rest/checkout/payment-methods (GET)` - -Returns available payment options for the current basket. - -Parameter-based default implementation can be replaced by overriding the service ID. - -Standard service ID: `siso_rest_api.payment_methods_service` - -### Request - -empty - -### Response - -``` -{ - "PaymentMethodDataResponse": { - "_media-type": "application\/vnd.ez.api.PaymentMethodDataResponse+json", - "paymentMethods": { - "paypal": "paypal", - "invoice": "invoice" - }, - "defaultMethod": "invoice" - } -} -``` - -## getBasketShippingMethods - -`/api/ezp/v2/siso-rest/checkout/shipping-methods (GET)` - -Returns available delivery options for the current basket. - -Parameter-based default implementation can be replaced by overriding the service ID. - -Standard service ID: `siso_rest_api.shipping_methods_service` - -### Request - -empty - -### Response - -``` -{ - "ShippingMethodDataResponse": { - "_media-type": "application\/vnd.ez.api.ShippingMethodDataResponse+json", - "shippingMethods": { - "LIEFERUNG": "standard_mail", - "mail": "mail", - "expressDel": "express_delivery" - }, - "defaultMethod": "LIEFERUNG" - } -} -``` diff --git a/docs/api/commerce_api/sisorestapibundle/common.md b/docs/api/commerce_api/sisorestapibundle/common.md deleted file mode 100644 index ca6bed8565..0000000000 --- a/docs/api/commerce_api/sisorestapibundle/common.md +++ /dev/null @@ -1,142 +0,0 @@ -# Common functions - -## getCountrySelection - -`/api/ezp/v2/siso-rest/country-selection (GET)` - -Returns the list of configured country codes and their translations for the current SiteAccess. - -Service ID: `siso_rest_api.country_selection_service`. - -Inside this service the countries are fetched from the `siso_tools.country` service. - -Parameter `ses_forms.default.preferred_country` is used for `defaultCountry`. - -### Request - -empty - -### Response - -``` -{ - "CountrySelectionResponse": { - "_media-type": "application\/vnd.ez.api.CountrySelectionResponse+json", - "countryOptions": { - "DZ": "Algerien", - "AU": "Australien", - "BE": "Belgien", - "BR": "Brasilien", - "BG": "Bulgarien", - "DK": "D\u00e4nemark", - "DE": "Deutschland", - "EE": "Estland", - "FI": "Finnland", - "FR": "Frankreich", - "GR": "Griechenland", - "IN": "Indien", - "ID": "Indonesien", - "IE": "Irland", - "IS": "Island", - "IT": "Italien", - "CA": "Kanada", - "KE": "Kenia", - "LV": "Lettland", - "LT": "Litauen", - "MY": "Malaysia", - "MT": "Malta", - "MX": "Mexiko", - "MZ": "Mosambik", - "NZ": "Neuseeland", - "NL": "Niederlande", - "NG": "Nigeria", - "NO": "Norwegen", - "AT": "\u00d6sterreich", - "PH": "Philippinen", - "PL": "Polen", - "PT": "Portugal", - "RO": "Rum\u00e4nien", - "RU": "Russland", - "SE": "Schweden", - "CH": "Schweiz", - "SG": "Singapur", - "SK": "Slowakei", - "SI": "Slowenien", - "MO": "Sonderverwaltungsregion Macau", - "ES": "Spanien", - "ZA": "S\u00fcdafrika", - "SZ": "Swasiland", - "TZ": "Tansania", - "TH": "Thailand", - "CZ": "Tschechische Republik", - "TN": "Tunesien", - "TR": "T\u00fcrkei", - "UG": "Uganda", - "HU": "Ungarn", - "AE": "Vereinigte Arabische Emirate", - "US": "Vereinigte Staaten", - "GB": "Vereinigtes K\u00f6nigreich", - "CY": "Zypern" - }, - "defaultCountry": "DE" - } -} -``` - -## getCustomerprice - -`/api/ezp/v2/siso-rest/customerprice (POST)` - -Returns the customer price. - -### Request - -``` -{ - "PriceRequest": { - "Meta": { - "Context": "product_list", - "CatalogElement": true - } - "Items": [{ - "Sku": "1234", - "Quantity": 2, - "UnitOfMeasure": "ST", - "DataMap": {} - }] - } -} -``` - -### Response - -``` -{ - "PriceResponse": [{ - "Sku": "1234", - "RequestedQuantity": 2, - "RequestedUnitOfMeasure": "ST", - "CustomerPrice": { - "Quantity": 2, - "UnitOfMeasure": "ST", - "Price": 1.50, - "LinePrice": 3.00, - "Currency": "EUR", - "PriceFormatted": "1.50 €", - "LinePriceFormatted": "3.00 €" - }, - "ListPrice": { - "Quantity": 2, - "UnitOfMeasure": "ST", - "Price": 1.5, - "LinePrice": 3.00, - "Currency": "EUR", - "PriceFormatted": "1.50 €", - "LinePriceFormatted": "3.00 €" - }, - "Stock": 32, - "Message": "", - "DataMap": {} - }] -} -``` diff --git a/docs/api/commerce_api/sisorestapibundle/customer_functions.md b/docs/api/commerce_api/sisorestapibundle/customer_functions.md deleted file mode 100644 index 6ee62abc25..0000000000 --- a/docs/api/commerce_api/sisorestapibundle/customer_functions.md +++ /dev/null @@ -1,72 +0,0 @@ -# Customer functions - -## getShippingAddresses - -`/api/ezp/v2/siso-rest/customer/addresses/shipping (GET)` - -Returns available delivery addresses for the current customer. - -Use the standard `ses.customer_profile_data.ez_erp` service to get shipping addresses: - -``` -getCustomerProfileData()->deliveryParties -``` - -### Request - -empty - -### Response - -``` -{ - "ShippingAddressesResponse": { - "_media-type": "application\/vnd.ez.api.ShippingAddressesResponse+json", - "Parties": [ - { - "_media-type": "application\/vnd.ez.api.Party+json", - "PartyIdentification": [], - "PartyName": [], - "PostalAddress": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json", - "StreetName": null, - "AdditionalStreetName": null, - "BuildingNumber": null, - "CityName": null, - "PostalZone": null, - "CountrySubentity": null, - "CountrySubentityCode": null, - "AddressLine": [], - "Country": { - "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json", - "IdentificationCode": null, - "Name": null - }, - "Department": null, - "SesExtension": {} - }, - "Contact": { - "_media-type": "application\/vnd.ez.api.Contact+json", - "ID": null, - "Name": null, - "Telephone": null, - "Telefax": null, - "ElectronicMail": null, - "OtherCommunication": null, - "Note": null, - "SesExtension": {} - }, - "Person": { - "_media-type": "application\/vnd.ez.api.PartyPerson+json", - "FirstName": null, - "FamilyName": null, - "Title": null, - "MiddleName": null, - "SesExtension": {} - }, - "SesExtension": {} - } - ] - } -} -``` diff --git a/docs/api/commerce_api/sisorestapibundle/sisorestapibundle.md b/docs/api/commerce_api/sisorestapibundle/sisorestapibundle.md deleted file mode 100644 index c31f107149..0000000000 --- a/docs/api/commerce_api/sisorestapibundle/sisorestapibundle.md +++ /dev/null @@ -1,110 +0,0 @@ -# SisoRestApiBundle - -## Using the REST interface - -For GET requests a token is not required. - -``` -axios.get('/api/ezp/v2/siso-rest/basket') - .then(function (response) { - - app.basketObject = response.data; - if(app.basketObject.Basket) { - ..... - } - -} -``` - -If you are using a PATCH request, a token is required. A Twig function provides the current token. - -``` -var token = "{{ csrf_token('rest') }}"; -axios.patch('/api/ezp/v2/siso-rest/basket/current/shippingmethod', { - "ShippingMethodData": { - "shippingMethod": app.shippingMethod - } -}, { - headers: { - 'X-CSRF-Token': token, - 'Content-Type': 'application/vnd.ez.api.ShippingMethodData+json', - 'Accept': 'application/vnd.ez.api.Basket+json' - } -}) -``` - -## Error handling / responses - -``` -{ - "ValidationResponse": { - "_media-type": "application\/vnd.ez.api.ValidationResponse+json", - "messages": { - "Party": { - "_errors": [ - { - "_code": 103, - "_message": "This address is already stored in your address book" - } - ] - "PartyName": [ - { - "Name": { - "_errors": [ - { - "_code": 100, - "_message": "This value should not be blank." - } - ] - } - } - ], - "PostalAddress": { - "StreetName": { - "_errors": [ - { - "_code": 100, - "_message": "This value should not be blank." - } - ] - }, - "CityName": { - "_errors": [ - { - "_code": 100, - "_message": "This value should not be blank." - } - ] - }, - "PostalZone": { - "_errors": [ - { - "_code": 100, - "_message": "This value should not be blank." - }, - { - "_code": 101, - "_message": "\"\" is not a valid ZIP code. For valid ZIP code use following pattern \"1234\"." - } - ] - } - }, - "Contact": { - "ElectronicMail": { - "_errors": [ - { - "_code": 100, - "_message": "This value should not be blank." - } - ] - } - } - } - } - } -} -``` - -``` -{{ error_message([Party][PartyName][Name]) }} -``` diff --git a/docs/api/commerce_rest_api.md b/docs/api/commerce_rest_api.md deleted file mode 100644 index eda7836e17..0000000000 --- a/docs/api/commerce_rest_api.md +++ /dev/null @@ -1,35 +0,0 @@ -# Commerce REST API - -[[= product_name_com =]] extends the REST API of [[= product_name =]] with the following features: - -## Basket API - -| Feature | Method | Notes | -| ------- | ------ | ----- | -|Gets current basket |`/api/ezp/v2/siso-rest/basket/current`|| -|Reads list of baskets| `/api/ezp/v2/siso-rest/basket/header/{basket_type}`|Returns wishlist/stored baskets| -|Updates party information in the basket|`/api/ezp/v2/siso-rest/basket/current/party/invoice (PATCH)`
    `/api/ezp/v2/siso-rest/basket/current/party/delivery (PATCH)`
    `/api/ezp/v2/siso-rest/basket/current/party/buyer (PATCH)`|| -|Updates shipping information| `/api/ezp/v2/siso-rest/basket/current/shippingmethod` || -|Updates payment information| `/api/ezp/v2/siso-rest/basket/current/paymentmethod` || -|Updates and checks voucher| `/api/ezp/v2/siso-rest/basket/current/voucher`|| -|Adds a product to a basket| `/api/ezp/v2/siso-rest/basket/current/lines`|| -|Updates information in the basketline |`/api/ezp/v2/siso-rest/basket/update` || - -## Checkout API - -| Feature | Method | -| ------- | ------ | -|Gets list of payment methods | `/api/ezp/v2/siso-rest/checkout/payment-methods` | -|Gets list of shipping methods | `/api/ezp/v2/siso-rest/checkout/shipping-methods` | - -## Common API - -| Feature | Method | -| ------- | ------ | -|Gets list of payment methods | `/api/ezp/v2/siso-rest/country-selection` | - -## Customer API - -| Feature | Method | -| ------- | ------ | -|Gets list of shipping addresses from customer | `/api/ezp/v2/siso-rest/customer/addresses/shipping`| diff --git a/docs/api/creating_content_with_binary_attachments_via_rest_api.md b/docs/api/creating_content_with_binary_attachments_via_rest_api.md deleted file mode 100644 index 0f29caf788..0000000000 --- a/docs/api/creating_content_with_binary_attachments_via_rest_api.md +++ /dev/null @@ -1,145 +0,0 @@ -# Creating content with binary attachments via REST API - -This page shows how to create content via the REST API on the example of uploading an image file. - -## Creating a draft - -A draft is created with a POST request to `/api/ezp/v2/content/objects`. -Its body should contain all of the required data in the structured format (see [Creating content: data property](field_type_reference.md#creating-content-data-property)). -As for the response, it's possible to use either JSON or XML in input. -The following examples use JSON (and assume [HTTP Basic Auth](general_rest_usage.md#http-basic-authentication) is enabled). - -``` php -// URL to [[= product_name =]] installation -$base_url = "http://127.0.0.1"; -// User credentials -$username = "admin"; -$password = "publish"; - -if ($argc < 2) { - // Print script usage - echo "Usage: php $argv[0] \n"; - exit(1); -} - -// Request payload -$data = [ - 'ContentCreate' => [ - 'ContentType' => [ - // "Image" content type - '_href' => "/api/ezp/v2/content/types/5", - ], - 'mainLanguageCode' => 'eng-GB', - 'LocationCreate' => [ - 'ParentLocation' => [ - // Destination location ("Media" root location in this case) - '_href' => "/api/ezp/v2/content/locations/1/43", - ], - 'sortField' => 'PATH', - 'sortOrder' => 'ASC', - ], - 'Section' => [ - // "Media" section - '_href' => "/api/ezp/v2/content/sections/3", - ], - 'fields' => [ - 'field' => [ - [ - 'fieldDefinitionIdentifier' => 'name', - 'fieldValue' => 'File uploaded via REST API', - ], - [ - 'fieldDefinitionIdentifier' => 'image', - 'fieldValue' => [ - // Original file name - 'fileName' => pathinfo($argv[1], PATHINFO_BASENAME), - // File size in bytes - 'fileSize' => filesize($argv[1]), - // File content must be encoded as BASE64 - 'data' => base64_encode(file_get_contents($argv[1])), - ], - ], - ], - ], - ], -]; - -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_URL => "$base_url/api/ezp/v2/content/objects", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "POST", - CURLOPT_POSTFIELDS => json_encode($data), - CURLOPT_USERPWD => "$username:$password", - CURLOPT_HTTPHEADER => [ - "accept: application/json", - "cache-control: no-cache", - "content-type: application/vnd.ez.api.ContentCreate+json", - ], -]); - -$response = curl_exec($curl); - -if (($err = curl_error($curl))) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} - -curl_close($curl); -``` - -## Publishing the image - -To publish the image use the following code: - -``` php -// URL to [[= product_name =]] installation -$base_url = "http://127.0.0.1"; -// User credentials -$username = "admin"; -$password = "publish"; - -if ($argc < 2) { - // Print script usage - echo "Usage: php $argv[0] \n"; - exit(1); -} - -$id = $argv[1]; - -// Publish the draft -$curl = curl_init(); - -curl_setopt_array($curl, [ - CURLOPT_URL => "$base_url/api/ezp/v2/content/objects/$id/versions/1", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_ENCODING => "", - CURLOPT_MAXREDIRS => 10, - CURLOPT_TIMEOUT => 30, - CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, - CURLOPT_CUSTOMREQUEST => "POST", - CURLOPT_USERPWD => "$username:$password", - CURLOPT_HTTPHEADER => [] - "accept: application/json", - "cache-control: no-cache", - "x-http-method-override: PUBLISH" - ], -]); - -$response = curl_exec($curl); -$err = curl_error($curl); - -curl_close($curl); - -if ($err) { - echo "cURL Error #:" . $err; -} else { - echo $response; -} -``` diff --git a/docs/api/creating_custom_rest_api_response.md b/docs/api/creating_custom_rest_api_response.md deleted file mode 100644 index 35b85f115c..0000000000 --- a/docs/api/creating_custom_rest_api_response.md +++ /dev/null @@ -1,267 +0,0 @@ -# Creating custom REST API response based on Accept header - -Customized REST API response can be used in many situations, both for headless and more traditional setups. REST responses can be enriched in a clean way and limit client-to-server round trips. - -To do this you can take advantage of [[= product_name =]]'s [HATEOAS-based](https://en.wikipedia.org/wiki/HATEOAS) REST API and extend it with custom Content Types for your own needs. In this section you will add comments count to `eZ\Publish\API\Repository\Values\Content\VersionInfo` responses. - -## Implementation of dedicated Visitor - -The first step is creating your own implementation of `ValueObjectVisitor`. It contains all the logic responsible for: - -- fetching data you want to present -- modifying the actual response - -```php -repository = $repository; - } - - protected function visitVersionInfoAttributes(Visitor $visitor, Generator $generator, APIVersionInfo $versionInfo) - { - parent::visitVersionInfoAttributes($visitor, $generator, $versionInfo); - - $this->visitCommentValue($generator, $versionInfo); - } - - protected function visitCommentValue(Generator $generator, APIVersionInfo $versionInfo) - { - $generator->startValueElement('commentsCount', $this->loadCommentsCount($versionInfo)); - $generator->endValueElement('commentsCount'); - } - - private function loadCommentsCount(APIVersionInfo $versionInfo) - { - // load comments count using the repository (injected), or any comments backend - } -} -``` - -## Overriding response type - -Next, make sure that your new implementation of serialization applies only to the selected objects. In order to do that, you need to -decorate `EzSystems\EzPlatformRest\Output\ValueObjectVisitorDispatcher` from `ezplatform-kernel`. - -```php -parentDispatcher = $parentDispatcher; - } - - public function setOutputVisitor(Visitor $outputVisitor) - { - parent::setOutputVisitor($outputVisitor); - $this->parentDispatcher->setOutputVisitor($outputVisitor); - } - - public function setOutputGenerator(Generator $outputGenerator) - { - parent::setOutputGenerator($outputGenerator); - $this->parentDispatcher->setOutputGenerator($outputGenerator); - } - - public function visit($data) - { - try { - return parent::visit($data); - } catch (NoVisitorFoundException $e) { - return $this->parentDispatcher->visit($data); - } - } -} -``` - -To be able to use the overridden type you also need to implement new Compiler Pass. For more details, see [Symfony doc.](https://symfony.com/doc/5.0/service_container/compiler_passes.html) - -```php -hasDefinition(self::DISPATCHER_DEFINITION_ID)) { - return; - } - - $definition = $container->getDefinition(self::DISPATCHER_DEFINITION_ID); - - foreach ($container->findTaggedServiceIds(self::TAG_NAME) as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['type'])) { - throw new \LogicException(self::TAG_NAME . ' service tag needs a "type" attribute to identify the field type. None given.'); - } - $definition->addMethodCall( - 'addVisitor', - [$attribute['type'], new Reference($id)] - ); - } - } - } -} -``` - -Also, do not forget to register it in your bundle. - -```php -addCompilerPass(new ValueObjectVisitorPass()); - } -} - -``` - -## Configuration - -The last thing you need to do is to set a configuration which should be located in the `services.yaml` file of your bundle. -The important part are the keys: - -- `app.rest.output.visitor.json.regexps` which helps identifying proper header -- `priority` which should be set high enough, to not be overridden by another implementation - -All the other keys need to correspond with the current namespace of your bundle. In this example it is just `ExampleBundle`. - -```yaml -parameters: - app.rest.output.visitor.json.regexps: - - '(^application/my\.api\.[A-Za-z]+\+json$)' - app.rest.generator.json.vendor: 'my.api' - -services: - app.rest.output.generator.json: - class: EzSystems\EzPlatformRest\Output\Generator\Json - arguments: - - '@ezpublish_rest.output.generator.json.field_type_hash_generator' - - '%app.rest.generator.json.vendor%' - calls: - - [ setFormatOutput, [ '%kernel.debug%' ] ] - - app.rest.output.visitor.json: - class: '%ezpublish_rest.output.visitor.class%' - arguments: - - '@app.rest.output.generator.json' - - '@app.rest.output.value_object_visitor.dispatcher' - tags: - - { name: ezpublish_rest.output.visitor, regexps: app.rest.output.visitor.json.regexps, priority: 200 } - - app.rest.output.value_object_visitor.dispatcher: - class: ExampleBundle\Rest\ValueObjectVisitorDispatcher - arguments: - - '@ezpublish_rest.output.value_object_visitor.dispatcher' - - app.rest.output.value_object_visitor.version_info: - class: ExampleBundle\Rest\ValueObjectVisitor\VersionInfo - parent: ezpublish_rest.output.value_object_visitor.base - arguments: - - '@ezpublish.api.repository' - tags: - - { name: app.value_object_visitor, type: eZ\Publish\API\Repository\Values\Content\VersionInfo } -``` - -## Fetching the modified response - -After following all the steps you should see an example of the modified API response below. As you see `media-type` is correctly interpreted and `commentsCount` is also appended (it's `null` as you did not provide any logic to fetch it). -Please note that you should set a proper `Accept` header value. For this example: `application/my.api.VersionList+json`. - -```json -{ - "VersionList": { - "_media-type": "application\/my.api.VersionList+json", - "_href": "\/api\/ezp\/v2\/content\/objects\/1\/versions", - "VersionItem": [ - { - "Version": { - "_media-type": "application\/my.api.Version+json", - "_href": "\/api\/ezp\/v2\/content\/objects\/1\/versions\/9" - }, - "VersionInfo": { - "id": 506, - "versionNo": 9, - "status": "PUBLISHED", - "modificationDate": "2015-11-30T14:10:46+01:00", - "Creator": { - "_media-type": "application\/my.api.User+json", - "_href": "\/api\/ezp\/v2\/user\/users\/14" - }, - "creationDate": "2015-11-30T14:10:45+01:00", - "initialLanguageCode": "eng-GB", - "languageCodes": "eng-GB", - "VersionTranslationInfo": { - "_media-type": "application\/my.api.VersionTranslationInfo+json", - "Language": [ - { - "languageCode": "eng-GB" - } - ] - }, - "names": { - "value": [ - { - "_languageCode": "eng-GB", - "#text": "Ibexa Platform" - } - ] - }, - "Content": { - "_media-type": "application\/my.api.ContentInfo+json", - "_href": "\/api\/ezp\/v2\/content\/objects\/1" - }, - "commentsCount": null - } - } - ] - } -} -``` - -!!! tip - - You can test your response by using JavaScript/AJAX example code, see [testing the API](rest_api_guide.md#testing-the-api). diff --git a/docs/api/extending_the_rest_api.md b/docs/api/extending_the_rest_api.md deleted file mode 100644 index c762568b40..0000000000 --- a/docs/api/extending_the_rest_api.md +++ /dev/null @@ -1,328 +0,0 @@ -# Extending the REST API - -The [[= product_name =]] REST API comes with a framework that makes it easy to extend the API for your own needs. - -## Requirements - -REST routes are required to use the [[= product_name =]] REST API prefix, `/api/ezp/v2`. You can create new resources below this prefix. - -To do so, you will/may need to create: - -- a controller that will handle your route actions -- a route, in your bundle's routing file -- a controller action -- optionally, a `ValueObjectVisitor` (if your controller returns an object that doesn't already have a converter) -- optionally, an `InputParser` - -### Controller - -To create a REST controller, you need to extend the `ezpublish_rest.controller.base` service, as well as the `EzSystems\EzPlatformRest\Server\Controller` class. - -First create a simple controller that has a `sayHello()` method which takes a name as an argument. - -**My/Bundle/RestBundle/Rest/Controller/DefaultController.php** - -``` php -namespace My\Bundle\RestBundle\Rest\Controller; - -use EzSystems\EzPlatformRest\Server\Controller as BaseController; - -class DefaultController extends BaseController -{ - public function sayHello( $name ) - { - // @todo Implement me - } -} -``` - -### Route - -As mentioned earlier, your REST routes are required to use the REST URI prefix. To do so, the easiest way is to import your routing file using this prefix. - -**config/routing.yaml** - -``` yaml -myRestBundle_rest_routes: - resource: '@MyRestBundle/Resources/config/routing_rest.yaml' - prefix: '%ezpublish_rest.path_prefix%' -``` - -Using a distinct file for REST routes allows you to use the prefix for all this file's routes without affecting other routes from your bundle. - -Next, you need to create the REST route. Define the route's [controller as a service](http://symfony.com/doc/5.0/cookbook/controller/service.html) since your controller was defined as such. - -**My/Bundle/RestBundle/Resources/config/routing\_rest.yaml** - -``` yaml -myRestBundle_hello_world: - path: '/my_rest_bundle/hello/{name}' - defaults: - _controller: My\Bundle\RestBundle\Rest\Controller\DefaultController::sayHelloAction - methods: [GET] -``` - -Due to [EZP-23016](https://jira.ez.no/browse/EZP-23016) - Custom REST API routes (v2) are not accessible from the legacy backend, custom REST routes must be prefixed with `ezpublish_rest_`, or they won't be detected correctly. - -**My/Bundle/RestBundle/Resources/config/services.yaml** - -``` yaml -services: - myRestBundle.controller.default: - class: My\Bundle\RestBundle\Rest\Controller\Default - parent: ezpublish_rest.controller.base -``` - -## Controller action - -Unlike standard Symfony controllers, the REST ones don't return an `HttpFoundation\Response` object, but a `ValueObject`. This object will during the kernel run be converted, using a `ValueObjectVisitor`, to a proper Symfony response. One benefit is that when multiple controllers return the same object, such as a Content item or a Location, the visitor will be re-used. - -Let's say that your controller will return a `My\Bundle\RestBundle\Rest\Values\Hello` - -**My/Bundle/RestBundle/Rest/Values/Hello.php** - -``` php -namespace My\Bundle\RestBundle\Rest\Values; - -class Hello -{ - public $name; - - public function __construct( $name ) - { - $this->name = $name; - } -} -``` - -An instance of this class will be returned from `sayHello()` controller method. - -**My/Bundle/RestBundle/Rest/Controller/DefaultController.php** - -``` php -namespace My\Bundle\RestBundle\Controller; - -use EzSystems\EzPlatformRest\Server\Controller as BaseController; -use My\Bundle\RestBundle\Rest\Values\Hello as HelloValue; - -class DefaultController extends BaseController -{ - public function sayHello( $name ) - { - return new HelloValue( $name ); - } -} -``` - -Outputting this object in the response requires that you create a `ValueObjectVisitor`. - -## ValueObjectVisitor - -A `ValueObjectVisitor` will take a Value returned by a REST controller, whatever the class, and will transform it into data that can be converted, either to JSON or XML format. Those visitors are registered as services, and tagged with `ezpublish_rest.output.value_object_visitor`. The tag attribute says which class this visitor applies to. - -Create the service for your `ValueObjectVisitor` first. - -**My/Bundle/RestBundle/Resources/config/services.yaml** - -``` yaml -services: - myRestBundle.value_object_visitor.hello: - parent: ezpublish_rest.output.value_object_visitor.base - class: My\Bundle\RestBundle\Rest\ValueObjectVisitor\Hello - tags: - - { name: ezpublish_rest.output.value_object_visitor, type: My\Bundle\RestBundle\Rest\Values\Hello } -``` - -Create your visitor next. It must extend the `EzSystems\EzPlatformRest\Output\ValueObjectVisitor` abstract class, and implement the `visit()` method. -It will receive as arguments: - -|Argument|Description| -|--------|-----------| -|`$visitor`| The output visitor. Can be used to set custom response headers ( `setHeader( $name, $value )`), HTTP status code ( `setStatus( $statusCode )` )| -|`$generator`| The actual response generator. It provides you with a DOM like API.| -|`$data`| The visited data. The exact object you returned from the controller| - -**My/Bundle/RestBundle/Rest/Controller/Default.php** - -``` php -namespace My\Bundle\RestBundle\Rest\ValueObjectVisitor; - -use EzSystems\EzPlatformRest\Output\ValueObjectVisitor; -use EzSystems\EzPlatformRest\Output\Generator; -use EzSystems\EzPlatformRest\Output\Visitor; - -class Hello extends ValueObjectVisitor -{ - public function visit( Visitor $visitor, Generator $generator, $data ) - { - $generator->startObjectElement('contentList'); - - $generator->startValueElement('Hello', $data->name); - $generator->endValueElement('Hello'); - - $generator->endObjectElement('contentList'); - $visitor->setStatus(200); // default - } -} -``` - -The easiest way to handle cache is to re-use the `CachedValue` value object. It acts as a proxy, and adds the cache headers, depending on the configuration, for a given object and set of options. - -When you want the response to be cached, return an instance of `CachedValue`, with your Value Object as the argument. You can also pass a Location ID using the second argument, so that the response is tagged with it: - -``` -use EzSystems\EzPlatformRest\Server\Values\CachedValue; -//... - public function sayHello( $name ) - { - return new CachedValue( - new HelloValue( $name ), - ['locationId'=> 42] - ); - - } - -``` - -Below you can find the corresponding response header when using Varnish as reverse proxy: - -``` -Age →30 -Cache-Control →private, no-cache -Via →1.1 varnish-v4 -X-Cache →HIT -X-Cache-Hits →2 -``` - -## Input parser - -If you need to provide your controller with parameters, either in JSON or XML, the parameter struct requires an input parser so that the payload can be converted to an actual `ValueObject`. - -Each payload is dispatched to its input parser based on the request's Content-Type header. For example, a request with a Content-Type of `application/vnd.ez.api.ContentCreate` will be parsed by `EzSystems\EzPlatformRest\Server\Input\Parser`. This parser will build and return a `ContentCreateStruct` that can then be used to create content with the Public API. - -Those input parsers are provided with a pre-parsed version of the input payload, as an associative array, and don't have to care about the actual format (XML or JSON). - -Let's see what it would look like with a Content-Type of `application/vnd.my.Greetings`, that would send this as XML: - -**application/vnd.my.Greetings+xml** - -``` xml - - - John doe - -``` - -First, you need to create a service with the appropriate tag in `services.yaml`. - -**My/Bundle/RestBundle/Resources/config/services.yaml** - -``` yaml -services: - myRestBundle.input_parser.Greetings: - parent: ezpublish_rest.input.parser - class: My\Bundle\RestBundle\Rest\InputParser\Greetings - tags: - - { name: ezpublish_rest.input.parser, mediaType: application/vnd.my.Greetings } -``` - -The mediaType attribute of the `ezpublish\_rest.input.parser` tag maps our Content Type to the input parser. - -Implement your parser. It must extend `EzSystems\EzPlatformRest\Server\Input\Parser`, and implement the `parse()` method. It accepts as an argument the input payload, `$data`, as an array, and an instance of `ParsingDispatcher` that can be used to forward parsing of embedded content. - -For convenience, consider that your input parser returns an instance of `Value\Hello` class. - -**My/Bundle/RestBundle/Rest/InputParser/Greetings.php** - -``` php -namespace My\Bundle\RestBundle\Rest\InputParser; - -use EzSystems\EzPlatformRest\Input\BaseParser; -use EzSystems\EzPlatformRest\Input\ParsingDispatcher; -use My\Bundle\RestBundle\Rest\Value\Hello; -use EzSystems\EzPlatformRest\Exceptions; - - -class Greetings extends BaseParser -{ - /** - * @return My\Bundle\RestBundle\Rest\Value\Hello - */ - public function parse( array $data, ParsingDispatcher $parsingDispatcher ) - { - // re-using the REST exceptions will make sure that those already have a ValueObjectVisitor - if ( !isset( $data['name'] ) ) - throw new Exceptions\Parser( "Missing or invalid 'name' element for Greetings." ); - - - return new Hello( $data['name'] ); - } -} -``` - -You should then add a new method to the previous `DefaultController` to handle the new POST request: - -``` -use EzSystems\EzPlatformRest\Message; -//... - public function sayHelloUsingPost() - { - - $createStruct = $this->inputDispatcher->parse( - new Message( - ['Content-Type' => $request->headers->get('Content-Type')], - $request->getContent() - ) - ); - - $name = $createStruct->name ; - - //... - - } - -``` - -The `inputDispatcher` is responsible for matching the `Content-Type` sent in the header with the Greetings `InputParser` class. - -Finally, a new Route should be added to `routing_rest.yaml` - -``` yaml -myRestBundle_hello_world_using_post: - path: /my_rest_bundle/hello/ - defaults: - _controller: My\Bundle\RestBundle\Rest\Controller\DefaultController::sayHelloUsingPostAction - methods: [POST] -``` - -!!! note - - POST requests are not able to access the Repository without performing a user authentication. For more information check [REST API Authentication](https://github.com/ezsystems/ezpublish-kernel/blob/v8.0.0-beta5/doc/specifications/rest/REST-API-V2.rst#authentication). - -You can look into the built-in `InputParsers`, in `eZ/Publish/Core/REST/Server/Input/Parser`, for more examples. - -## Registering resources in the REST root - -You can register newly added resources so that they show up in the REST root resource for automatic discovery. - -New resources can be registered with code like this: - -``` yaml -ez_publish_rest: - system: - : - rest_root_resources: - someresource: - mediaType: Content - href: 'router.generate("ezpublish_rest_loadContent", {"contentId": 2})' -``` - -with `someresource` being a unique key. - -The `router.generate` call dynamically renders a URI based on the name of the route and the optional parameters that are passed as the other arguments (in the code above this is the `contentId`). - -This syntax is based on [Symfony's expression language](http://symfony.com/doc/5.0/components/expression_language/index.html), an extensible component that allows limited/readable scripting to be used outside code context. - -The above configuration will add the following entry to the root resource: - -`` diff --git a/docs/api/field_type/create_custom_field_type_comparison.md b/docs/api/field_type/create_custom_field_type_comparison.md new file mode 100644 index 0000000000..f6ad093085 --- /dev/null +++ b/docs/api/field_type/create_custom_field_type_comparison.md @@ -0,0 +1,81 @@ +--- +description: Enable comparison of content Fields based on a custom Field Type. +--- + +# Create custom Field Type comparison + +In the Back Office, you can compare the contents of Fields. +Comparing is possible only between two versions of the same Field that are in the same language. + +You can add the possibility to compare custom and other unsupported Field Types. + +!!! note + + The following task uses the [custom "Hello World" Field Type](create_custom_generic_field_type.md). + The configuration is based on the comparison mechanism created for the `ezstring` Field Type. + +## Create Comparable class + +First, create a `Comparable.php` class in `src/FieldType/HelloWorld/Comparison`. + +This class implements the `EzSystems\EzPlatformVersionComparison\FieldType\Comparable` interface with the `getDataToCompare()` method: + +``` php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Comparable.php') =]] +``` + +The `getDataToCompare()` fetches the data to compare and determines which [comparison engines](#create-comparison-engine) should be used. + +Register this class as a service: + +``` yaml +[[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 0, 1) =]][[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 7, 10) =]] +``` + +## Create comparison value + +Next, create a `src/FieldType/HelloWorld/Comparison/Value.php` file that holds the comparison value: + +``` php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/Value.php') =]] +``` + +## Create comparison engine + +The comparison engine handles the operations required for comparing the contents of Fields. +Each Field Type requires a separate comparison engine, which implements the `EzSystems\EzPlatformVersionComparison\Engine\FieldTypeComparisonEngine` interface. + +For the "Hello World" Field Type, create the following comparison engine based on the engine for the TextLine Field Type. +Place it in `src/FieldType/HelloWorld/Comparison/HelloWorldComparisonEngine.php`: + +``` php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonEngine.php') =]] +``` + +Register the comparison engine as a service: + +``` yaml +[[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 0, 1) =]][[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 11, 14) =]] +``` + +## Add comparison result + +Next, create a comparison result class in `src/FieldType/HelloWorld/Comparison/HelloWorldComparisonResult.php`. + +``` php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Comparison/HelloWorldComparisonResult.php') =]] +``` + +## Provide templates + +Finally, create a template for the new comparison view in `templates/field_type_comparison.html.twig`: + +``` html+twig +[[= include_file('code_samples/field_types/generic_ft/templates/field_type_comparison.html.twig') =]] +``` + +Add configuration for this template in `config/packages/ezplatform.yaml`: + +```yaml +[[= include_file('code_samples/field_types/generic_ft/config/packages/field_templates.yaml', 0, 3) =]][[= include_file('code_samples/field_types/generic_ft/config/packages/field_templates.yaml', 5, 7) =]] +``` diff --git a/docs/api/field_type/create_custom_generic_field_type.md b/docs/api/field_type/create_custom_generic_field_type.md new file mode 100644 index 0000000000..3b59008892 --- /dev/null +++ b/docs/api/field_type/create_custom_generic_field_type.md @@ -0,0 +1,113 @@ +--- +description: Create a new Field Type based on the Generic Field Type. +--- + +# Create custom generic Field Type + +The Generic Field Type is an abstract implementation of Field Types holding structured data for example, address. +You can use it as a base for custom Field Types. +The Generic Field Type comes with the implementation of basic methods, +reduces the number of classes which must be created, and simplifies the tagging process. + +!!! tip + + You should not use the Generic Field Type when you need a very specific implementation or complete control over the way data is stored. + +[[= include_file('docs/snippets/simple_hash_value_caution.md') =]] + +## Define value object + +First, create `Value.php` in the `src/FieldType/HelloWorld` directory. +The Value class of a Field Type contains only the basic logic of a Field Type, the rest of it is handled by the `Type` class. +For more information about Field Type Value see [Value handling](../field_type_type_and_value.md#value-handling). + +The `HelloWorld` Value class should contain: + +- public properties that retrieve `name` +- an implementation of the `__toString()` method + +```php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Value.php') =]] +``` + +## Define fields and configuration + +Next, implement a definition of a Field Type extending the Generic Field Type in the `src/FieldType/HelloWorld/Type.php` class. +It provides settings for the Field Type and an implementation of the `eZ\Publish\SPI\FieldType\FieldType` abstract class. + +```php +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Type.php', 0, 6) =]][[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Type.php', 9, 16) =]]} +``` + +!!! tip + + For more information about the Type class of a Field Type, see [Type class](../field_type_type_and_value.md#type-class). + +Next, register the Field Type as a service and tag it with `ezplatform.field_type`: + +```yaml +[[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 0, 5) =]] +``` + +## Define form for value object + +Create a `src/Form/Type/HelloWorldType.php` form. +It enables you to edit the new Field Type. + +```php +[[= include_file('code_samples/field_types/generic_ft/src/Form/Type/HelloWorldType.php') =]] +``` + +Now you can map Field definitions into Symfony forms with FormMapper. +Add the `mapFieldValueForm()` method required by `FieldValueFormMapperInterface` +and the required `use` statements to `src/FieldType/HelloWorld/Type.php`: + +```php hl_lines="7-9 18-26" +[[= include_file('code_samples/field_types/generic_ft/src/FieldType/HelloWorld/Type.php') =]] +``` + +!!! tip + + For more information about the FormMappers see [Field Type form and template](../field_type_form_and_template.md). + +Next, add the `ezplatform.field_type.form_mapper.value` tag to the service definition: + +```yaml hl_lines="6" +[[= include_file('code_samples/field_types/generic_ft/config/custom_services.yaml', 0, 6) =]] +``` + +## Render fields + +### Create a template + +Create a template for the new Field Type. It defines the default rendering of the `HelloWorld` field. +In the `templates` directory create a `field_type.html.twig` file: + +```html+twig +[[= include_file('code_samples/field_types/generic_ft/templates/field_type.html.twig') =]] +``` + +### Template mapping + +Provide the template mapping in `config/packages/ezplatform.yaml`: + +```yaml +[[= include_file('code_samples/field_types/generic_ft/config/packages/field_templates.yaml', 0, 5) =]] +``` + +## Final results + +Finally, you should be able to add a new Content Type in the Back Office interface. +Navigate to **Content Types** tab and under **Content** category create a new Content Type: + +![Creating new Content Type](../img/extending_field_type_create.png) + +Next, define a **Hello World** field: + +![Defining Hello World](../img/extending_field_type_definition.png) + +After saving, your **Hello World** Content Type should be available under **Content** in the sidebar menu. + +![Creating Hello World](../img/extending_field_type_hello_world.png) + +For more detailed tutorial on Generic Field Type follow [Creating a Point 2D Field Type ](../../tutorials/generic_field_type/creating_a_point2d_field_type.md). diff --git a/docs/api/field_type_api.md b/docs/api/field_type_api.md index f558b9cf9f..8b51d43c13 100644 --- a/docs/api/field_type_api.md +++ b/docs/api/field_type_api.md @@ -1,6 +1,8 @@ -# Field Type API +--- +description: Field Types define the Fields that a Content item is built of. +--- -## Basic information +# Field Type API Field Types are the smallest building blocks of content. [[= product_name =]] comes with many [built-in Field Types](field_type_reference.md#available-field-types) that cover most common needs e.g. Text line, Email address, Author list, Content relation, Map location, Float, etc. @@ -23,7 +25,7 @@ available in the [`eZ\Publish\SPI\FieldType`](https://github.com/ezsystems/ezpla !!! note "Registration" Remember that all your custom Field Types must be registered in `config/services.yml`. - For more information see [Registration section](field_type_type_and_value.md#registration). + For more information see [Registration](field_type_type_and_value.md#registration). In order to provide custom functionality for a Field Type, the SPI interacts with multiple layers of the [[= product_name =]] architecture: @@ -31,6 +33,8 @@ In order to provide custom functionality for a Field Type, the SPI interacts wit On the top layer, the Field Type needs to provide conversion from and to a simple PHP hash value to support the **REST API**. The generated hash value may only consist of scalar values and hashes. It must not contain objects or arrays with numerical indexes that aren't sequential and/or don't start with zero. +[[= include_file('docs/snippets/simple_hash_value_caution.md') =]] + Below that, the Field Type must support the **Public API** implementation regarding: - Settings definition for `FieldDefinition` @@ -40,27 +44,27 @@ Below that, the Field Type must support the **Public API** implementation regard On the bottom level, a Field Type can additionally hook into the **Persistence SPI** in order to store data from a `FieldValue` in an external service. Note that all non-standard [[= product_name =]] database tables (e.g. `ezurl`) -will be treated as [external storage](field_type_storage.md#external-storage). +are treated as [external storage](field_type_storage.md#storing-external-data). The following sequence diagrams visualize the process of creating and publishing new content across all layers, especially focused on the interaction with a Field Type. -## Create Content Sequence +## Creating content -![Create Content Sequence](img/create_content_sequence.png) +![Create content sequence](img/create_content_sequence.png) -## Publish Content Sequence +## Publishing content !!! note "indexLocation()" For **Solr** Locations are indexed during Content indexing. For **Legacy/SQL** indexing is not required as Location data already exists in a database. -![Publish Content Sequence](img/publish_content_sequence.png) +![Publish content sequence](img/publish_content_sequence.png) -## Update Content Sequence +## Updating content -![Update Content Sequence](img/update_content_sequence.png) +![Update content sequence](img/update_content_sequence.png) -## Load Content Sequence +## Loading content -![Load Content Sequence](img/load_content_sequence.png) +![Load content sequence](img/load_content_sequence.png) diff --git a/docs/api/field_type_form_and_template.md b/docs/api/field_type_form_and_template.md index 7edbc654f4..1282e2786c 100644 --- a/docs/api/field_type_form_and_template.md +++ b/docs/api/field_type_form_and_template.md @@ -1,3 +1,7 @@ +--- +description: Field Type FormMappers allow Field editing, while custom templates ensure the Field can be rendered both in the Back office and on site front. +--- + # Field Type form and template ## FormMapper @@ -53,7 +57,7 @@ In the example above, `CheckboxFieldType::class` is used, but you can use standa It's good practice to encapsulate Fields with custom types as it allows easier templating. Type has to be compatible with your Field Type's `eZ\Publish\Core\FieldType\Value` implementation. -You can use a [`DataTransformer`](https://symfony.com/doc/5.0/form/data_transformers.html) to achieve that or just assure correct property and form field names. +You can use a [`DataTransformer`]([[= symfony_doc =]]/form/data_transformers.html) to achieve that or just assure correct property and form field names. ### FieldDefinitionFormMapperInterface @@ -77,7 +81,6 @@ public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, Field ] ) ->add( - // Creating from FormBuilder as we need to add a DataTransformer. $fieldDefinitionForm->getConfig()->getFormFactory()->createBuilder() ->create( 'defaultValue', @@ -117,7 +120,7 @@ The `fieldType` key has to correspond to the name of your Field Type. ## Content view templates -To render the Field in content view using the [`ez_render_field()` Twig helper](../guide/twig_functions_reference.md#ez_render_field), +To render the Field in content view by using the [`ez_render_field()` Twig helper](../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field), you need to define a template containing a block for the Field. ``` html+twig @@ -134,7 +137,7 @@ By convention, your block must be named `_field`. Template blocks for built-in Field Types are available in [`EzPublishCoreBundle/Resources/views/content_fields.html.twig`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig). - This template is also exposed as a part of Standard Design, so you can override it by [eZ Design Engine](../guide/design_engine.md). + This template is also exposed as a part of Standard Design, so you can override it with the [design engine](../guide/content_rendering/design_engine/design_engine.md). To do so, place the template `themes/standard/content_fields.html.twig` in your `Resources/views` (assuming `ez_platform_standard_design.override_kernel_templates` is set to true). @@ -161,9 +164,9 @@ You can for example use `simple_block_field`, `simple_inline_field` or `field_at To be able to reuse built-in blocks, your template must inherit from `@EzPublishCore/content_fields.html.twig`. -### Registering your template +### Registering a template -If you don't use [eZ Design Engine](../guide/design_engine.md) or you want to have separate templates per Field Type and/or SiteAccess, +If you don't use the [design engine](../guide/content_rendering/design_engine/design_engine.md) or you want to have separate templates per Field Type and/or SiteAccess, you can register a template with the following configuration: ``` yaml @@ -218,4 +221,4 @@ All built-in Field Types are implemented with this approach. In that case overri !!! tip - For more information on creating and overriding form type templates, see [Symfony documentation](https://symfony.com/doc/5.0/form/create_custom_field_type.html#creating-the-form-type-template). + For more information on creating and overriding form type templates, see [Symfony documentation]([[= symfony_doc =]]/form/create_custom_field_type.html#creating-the-form-type-template). diff --git a/docs/api/field_type_reference.md b/docs/api/field_type_reference.md index 2fad865a9e..6fb28d883d 100644 --- a/docs/api/field_type_reference.md +++ b/docs/api/field_type_reference.md @@ -1,4 +1,8 @@ -# Field Types reference +--- +description: Ibexa DXP offers a range of built-in Field Types that cover most common needs when creating content. +--- + +# Field Type reference A Field Type is the underlying building block of the content model. It consists of two entities: Field value and Field definition. Field value is determined by values entered into the Content Field. Field definition is provided by the Content Type, and holds any user defined rules used by Field Type to determine how a Field Value is validated, stored, retrieved, formatted and so on. @@ -13,37 +17,42 @@ Custom Field Types have to be programmed in PHP. However, the built-in Field Ty ## Available Field Types -| Field Type | Description | Searchable in Legacy Storage engine | Searchable with Solr | +| Field Type | Description | Searchable in Legacy Storage engine | Searchable with Solr/Elasticsearch | |------------|-------------|-------------------------------------|----------------------| -| [Author](#author-field-type) | Stores a list of authors, each consisting of author name and author email. | No | Yes | -| [BinaryFile](#binaryfile-field-type) | Stores a file.| Yes | Yes | -| [Checkbox](#checkbox-field-type) | Stores a boolean value. | Yes | Yes | -| [Country](#country-field-type) | Stores country names as a string. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | -| [DateAndTime](#dateandtime-field-type) | Stores a full date including time information. | Yes | Yes | -| [Date](#date-field-type) | Stores date information. | Yes | Yes | -| [EmailAddress](#emailaddress-field-type) | Validates and stores an email address. | Yes | Yes | -| [Float](#float-field-type) | Validates and stores a floating-point number. | No | Yes | -| [Form](#form-field-type) | Stores a form. | No | Yes | -| [Image](#image-field-type) | Validates and stores an image. | No | Yes | -|[ImageAsset](#imageasset-field-type)|Stores images in independent content items of a generic Image content type.| No | Yes | -| [Integer](#integer-field-type) | Validates and stores an integer value. | Yes | Yes | -| [ISBN](#isbn-field-type) | Handles International Standard Book Number (ISBN) in 10-digit or 13-digit format. | Yes | Yes | -| [Keyword](#keyword-field-type) | Stores keywords. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | -| [MapLocation](#maplocation-field-type) | Stores map coordinates. | Yes, with [`MapLocationDistance` Criterion](../guide/search/criteria_reference/maplocationdistance_criterion.md) | Yes | -| [Matrix](#matrix-field-type) | Represents and handles a table of rows and columns of data. | No | No | -| [Media](#media-field-type) | Validates and stores a media file. | No | Yes | -| [Null](#null-field-type) | Used as fallback for missing Field Types and for testing purposes. | N/A | N/A | -| [Page](#page-field-type) | Stores a Page with a layout consisting of multiple zones. | N/A | N/A | -| [Rating](#rating-field-type) | **Deprecated** | N/A | N/A | -| [Relation](#relation-field-type) | Validates and stores a relation to a Content item. | Yes, with both [`Field`](../guide/search/criteria_reference/field_criterion.md) and [`FieldRelation`](../guide/search/criteria_reference/fieldrelation_criterion.md) Criteria | Yes | -| [RelationList](#relationlist-field-type) | Validates and stores a list of relations to Content items. | Yes, with [`FieldRelation` Criterion](../guide/search/criteria_reference/fieldrelation_criterion.md) | Yes | -| [RichText](#richtext-field-type) | Validates and stores structured rich text in DocBook xml format, and exposes it in several formats. Available via [RichTextBundle](https://github.com/ezsystems/ezplatform-richtext). | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | -| [Selection](#selection-field-type) | Validates and stores a single selection or multiple choices from a list of options. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | -| [TextBlock](#textblock-field-type) | Validates and stores a larger block of text. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | -| [TextLine](#textline-field-type) | Validates and stores a single line of text. | Yes | Yes | -| [Time](#time-field-type) | Stores time information. | Yes | Yes | -| [Url](#url-field-type) | Stores a URL / address. | No | Yes | -| [User](#user-field-type) | Validates and stores information about a user. | No | No | +| [Author](field_types_reference/authorfield.md) | Stores a list of authors, each consisting of author name and author email. | No | Yes | +| [BinaryFile](field_types_reference/binaryfilefield.md) | Stores a file.| Yes | Yes | +| [Checkbox](field_types_reference/checkboxfield.md) | Stores a boolean value. | Yes | Yes | +| [Content query](field_types_reference/contentqueryfield.md) | Maps an executable Repository query to a Field. | No | No | +| [Country](field_types_reference/countryfield.md) | Stores country names as a string. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | +| [DateAndTime](field_types_reference/dateandtimefield.md) | Stores a full date including time information. | Yes | Yes | +| [Date](field_types_reference/datefield.md) | Stores date information. | Yes | Yes | +| [EmailAddress](field_types_reference/emailaddressfield.md) | Validates and stores an email address. | Yes | Yes | +| [Float](field_types_reference/floatfield.md) | Validates and stores a floating-point number. | No | Yes | +| [Form](field_types_reference/formfield.md) | Stores a form. | No | Yes | +| [Image](field_types_reference/imagefield.md) | Validates and stores an image. | No | Yes | +|[ImageAsset](field_types_reference/imageassetfield.md)|Stores images in independent Content items of a generic Image Content Type.| No | Yes | +| [Integer](field_types_reference/integerfield.md) | Validates and stores an integer value. | Yes | Yes | +| [ISBN](field_types_reference/isbnfield.md) | Handles International Standard Book Number (ISBN) in 10-digit or 13-digit format. | Yes | Yes | +| [Keyword](field_types_reference/keywordfield.md) | Stores keywords. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | +| [MapLocation](field_types_reference/maplocationfield.md) | Stores map coordinates. | Yes, with [`MapLocationDistance` Criterion](../guide/search/criteria_reference/maplocationdistance_criterion.md) | Yes | +| [Matrix](field_types_reference/matrixfield.md) | Represents and handles a table of rows and columns of data. | No | No | +| [Media](field_types_reference/mediafield.md) | Validates and stores a media file. | No | Yes | +| [Null](field_types_reference/nullfield.md) | Used as fallback for missing Field Types and for testing purposes. | N/A | N/A | +| [Page](field_types_reference/pagefield.md) | Stores a Page with a layout consisting of multiple zones. | N/A | N/A | +| [Relation](field_types_reference/relationfield.md) | Validates and stores a relation to a Content item. | Yes, with both [`Field`](../guide/search/criteria_reference/field_criterion.md) and [`FieldRelation`](../guide/search/criteria_reference/fieldrelation_criterion.md) Criteria | Yes | +| [RelationList](field_types_reference/relationlistfield.md) | Validates and stores a list of relations to Content items. | Yes, with [`FieldRelation` Criterion](../guide/search/criteria_reference/fieldrelation_criterion.md) | Yes | +| [RichText](field_types_reference/richtextfield.md) | Validates and stores structured rich text in DocBook xml format, and exposes it in several formats. Available via [RichTextBundle](https://github.com/ezsystems/ezplatform-richtext). | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | +| [Selection](field_types_reference/selectionfield.md) | Validates and stores a single selection or multiple choices from a list of options. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | +| [SesExternaldata](field_types_reference/sesexternaldata.md) | Uses external storage to store data. ||| +| [SesProfiledata](field_types_reference/sesprofiledata.md) | Stores address data for a customer. | No | No | +| [SesSelection](field_types_reference/sesselection.md) | Stores a single selection choice based on options from a YAML file. | Yes | Yes | +| [SpecificationsType](field_types_reference/specificationstype.md) | Stores a structured list of specification data for products. | Yes | Yes | +| [TextBlock](field_types_reference/textblockfield.md) | Validates and stores a larger block of text. | Yes[^1^](#1-note-on-legacy-search-engine) | Yes | +| [TextLine](field_types_reference/textlinefield.md) | Validates and stores a single line of text. | Yes | Yes | +| [Time](field_types_reference/timefield.md) | Stores time information. | Yes | Yes | +| [Url](field_types_reference/urlfield.md) | Stores a URL / address. | No | Yes | +| [User](field_types_reference/userfield.md) | Validates and stores information about a user. | No | No | +| [VariantType](field_types_reference/varianttype.md) | Stores variant data for products. | Yes | Yes | **^[1]^ Note on Legacy Search Engine** @@ -51,2695 +60,4 @@ Legacy Search/Storage Engine index is limited to 255 characters in database desi so formatted and unformatted text blocks will only index the first part. In case of multiple selection Field Types like Keyword, Selection, Country, etc., only the first choices are indexed. They are indexed only as a text blob separated by string separator. -Proper indexing of these Field Types is done with [Solr Search Bundle](../guide/search/solr.md). - -### Other Field Types - -|FieldType|Description|Searchable|Editing support in Platform UI|Planned to be included in the future| -|------|------|------|------|------| -| [XmlText](#xmltext-field-type)|Validates and stores multiple lines of formatted text using XML format.|Yes|Partial *(Raw XML editing)*|No *(has been superseded by [RichText](#richtext-field-type))*
    The XmlText Field Type is not enabled by default in [[= product_name =]].| - -### Field Types provided by Community - -|FieldType|Description|Searchable|Editing support in Platform UI|Planned to be included in the future| -|------|------|------|------|------| -|[Tags](https://github.com/netgen/TagsBundle)|Tags Field and full-fledged taxonomy management|Yes|Yes, since Netgen Tags v3.0.0|No (but can be previewed in Studio Demo)| -|[Price](https://github.com/ezcommunity/EzPriceBundle)|Price Field for use in product catalogs|Yes|No|Yes| - -### Generate new Field Type - -You can also make use of the [Field Type Generator Bundle](https://github.com/Smile-SA/EzFieldTypeGeneratorBundle) from our partner Smile. -It helps you get started by creating a skeleton for a Field Type, including templates for the editorial interface.  - -## Author Field Type - -This Field Type allows the storage and retrieval of one or more authors. For each author, it can handle a name and an email address. It is typically used to store information about additional authors who have written/created different parts of a Content item. - -| Name | Internal name | Expected input | Output | -|----------|---------------|----------------|----------| -| `Author` | `ezauthor` | mixed | `string` | - - - -### PHP API Field Type - -#### Value Object - -###### Properties - -|Attribute|Type|Description|Example| -|------|------|------|------| -|`authors`|`\eZ\Publish\Core\FieldType\Author\Author[] `|List of authors.|See below| - -Example: - -``` php -$authorList = Author\Value([ - new Author\Author([ - 'id' => 1, - 'name' => 'Boba Fett', - 'email' => 'boba.fett@example.com' - ]), - new Author\Author([ - 'id' => 2, - 'name' => 'Darth Vader', - 'email' => 'darth.vader@example.com' - ]), -]); -``` - -#### Hash format - -The hash format mostly matches the value object. It has the following key `authors`. - - -Example - -``` php -[ - [ - 'id' => 1, - 'name' => 'Boba Fett', - 'email' => 'boba.fett@example.com' - ], - [ - 'id' => 2, - 'name' => 'Darth Vader', - 'email' => 'darth.vader@example.com' - ] -] -``` - -###### String representation - -The string will contain all the authors with their names and emails. - -Example: `John Doe john@doe.com` - -#### Validation - -This Field Type does not perform any special validation of the input value. - -#### Settings - -The Field definition of this Field Type can be configured with a single option: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`defaultAuthor`|`mixed`|`Type::DEFAULT_VALUE_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| - -Following `defaultAuthor` default value options are available as constants in the `eZ\Publish\Core\FieldType\Author\Type` class: - -|Constant|Description| -|------|------| -|`DEFAULT_VALUE_EMPTY`|Default value will be empty.| -|`DEFAULT_CURRENT_USER`|Default value will use currently logged user.| - -``` php -// Author Field Type example settings - -use eZ\Publish\Core\FieldType\Author\Type; - -$settings = [ - "defaultAuthor" => Type::DEFAULT_VALUE_EMPTY -]; -``` - -## BinaryFile Field Type - -This Field Type represents and handles a single binary file. It also counts the number of times the file has been downloaded from the `content/download` module. - -It is capable of handling virtually any file type and is typically used for storing legacy document types such as PDF files, Word documents, spreadsheets, etc. The maximum allowed file size is determined by the "Max file size" class attribute edit parameter and the `upload_max_filesize` directive in the main PHP configuration file (`php.ini`). - -| Name | Internal name | Expected input | Output | -|--------------|----------------|----------------|---------| -| `BinaryFile` | `ezbinaryfile` | mixed | mixed | - -### PHP API Field Type - -#### Value Object - -###### Properties - - -Note that both `BinaryFile` and `Media` Value and Type inherit from the `BinaryBase` abstract Field Type, and share common properties. - -`eZ\Publish\Core\FieldType\BinaryFile\Value` offers the following properties: - -|Attribute|Type|Description|Example| -|------|------|------|------| -|`id`|string|Binary file identifier. This ID depends on the [IO Handler](../guide/clustering.md#dfs-io-handler) that is being used. With the native, default handlers (FileSystem and Legacy), the ID is the file path, relative to the binary file storage root dir (`var//storage/original` by default).|application/63cd472dd7.pdf| -|`fileName`|string|The human-readable file name, as exposed to the outside. Used when sending the file for download in order to name the file.|20130116_whitepaper.pdf| -|`fileSize`|int|File size, in bytes.|1077923| -|`mimeType`|string|The file's MIME type.|application/pdf| -|`uri`|string|The binary file's `content/download` URI. If the URI doesn't include a host or protocol, it applies to the request domain.|/content/download/210/2707| -|`downloadCount`|integer|Number of times the file was downloaded|0| -|`path`|string|**deprecated**|N/A| - - -#### Hash format - -The hash format mostly matches the value object. It has the following keys: - -- `id` -- `path` (for backwards compatibility) -- `fileName` -- `fileSize` -- `mimeType` -- `uri` -- `downloadCount` - -Example: - -```php -[ - 'id' => 'some/file/here', - 'fileName' => 'sindelfingen.jpg', - 'fileSize' => 2342, - 'downloadCount' => 0, - 'mimeType' => 'image/jpeg', - 'uri' => 'http://some/file/here', -] -``` - -### REST API specifics - -Used in the REST API, a BinaryFile Field will mostly serialize the hash described above. However there are a couple specifics worth mentioning. - -#### Reading content: `url` property - -When reading the contents of a Field of this type, an extra key is added: `url`. This key gives you the absolute file URL, protocol and host included. - -Example:  - -#### Creating content: data property - -When creating BinaryFile content with the REST API, it is possible to provide data as a base64 encoded string, using the `data` fieldValue key: - -``` xml - - file - eng-GB - - My file.pdf - 17589 - - - -``` - -## Checkbox Field Type - -The Checkbox Field Type stores the current status for a checkbox input, checked or unchecked, by storing a boolean value. - -| Name | Internal name | Expected input type | -|------------|---------------|---------------------| -| `Checkbox` | `ezboolean` | `boolean` | - -### PHP API Field Type  - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Default value | Description| -|----------|-----------|---------------|------------| -| `$bool` | `boolean` | `false` | This property is used for the checkbox status, represented by a boolean value. | - -``` php -//Value object content examples -use eZ\Publish\Core\FieldType\Checkbox\Type; -  -// Instantiates a checkbox value with a default state (false) -$checkboxValue = new Checkbox\Value(); -  -// Checked -$value->bool = true; - -// Unchecked -$value->bool = false; -``` - -###### Constructor - -The `Checkbox\Value` constructor accepts a boolean value: - -``` php -// Constructor example -use eZ\Publish\Core\FieldType\Checkbox\Type; -  -// Instantiates a checkbox value with a checked state -$checkboxValue = new Checkbox\Value( true ); -``` - -###### String representation - -As this Field Type is not a string but a boolean, it will return "1" (true) or "0" (false) in cases where it is cast to string. - -## Content query Field Type - -This Field Type maps an executable Repository query to a Field. - -| Name | Internal name | Expected input | -|-----------|---------------|----------------| -| `Content query` | `ezcontentquery` | `string` | - -The Content query Field Type is available via the Query Field Type Bundle -provided by the [ezplatform-query-fieldtype](https://github.com/ezsystems/ezplatform-query-fieldtype) package. - -For information on the Field Type's usage, see [Query Field Type in controller documentation](../guide/controllers.md#query-field-type). - -## Country Field Type - -This Field Type represents one or multiple countries. - -| Name | Internal name | Expected input | -|-----------|---------------|----------------| -| `Country` | `ezcountry` | `array` | - -### PHP API Field Type  - -#### Input expectations - -Example array: - -``` php -[ - "JP" => [ - "Name" => "Japan", - "Alpha2" => "JP", - "Alpha3" => "JPN", - "IDC" => 81 - ] -]; -``` - -When you set an array directly on a Content field you don't need to provide all this information, the Field Type will assume it is a hash and in this case will accept a simplified structure described below under [Hash format](#hash-format). - -#### Validation - -This Field Type validates whether multiple countries are allowed by the Field definition, and whether the [Alpha2](https://www.iso.org/iso-3166-country-codes.html) is valid according to the countries configured in [[= product_name =]]. - -#### Settings - -The Field definition of this Field Type can be configured with one option: - -| Name | Type | Default value | Description| -|--------------|-----------|---------------|------------| -| `isMultiple` | `boolean` | `false` | This setting allows (if true) or prohibits (if false) the selection of multiple countries. | - -``` php -// Country FieldType example settings -$settings = [ - "isMultiple" => true -]; -``` - -#### Hash format - -The format used for serialization is simpler than the full format. It is also available when setting value on the content field, by setting the value to an array instead of the Value object. Example of that shown below: - -``` php -// Value object content example -$content->fields["countries"] = [ "JP", "NO" ]; -``` - -The format used by the toHash method is the Alpha2 value, however the input is capable of accepting either Name, Alpha2 or Alpha3 value as shown below in the Value object section. - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|--------------|-----------|------------| -| `$countries` | `array[]` | This property is used for the country selection provided as input, as its attributes. | - -``` php -// Value object content example -$value->countries = [ - "JP" => [ - "Name" => "Japan", - "Alpha2" => "JP", - "Alpha3" => "JPN", - "IDC" => 81 - ] -]; -``` - -###### Constructor - -The `Country\Value` constructor will initialize a new Value object with the value provided. It expects an array as input. - -``` php -// Constructor example - -// Instantiates a Country Value object -$countryValue = new Country\Value( - [ - "JP" => [ - "Name" => "Japan", - "Alpha2" => "JP", - "Alpha3" => "JPN", - "IDC" => 81 - ] - ] -); -``` - -## DAM Field Type - -This Field Type stores asset information from a DAM system. -It replaces the [ImageAsset Field Type](#imageasset-field-type). - -| Name | Internal name | Expected input type | -|--------|---------------|---------------------| -| `Image Asset` | `ezimageasset` | mixed | - -#### Value object - -###### Properties - -Value object of `dam` contains the following properties: - -| Property | Type | Description| -|----------|-------|------------| -| `destinationContentId` | `string` | Related content ID. | -| `alternativeText` | `string` | The alternative image text. | -| `source` | `string` | Source of the DAM asset. | - -## Date Field Type - -This Field Type represents a date without time information. - -| Name | Internal name | Expected input type | -|--------|---------------|---------------------| -| `Date` | `ezdate` | mixed | - -##### PHP API Field Type  - -#### Input expectations - -If input value is in `string` or `integer` format, it will be passed directly to [PHP's built-in `\DateTime` class constructor](http://www.php.net/manual/en/datetime.construct.php), therefore the same input format expectations apply. - -It is also possible to directly pass an instance of `\DateTime`. - -|Type|Example| -|------|------| -|`string`|`"2012-08-28 12:20 Europe/Berlin"`| -|`integer`|`1346149200`| -|`\DateTime`|`new \DateTime()`| - -Time information is **not stored**. - -Before storing, the provided input value will be set to the beginning of the day in the given or the environment timezone. - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|-------------|------------| -| `$date` | `\DateTime` | This property will be used for the text content. | - -###### String representation - -String representation of the date value will generate the date string in the format "l d F Y" as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). - -|Character|Description|Example| -|---------|----------|--------| -|l|Textual representation of a day of the week, range Monday to Sunday|Wednesday| -|d|Two digit representation of a day, range 01 to 31|22| -|F|Textual representation of a month, range January to December|May| -|Y|Four digit representation of a year|2016| - -Example: `Wednesday 22 May 2016` - -###### Constructor - -The constructor for this value object will initialize a new Value object with the value provided. It accepts an instance of [PHP's built-in `\DateTime` class](http://www.php.net/manual/en/datetime.construct.php). - -#### Hash format - -Hash value of this Field Type is an array with two keys: - -|Key|Type|Description|Example| -|------|------|------|------| -|`timestamp`|`integer`|Time information in [unix format timestamp](http://en.wikipedia.org/wiki/Unix_time).|`1400856992`| -|`rfc850`|`string`|Time information as a string in [RFC 850 date format](http://tools.ietf.org/html/rfc850). As input, this will have higher precedence over the timestamp value.|`"Friday, 23-May-14 14:56:14 GMT+0000"`| - -``` php -// Example of the hash value in PHP -$hash = [ - "timestamp" => 1400856992, - "rfc850" => "Friday, 23-May-14 14:56:14 GMT+0000" -]; -``` - -#### Validation - -This Field Type does not perform any special validation of the input value. - -#### Settings - -The Field definition of this Field Type can be configured with a single option: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`defaultType`|`mixed`|`Type::DEFAULT_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| - -Following `defaultType` default value options are available as constants in the `eZ\Publish\Core\FieldType\Date\Type` class: - -|Constant|Description| -|------|------| -|`DEFAULT_EMPTY`|Default value will be empty.| -|`DEFAULT_CURRENT_DATE`|Default value will use current date.| - -``` php -// Date Field Type example settings - -use eZ\Publish\Core\FieldType\Date\Type; - -$settings = [ - "defaultType" => Type::DEFAULT_EMPTY -]; -``` - -### Template rendering - -The template called by [the `ez_render_field()` Twig function](../guide/twig_functions_reference.md#ez_render_field) while rendering a Date Field has access to the following parameters: - -| Parameter | Type |Description| -|-----------|----------|------------| -| `locale` | `string` |Internal parameter set by the system based on current request locale or if not set, calculated based on the language of the Field. | - -Example: - -``` html+twig -{{ ez_render_field(content, 'date') }} -``` - -## DateAndTime Field Type - -This Field Type represents a full date and time information. - -| Name | Internal name | Expected input type | -|---------------|---------------|---------------------| -| `DateAndTime` | `ezdatetime` | mixed | - -### PHP API Field Type  - -#### Input expectations - -If input value is of type `string` or `integer`, it will be passed directly to the [PHP's built-in `\DateTime` class constructor](http://www.php.net/manual/en/datetime.construct.php), therefore the same input format expectations apply. - -It is also possible to directly pass an instance of `\DateTime`. - -|Type|Example| -|------|------| -|`integer`|`"2017-08-28 12:20 Europe/Berlin"`| -|`integer`|`1346149200`| -|`\DateTime`|`new \DateTime()`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|-------------|------------| -| `$value` | `\DateTime` | The date and time value as an instance of `\DateTime`. | - -###### Constructor - -The constructor for this value object will initialize a new Value object with the value provided. It accepts an instance of PHP's built-in `\DateTime` class. - -###### String representation - -String representation of the date value will generate the date string in the format `D Y-d-m H:i:s` as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). - -|Character|Description|Example| -|---------|----------|--------| -|D|Three letter representation of a day, range Mon to Sun|Wed| -|Y|Four digit representation of a year|2016| -|d|Two digit representation of a day, range 01 to 31|22| -|m|Two digit representation of a month, range 01 to 12|05| -|H|Two digit representation of an hour, 24-hour format, range 00 to 23 |12| -|i|Two digit representation of minutes, range 00 to 59|19| -|s|Two digit representation of seconds, range 00 to 59|18| - -Example: `Wed 2016-22-05 12:19:18` - -#### Hash format - -Hash value of this Field Type is an array with two keys: - -|Key|Type|Description|Example| -|------|------|------|------| -|`timestamp`|`integer`|Time information in [Unix format timestamp](http://en.wikipedia.org/wiki/Unix_time).|`1400856992`| -|`rfc850`|`string`|Time information as a string in [RFC 850 date format](http://tools.ietf.org/html/rfc850). As input, this will have precedence over the timestamp value.|`"Friday, 23-May-14 14:56:14 GMT+0000"`| - -``` php -$hash = [ - "timestamp" => 1400856992, - "rfc850" => "Friday, 23-May-14 14:56:14 GMT+0000" -]; -``` - -#### Validation - -This Field Type does not perform any special validation of the input value. - -#### Settings - -The Field definition of this Field Type can be configured with several options: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`useSeconds`|`boolean`|`false`|Used to control displaying of seconds in the output.| -|`defaultType`|`mixed`|`Type::DEFAULT_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| -|`dateInterval`|`null|\DateInterval`|`null`|This setting complements `defaultType` setting and can be used only when the latter is set to `Type::DEFAULT_CURRENT_DATE_ADJUSTED`. In that case the default input value when using administration interface will be adjusted by the given `\DateInterval`.| - -Following `defaultType` default value options are available as constants in the `eZ\Publish\Core\FieldType\DateAndTime\Type` class: - -|Constant|Description| -|------|------| -|`DEFAULT_EMPTY`|Default value will be empty.| -|`DEFAULT_CURRENT_DATE`|Default value will use current date.| -|`DEFAULT_CURRENT_DATE_ADJUSTED`|Default value will use current date, adjusted by the interval defined in `dateInterval` setting.| - -``` php -// DateAndTime FieldType example settings - -use eZ\Publish\Core\FieldType\DateAndTime\Type; - -$settings = [ - "useSeconds" => false, - "defaultType" => Type::DEFAULT_EMPTY, - "dateInterval" => null -]; -``` - -### Template rendering - -The template called by the [`ez_render_field()` Twig function](../guide/twig_functions_reference.md#ez_render_field) while rendering a Date Field has access to the following parameters: - -| Parameter | Type | Default | Description| -|-----------|----------|---------|------------| -| `locale` | `string` |   n/a | Internal parameter set by the system based on current request locale or if not set calculated based on the language of the Field. | - -Example: - -``` php -{{ ez_render_field(content, 'datetime') }} -``` - -## EmailAddress Field Type - -The EmailAddress Field Type represents an email address, in the form of a string. - -| Name | Internal name | Expected input type | -|----------------|---------------|---------------------| -| `EmailAddress` | `ezemail` | `string` | - -### PHP API Field Type  - -#### Value object - -###### Properties - -The `Value` class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|----------|------------| -| `$email` | `string` | This property will be used for the input string provided as email address. | - -``` php -// Value object content example - -use eZ\Publish\Core\FieldType\EmailAddress\Type; - -// Instantiates an EmailAddress Value object with default value (empty string) -$emailaddressValue = new Type\Value(); - -// Email definition -$emailaddressValue->email = "someuser@example.com"; -``` - -###### Constructor - -The `EmailAddress\Value` constructor will initialize a new Value object with the value provided. It accepts a string as input. - -``` php -// Constructor example - -use eZ\Publish\Core\FieldType\EmailAddress\Type; -  -// Instantiates an EmailAddress Value object -$emailaddressValue = new Type\Value( "someuser@example.com" ); -``` - -###### String representation - -String representation of the Field Type's Value object is the email address contained in it. - -Example: `someuser@example.com` - -#### Hash format - -Hash value for this Field Type's Value is simply the email address as a string. - -Example: `someuser@example.com` - -#### Validation - -This Field Type uses the `EmailAddressValidator` validator as a resource which will test the string supplied as input against a pattern, to make sure that a valid email address has been provided. -If the validations fail, a `ValidationError` is thrown, specifying the error message. - -#### Settings - -This Field Type does not support settings. - -## Float Field Type - -This Field Type stores numeric values which will be provided as floats. - -| Name | Internal name | Expected input | -|---------|---------------|----------------| -| `Float` | `ezfloat` | `float` | - -### PHP API Field Type  - -#### Input expectations - -The Field Type expects a number as input. Both decimal and integer numbers are accepted. - -|Type|Example| -|------|------| -|`float`|`194079.572`| -|`int`|`144`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|---------|------------| -| `$value` | `float` | This property will be used to store the value provided as a float. | - -``` php -// Value object content example - -use eZ\Publish\Core\FieldType\Float\Type; - -// Instantiates a Float Value object -$floatValue = new Type\Value(); - -$float->value = 284.773 -``` - -###### Constructor - -The `Float\Value` constructor will initialize a new Value object with the value provided. It expects a numeric value with or without decimals. - -``` php -// Constructor example - -use eZ\Publish\Core\FieldType\Float\Type; - -// Instantiates a Float Value object -$floatValue = new Type\Value( 284.773 ); -``` - -#### Validation - -This Field Type supports `FloatValueValidator`, defining maximum and minimum float value: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`minFloatValue`|`float`|`null|This setting defines the minimum value this Field Type will allow as input.| -|`maxFloatValue`|`float`|`null|This setting defines the maximum value this Field Type will allow as input.| - -``` php -// Validator configuration example in PHP - -use eZ\Publish\Core\FieldType\Float\Type; - -$contentTypeService = $repository->getContentTypeService(); -$floatFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "float", "ezfloat" ); - -// Accept only numbers between 0.1 and 203.99 -$floatFieldCreateStruct->validatorConfiguration = [ - "FileSizeValidator" => [ - "minFloatValue" => 0.1, - "maxFloatValue" => 203.99 - ] -]; -``` - -#### Settings - -This Field Type does not support settings. - -## Form Field Type [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -| Name | Internal name | -|---------|---------------| -| `Image` | `ezform` | - -The Form Field Type stores a Form consisting of one or more form fields. - -See [Extending Form Builder](../extending/extending_form_builder.md) for more information -about working with Forms. - -## Image Field Type - -| Name | Internal name | -|---------|---------------| -| `Image` | `ezimage` | - -The Image Field Type allows you to store an image file. - -A **variation service** handles the conversion of the original image into different formats and sizes through a set of preconfigured named variations: large, small, medium, black and white thumbnail, etc. - -### PHP API Field Type - -#### Value object - -The `value` property of an Image Field will return an `\eZ\Publish\Core\FieldType\Image\Value` object with the following properties: - -###### Properties - -|Property|Type|Example|Description| -|------|------|------|------| -|`id`|string|`0/8/4/1/1480-1-eng-GB/image.png`|The image's unique identifier. Usually the path, or a part of the path. To get the full path, use the `uri` property.| -|`alternativeText`|string|`Picture of an apple.`|The alternative text, as entered in the Field's properties| -|`fileName`|string|`image.png`|The original image's filename, without the path| -|`fileSize`|int|`37931`|The original image's size, in bytes| -|`uri`|string|`var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/image.png`|The original image's URI| -|`imageId`|string|`240-1480`|A special image ID, used by REST| -|`inputUri`|string|`var/storage/images/test/199-2-eng-GB/image.png`|Input image file URI.| -|`width`*|int|`null`|Original image width in pixels. For more details see Caution note below.| -|`height`*|int|`null`|Original image height in pixels. For more details see Caution note below.| - -!!! caution - - Properties marked with an asterisk are currently unsupported. They are available but their value is always `null`. - - Follow [EZP-27987](https://jira.ez.no/browse/EZP-27987) for future progress on this issue. - -#### Settings - -This Field Type does not support settings. - -#### Image Variations - -Using the variation Service, variations of the original image can be obtained. They are `\eZ\Publish\SPI\Variation\Values\ImageVariation` objects with the following properties: - -| Property | Type | Example | Description| -|----------------|----------|----------|------------| -| `width`* | int | `null` | The variation's width in pixels. For more details see Caution note below.| -| `height`* | int | `null` | The variation's height in pixels. For more details see Caution note below.| -| `name` | string | `medium` | The variation's identifier, name of the image alias| -| `info` | mixed |n/a| Extra information about the image, depending on the image type, such as EXIF data. If there is no information, the `info` value will be `null`.| -| `fileSize` | int |`31010` |Size (in byte) of current variation| -| `mimeType` | string |`image/png`|The MIME type| -| `fileName` | string |`my_image.png`|The name of the file| -| `dirPath` | string |`var/storage/images/test/199-2-eng-GB`|The path to the file| -| `uri` | string |`var/storage/images/test/199-2-eng-GB/apple.png`| The variation's URI. Complete path with a name of image file| -| `lastModified` | DateTime |``"2017-08-282 12:20 Europe/Berlin"``| When the variation was last modified | - -!!! caution - - Properties marked with an asterisk are currently unsupported. They are available but their value is always `null`. - - Follow [EZP-27987](https://jira.ez.no/browse/EZP-27987) for future progress on this issue. - -#### Field Definition options - -The Image Field Type supports one `FieldDefinition` option: the maximum size for the file. - -!!!note - - Maximum size is rounded to 1 MB (legacy storage limitation). - -!!! note - - As the default value for maximum size is set to 10MB, we recommend setting the `upload_max_filesize` key in the `php.ini` configuration file to a value equal to or higher than that. It will prevent validation errors while editing Content Types. - -### Using an Image Field - -To read more about handling images and image variations, see the [Images documentation](../guide/images.md). - -#### Template Rendering - -When displayed using `ez_render_field`, an Image Field will output this type of HTML: - -``` html+twig -Alternative text -``` - -The template called by the [`ez_render_field()` Twig function](../guide/twig_functions_reference.md#ez_render_field) while rendering a Image Field accepts the following parameters: - -| Parameter | Type | Default | Description | -|-----------|----------|----------------|-------------| -| `alias` | `string` | `"original"` | The image variation name, must be defined in your SiteAccess's `image_variations` settings. Defaults to "original", the originally uploaded image.| -| `width` | `int` |   n/a | Optionally to specify a different width set on the image HTML tag then then one from image alias. | -| `height` | `int` |   n/a | Optionally to specify a different height set on the image HTML tag then then one from image alias. | -| `class` | `string` |   n/a | Optionally to specify a specific html class for use in custom JavaScript and/or CSS. | - -Example:  - -``` html+twig -{{ ez_render_field( content, 'image', { 'parameters':{ 'alias': 'imagelarge', 'width': 400, 'height': 400 } } ) }} -``` - -The raw Field can also be used if needed. Image variations for the Field's content can be obtained using the `ez_image_alias` Twig helper: - -``` html+twig -{% set imageAlias = ez_image_alias( field, versionInfo, 'medium' ) %} -``` - -The variation's properties can be used to generate the required output: - -``` html+twig -{{ field.value.alternativeText }} -``` - -#### With the REST API - -Image Fields within REST are exposed by the `application/vnd.ez.api.Content` media-type. An Image Field will look like this: - -``` xml - - 1480 - image - eng-GB - - /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding.png - - kidding.png - 37931 - 240-1480 - /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding.png - - - /api/ezp/v2/content/binary/images/240-1480/variations/articleimage - - - /api/ezp/v2/content/binary/images/240-1480/variations/articlethumbnail - - - - -``` - -Children of the `fieldValue` node will list the general properties of the Field's original image (`fileSize`, `fileName`, `inputUri`, etc.), as well as variations. For each variation, a URI is provided. Requested through REST, this resource will generate the variation if it doesn't exist yet, and list the variation details: - -``` xml - - /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding_tiny.png - image/png - 30 - 30 - 1361 - -``` - -#### From PHP code - -##### Getting an image variation - -The variation service, `ezpublish.fieldType.ezimage.variation_service`, can be used to generate/get variations for a Field. It expects a VersionInfo, the Image Field, and the variation name as a string (`large`, `medium`, etc.) - -``` php -$variation = $imageVariationHandler->getVariation( - $imageField, $versionInfo, 'large' -); - -echo $variation->uri; -``` - -### Manipulating image content - -#### From PHP - -As for any Field Type, there are several ways to input content to a Field. For an Image, the quickest is to call `setField()` on the ContentStruct: - -``` php -$createStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentType( 'image' ), - 'eng-GB' -); - -$createStruct->setField( 'image', '/tmp/image.png' ); -``` - -In order to customize the Image's alternative texts, you must first get an `Image\Value` object, and set this property. For that, you can use the `Image\Value::fromString()` method that accepts the path to a local file: - -``` php -$createStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentType( 'image' ), - 'eng-GB' -); - -$imageField = \eZ\Publish\Core\FieldType\Image\Value::fromString( '/tmp/image.png' ); -$imageField->alternativeText = 'My alternative text'; -$createStruct->setField( 'image', $imageField ); -``` - -You can also provide a hash of `Image\Value` properties, either to `setField()`, or to the constructor: - -``` php -$imageValue = new \eZ\Publish\Core\FieldType\Image\Value( - [ - 'id' => '/tmp/image.png', - 'fileSize' => 37931, - 'fileName' => 'image.png', - 'alternativeText' => 'My alternative text' - ] -); - -$createStruct->setField( 'image', $imageValue ); -``` - -#### From REST - -The REST API expects Field values to be provided in a hash-like structure. Those keys are identical to those expected by the `Image\Value` constructor: `fileName`, `alternativeText`. In addition, image data can be provided using the `data` property, with the image's content encoded as base64. - -##### Creating an Image Field - -``` - - - - - - - 247 - image - eng-GB - - rest-rocks.jpg - HTTP - - - - - -``` - -#### Updating an Image Field - -Updating an Image Field requires that you re-send existing data. This can be done by re-using the Field obtained via REST, **removing the variations key**, and updating `alternativeText`, `fileName` or `data`. If you do not want to change the image itself, do not provide the `data` key. - -``` xml - - - - - 247 - image - eng-GB - - media/images/507-1-eng-GB/Existing-image.png - Updated alternative text - Updated-filename.png - - - - -``` - -### Naming - -Each storage engine determines how image files are named. - -#### Legacy Storage Engine naming - -Images are stored within the following directory structure: - -`///////--/` - -With the following values: - -- `VarDir` = `var` (default) -- `StorageDir` = `storage` (default) -- `ImagesStorageDir` = `images` (default) -- `FieldId` = `1480` -- `VersionNumber` = `1` -- `LanguageCode` = `eng-GB` - -Images will be stored in `web/var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB`. - -Using the Field ID digits in reverse order as the folder structure maximizes sharding of files through multiple folders on the filesystem. - -Within this folder, images will be named like the uploaded file, suffixed with an underscore and the variation name: - -- `MyImage.png` -- `MyImage_large.png` -- `MyImage_rss.png` - -## ImageAsset Field Type - -ImageAsset Field Type enables storing images in independent Content items of a generic Image content type, in the media library. It makes them reusable across system. - -#### Input expectations - -Example array: - -|Type|Description|Example| -|------|------|------| -|`eZ\Publish\Core\FieldType\ImageAsset\Value`|ImageAsset Field Type value object|See below.| -|`eZ\Publish\API\Repository\Values\Content\ContentInfo`|ContentInfo instance of the Asset Content item |n/a| -|`string`| ID of the Asset Content item |`"150"`| -|`integer`| ID of the Asset Content item | `150`| - -#### Value object - -###### Properties - -Value object of `ezimageasset` contains the following properties: - -| Property | Type | Description| -|----------|-------|------------| -| `destinationContentId` | `int` | Related content ID. | -| `alternativeText` | `string` | The alternative image text (for example "Picture of an apple."). | - -``` php -// Value object content example - -$imageAssetValue->destinationContentId = $contentInfo->id; -$imageAssetValue->alternativeText = "Picture of an apple."; -``` - -###### Constructor - -The `ImageAsset\Value` constructor will initialize a new value object with the value provided. It expects an ID of content object representing asset and the alternative text. - -``` php -// Constructor example - -// Instantiates a ImageAsset Value object -$imageAssetValue = new ImageAsset\Value($contentInfo->id, "Picture of an apple."); -``` - -#### Validation - -This Field Type validates if: - -- `destinationContentId` points to Content Object which has correct Content Type - -#### Configuration - -ImageAsset Field Type allows configuring the following options: - -|Name|Description|Default value| -|----|-----------|-------------| -|`content_type_identifier`|Content type used to store assets.|`image`| -|`content_field_identifier`|Field identifier used for asset data.|`image`| -|`name_field_identifier`|Field identifier used for asset name.|`name`| -|`parent_location_id`|Location where the assets are created.|`51`| - -Example configuration: - -``` yaml -ezplatform: - system: - default: - fieldtypes: - ezimageasset: - content_type_identifier: photo - content_field_identifier: image - name_field_identifier: title - parent_location_id: 106 -``` - -### Customizing ImageAsset Field Type rendering - -Internally the Image Asset Type is rendered via subrequest (similar to other relation types). Rendering customization is possible by configuring view type `asset_image`: - -```php -ezplatform: - system: - default: - content_view: - asset_image: - default: - template: ::custom_image_asset_template.html.twig - match: [] -``` - -### Generating image variation from the Image Asset - -Thanks to the `eZ\Bundle\EzPublishCoreBundle\Imagine\ImageAsset\AliasGenerator` decorator you can work with `\eZ\Publish\SPI\Variation\VariationHandler` in the same way as with [Image Field Type](#image-field-type). - -## Integer Field Type - -This Field Type represents an integer value. - -| Name | Internal name | Expected input | -|-----------|---------------|----------------| -| `Integer` | `ezinteger` | `integer` | - -### PHP API Field Type  - -#### Input expectations - -|Type|Example| -|-------|------| -|`integer`|`2397`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|-------|------------| -| `$value` | `int` | This property is used to store the value provided as an integer. | - -``` php -// Value object content example -$integer->value = 8 -``` - -###### Constructor - -The `Integer\Value` constructor will initialize a new Value object with the value provided. It expects a numeric, integer value. - -``` php -// Constructor example -use eZ\Publish\Core\FieldType\Integer; -  -// Instantiates a Integer Value object -$integerValue = new Integer\Value( 8 ); -``` - -#### Hash format - -Hash value of this Field Type is an integer value as a string. - -Example: `"8"` - -#### String representation - -String representation of the Field Type's value will return the integer value as a string. - -Example: `"8"` - -#### Validation - -This Field Type supports `IntegerValueValidator`, defining maximum and minimum float value: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`minIntegerValue`|`int`|`0`|This setting defines the minimum value this Field Type will allow as input.| -|`maxIntegerValue`|`int`|`null`|This setting defines the maximum value this Field Type will allow as input.| - -``` php -// Example of validator configuration in PHP -$validatorConfiguration = [ - "minIntegerValue" => 1, - "maxIntegerValue" => 24 -]; -``` - -#### Settings - -This Field Type does not support settings. - -## ISBN Field Type - -This Field Type represents an ISBN string either an ISBN-10 or ISBN-13 format. - -| Name | Internal name | Expected input type | -|--------|---------------|---------------------| -| `ISBN` | `ezisbn` | `string` | - -### PHP API Field Type  - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|----------|------------| -| `$isbn` | `string` | This property will be used for the ISBN string. | - -###### String representation - -An ISBN's string representation is the `$isbn` property's value, as a string. - -###### Constructor - -The constructor for this value object will initialize a new Value object with the value provided. It accepts a string as argument and will set it to the `isbn` attribute. - -#### Validation - -The input passed into this Field Type is subject of ISBN validation depending on the Field settings in its FieldDefinition stored in the Content Type. An example of this Field setting is shown below and will control if input is validated as ISBN-13 or ISBN-10: - -``` php -Array -( - [isISBN13] => true -) -``` - - -## Keyword Field Type - -This Field Type stores one or several comma-separated keywords as a string or array of strings. - -| Name | Internal name | Expected input| -|-----------|---------------|---------------| -| `Keyword` | `ezkeyword` | `string[]|string` | - -### PHP API Field Type  - -#### Input expectations - -|Type|Example| -|------|------| -|`string`|`"documentation"`| -|`string`|`"php, Ibexa Platform, html5"`| -|`string[]`|`[ "Ibexa", "Enterprise", "User Experience Management" ]`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|------------|------------| -| `$value` | `string[]` | Holds an array of keywords as strings.| - -``` php -// Value object content example -use eZ\Publish\Core\FieldType\Keyword\Value; -  -// Instantiates a Value object -$keywordValue = new Value(); -  -// Sets an array of keywords as a value -$keyword->value = [ "php", "css3", "html5", "Ibexa Platform" ]; -``` - -##### Constructor - -The `Keyword\Value` constructor will initialize a new Value object with the value provided. - -It expects a list of keywords, either comma-separated in a string or as an array of strings. - -``` php -// Constructor example -use eZ\Publish\Core\FieldType\Keyword\Value; -  -// Instantiates a Value object with an array of keywords -$keywordValue = new Value( [ "php5", "css3", "html5" ] ); -  -// Instantiates a Value object with a list of keywords in a string -// This is equivalent to the example above -$keywordValue = new Value( "php5,css3,html5" ); -``` - -## MapLocation Field Type - -This Field Type represents a geographical location. - -As input it expects three values: -- two float values latitude and longitude, -- a string value, corresponding to the name or address of the location. - -| Name | Internal name | Expected input | -|---------------|------------------|----------------| -| `MapLocation` | `ezgmaplocation` | `mixed` | - -### PHP API Field Type  - -#### Input expectations - -|Type|Example| -|------|------| -|`array`|`[ 'latitude' => 59.928732, 'longitude' => 10.777888, 'address' => "Ibexa Nordics" ]`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -|Property|Type|Description| -|------|------|------| -|`$latitude`|`float`|This property stores the latitude value of the map location reference.| -|`$longitude`|`float`|This property stores the longitude value of the map location reference.| -|`$address`|`string`|This property stores the address of map location.| - -###### Constructor - -The `MapLocation\Value` constructor will initialize a new Value object with values provided as hash. Accepted keys are `latitude` (`float`), `longitude` (`float`), `address` (`string`). - -``` php -// Constructor example - -// Instantiates a MapLocation Value object -$MapLocationValue = new MapLocation\Value( - [ - 'latitude' => 59.928732, - 'longitude' => 10.777888, - 'address' => "Ibexa Nordics" - ] - ); -``` - -### Template rendering - -The template called by [the `ez_render_field()` Twig function](../guide/twig_functions_reference.md#ez_render_field) while rendering a Map Location Field accepts the following parameters: - -|Parameter|Type|Default|Description| -|------|------|------|------| -|`draggable`|`boolean`|`true`|Whether to enable a draggable map| -|`height`|`string|false`|`"200px"`|The height of the rendered map with its unit (for example "200px" or "20em"), set to false to not set any height style inline.| -|`mapType`|`string`|`"ROADMAP"`|[One of the GMap types of map](https://developers.google.com/maps/documentation/javascript/maptypes#BasicMapTypes)| -|`scrollWheel`|`boolean`|`true`| Allows you to disable scroll wheel starting to zoom when mouse comes over the map as user scrolls down a page.| -|`showInfo`|`booolean`|`true`|Whether to show a latitude, longitude and the address outside of the map| -|`showMap`|`boolean`|`true`|Whether to show a Google Map| -|`width`|`string|false`|`"500px"`|The width of the rendered map with its unit (for example "500px" or "50em"), set to false to not set any width style inline.| -|`zoom`|`integer`|`13`|The initial zoom level on the map| - -Example: - -``` html+twig -{{ ez_render_field(content, 'location', {'parameters': {'width': '100%', 'height': '330px', 'showMap': true, 'showInfo': false}}) }} -``` - -#### Configuration - -| Config | SiteAccess/Group-aware | Description | -|--------|-------------------------|-------------| -| `api_keys.google_maps` | yes | Google maps requires the use of an API key for serving maps to web pages. This setting allows you to specify your personal [Google Maps API key](https://developers.google.com/maps/documentation/javascript/get-api-key) used during template rendering. | - -Example use: - -``` yaml -# ezplatform.yaml -ezplatform: - system: - site_group: - api_keys: { google_maps: MY_KEY } -``` - -!!! note - - The option to automatically get user coordinates through the "Locate me" button - is only available when the back office is served through the `https://` protocol. - -## Matrix Field Type - -This Field represents and handles a table of rows and columns of data. - -| Name | Internal name | Expected input | -|----------|---------------|----------------| -| `Matrix` | `ezmatrix` | `array` | - -The Matrix Field Type is available via the Matrix Bundle -provided by the [ezplatform-matrix-fieldtype](https://github.com/ezsystems/ezplatform-matrix-fieldtype) package. - -### PHP API Field Type - -#### Input expectations - -|Type|Description|Example| -|------|------|------| -|`array`|array of `EzSystems\EzPlatformMatrixFieldtype\FieldType\Value\Row` objects which contain column data|see below| - -Example of input: - -```php -new FieldType\Value([ - new FieldType\Value\Row(['col1' => 'Row 1, Col 1', 'col2' => 'Row 1, Col 2']), - new FieldType\Value\Row(['col1' => 'Row 2, Col 1', 'col2' => 'Row 2, Col 2']), - new FieldType\Value\Row(['col1' => 'Row 3, Col 1', 'col2' => 'Row 3, Col 2']), -]); -``` - -#### Value Object - -`EzSystems\EzPlatformMatrixFieldtype\FieldType\Value` offers the following properties: - -|Property|Type|Description| -|------|------|------| -|`rows`|`RowsCollection`|Array of `Row` objects containing an array of cells (`Row::getCells()` returns array `['col1' => 'Value 1', /* ... */]`)| - -#### Validation - -The minimum number of rows is set on Content Type level for each Field. - -Validation checks for empty rows. A row is considered empty if it contains only empty cells (or cells containing only spaces). Empty rows are removed. - -If, after removing empty rows, the number of rows does not fulfill the configured `Minimum number of rows`, the Field will not validate. - -For example, the following input will not validate if `Minimum number of rows` is set to 3, because the second row is empty: - -```php -new FieldType\Value([ - new FieldType\Value\Row(['col1' => 'Row 1, Col 1', 'col2' => 'Row 1, Col 2']), - new FieldType\Value\Row(['col1' => '', 'col2' => '']), - new FieldType\Value\Row(['col1' => 'Row 3, Col 1', 'col2' => 'Row 3, Col 2']), -]); -``` - -### GraphQL Field Type operations - -To get a Field of the Matrix Field Type with GraphQL, you will need to specify a Content ID, a Content Type, and a Field Type. - -The types that are returned are named after the Type and the Field: - -- `{TypeIdentifier}{FieldIdentifier}Row` - -The example below shows a GraphQL query for a Recipe Content item (belonging to a Content Type with a Matrix Field added), that has two Fields: - -- `name`: `ezstring` -- `ingredients`: `ezmatrix` with two columns: `ingredient` and `quantity` - -``` -{ - content { - recipe(id: 123) { - name - ingredients { - ingredient - quantity - } - } - } -} -``` - -The Type returned for the Matrix Field exposes columns defined in the Field definition: - -``` -{ - "data": { - "content": { - "recipe": { - "name": "Cake ingredients", - "ingredients": [ - { - "ingredient": "Butter", - "quantity": "200 grams" - }, - { - "ingredient": "Sugar", - "quantity": "100 grams" - } - ] - } - } - } -} -``` - -#### Query for the Field Type and Field definition's details - -With this query you can inspect details of specific Content Type. In case of a Matrix Field, you can ask for the list of columns, their names and identifiers. - -``` -{ - content { - _types { - recipe { - ingredients { - settings { - minimumRows - columns { - name - identifier - } - } - } - } - } - } -} -``` - -The response will list the exposed Field Type settings: - -- minimumRows -- columns - - name - - identifier - -Example response: - -``` -{ - "data": { - "content": { - "_types": { - "recipe": { - "ingredients": { - "settings": { - "minimumRows": 1, - "columns": [ - { - "name": "ingredient", - "identifier": "ingredient" - }, - { - "name": "quantity", - "identifier": "quantity" - } - ] - } - } - } - } - } - } -} -``` - -#### Mutation - -To create a Matrix Field Type you need to define Field Type and Field definition identifiers. -The types that are used for input are named after the Type and the Field: - -- `{TypeIdentifier}{FieldIdentifier}RowInput` e.g. `dish.nutritionFacts`, `event.agenda`: `DishNutritionFactsRowInput`, `EventAgendaRowInput` - -The example below shows how to create a Recipe Content item (belonging to a Content Type with a Matrix Field Type added) that has two Fields: - -- `name`: `"Cake Ingredient List"` -- `ingredients`: `ezmatrix` with two columns: `ingredient` and `quantity` - -``` - mutation AddRecipe { - createRecipe( - language: eng_GB - parentLocationId: 2, - input: { - name: "Cake Ingredient List", - ingredients: [ - {ingredient: "sugar", quantity: "100 grams"} - {ingredient: "butter", quantity: "200 grams"} - ] - } - ) { - name - } -} -``` - -The response will confirm creation of the new Recipe Field: - -``` -{ - "data": { - "createRecipe": { - "name": "Cake Ingredient List" - } - } -} - -``` - -## Media Field Type - -This Field Type represents and handles a media (audio/video) binary file. - -It is capable of handling the following types of files: - -- Apple QuickTime -- Adobe Flash -- Microsoft Windows Media -- Real Media -- Silverlight -- HTML5 Video -- HTML5 Audio - -| Name | Internal name | Expected input | -|---------|---------------|----------------| -| `Media` | `ezmedia` | mixed | - -### PHP API Field Type  - -#### Input expectations - -| Type | Description | Example| -|------|-------------|--------| -| `string` | Path to the media file.| `/Users/jane/butterflies.mp4` | -| `eZ\Publish\Core\FieldType\Media\Value` | Media Field Type Value Object with path to the media file as the value of `id` property. | See below. | - -#### Value object - -###### Properties - -`eZ\Publish\Core\FieldType\Media\Value` offers the following properties. - -Note that both `Media` and `BinaryFile` Value and Type inherit from the `BinaryBase` abstract Field Type and share common properties. - -|Property|Type|Description|Example| -|------|------|------|------| -|`id`|string|Media file identifier. This ID depends on the [IO Handler](../guide/clustering.md#dfs-io-handler) that is being used. With the native, default handlers (FileSystem and Legacy), the ID is the file path, relative to the binary file storage root dir (`var//storage/original` by default).|application/63cd472dd7819da7b75e8e2fee507c68.mp4| -|`fileName`|string| The human-readable file name, as exposed to the outside. Used to name the file when sending it for download.|butterflies.mp4| -|`fileSize`|int|File size, in bytes.|1077923| -|`mimeType`|string|The file's MIME type.|video/mp4| -|`uri`|string|The binary file's HTTP URI. If the URI doesn't include a host or protocol, it applies to the request domain. **The URI is not publicly readable, and must NOT be used to link to the file for download.** Use `ez_render_field` to generate a valid link to the download controller.|/var/ezdemo_site/storage/original/application/63cd472dd7819da7b75e8e2fee507c68.mp4| -|`hasController`|boolean|Whether the media has a controller when being displayed.|true| -|`autoplay`|boolean|Whether the media should be automatically played.|true| -|`loop`|boolean|Whether the media should be played in a loop.|false| -|`height`|int|Height of the media.|300| -|`width`|int|Width of the media.|400| -|`path`|string|**deprecated**|| - -#### Hash format - -The hash format mostly matches the value object. It has the following keys: - -- `id` -- `path` (for backwards compatibility) -- `fileName` -- `fileSize` -- `mimeType` -- `uri` -- `hasController` -- `autoplay` -- `loop` -- `height` -- `width` - -#### Validation - -The Field Type supports `FileSizeValidator`, defining maximum size of media file in bytes: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`maxFileSize`|`int`|`false`|Maximum size of the file in bytes.| - -``` php -// Example of using Media Field Type validator in PHP - -use eZ\Publish\Core\FieldType\Media\Type; - -$contentTypeService = $repository->getContentTypeService(); -$mediaFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "media", "ezmedia" ); - -// Setting maximum file size to 5 megabytes -$mediaFieldCreateStruct->validatorConfiguration = [ - "FileSizeValidator" => [ - "maxFileSize" => 5 * 1024 * 1024 - ] -]; -``` - -#### Settings - -The Field Type supports the `mediaType` setting, defining how the media file should be handled in output. - -|Name|Type|Default value|Description| -|------|------|------|------| -|`mediaType`|mixed|`Type::TYPE_HTML5_VIDEO`|Type of the media, accepts one of the predefined constants.| - -List of all available `mediaType` constants is defined in the `eZ\Publish\Core\FieldType\Media\Type` class: - -|Name|Description| -|------|------| -|`TYPE_FLASH`|Adobe Flash| -|`TYPE_QUICKTIME`|Apple QuickTime| -|`TYPE_REALPLAYER`|Real Media| -|`TYPE_SILVERLIGHT`|Silverlight| -|`TYPE_WINDOWSMEDIA`|Microsoft Windows Media| -|`TYPE_HTML5_VIDEO`|HTML5 Video| -|`TYPE_HTML5_AUDIO`|HTML5 Audio| - -``` php -// Example of using Media Field Type settings in PHP - -use eZ\Publish\Core\FieldType\Media\Type; -  -$contentTypeService = $repository->getContentTypeService(); -$mediaFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "media", "ezmedia" ); - -// Setting Adobe Flash as the media type -$mediaFieldCreateStruct->fieldSettings = [ - "mediaType" => Type::TYPE_FLASH, -]; -``` - -## Null Field Type - -This Field Type is used as fallback for migration scenarios, and for testing purposes. - -| Name | Internal name | Expected input type | -|--------|---------------|---------------------| -| `Null` | variable | mixed | - -### Description - -The Null Field Type serves as an aid when migrating from eZ Publish Platform and earlier legacy versions. It is a dummy for legacy Field Types that are not implemented in [[= product_name =]]. - -Null Field Type will accept anything provided as a value and is usually combined with: -- NullConverter: Makes it not store anything to the legacy storage engine (database), nor will it read any data. -- Unindexed: Indexable class making sure nothing is indexed to configured search engine. - -This Field Type does not have its own fixed internal name. Its identifier is instead configured as needed by passing it as an argument to the constructor. - -#### Example for usage of Null Field Type - -The following example shows how an `example` Field Type could be configured as a Null Field Type: - -``` yaml -# Null Fieldtype example configuration -services: - ezpublish.fieldType.example: - class: eZ\Publish\Core\FieldType\Null\Type - autowire: true - autoconfigure: false - arguments: [example] - tags: [{name: ezplatform.field_type, alias: example}] - ezpublish.fieldType.example.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: [{name: ezplatform.field_type.legacy_storage.converter, alias: example}] - ezpublish.fieldType.example.indexable: - class: '%ezpublish.fieldType.indexable.unindexed.class%' - tags: [{name: ezplatform.field_type.indexable, alias: example}] -``` - -## Page Field Type [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -Page Field Type represents a Page with a layout consisting of multiple zones. Each zone can in turn contain blocks. - -Page Field Type is only used in the Page Content Type that is included in [[= product_name_exp =]]. - -| Name | Internal name | Expected input | -|----------------|-----------------|-----------------| -| `Landing page` | `ezlandingpage` | `string (JSON)` | - -!!! caution "Page Builder" - - If you create Content Type with both `ezlandingpage` and `ezuser` Field Types, - you will not be redierected to Page Builder after selecting `Edit` or `Create`. - This is caused by `ezuser` Field Type which requires separate handling. You will be redirected to the standard AdminUI edit or create mode. - -### Layout and zones - -Layout defines how a Page is divided into zones. - -The placement of zones is defined in a template which is a part of the layout configuration. You can modify the template in order to define your own zone layout. - -For information on how to create and configure new blocks for the Page, see [Page layouts](../guide/page_rendering.md#page-layouts). - -### Blocks - -For information on how to create and configure new blocks for the Page, see [Creating Page blocks](../extending/extending_page.md#creating-page-blocks). - -### Rendering Pages - -Page rendering takes place while editing or viewing. - -When rendering a Page, its zones are passed to the layout as a `zones` array with a `blocks` array each. You can access them using twig (e.g. `{{ zones[0].id }}` ). - -Each div that's a zone should have the `data-ez-zone-id` attribute with zone ID as a value for a zone container. - -To render a block inside the layout, use the Twig `render_esi()` function to call `EzPlatformPageFieldTypeBundle:Block:render`. - -The `renderAction` has the following parameters: - -|Parameter|Description| -|---------|-----------| -|`locationId`|ID of the Location of the Content item which can be accessed by `contentInfo.id`| -|`blockId`|ID of the block which you want to render| -|`versionNo`|Version number of the Content item to render| -|`languageCode`|Language code of the Content item to render| - -Example usage: - -``` html+twig -{{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction', { - 'locationId': locationId, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo, - 'languageCode': field.languageCode -})) }} -``` - -As a whole a sample layout could look as follows: - -``` html+twig -
    - {# The required attribute for the displayed zone #} -
    - {# If a zone with [0] index contains any blocks #} - {% if zones[0].blocks %} - {# for each block #} - {% for block in blocks %} - {# create a new layer with appropriate ID #} -
    - {# render the block by using the "EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction" controller #} - {# location.id is the ID of the Location of the current Content item, block.id is the ID of the current block #} - {{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction', { - 'locationId': locationId, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo, - 'languageCode': field.languageCode - })) }} -
    - {% endfor %} - {% endif %} -
    -
    -``` - -## Rating Field Type - -!!! caution - - The Rating Field Type is available in the back-end interface, but it does not have editing templates, which makes it unusable in practice. - - Follow [EZP-25802](https://jira.ez.no/browse/EZP-25802) for future progress on this Field Type. - -## Relation Field Type - -!!! caution "Deprecated" - - The Relation Field Type is deprecated since v2.0. - - Use [RelationList](#relationlist-field-type) with a selection limit instead. - -This Field Type makes it possible to store and retrieve the value of a relation to another Content item. - -| Name | Internal name | Expected input | -|------------|--------------------|----------------| -| `Relation` | `ezobjectrelation` | mixed | - -### PHP API Field Type  - -#### Input expectations - -|Type|Example| -|------|------| -|`string`|`"150"`| -|`integer`|`150`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property|Type| Description| -|---------|-----|-----------| -| `$destinationContentId` | `string|int|null` | This property is used to store the value provided, which represents the related content. | - -``` php -// Value object content example - -$relation->destinationContentId = $contentInfo->id; -``` - -###### Constructor - -The `Relation\Value` constructor will initialize a new Value object with the value provided. It expects a mixed value. - -``` php -// Constructor example - -// Instantiates a Relation Value object -$relationValue = new Relation\Value( $contentInfo->id ); -``` - -#### Validation - -This Field Type validates whether the provided relation exists, but before that it will check that the value is either a string or an int. - -#### Settings - -The Field definition of this Field Type can be configured with three options: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`selectionMethod`|`int`|`Relation\Type::SELECTION_BROWSE`| *This setting is not implemented yet, only one selection method is available.* | -|`selectionRoot`|`string`|`null`|This setting defines the selection root.| -|`selectionContentTypes`|`array`|`[]`|An array of Content Type IDs that are allowed for related Content| - -``` php -// Relation FieldType example settings - -use eZ\Publish\Core\FieldType\Relation\Type; - -$settings = [ - "selectionMethod" => 1, - "selectionRoot" => null, - "selectionContentTypes" => [] -]; -``` - -## RelationList Field Type - -This Field Type makes it possible to store and retrieve values of a relation to other Content items. - -| Name | Internal name | Expected input | -|----------------|------------------------|----------------| -| `RelationList` | `ezobjectrelationlist` | `mixed` | - -### PHP API Field Type  - -#### Input expectations - -|Type|Description|Example| -|------|------|------| -|`int|string`|ID of the related Content item|`42`| -|`array`|An array of related Content IDs|`[ 24, 42 ]`| -|`eZ\Publish\API\Repository\Values\Content\ContentInfo`|ContentInfo instance of the related Content|n/a| -|`eZ\Publish\Core\FieldType\RelationList\Value`|RelationList Field Type Value Object|See below.| - -#### Value Object - -###### Properties - -`eZ\Publish\Core\FieldType\RelationList\Value` contains the following properties: - -|Property|Type|Description|Example| -|------|------|------|------| -|`destinationContentIds`|`array`|An array of related Content IDs|`[ 24, 42 ]`| - -``` php -// Value object content example -$relationList->destinationContentId = [ - $contentInfo1->id, - $contentInfo2->id, - 170 -]; -``` - -###### Constructor - -The `RelationList\Value` constructor will initialize a new Value object with the value provided. It expects a mixed array as value. - -``` php -//Constructor example - -// Instantiates a RelationList Value object -$relationListValue = new RelationList\Value( - [ - $contentInfo1->id, - $contentInfo2->id, - 170 - ] -); -``` - -#### Validation - -This Field Type validates if: - -- the `selectionMethod` specified is `\eZ\Publish\Core\FieldType\RelationList\Type::SELECTION_BROWSE` or `\eZ\Publish\Core\FieldType\RelationList\Type::SELECTION_DROPDOWN`. A validation error is thrown if the value does not match. -- the `selectionDefaultLocation` specified is `null`, `string` or `integer`. If the type validation fails a validation error is thrown. -- the value specified in `selectionContentTypes` is an `array`. If not, a validation error in given. -- the number of Content items selected in the Field is not greater than the `selectionLimit`. - -!!! note - - The dropdown selection method is not implemented yet. - -#### Settings - -The Field definition of this Field Type can be configured with the following options: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`selectionMethod`|`mixed`|`SELECTION_BROWSE`|Method of selection in the back-end interface| -|`selectionDefaultLocation`|`string|integer`|`null`|ID of the default Location for the selection when using the back-end interface| -|`selectionContentTypes`|`array`|`[]`|An array of Content Type IDs that are allowed for related Content| - -Following selection methods are available: - -| Name| Description| -|-----|------------| -| `SELECTION_BROWSE` | Selection will use browse mode| -| `SELECTION_DROPDOWN` | *Not implemented yet* | - -#### Validators - -|Name|Type|Default value|Description| -|------|------|------|------| -|`RelationListValueValidator[selectionLimit]`|`integer`|`0`|The number of Content items that can be selected in the Field. When set to 0, any number can be selected| - -``` php -// Example of using settings and validators configuration in PHP - -use eZ\Publish\Core\FieldType\RelationList\Type; - -$fieldSettings = [ - "selectionMethod" => Type::SELECTION_BROWSE, - "selectionDefaultLocation" => null, - "selectionContentTypes" => [] - ]; - -$validators = [ - "RelationListValueValidator" => [ - "selectionLimit" => 0, - ] -]; -``` - -## RichText Field Type - -The RichText Field Type is available via the RichText Field Type Bundle provided by the [ezplatform-richtext](https://github.com/ezsystems/ezplatform-richtext) package. - -This Field Type validates and stores structured rich text, and exposes it in several formats. - -|Name|Internal name|Expected input| -|------|------|------| -|`RichText`|`ezrichtext`|mixed| - -### PHP API Field Type  - -#### Input expectations - -|Type|Description|Example| -|------|------|------| -|`string`|XML document in one of the Field Type's input formats as a string.|See the example below.| -|`DOMDocument`|XML document in one of the Field Type's input formats as a `DOMDocument` object.|See the example below.| -|`EzSystems\EzPlatformRichText\eZ\FieldType\RichText\Value`|An instance of the Field Type's `Value` object.|See the example below.| - -###### Input formats - -The Field Type works with XML and also expects an XML value as input, whether as a string, `DOMDocument` object or Field Type's `Value` object. When the value is given as a string or a `DOMDocument` object, it will be checked for conformance with one of the supported input formats, then dispatched to the appropriate converter, to be converted to the Field Type's internal format. No conversion will be performed if providing the value in Field Type's internal format or as Field Type's `Value` object. In the latter case it will be expected that the `Value` object holds the value in the Field Type's internal format. - -Currently supported input formats are described in the table below: - -|Name|Description| -|------|------| -|[[= product_name =]]'s DocBook variant|Field Type's internal format| -|XHTML5 editing format|Typically used with in-browser HTML editor| -|Legacy eZXML format|Compatibility with legacy XML format, used by [XmlText Field Type](#xmltext-field-type)| - -###### Example of the Field Type's internal format - -``` xml - -
    - This is a title. - This is a paragraph. -
    -``` - -###### Example of the Field Type's XHTML5 edit format - -This format is used by [[= product_name =]]'s Online Editor. - -``` xml - -
    -

    This is a title.

    -

    This is a paragraph.

    -
    -``` - -For more information about internal format and input formats, see [Field Type's conversion test fixtures on GitHub](https://github.com/ezsystems/ezplatform-richtext/tree/master/tests/lib/eZ/RichText/Converter/Xslt/_fixtures). - -For example, eZXML does not use explicit level attributes for `
    ` elements, instead `
    ` element levels are indicated through the level of nesting inside `
    ` elements. - -###### Example of using XML document in internal format as a string - -``` php -... - -$contentService = $repository->getContentService(); -$contentTypeService = $repository->getContentTypeService(); - -$contentType = $contentTypeService->loadContentTypeByIdentifier( "article" ); -$contentCreateStruct = $contentService->newContentCreateStruct( $contentType, "eng-GB" ); - -$inputString = << -
    - This is a title. - This is a paragraph. -
    -DOCBOOK; - -$contentCreateStruct->setField( "description", $inputString ); - -... -``` - -#### Value object - -`EzSystems\EzPlatformRichText\eZ\FieldType\RichText\Value` offers the following properties: - -|Property|Type|Description| -|------|------|------| -|`xml`|`DOMDocument`|Internal format value as an instance of `DOMDocument`.| - -### REST API specifics - -#### Creating or updating Content - -When creating RichText content with the REST API, it is possible to provide data as a string, using the `xml` fieldValue key: - -``` xml - - <?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> -<title ezxhtml:level="2">This is a title.</title> -</section> - - -``` - -When the value given over REST API is transformed into a Field Type's `Value` object, it will be treated as a string. This means you can use any supported input format for input over REST API. - -## Selection Field Type - -The Selection Field Type stores single selections or multiple choices from a list of options, by populating a hash with the list of selected values. - -| Name | Internal name | Expected input type | -|-------------|---------------|---------------------| -| `Selection` | `ezselection` | mixed | - -### PHP API Field Type - -#### Input expectations - -| Type | Example | -|---------|-----------------| -| `array` | `[ 1, 2 ]` | - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|--------------|---------|------------| -| `$selection` | `int[]` | This property is used for the list of selections, which is a list of integer values, or one single integer value. | - -``` php -// Value object content examples - -// Single selection -$value->selection = 1; - -// Multiple selection -$value->selection = [ 1, 4, 5 ]; -``` - -###### Constructor - -The `Selection\Value` constructor accepts an array of selected element identifiers. - -``` php -// Constructor example - -// Instanciates a selection value with items #1 and #2 selected -$selectionValue = new Selection\Value( [ 1, 2 ] ); -``` - -###### String representation - -String representation of this Field Type is its list of selections as a string, concatenated with a comma. - -Example: `"1,2,24,42"` - -#### Hash format - -Hash format of this Field Type is the same as Value object's `selection` property. - -``` php -// Example of value in hash format - -$hash = [ 1, 2 ]; -``` - -#### Validation - -This Field Type validates the input, verifying if all selected options exist in the Field definition and checks if multiple selections are allowed in the Field definition. -If any of these validations fail, a `ValidationError` is thrown, specifying the error message. When option validation fails, a list with the invalid options is also presented. - -#### Settings - -| Name | Type | Default value | Description| -|--------------|-----------|---------------|------------| -| `isMultiple` | `boolean` | `false` | Used to allow or prohibit multiple selection from the option list. | -| `options` | `hash` | `[]` | Stores the list of options defined in the Field definition. | - -``` php -// Selection Field Type example settings - -use eZ\Publish\Core\FieldType\Selection\Type; - -$settings = [ - "isMultiple" => true, - "options" => [1 => 'One', 2 => 'Two', 3 => 'Three'] -]; -``` - -## TextBlock Field Type - -The Field Type handles a block of multiple lines of unformatted text. It is capable of handling up to 16,777,216 characters. - -| Name | Internal name | Expected input type | -|-------------|---------------|---------------------| -| `TextBlock` | `eztext` | `string` | - -### PHP API Field Type - -#### Input expectations - -|Type|Example| -|----|-------| -|`string`|`"This is a block of unformatted text"`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -|Property|Type|Description| -|--------|----|-----------| -|`$text`|`string`|This property will be used for the text content.| - -###### String representation - -A TextBlock's string representation is the `$text` property's value, as a string. - -###### Constructor - -The constructor for this Value object will initialize a new Value object with the value provided. It accepts a string as argument and will import it to the `$text` attribute. - -#### Validation - -This Field Type does not perform any special validation of the input value. - -#### Settings - -Settings contain only one option: - -| Name | Type | Default value | Description| -|------------|-----------|---------------|------------| -| `textRows` | `integer` | `10` | Number of rows for the editing box in the back-end interface. | - -## TextLine Field Type - -This Field Type makes possible to store and retrieve a single line of unformatted text. It is capable of handling up to 255 characters. - -| Name | Internal name | Expected input type | -|------------|---------------|---------------------| -| `TextLine` | `ezstring` | `string` | - -### PHP API Field Type - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|----------|------------| -| `$text` | `string` | This property will be used for the text content. | - -###### String representation - -A TextLine's string representation is the `$text` property's value, as a string. - -###### Constructor - -The constructor for this Value object will initialize a new Value object with the value provided. It accepts a string as argument and will import it to the `$text` attribute. - -#### Validation - -The input passed into this Field Type is subject to validation by the `StringLengthValidator`. The length of the string provided must be between the minimum length defined in `minStringLength` and the maximum defined in `maxStringLength`. The default value for both properties is 0, which means that the validation is disabled by default. -To set the validation properties, the `validateValidatorConfiguration()` method needs to be inspected, which will receive an array with `minStringLength` and `maxStringLength` like in the following representation: - -``` -Array -( - [minStringLength] => 1 - [maxStringLength] => 60 -) -``` - -## Time Field Type - -This Field Type represents time information. - -Date information is **not stored**. - -What is stored is the number of seconds, calculated from the beginning of the day in the given or the environment timezone. - -| Name | Internal name | Expected input type | -|--------|---------------|---------------------| -| `Time` | `eztime` | mixed | - -### PHP API Field Type - -#### Input expectations - -If input value is of type `string` or `integer`, it will be passed directly to the [PHP's built-in `\DateTime` class](http://www.php.net/manual/en/datetime.construct.php) constructor, therefore the same input format expectations apply. - -It is also possible to directly pass an instance of `\DateTime`. - -|Type|Example| -|------|------| -|`string`|`"2012-08-28 12:20 Europe/Berlin"`| -|`integer`|`1346149200`| -|`\DateTime`|`new \DateTime()`| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|----------------|------------| -| `$time` | `integer|null` | Holds the time information as a number of seconds since the beginning of the day. | - -###### Constructor - -The constructor for this Value object will initialize a new Value object with the value provided. It accepts an integer representing the number of seconds since the beginning of the day. - -###### String representation - -String representation of the date value will generate the date string in the format "H:i:s" as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). - -|Character|Description|Example| -|---------|----------|--------| -|H|Two digit representation of an hour, 24-hour format, range 00 to 23 |12| -|i|Two digit representation of minutes, range 00 to 59|14| -|s|Two digit representation of seconds, range 00 to 59|56| - -Example: `"12:14:56"` - -#### Hash format - -Value in hash format is an integer representing a number of seconds since the beginning of the day. - -Example: `36000` - -#### Validation - -This Field Type does not perform validation of the input value. - -#### Settings - -The Field definition of this Field Type can be configured with several options: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`useSeconds`|`boolean`|`false`|Used to control displaying of seconds in the output.| -|`defaultType`|`Type::DEFAULT_EMPTY Type::DEFAULT_CURRENT_TIME`|`Type::DEFAULT_EMPTY`|The constant used here defines default input value when using back-end interface.| - -``` php -// Time Field Type example settings -use eZ\Publish\Core\FieldType\Time\Type; - -$settings = [ - "defaultType" => DateAndTime::DEFAULT_EMPTY -]; -``` - -### Template rendering - -The template called by [the `ez_render_field()` Twig function](../guide/twig_functions_reference.md#ez_render_field) while rendering a Date Field has access to the following parameters: - -| Parameter | Type | Default | Description| -|-----------|----------|---------|------------| -| `locale` | `string` |   n/a | Internal parameter set by the system based on current request locale or, if not set, calculated based on the language of the Field. | - -Example: - -``` php -{{ ez_render_field(content, 'time') }} -``` -  -## URL Field Type - -This Field Type makes it possible to store and retrieve a URL. It is formed by the combination of a link and the respective text. - -| Name | Internal name | Expected input | -|-------|---------------|----------------| -| `Url` | `ezurl` | `string` | - -### PHP API Field Type - -#### Input expectations - -|Type|Description|Example| -|------|------|------| -|`string`|Link content provided to the value.|"http://www.ibexa.co"| -|`string`|Text content that represents the stored link.|"Ibexa"| - -#### Value object - -###### Properties - -The Value class of this Field Type contains the following properties: - -| Property | Type | Description| -|----------|----------|------------| -| `$link` | `string` | This property stores the link provided to the value of this Field Type. | -| `$text` | `string` | This property stores the text to represent the stored link provided to the value of this Field Type. | - -``` php -// Value object content example - -$url->link = "http://www.ibexa.co"; -$url->text = "Ibexa"; -``` - -###### Constructor - -The `Url\Value` constructor initializes a new Value object with the provided value. It expects two comma-separated strings, corresponding to the link and text. - -``` php -// Constructor example - -// Instantiates an Url Value object -$UrlValue = new Url\Value( "http://www.ibexa.co", "Ibexa" ); -``` -#### Hash format - -|Key|Type|Description|Example| -|------|------|------|------| -|`link`|`string`|Link content.|"http://ibexa.co"| -|`text`|`string`|Text content.|"Ibexa"| - -```php -// Example of the hash value in PHP -$hash = [ - "link" => "http://ibexa.co", - "text" => "Ibexa" -]; - -``` - -#### Validation - -This Field Type does not perform validation. - -#### Settings - -This Field Type does not have settings. - -## User Field Type - -This Field Type validates and stores information about a user. - -| Name | Internal name | Expected input | -|--------|---------------|----------------| -| `User` | `ezuser` | ignored | - -### PHP API Field Type - -#### Value Object - -|Property|Type|Description|Example| -|------|------|------|------| -|`hasStoredLogin`|`boolean`|Denotes if user has stored login.|`true`| -|`contentId`|`int|string`|ID of the Content item corresponding to the user.|`42`| -|`login`|`string`|Username.|`john`| -|`email`|`string`|The user's email address.|`john@smith.com`| -|`passwordHash`|`string`|Hash of the user's password.|`1234567890abcdef`| -|`passwordHashType`|`mixed`|Algorithm user for generating password hash as a `PASSWORD_HASH_*` constant defined in `eZ\Publish\Core\Repository\Values\User\User` class.|`User::PASSWORD_HASH_PHP_DEFAULT`| -|`maxLogin`|`int`|Maximum number of concurrent logins.|`1000`| - -###### Available password hash types - -|Constant|Description| -|------|------| -|`eZ\Publish\Core\Repository\Values\User\User::DEFAULT_PASSWORD_HASH`|Default password hash, used when none is specified, may change over time.| -|`eZ\Publish\Core\Repository\Values\User\User::PASSWORD_HASH_PHP_DEFAULT`|Passwords hashed by PHP's default algorithm, which may change over time.| -|`eZ\Publish\Core\Repository\Values\User\User::PASSWORD_HASH_BCRYPT`|Bcrypt hash of the password.| - -!!! caution - - Using the MD5-based deprecated hash types is a security risk, because if the hashes are leaked, they are too easily broken by brute-force attacks. - The plaintext type offers no security. It was only ever intended for testing, and should never be used now. - - We strongly recommend switching to one of the new hash types. If you do, it will be used for new users. - Existing users will also have their hashes updated to the new type when they log in. - (A mass update of all hashes is not possible, because this requires knowing the passwords, which only the users themselves do.) - - Removal notice: https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#password-hashes - -## XmlText Field Type - -The XmlText Field Type isn't officially supported by [[= product_name =]]. It can be installed by requiring `ezsystems/ezplatform-xmltext-fieldtype`. PlatformUI does not support WYSIWYG editing of Fields of this type. - -This Field Type validates and stores formatted text using the eZ Publish legacy format, eZXML.  - -| Name | Internal name | Expected input | -|-----------|---------------|----------------| -| `XmlText` | `ezxmltext` | `mixed` | - -### Input expectations - -|Type|Description|Example| -|------|------|------| -|`string`|XML document in the Field Type internal format as a string.|See the example below.| -|`eZ\Publish\Core\FieldType\XmlText\Input`|An instance of the class implementing the Field Type's abstract `Input` class.|See the example below.| -|`eZ\Publish\Core\FieldType\XmlText\Value`|An instance of the Field Type's `Value` object.|See the example below.| - -#### Example of the Field Type's internal format - -``` xml - -
    - This is a paragraph. -
    -``` - -#### For XHTML Input - -The XML output uses `` and `` by default, respecting the semantic XHTML notation. - -Learn more about ``, ``, ``, ``: - -- Learn more [about the semantic tags vs the presentational tags.](http://html5doctor.com/i-b-em-strong-element/) - -### Input object API - -`Input` object is intended as a vector for different input formats. It should accept input value in a foreign format and convert it to the Field Type's internal format. - -It should implement the abstract `eZ\Publish\Core\FieldType\XmlText\Input` class, which defines only one method: - -|Method|Description| -|------|------| -|`getInternalRepresentation`|The method returns the input value in the internal format.| - -At the moment there is only one implementation of the `Input` class, `eZ\Publish\Core\FieldType\XmlText\Input\EzXml`, which accepts input value in the internal format, and therefore only performs validation of the input value. - -``` php -// Example of using the Input object - -... -  -use eZ\Publish\Core\FieldType\XmlText\Input\EzXml as EzXmlInput; - -... - -$contentService = $repository->getContentService(); -$contentTypeService = $repository->getContentTypeService(); -  -$contentType = $contentTypeService->loadContentTypeByIdentifier( "article" ); -$contentCreateStruct = $contentService->newContentCreateStruct( $contentType, "eng-GB" ); - -$inputString = << -
    - This is a paragraph. -
    -EZXML; -  -$ezxmlInput = new EzXmlInput( $inputString ); - -$contentCreateStruct->setField( "description", $ezxmlInput ); -  -... -``` - -### Value object API - -`eZ\Publish\Core\FieldType\XmlText\Value` offers the following properties: - -|Property|Type|Description| -|------|------|------| -|`xml`|`DOMDocument`|Internal format value as an instance of `DOMDocument`.| - -### Validation - -Validation of the internal format is performed in the `eZ\Publish\Core\FieldType\XmlText\Input\EzXml` class. - -### Settings - -Following settings are available: - -|Name|Type|Default value|Description| -|------|------|------|------| -|`numRows`|`int`|`10`|Defines the number of rows for the online editor in the back-end interface.| -|`tagPreset`|`mixed`|`Type::TAG_PRESET_DEFAULT`|Preset of tags for the online editor in the back-end interface.| - -#### Tag presets - -Following tag presets are available as constants in the `eZ\Publish\Core\FieldType\XmlText` class: - -|Constant|Description| -|------|------| -|`TAG_PRESET_DEFAULT`|Default tag preset.| -|`TAG_PRESET_SIMPLE_FORMATTING`|Preset of tags for online editor intended for simple formatting options.| - -``` php -// Example of using settings in PHP - -... -  -use eZ\Publish\Core\FieldType\XmlText\Type; - -... - -$contentTypeService = $repository->getContentTypeService(); -$xmltextFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "description", "ezxmltext" ); - -$xmltextFieldCreateStruct->fieldSettings = [ - "numRows" => 25, - "tagPreset" => Type::TAG_PRESET_SIMPLE_FORMATTING -]; -  -... -``` +Proper indexing of these Field Types is done with [Solr Search engine](../guide/search/solr.md). diff --git a/docs/api/field_type_search.md b/docs/api/field_type_search.md index 501de6f9a5..dfa7be9e3c 100644 --- a/docs/api/field_type_search.md +++ b/docs/api/field_type_search.md @@ -1,8 +1,14 @@ +--- +description: To be searchable, a Field Type must implement the Indexable interface. +--- + # Field Type searching Fields, or a custom Field Type, might contain or maintain data relevant for user searches. To make the search engine aware of the data in your Field Type you need to implement an additional interface and register the implementation. +## `Indexable` interface + The `eZ\Publish\SPI\FieldType\Indexable` interface defines the methods below which are required if the Field Type provides data relevant to search engines. ### `getIndexData(Field $field, FieldDefinition $fieldDefinition)` @@ -24,13 +30,13 @@ This example from the `Url` Field Type shows that the Field Type will always ret ### `getDefaultMatchField()` -This method retrieves the name of the default Field to be used for matching. As Field Types can index multiple Fields (see [MapLocation](field_type_reference.md#maplocation-field-type) Field Type's implementation of this interface), this method is used to define the default field for matching. The default Field is typically used by the [`Field` Search Criterion](../guide/search/criteria_reference/field_criterion.md). +This method retrieves the name of the default Field to be used for matching. As Field Types can index multiple Fields (see [MapLocation](field_types_reference/maplocationfield.md) Field Type's implementation of this interface), this method is used to define the default field for matching. The default Field is typically used by the [`Field` Search Criterion](../guide/search/criteria_reference/field_criterion.md). ### `getDefaultSortField()` -This method gets name of the default Field to be used for sorting. As Field Types can index multiple Fields (see [MapLocation](field_type_reference.md#maplocation-field-type) Field Type's implementation of this interface), this method is used to define default field for sorting. Default Field is typically used by the [`Field` Sort Clause](../guide/search/sort_clause_reference/field_sort_clause.md). +This method gets name of the default Field to be used for sorting. As Field Types can index multiple Fields (see [MapLocation](field_types_reference/maplocationfield.md) Field Type's implementation of this interface), this method is used to define default field for sorting. Default Field is typically used by the [`Field` Sort Clause](../guide/search/sort_clause_reference/field_sort_clause.md). -## Register Indexable Implementations +## Register `Indexable` implementations Implement `eZ\Publish\SPI\FieldType\Indexable` as an extra service and register this Service using the `ezplatform.field_type.indexable` tag. Example from [`indexable_fieldtypes.yaml`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/settings/indexable_fieldtypes.yml): @@ -43,7 +49,7 @@ ezpublish.fieldType.indexable.ezkeyword: Note that `alias` should be the same as Field Type ID. -## Search Field Values +## Search Field values The search Field values returned by the `getIndexData` method are simple value objects consisting of the following properties: diff --git a/docs/api/field_type_storage.md b/docs/api/field_type_storage.md index 6cff232633..31830a62e9 100644 --- a/docs/api/field_type_storage.md +++ b/docs/api/field_type_storage.md @@ -1,3 +1,7 @@ +--- +description: To be able to store the data saved to a Field, you must configure storage conversion for the Field Type. +--- + # Field Type storage ## Storage conversion @@ -56,7 +60,7 @@ which you must implement in your Field Type. The interface contains the followin |`toFieldDefinition`|Converts the other way around.| |`getIndexColumn()`|Returns the storage column which is used for indexing either `sort_key_string` or `sort_key_int`.| -Just like a Type, a Legacy Converter needs to be registered and tagged in the service container. +Just like a Type, a Legacy Converter needs to be registered and tagged in the [service container](../api/public_php_api.md#service-container). #### Registering a converter @@ -124,7 +128,7 @@ The registry mechanism is realized as a base class for `FieldStorage` implementa |Method|Description| |------|-----------| -|`addGateway()`|Allows the registration of additional `StorageGateway`s from the outside. Furthermore, an associative array of `StorageGateway`s can be given to the constructor for basic initialization. This array should originate from the Dependency injection mechanism.| +|`addGateway()`|Allows the registration of additional `StorageGateway`s from the outside. Furthermore, an associative array of `StorageGateway`s can be given to the constructor for basic initialization. This array should originate from the dependency injection mechanism.| |`getGateway()`|This protected method is used by the implementation to retrieve the correct `StorageGateway` for the current context.| !!! tip @@ -150,12 +154,10 @@ services: - {name: ezplatform.field_type.external_storage_handler, alias: myfield} ``` -The configuration requires providing the `ezplatform.field_type.external_storage_handler` tag, with the `alias` attribute being the *fieldTypeIdentifier*. You also have to inject the gateway in `arguments`, [see below](#gateway-based-storage). +The configuration requires providing the `ezplatform.field_type.external_storage_handler` tag, with the `alias` attribute being the *fieldTypeIdentifier*. You also have to inject the gateway in `arguments`, [see Gateway-based storage](#gateway-based-storage). External storage configuration for basic Field Types is located in [eZ/Publish/Core/settings/fieldtype_external_storages.yaml](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/settings/fieldtype_external_storages.yml). -#### Registration - Using gateway-based storage requires another service implementing `eZ\Publish\SPI\FieldType\StorageGateway` to be injected into the [external storage handler](#storing-external-data)). ``` yaml diff --git a/docs/api/field_type_type_and_value.md b/docs/api/field_type_type_and_value.md index 4e8fc06b3f..a6b26d2316 100644 --- a/docs/api/field_type_type_and_value.md +++ b/docs/api/field_type_type_and_value.md @@ -1,12 +1,16 @@ -# Type and Value classes +--- +description: The basis of all Field Types are their Type and Value classes, containing, respectively, the logic and the data for the Fields. +--- + +# Type and Value A Field Type must contain a Type class which contains the logic of the Field Type: validating data, transforming from various formats, describing the validators, etc. A Type class must implement `eZ\Publish\SPI\FieldType\FieldType` ("Field Type interface"). All native Field Types also extend the `eZ\Publish\SPI\FieldType\FieldType` abstract class that implements this interface and provides implementation facilities through a set of abstract methods of its own. -You should also provide a Value object class for storing the custom Field value provided by the Field Type. +You should also provide a value object class for storing the custom Field value provided by the Field Type. The Value is used to represent an instance of the Field Type within a Content item. -Each Field will present its data using an instance of the Type's Value class. +Each Field presents its data using an instance of the Type's Value class. A Value class must implement the `eZ\Publish\SPI\FieldType\Value` interface. It may also extend the `eZ\Publish\Core\FieldType\Value` abstract class. It is meant to be stateless and as lightweight as possible. @@ -49,7 +53,7 @@ It is important to note that the schema definitions of the Field Type can be bot Since it is not possible to enforce a schema format, the code using a specific Field Type must basically know all Field Types it deals with. -This will also apply to all user interfaces and the REST API, which therefore must provide extension points to register handling code for custom Field Type. These extensions are not defined yet. +This also applies to all user interfaces and the REST API, which therefore must provide extension points to register handling code for custom Field Type. These extensions are not defined yet. ### Field Type name @@ -87,22 +91,23 @@ It is based on the Field Type settings and validator configuration and stored in ### Serialization -When [REST API](rest_api_guide.md) is used, conversion needs to be done for Field Type values, settings and validator configurations. These are converted to and from a simple hash format that can be encoded in REST payload. As conversion needs to be done both when transmitting and receiving data through REST, Field Type implements the following pairs of methods: +When [REST API](rest_api_usage.md) is used, conversion needs to be done for Field Type values, settings and validator configurations. These are converted to and from a simple hash format that can be encoded in REST payload. As conversion needs to be done both when transmitting and receiving data through REST, Field Type implements the following pairs of methods: |Method|Description| |------|-----------| -|`toHash()`|Converts Field Type Value into a plain hash format.| +|`toHash()`|Converts Field Type Value into a simple hash format.| |`fromHash()`|Converts the other way around.| |`fieldSettingsToHash()`|Converts Field Type settings to a simple hash format.| |`fieldSettingsFromHash()`|Converts the other way around.| |`validatorConfigurationToHash()`|Converts Field Type validator configuration to a simple hash format.| |`validatorConfigurationFromHash()`|Converts the other way around.| -## Registration +[[= include_file('docs/snippets/simple_hash_value_caution.md') =]] -A Field Type needs to have an indexable class defined. -If you are using Solr Bundle, each Field Type must be registered in `config/services.yml`: +## Registration +The Field Type must be registered in `config/services.yml`: + ``` yaml services: EzSystems\EzPlatformMatrixFieldtype\FieldType\Type: @@ -111,26 +116,18 @@ services: - {name: ezplatform.field_type, alias: ezmatrix} ``` -Items that are not to be indexed should be registered with the `unindexed` class with the parameter `ezpublish.fieldType.indexable.unindexed.class`: - -```yaml -services: - EzSystems\EzPlatformMatrixFieldtype\FieldType\Type: - class: %ezpublish.fieldType.indexable.unindexed.class% - tags: - - {name: ezplatform.field_type, alias: ezmatrix} -``` - #### `parent` -As described in the [Symfony Dependency Injection Component documentation](http://symfony.com/doc/5.0/components/dependency_injection/parentservices.html), the `parent` config key indicates that you want your service to inherit from the parent's dependencies, including constructor arguments and method calls. This helps avoiding repetition in your Field Type configuration and keeps consistency between all Field Types. +As described in the [Symfony service container documentation]([[= symfony_doc =]]/components/dependency_injection/parentservices.html), the `parent` config key indicates that you want your service to inherit from the parent's dependencies, including constructor arguments and method calls. This helps avoiding repetition in your Field Type configuration and keeps consistency between all Field Types. +If you need to inject other services into your Type class, skip using the `parent` config key. #### `tags` -Like most API components, Field Types use the [Symfony service tag mechanism](http://symfony.com/doc/5.0/service_container/tags.html). +Like most API components, Field Types use the [Symfony service tag mechanism]([[= symfony_doc =]]/service_container/tags.html). A service can be assigned one or several tags, with specific parameters. -When the dependency injection container is compiled into a PHP file, tags are read by `CompilerPass` implementations that add extra handling for tagged services. +When the [service container](../api/public_php_api.md#service-container) is compiled into a PHP file, +tags are read by `CompilerPass` implementations that add extra handling for tagged services. Each service tagged as `ezplatform.field_type` is added to a [registry](http://martinfowler.com/eaaCatalog/registry.html) using the `alias` key as its unique `fieldTypeIdentifier` e.g. `ezstring`. Each Field Type must also inherit from the abstract `ezplatform.field_type` service. This ensures that the initialization steps shared by all Field Types are executed. @@ -139,6 +136,10 @@ This ensures that the initialization steps shared by all Field Types are execute The configuration of built-in Field Types is located in [`EzPublishCoreBundle/Resources/config/fieldtypes.yaml`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/settings/fieldtypes.yml). +### Indexing + +To make the search engine aware of the data stored in a Field Type, register it as [indexable](field_type_search.md) + ## Field Type settings It is recommended to use a simple associative array format for the settings schema returned by `eZ\Publish\SPI\FieldType\FieldType::getSettingsSchema()`, which follows these rules: @@ -167,7 +168,7 @@ The settings are mapped into Symfony forms via the [FormMapper](field_type_form_ ## Extensibility points -Some Field Types will require additional processing, for example a Field Type storing a binary file, or one having more complex settings or validator configuration. For this purpose specific implementations of an abstract class `EzSystems\EzPlatformRest\FieldTypeProcessor` are used. +Some Field Types require additional processing, for example a Field Type storing a binary file, or one having more complex settings or validator configuration. For this purpose specific implementations of an abstract class `EzSystems\EzPlatformRest\FieldTypeProcessor` are used. This class provides the following methods: diff --git a/docs/api/field_type_validation.md b/docs/api/field_type_validation.md index 2356181f30..72bdbb4bde 100644 --- a/docs/api/field_type_validation.md +++ b/docs/api/field_type_validation.md @@ -1,3 +1,7 @@ +--- +description: Field Type validation allows you to validate if data entered stored in the Field conforms to the schema. +--- + # Field Type validation ## Validator schema diff --git a/docs/api/field_types_reference/authorfield.md b/docs/api/field_types_reference/authorfield.md new file mode 100644 index 0000000000..42a04239d2 --- /dev/null +++ b/docs/api/field_types_reference/authorfield.md @@ -0,0 +1,89 @@ +# Author Field Type + +This Field Type allows the storage and retrieval of one or more authors. For each author, it can handle a name and an email address. It is typically used to store information about additional authors who have written/created different parts of a Content item. + +| Name | Internal name | Expected input | Output | +|----------|---------------|----------------|----------| +| `Author` | `ezauthor` | mixed | `string` | +## PHP API Field Type + +### Value Object + +##### Properties + +|Attribute|Type|Description|Example| +|------|------|------|------| +|`authors`|`\eZ\Publish\Core\FieldType\Author\Author[] `|List of authors.|See below| + +Example: + +``` php +$authorList = Author\Value([ + new Author\Author([ + 'id' => 1, + 'name' => 'Boba Fett', + 'email' => 'boba.fett@example.com' + ]), + new Author\Author([ + 'id' => 2, + 'name' => 'Darth Vader', + 'email' => 'darth.vader@example.com' + ]), +]); +``` + +### Hash format + +The hash format mostly matches the value object. It has the following key `authors`. + +Example + +``` php +[ + [ + 'id' => 1, + 'name' => 'Boba Fett', + 'email' => 'boba.fett@example.com' + ], + [ + 'id' => 2, + 'name' => 'Darth Vader', + 'email' => 'darth.vader@example.com' + ] +] +``` + +##### String representation + +The string contains all the authors with their names and emails. + +Example: `John Doe john@doe.com` + +### Validation + +This Field Type does not perform any special validation of the input value. + +### Settings + +The Field definition of this Field Type can be configured with a single option: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`defaultAuthor`|`mixed`|`Type::DEFAULT_VALUE_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| + +Following `defaultAuthor` default value options are available as constants in the `eZ\Publish\Core\FieldType\Author\Type` class: + +|Constant|Description| +|------|------| +|`DEFAULT_VALUE_EMPTY`|Default value will be empty.| +|`DEFAULT_CURRENT_USER`|Default value will use currently logged user.| + +``` php +// Author Field Type example settings + +use eZ\Publish\Core\FieldType\Author\Type; + +$settings = [ + "defaultAuthor" => Type::DEFAULT_VALUE_EMPTY +]; +``` diff --git a/docs/api/field_types_reference/binaryfilefield.md b/docs/api/field_types_reference/binaryfilefield.md new file mode 100644 index 0000000000..197e941970 --- /dev/null +++ b/docs/api/field_types_reference/binaryfilefield.md @@ -0,0 +1,82 @@ +# BinaryFile Field Type + +This Field Type represents and handles a single binary file. It also counts the number of times the file has been downloaded from the `content/download` module. + +It is capable of handling virtually any file type and is typically used for storing legacy document types such as PDF files, Word documents, spreadsheets, etc. The maximum allowed file size is determined by the "Max file size" class attribute edit parameter and the `upload_max_filesize` directive in the main PHP configuration file (`php.ini`). + +| Name | Internal name | Expected input | Output | +|--------------|----------------|----------------|---------| +| `BinaryFile` | `ezbinaryfile` | mixed | mixed | + +## PHP API Field Type + +### Value Object + +##### Properties + +Note that both `BinaryFile` and `Media` Value and Type inherit from the `BinaryBase` abstract Field Type, and share common properties. + +`eZ\Publish\Core\FieldType\BinaryFile\Value` offers the following properties: + +|Attribute|Type|Description|Example| +|------|------|------|------| +|`id`|string|Binary file identifier. This ID depends on the [IO Handler](../../guide/clustering.md#dfs-io-handler) that is being used. With the native, default handlers (FileSystem and Legacy), the ID is the file path, relative to the binary file storage root dir (`var//storage/original` by default).|application/63cd472dd7.pdf| +|`fileName`|string|The human-readable file name, as exposed to the outside. Used when sending the file for download in order to name the file.|20130116_whitepaper.pdf| +|`fileSize`|int|File size, in bytes.|1077923| +|`mimeType`|string|The file's MIME type.|application/pdf| +|`uri`|string|The binary file's `content/download` URI. If the URI doesn't include a host or protocol, it applies to the request domain.|/content/download/210/2707| +|`downloadCount`|integer|Number of times the file was downloaded|0| +|`path`|string|**deprecated**|N/A| + +### Hash format + +The hash format mostly matches the value object. It has the following keys: + +- `id` +- `path` (for backwards compatibility) +- `fileName` +- `fileSize` +- `mimeType` +- `uri` +- `downloadCount` + +Example: + +```php +[ + 'id' => 'some/file/here', + 'fileName' => 'sindelfingen.jpg', + 'fileSize' => 2342, + 'downloadCount' => 0, + 'mimeType' => 'image/jpeg', + 'uri' => 'http://some/file/here', +] +``` + +## REST API specifics + +Used in the REST API, a BinaryFile Field will mostly serialize the hash described above. However there are a couple specifics worth mentioning. + +### Reading content: `url` property + +When reading the contents of a Field of this type, an extra key is added: `url`. This key gives you the absolute file URL, protocol and host included. + +Example: `http://example.com/var/ezdemo_site/storage/original/application/63cd472dd7819da7b75e8e2fee507c68.pdf` + +### Creating content: data property + +When creating BinaryFile content with the REST API, it is possible to provide data as a base64 encoded string, using the `data` fieldValue key: + +``` xml + + file + eng-GB + + My file.pdf + 17589 + + + +``` diff --git a/docs/api/field_types_reference/checkboxfield.md b/docs/api/field_types_reference/checkboxfield.md new file mode 100644 index 0000000000..9573897987 --- /dev/null +++ b/docs/api/field_types_reference/checkboxfield.md @@ -0,0 +1,49 @@ +# Checkbox Field Type + +The Checkbox Field Type stores the current status for a checkbox input, checked or unchecked, by storing a boolean value. + +| Name | Internal name | Expected input type | +|------------|---------------|---------------------| +| `Checkbox` | `ezboolean` | `boolean` | + +## PHP API Field Type  + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Default value | Description| +|----------|-----------|---------------|------------| +| `$bool` | `boolean` | `false` | This property is used for the checkbox status, represented by a boolean value. | + +``` php +//Value object content examples +use eZ\Publish\Core\FieldType\Checkbox\Type; +  +// Instantiates a checkbox value with a default state (false) +$checkboxValue = new Checkbox\Value(); +  +// Checked +$value->bool = true; + +// Unchecked +$value->bool = false; +``` + +##### Constructor + +The `Checkbox\Value` constructor accepts a boolean value: + +``` php +// Constructor example +use eZ\Publish\Core\FieldType\Checkbox\Type; +  +// Instantiates a checkbox value with a checked state +$checkboxValue = new Checkbox\Value( true ); +``` + +##### String representation + +As this Field Type is not a string but a boolean, it will return "1" (true) or "0" (false) in cases where it is cast to string, and it is never considered empty. diff --git a/docs/api/field_types_reference/contentqueryfield.md b/docs/api/field_types_reference/contentqueryfield.md new file mode 100644 index 0000000000..161b5fe870 --- /dev/null +++ b/docs/api/field_types_reference/contentqueryfield.md @@ -0,0 +1,12 @@ +# Content query Field Type + +This Field Type maps an executable Repository query to a Field. + +| Name | Internal name | Expected input | +|-----------|---------------|----------------| +| `Content query` | `ezcontentquery` | `string` | + +The Content query Field Type is available via the Query Field Type Bundle +provided by the [ezplatform-query-fieldtype](https://github.com/ezsystems/ezplatform-query-fieldtype) package. + +For information about the Field Type's usage, see [Content queries](../../guide/content_rendering/queries_and_controllers/content_queries.md#content-query-field). diff --git a/docs/api/field_types_reference/countryfield.md b/docs/api/field_types_reference/countryfield.md new file mode 100644 index 0000000000..7b2854c960 --- /dev/null +++ b/docs/api/field_types_reference/countryfield.md @@ -0,0 +1,98 @@ +# Country Field Type + +This Field Type represents one or multiple countries. + +| Name | Internal name | Expected input | +|-----------|---------------|----------------| +| `Country` | `ezcountry` | `array` | + +## PHP API Field Type  + +### Input expectations + +Example array: + +``` php +[ + "JP" => [ + "Name" => "Japan", + "Alpha2" => "JP", + "Alpha3" => "JPN", + "IDC" => 81 + ] +]; +``` + +When you set an array directly on a Content Field you don't need to provide all this information, the Field Type will assume it is a hash and in this case will accept a simplified structure described below under [Hash format](#hash-format). + +### Validation + +This Field Type validates whether multiple countries are allowed by the Field definition, and whether the [Alpha2](https://www.iso.org/iso-3166-country-codes.html) is valid according to the countries configured in [[= product_name =]]. + +### Settings + +The Field definition of this Field Type can be configured with one option: + +| Name | Type | Default value | Description| +|--------------|-----------|---------------|------------| +| `isMultiple` | `boolean` | `false` | This setting allows (if true) or prohibits (if false) the selection of multiple countries. | + +``` php +// Country FieldType example settings +$settings = [ + "isMultiple" => true +]; +``` + +### Hash format + +The format used for serialization is simpler than the full format. It is also available when setting value on the content Field, by setting the value to an array instead of the Value object. Example of that shown below: + +``` php +// Value object content example +$content->fields["countries"] = [ "JP", "NO" ]; +``` + +The format used by the toHash method is the Alpha2 value, however the input is capable of accepting either Name, Alpha2 or Alpha3 value as shown below in the Value object section. + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|--------------|-----------|------------| +| `$countries` | `array[]` | This property is used for the country selection provided as input, as its attributes. | + +``` php +// Value object content example +$value->countries = [ + "JP" => [ + "Name" => "Japan", + "Alpha2" => "JP", + "Alpha3" => "JPN", + "IDC" => 81 + ] +]; +``` + +##### Constructor + +The `Country\Value` constructor will initialize a new Value object with the value provided. It expects an array as input. + +``` php +// Constructor example + +// Instantiates a Country Value object +$countryValue = new Country\Value( + [ + "JP" => [ + "Name" => "Japan", + "Alpha2" => "JP", + "Alpha3" => "JPN", + "IDC" => 81 + ] + ] +); +``` diff --git a/docs/api/field_types_reference/dateandtimefield.md b/docs/api/field_types_reference/dateandtimefield.md new file mode 100644 index 0000000000..fbeebf99e4 --- /dev/null +++ b/docs/api/field_types_reference/dateandtimefield.md @@ -0,0 +1,115 @@ +# DateAndTime Field Type + +This Field Type represents a full date and time information. + +| Name | Internal name | Expected input type | +|---------------|---------------|---------------------| +| `DateAndTime` | `ezdatetime` | mixed | + +## PHP API Field Type  + +### Input expectations + +If input value is of type `string` or `integer`, it will be passed directly to the [PHP's built-in `\DateTime` class constructor](http://www.php.net/manual/en/datetime.construct.php), therefore the same input format expectations apply. + +It is also possible to directly pass an instance of `\DateTime`. + +|Type|Example| +|------|------| +|`integer`|`"2017-08-28 12:20 Europe/Berlin"`| +|`integer`|`1346149200`| +|`\DateTime`|`new \DateTime()`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|-------------|------------| +| `$value` | `\DateTime` | The date and time value as an instance of `\DateTime`. | + +##### Constructor + +The constructor for this value object will initialize a new Value object with the value provided. It accepts an instance of PHP's built-in `\DateTime` class. + +##### String representation + +String representation of the date value will generate the date string in the format `D Y-d-m H:i:s` as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). + +|Character|Description|Example| +|---------|----------|--------| +|D|Three letter representation of a day, range Mon to Sun|Wed| +|Y|Four digit representation of a year|2016| +|d|Two digit representation of a day, range 01 to 31|22| +|m|Two digit representation of a month, range 01 to 12|05| +|H|Two digit representation of an hour, 24-hour format, range 00 to 23 |12| +|i|Two digit representation of minutes, range 00 to 59|19| +|s|Two digit representation of seconds, range 00 to 59|18| + +Example: `Wed 2016-22-05 12:19:18` + +### Hash format + +Hash value of this Field Type is an array with two keys: + +|Key|Type|Description|Example| +|------|------|------|------| +|`timestamp`|`integer`|Time information in [Unix format timestamp](http://en.wikipedia.org/wiki/Unix_time).|`1400856992`| +|`rfc850`|`string`|Time information as a string in [RFC 850 date format](http://tools.ietf.org/html/rfc850). As input, this will have precedence over the timestamp value.|`"Friday, 23-May-14 14:56:14 GMT+0000"`| + +``` php +$hash = [ + "timestamp" => 1400856992, + "rfc850" => "Friday, 23-May-14 14:56:14 GMT+0000" +]; +``` + +### Validation + +This Field Type does not perform any special validation of the input value. + +### Settings + +The Field definition of this Field Type can be configured with several options: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`useSeconds`|`boolean`|`false`|Used to control displaying of seconds in the output.| +|`defaultType`|`mixed`|`Type::DEFAULT_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| +|`dateInterval`|`null|\DateInterval`|`null`|This setting complements `defaultType` setting and can be used only when the latter is set to `Type::DEFAULT_CURRENT_DATE_ADJUSTED`. In that case the default input value when using administration interface will be adjusted by the given `\DateInterval`.| + +Following `defaultType` default value options are available as constants in the `eZ\Publish\Core\FieldType\DateAndTime\Type` class: + +|Constant|Description| +|------|------| +|`DEFAULT_EMPTY`|Default value will be empty.| +|`DEFAULT_CURRENT_DATE`|Default value will use current date.| +|`DEFAULT_CURRENT_DATE_ADJUSTED`|Default value will use current date, adjusted by the interval defined in `dateInterval` setting.| + +``` php +// DateAndTime FieldType example settings + +use eZ\Publish\Core\FieldType\DateAndTime\Type; + +$settings = [ + "useSeconds" => false, + "defaultType" => Type::DEFAULT_EMPTY, + "dateInterval" => null +]; +``` + +## Template rendering + +The template called by the [`ez_render_field()` Twig function](../../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field) while rendering a Date Field has access to the following parameters: + +| Parameter | Type | Default | Description| +|-----------|----------|---------|------------| +| `locale` | `string` |   n/a | Internal parameter set by the system based on current request locale or if not set calculated based on the language of the Field. | + +Example: + +``` php +{{ ez_render_field(content, 'datetime') }} +``` diff --git a/docs/api/field_types_reference/datefield.md b/docs/api/field_types_reference/datefield.md new file mode 100644 index 0000000000..4f588e49f2 --- /dev/null +++ b/docs/api/field_types_reference/datefield.md @@ -0,0 +1,112 @@ +# Date Field Type + +This Field Type represents a date without time information. + +| Name | Internal name | Expected input type | +|--------|---------------|---------------------| +| `Date` | `ezdate` | mixed | + +#### PHP API Field Type  + +### Input expectations + +If input value is in `string` or `integer` format, it will be passed directly to [PHP's built-in `\DateTime` class constructor](http://www.php.net/manual/en/datetime.construct.php), therefore the same input format expectations apply. + +It is also possible to directly pass an instance of `\DateTime`. + +|Type|Example| +|------|------| +|`string`|`"2012-08-28 12:20 Europe/Berlin"`| +|`integer`|`1346149200`| +|`\DateTime`|`new \DateTime()`| + +Time information is **not stored**. + +Before storing, the provided input value will be set to the beginning of the day in the given or the environment timezone. + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|-------------|------------| +| `$date` | `\DateTime` | This property will be used for the text content. | + +##### String representation + +String representation of the date value will generate the date string in the format "l d F Y" as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). + +|Character|Description|Example| +|---------|----------|--------| +|l|Textual representation of a day of the week, range Monday to Sunday|Wednesday| +|d|Two digit representation of a day, range 01 to 31|22| +|F|Textual representation of a month, range January to December|May| +|Y|Four digit representation of a year|2016| + +Example: `Wednesday 22 May 2016` + +##### Constructor + +The constructor for this value object will initialize a new Value object with the value provided. It accepts an instance of [PHP's built-in `\DateTime` class](http://www.php.net/manual/en/datetime.construct.php). + +### Hash format + +Hash value of this Field Type is an array with two keys: + +|Key|Type|Description|Example| +|------|------|------|------| +|`timestamp`|`integer`|Time information in [unix format timestamp](http://en.wikipedia.org/wiki/Unix_time).|`1400856992`| +|`rfc850`|`string`|Time information as a string in [RFC 850 date format](http://tools.ietf.org/html/rfc850). As input, this will have higher precedence over the timestamp value.|`"Friday, 23-May-14 14:56:14 GMT+0000"`| + +``` php +// Example of the hash value in PHP +$hash = [ + "timestamp" => 1400856992, + "rfc850" => "Friday, 23-May-14 14:56:14 GMT+0000" +]; +``` + +### Validation + +This Field Type does not perform any special validation of the input value. + +### Settings + +The Field definition of this Field Type can be configured with a single option: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`defaultType`|`mixed`|`Type::DEFAULT_EMPTY`|One of the `DEFAULT_*` constants, used by the administration interface for setting the default Field value. See below for more details.| + +Following `defaultType` default value options are available as constants in the `eZ\Publish\Core\FieldType\Date\Type` class: + +|Constant|Description| +|------|------| +|`DEFAULT_EMPTY`|Default value will be empty.| +|`DEFAULT_CURRENT_DATE`|Default value will use current date.| + +``` php +// Date Field Type example settings + +use eZ\Publish\Core\FieldType\Date\Type; + +$settings = [ + "defaultType" => Type::DEFAULT_EMPTY +]; +``` + +## Template rendering + +The template called by [the `ez_render_field()` Twig function](../../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field) while rendering a Date Field has access to the following parameters: + +| Parameter | Type |Description| +|-----------|----------|------------| +| `locale` | `string` |Internal parameter set by the system based on current request locale or if not set, calculated based on the language of the Field. | + +Example: + +``` html+twig +{{ ez_render_field(content, 'date') }} +``` diff --git a/docs/api/field_types_reference/emailaddressfield.md b/docs/api/field_types_reference/emailaddressfield.md new file mode 100644 index 0000000000..772a623812 --- /dev/null +++ b/docs/api/field_types_reference/emailaddressfield.md @@ -0,0 +1,65 @@ +# EmailAddress Field Type + +The EmailAddress Field Type represents an email address, in the form of a string. + +| Name | Internal name | Expected input type | +|----------------|---------------|---------------------| +| `EmailAddress` | `ezemail` | `string` | + +## PHP API Field Type  + +### Value object + +##### Properties + +The `Value` class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|----------|------------| +| `$email` | `string` | This property will be used for the input string provided as email address. | + +``` php +// Value object content example + +use eZ\Publish\Core\FieldType\EmailAddress\Type; + +// Instantiates an EmailAddress Value object with default value (empty string) +$emailaddressValue = new Type\Value(); + +// Email definition +$emailaddressValue->email = "someuser@example.com"; +``` + +##### Constructor + +The `EmailAddress\Value` constructor will initialize a new Value object with the value provided. It accepts a string as input. + +``` php +// Constructor example + +use eZ\Publish\Core\FieldType\EmailAddress\Type; +  +// Instantiates an EmailAddress Value object +$emailaddressValue = new Type\Value( "someuser@example.com" ); +``` + +##### String representation + +String representation of the Field Type's Value object is the email address contained in it. + +Example: `someuser@example.com` + +### Hash format + +Hash value for this Field Type's Value is simply the email address as a string. + +Example: `someuser@example.com` + +### Validation + +This Field Type uses the `EmailAddressValidator` validator as a resource which will test the string supplied as input against a pattern, to make sure that a valid email address has been provided. +If the validations fail, a `ValidationError` is thrown, specifying the error message. + +### Settings + +This Field Type does not support settings. diff --git a/docs/api/field_types_reference/floatfield.md b/docs/api/field_types_reference/floatfield.md new file mode 100644 index 0000000000..f2f3914875 --- /dev/null +++ b/docs/api/field_types_reference/floatfield.md @@ -0,0 +1,82 @@ +# Float Field Type + +This Field Type stores numeric values which are provided as floats. + +| Name | Internal name | Expected input | +|---------|---------------|----------------| +| `Float` | `ezfloat` | `float` | + +## PHP API Field Type  + +### Input expectations + +The Field Type expects a number as input. Both decimal and integer numbers are accepted. + +|Type|Example| +|------|------| +|`float`|`194079.572`| +|`int`|`144`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|---------|------------| +| `$value` | `float` | This property will be used to store the value provided as a float. | + +``` php +// Value object content example + +use eZ\Publish\Core\FieldType\Float\Type; + +// Instantiates a Float Value object +$floatValue = new Type\Value(); + +$float->value = 284.773 +``` + +##### Constructor + +The `Float\Value` constructor will initialize a new Value object with the value provided. It expects a numeric value with or without decimals. + +``` php +// Constructor example + +use eZ\Publish\Core\FieldType\Float\Type; + +// Instantiates a Float Value object +$floatValue = new Type\Value( 284.773 ); +``` + +### Validation + +This Field Type supports `FloatValueValidator`, defining maximum and minimum float value: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`minFloatValue`|`float`|`null|This setting defines the minimum value this Field Type will allow as input.| +|`maxFloatValue`|`float`|`null|This setting defines the maximum value this Field Type will allow as input.| + +``` php +// Validator configuration example in PHP + +use eZ\Publish\Core\FieldType\Float\Type; + +$contentTypeService = $repository->getContentTypeService(); +$floatFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "float", "ezfloat" ); + +// Accept only numbers between 0.1 and 203.99 +$floatFieldCreateStruct->validatorConfiguration = [ + "FileSizeValidator" => [ + "minFloatValue" => 0.1, + "maxFloatValue" => 203.99 + ] +]; +``` + +### Settings + +This Field Type does not support settings. diff --git a/docs/api/field_types_reference/formfield.md b/docs/api/field_types_reference/formfield.md new file mode 100644 index 0000000000..0b3efcd314 --- /dev/null +++ b/docs/api/field_types_reference/formfield.md @@ -0,0 +1,15 @@ +--- +edition: experience +--- + +# Form Field Type + +The Form Field Type stores a Form consisting of one or more form fields. + +| Name | Internal name | +|--------|---------------| +| `Form` | `ezform` | + + + +See [Forms](../../guide/form_builder/forms.md) for more information about working with Forms. diff --git a/docs/api/field_types_reference/imageassetfield.md b/docs/api/field_types_reference/imageassetfield.md new file mode 100644 index 0000000000..6d0ccd8953 --- /dev/null +++ b/docs/api/field_types_reference/imageassetfield.md @@ -0,0 +1,93 @@ +# ImageAsset Field Type + +ImageAsset Field Type enables storing images in independent Content items of a generic Image Content Type, in the media library. It makes them reusable across system. + +### Input expectations + +Example array: + +|Type|Description|Example| +|------|------|------| +|`eZ\Publish\Core\FieldType\ImageAsset\Value`|ImageAsset Field Type value object.|See below.| +|`eZ\Publish\API\Repository\Values\Content\ContentInfo`|ContentInfo instance of the Asset Content item. |n/a| +|`string`| ID of the Asset Content item. |`"150"`| +|`integer`| ID of the Asset Content item. | `150`| + +### Value object + +##### Properties + +Value object of `ezimageasset` contains the following properties: + +| Property | Type | Description| +|----------|-------|------------| +| `destinationContentId` | `int` | Related content ID. | +| `alternativeText` | `string` | The alternative image text (for example "Picture of an apple."). | + +``` php +// Value object content example + +$imageAssetValue->destinationContentId = $contentInfo->id; +$imageAssetValue->alternativeText = "Picture of an apple."; +``` + +##### Constructor + +The `ImageAsset\Value` constructor will initialize a new value object with the value provided. It expects an ID of a Content item representing asset and the alternative text. + +``` php +// Constructor example + +// Instantiates a ImageAsset Value object +$imageAssetValue = new ImageAsset\Value($contentInfo->id, "Picture of an apple."); +``` + +### Validation + +This Field Type validates if: + +- `destinationContentId` points to a Content item which has correct Content Type + +### Configuration + +ImageAsset Field Type allows configuring the following options: + +|Name|Description|Default value| +|----|-----------|-------------| +|`content_type_identifier`|Content Type used to store assets.|`image`| +|`content_field_identifier`|Field identifier used for asset data.|`image`| +|`name_field_identifier`|Field identifier used for asset name.|`name`| +|`parent_location_id`|Location where the assets are created.|`51`| + +Example configuration: + +``` yaml +ezplatform: + system: + default: + fieldtypes: + ezimageasset: + content_type_identifier: photo + content_field_identifier: image + name_field_identifier: title + parent_location_id: 106 +``` + +## Customizing ImageAsset Field Type rendering + +Internally the Image Asset Type is rendered via subrequest (similar to other relation types). Rendering customization is possible by configuring view type `asset_image`: + +```php +ezplatform: + system: + default: + content_view: + asset_image: + default: + template: ::custom_image_asset_template.html.twig + match: [] +``` + +## Generating image variation from the Image Asset + +Thanks to the `eZ\Bundle\EzPublishCoreBundle\Imagine\ImageAsset\AliasGenerator` decorator you can work with `\eZ\Publish\SPI\Variation\VariationHandler` in the same way as with [Image Field Type](imagefield.md). diff --git a/docs/api/field_types_reference/imagefield.md b/docs/api/field_types_reference/imagefield.md new file mode 100644 index 0000000000..7d022b08fb --- /dev/null +++ b/docs/api/field_types_reference/imagefield.md @@ -0,0 +1,288 @@ +# Image Field Type + +The Image Field Type allows you to store an image file. + +| Name | Internal name | +|---------|---------------| +| `Image` | `ezimage` | + +A **variation service** handles the conversion of the original image into different formats and sizes through a set of preconfigured named variations: large, small, medium, black and white thumbnail, etc. + +## PHP API Field Type + +### Value object + +The `value` property of an Image Field returns an `\eZ\Publish\Core\FieldType\Image\Value` object with the following properties: + +##### Properties + +|Property|Type|Example|Description| +|------|------|------|------| +|`id`|string|`0/8/4/1/1480-1-eng-GB/image.png`|The image's unique identifier. Usually the path, or a part of the path. To get the full path, use the `uri` property.| +|`alternativeText`|string|`Picture of an apple.`|The alternative text, as entered in the Field's properties. This property is optional. It is recommended that you require the alternative text for an image when you add the Image Field to a Content Type, by selecting the "Alternative text is required" checkbox.| +|`fileName`|string|`image.png`|The original image's filename, without the path.| +|`fileSize`|int|`37931`|The original image's size, in bytes.| +|`uri`|string|`var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/image.png`|The original image's URI.| +|`imageId`|string|`240-1480`|A special image ID, used by REST.| +|`inputUri`|string|`var/storage/images/test/199-2-eng-GB/image.png`|Input image file URI.| +|`width`*|int|`null`|Original image width in pixels. For more details see Caution note below.| +|`height`*|int|`null`|Original image height in pixels. For more details see Caution note below.| + +!!! caution + + Properties marked with an asterisk are currently unsupported. They are available but their value is always `null`. + + Follow [EZP-27987](https://jira.ez.no/browse/EZP-27987) for future progress on this issue. + +### Settings + +This Field Type does not support settings. + +### Image Variations + +Using the variation Service, variations of the original image can be obtained. They are `\eZ\Publish\SPI\Variation\Values\ImageVariation` objects with the following properties: + +| Property | Type | Example | Description| +|----------------|----------|----------|------------| +| `width`* | int | `null` | The variation's width in pixels. For more details see Caution note below.| +| `height`* | int | `null` | The variation's height in pixels. For more details see Caution note below.| +| `name` | string | `medium` | The variation's identifier, name of the image alias.| +| `info` | mixed |n/a| Extra information about the image, depending on the image type, such as EXIF data. If there is no information, the `info` value will be `null`.| +| `fileSize` | int |`31010` |Size (in byte) of current variation.| +| `mimeType` | string |`image/png`|The MIME type.| +| `fileName` | string |`my_image.png`|The name of the file.| +| `dirPath` | string |`var/storage/images/test/199-2-eng-GB`|The path to the file.| +| `uri` | string |`var/storage/images/test/199-2-eng-GB/apple.png`| The variation's URI. Complete path with a name of image file.| +| `lastModified` | DateTime |``"2017-08-282 12:20 Europe/Berlin"``| When the variation was last modified.| + +!!! caution + + Properties marked with an asterisk are currently unsupported. They are available but their value is always `null`. + + Follow [EZP-27987](https://jira.ez.no/browse/EZP-27987) for future progress on this issue. + +### Field Definition options + +The Image Field Type supports one `FieldDefinition` option: the maximum size for the file. + +!!!note + + Maximum size is rounded to 1 MB (legacy storage limitation). + +!!! note + + As the default value for maximum size is set to 10MB, we recommend setting the `upload_max_filesize` key in the `php.ini` configuration file to a value equal to or higher than that. It will prevent validation errors while editing Content Types. + +## Using an Image Field + +To read more about handling images and image variations, see the [Images documentation](../../guide/images/images.md). + +### Template Rendering + +When displayed using `ez_render_field`, an Image Field will output this type of HTML: + +``` html+twig +Alternative text +``` + +The template called by the [`ez_render_field()` Twig function](../../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field) while rendering a Image Field accepts the following parameters: + +| Parameter | Type | Default | Description | +|-----------|----------|----------------|-------------| +| `alias` | `string` | `"original"` | The image variation name, must be defined in your SiteAccess's `image_variations` settings. Defaults to "original", the originally uploaded image.| +| `width` | `int` |   n/a | Optionally to specify a different width set on the image HTML tag then then one from image alias. | +| `height` | `int` |   n/a | Optionally to specify a different height set on the image HTML tag then then one from image alias. | +| `class` | `string` |   n/a | Optionally to specify a specific html class for use in custom JavaScript and/or CSS. | + +Example:  + +``` html+twig +{{ ez_render_field( content, 'image', { 'parameters':{ 'alias': 'imagelarge', 'width': 400, 'height': 400 } } ) }} +``` + +The raw Field can also be used if needed. Image variations for the Field's content can be obtained using the `ez_image_alias` Twig helper: + +``` html+twig +{% set imageAlias = ez_image_alias( field, versionInfo, 'medium' ) %} +``` + +The variation's properties can be used to generate the required output: + +``` html+twig +{{ field.value.alternativeText }} +``` + +### With the REST API + +Image Fields within REST are exposed by the `application/vnd.ez.api.Content` media-type. An Image Field will look like this: + +``` xml + + 1480 + image + eng-GB + + /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding.png + + kidding.png + 37931 + 240-1480 + /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding.png + + + /api/ezp/v2/content/binary/images/240-1480/variations/articleimage + + + /api/ezp/v2/content/binary/images/240-1480/variations/articlethumbnail + + + + +``` + +Children of the `fieldValue` node will list the general properties of the Field's original image (`fileSize`, `fileName`, `inputUri`, etc.), as well as variations. For each variation, a URI is provided. Requested through REST, this resource will generate the variation if it doesn't exist yet, and list the variation details: + +``` xml + + /var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB/kidding_tiny.png + image/png + 30 + 30 + 1361 + +``` + +### From PHP code + +#### Getting an image variation + +The variation service, `ezpublish.fieldType.ezimage.variation_service`, can be used to generate/get variations for a Field. It expects a VersionInfo, the Image Field, and the variation name as a string (`large`, `medium`, etc.): + +``` php +$variation = $imageVariationHandler->getVariation( + $imageField, $versionInfo, 'large' +); + +echo $variation->uri; +``` + +## Manipulating image content + +### From PHP + +As for any Field Type, there are several ways to input content to a Field. For an Image, the quickest is to call `setField()` on the ContentStruct: + +``` php +$createStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentType( 'image' ), + 'eng-GB' +); + +$createStruct->setField( 'image', '/tmp/image.png' ); +``` + +In order to customize the Image's alternative texts, you must first get an `Image\Value` object, and set this property. For that, you can use the `Image\Value::fromString()` method that accepts the path to a local file: + +``` php +$createStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentType( 'image' ), + 'eng-GB' +); + +$imageField = \eZ\Publish\Core\FieldType\Image\Value::fromString( '/tmp/image.png' ); +$imageField->alternativeText = 'My alternative text'; +$createStruct->setField( 'image', $imageField ); +``` + +You can also provide a hash of `Image\Value` properties, either to `setField()`, or to the constructor: + +``` php +$imageValue = new \eZ\Publish\Core\FieldType\Image\Value( + [ + 'id' => '/tmp/image.png', + 'fileSize' => 37931, + 'fileName' => 'image.png', + 'alternativeText' => 'My alternative text' + ] +); + +$createStruct->setField( 'image', $imageValue ); +``` + +### From REST + +The REST API expects Field values to be provided in a hash-like structure. Those keys are identical to those expected by the `Image\Value` constructor: `fileName`, `alternativeText`. In addition, image data can be provided using the `data` property, with the image's content encoded as base64. + +#### Creating an Image Field + +``` + + + + + + + 247 + image + eng-GB + + rest-rocks.jpg + HTTP + + + + + +``` + +### Updating an Image Field + +Updating an Image Field requires that you re-send existing data. This can be done by re-using the Field obtained via REST, **removing the variations key**, and updating `alternativeText`, `fileName` or `data`. If you do not want to change the image itself, do not provide the `data` key. + +``` xml + + + + + 247 + image + eng-GB + + media/images/507-1-eng-GB/Existing-image.png + Updated alternative text + Updated-filename.png + + + + +``` + +## Naming + +Each storage engine determines how image files are named. + +### Legacy Storage Engine naming + +Images are stored within the following directory structure: + +`///////--/` + +With the following values: + +- `VarDir` = `var` (default) +- `StorageDir` = `storage` (default) +- `ImagesStorageDir` = `images` (default) +- `FieldId` = `1480` +- `VersionNumber` = `1` +- `LanguageCode` = `eng-GB` + +Images will be stored in `web/var/ezdemo_site/storage/images/0/8/4/1/1480-1-eng-GB`. + +Using the Field ID digits in reverse order as the folder structure maximizes sharding of files through multiple folders on the filesystem. + +Within this folder, images will be named like the uploaded file, suffixed with an underscore and the variation name: + +- `MyImage.png` +- `MyImage_large.png` +- `MyImage_rss.png` diff --git a/docs/api/field_types_reference/integerfield.md b/docs/api/field_types_reference/integerfield.md new file mode 100644 index 0000000000..45678b93b0 --- /dev/null +++ b/docs/api/field_types_reference/integerfield.md @@ -0,0 +1,75 @@ +# Integer Field Type + +This Field Type represents an integer value. + +| Name | Internal name | Expected input | +|-----------|---------------|----------------| +| `Integer` | `ezinteger` | `integer` | + +## PHP API Field Type  + +### Input expectations + +|Type|Example| +|-------|------| +|`integer`|`2397`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|-------|------------| +| `$value` | `int` | This property is used to store the value provided as an integer. | + +``` php +// Value object content example +$integer->value = 8 +``` + +##### Constructor + +The `Integer\Value` constructor will initialize a new Value object with the value provided. It expects a numeric, integer value. + +``` php +// Constructor example +use eZ\Publish\Core\FieldType\Integer; +  +// Instantiates a Integer Value object +$integerValue = new Integer\Value( 8 ); +``` + +### Hash format + +Hash value of this Field Type is an integer value as a string. + +Example: `"8"` + +### String representation + +String representation of the Field Type's value will return the integer value as a string. + +Example: `"8"` + +### Validation + +This Field Type supports `IntegerValueValidator`, defining maximum and minimum float value: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`minIntegerValue`|`int`|`0`|This setting defines the minimum value this Field Type will allow as input.| +|`maxIntegerValue`|`int`|`null`|This setting defines the maximum value this Field Type will allow as input.| + +``` php +// Example of validator configuration in PHP +$validatorConfiguration = [ + "minIntegerValue" => 1, + "maxIntegerValue" => 24 +]; +``` + +### Settings + +This Field Type does not support settings. diff --git a/docs/api/field_types_reference/isbnfield.md b/docs/api/field_types_reference/isbnfield.md new file mode 100644 index 0000000000..2da7557e72 --- /dev/null +++ b/docs/api/field_types_reference/isbnfield.md @@ -0,0 +1,38 @@ +# ISBN Field Type + +This Field Type represents an ISBN string either an ISBN-10 or ISBN-13 format. + +| Name | Internal name | Expected input type | +|--------|---------------|---------------------| +| `ISBN` | `ezisbn` | `string` | + +## PHP API Field Type  + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|----------|------------| +| `$isbn` | `string` | This property will be used for the ISBN string. | + +##### String representation + +An ISBN's string representation is the `$isbn` property's value, as a string. + +##### Constructor + +The constructor for this value object will initialize a new Value object with the value provided. It accepts a string as argument and will set it to the `isbn` attribute. + +### Validation + +The input passed into this Field Type is subject of ISBN validation depending on the Field settings in its FieldDefinition stored in the Content Type. An example of this Field setting is shown below and will control if input is validated as ISBN-13 or ISBN-10: + +``` php +Array +( + [isISBN13] => true +) +``` diff --git a/docs/api/field_types_reference/keywordfield.md b/docs/api/field_types_reference/keywordfield.md new file mode 100644 index 0000000000..aba29b55ff --- /dev/null +++ b/docs/api/field_types_reference/keywordfield.md @@ -0,0 +1,56 @@ +# Keyword Field Type + +This Field Type stores one or several comma-separated keywords as a string or array of strings. + +| Name | Internal name | Expected input| +|-----------|---------------|---------------| +| `Keyword` | `ezkeyword` | `string[]|string` | + +## PHP API Field Type  + +### Input expectations + +|Type|Example| +|------|------| +|`string`|`"documentation"`| +|`string`|`"php, Ibexa Platform, html5"`| +|`string[]`|`[ "Ibexa", "Enterprise", "User Experience Management" ]`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|------------|------------| +| `$value` | `string[]` | Holds an array of keywords as strings.| + +``` php +// Value object content example +use eZ\Publish\Core\FieldType\Keyword\Value; +  +// Instantiates a Value object +$keywordValue = new Value(); +  +// Sets an array of keywords as a value +$keyword->value = [ "php", "css3", "html5", "Ibexa Platform" ]; +``` + +#### Constructor + +The `Keyword\Value` constructor will initialize a new Value object with the value provided. + +It expects a list of keywords, either comma-separated in a string or as an array of strings. + +``` php +// Constructor example +use eZ\Publish\Core\FieldType\Keyword\Value; +  +// Instantiates a Value object with an array of keywords +$keywordValue = new Value( [ "php5", "css3", "html5" ] ); +  +// Instantiates a Value object with a list of keywords in a string +// This is equivalent to the example above +$keywordValue = new Value( "php5,css3,html5" ); +``` diff --git a/docs/api/field_types_reference/maplocationfield.md b/docs/api/field_types_reference/maplocationfield.md new file mode 100644 index 0000000000..592fead3f9 --- /dev/null +++ b/docs/api/field_types_reference/maplocationfield.md @@ -0,0 +1,74 @@ +# MapLocation Field Type + +This Field Type represents a geographical location. + +As input it expects three values: + +- two float values latitude and longitude, +- a string value, corresponding to the name or address of the location. + +| Name | Internal name | Expected input | +|---------------|------------------|----------------| +| `MapLocation` | `ezgmaplocation` | `mixed` | + +## PHP API Field Type  + +### Input expectations + +|Type|Example| +|------|------| +|`array`|`[ 'latitude' => 59.928732, 'longitude' => 10.777888, 'address' => "Ibexa Nordics" ]`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +|Property|Type|Description| +|------|------|------| +|`$latitude`|`float`|This property stores the latitude value of the map location reference.| +|`$longitude`|`float`|This property stores the longitude value of the map location reference.| +|`$address`|`string`|This property stores the address of map location.| + +##### Constructor + +The `MapLocation\Value` constructor will initialize a new Value object with values provided as hash. Accepted keys are `latitude` (`float`), `longitude` (`float`), `address` (`string`). + +``` php +// Constructor example + +// Instantiates a MapLocation Value object +$MapLocationValue = new MapLocation\Value( + [ + 'latitude' => 59.928732, + 'longitude' => 10.777888, + 'address' => "Ibexa Nordics" + ] + ); +``` + +## Template rendering + +The template called by [the `ez_render_field()` Twig function](../../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field) while rendering a Map Location Field accepts the following parameters: + +|Parameter|Type|Default|Description| +|------|------|------|------| +|`draggable`|`boolean`|`true`|Whether to enable a draggable map.| +|`height`|`string|false`|`"200px"`|The height of the rendered map with its unit (for example "200px" or "20em"), set to false to not set any height style inline.| +|`scrollWheel`|`boolean`|`true`| Allows you to disable scroll wheel starting to zoom when mouse comes over the map as user scrolls down a page.| +|`showInfo`|`booolean`|`true`|Whether to show a latitude, longitude and the address outside of the map.| +|`showMap`|`boolean`|`true`|Whether to show the OpenStreetMap.| +|`width`|`string|false`|`"500px"`|The width of the rendered map with its unit (for example "500px" or "50em"), set to false to not set any width style inline.| +|`zoom`|`integer`|`13`|The initial zoom level on the map.| + +Example: + +``` html+twig +{{ ez_render_field(content, 'location', {'parameters': {'width': '100%', 'height': '330px', 'showMap': true, 'showInfo': false}}) }} +``` + +!!! note + + The option to automatically get user coordinates through the "Locate me" button + is only available when the Back Office is served through the `https://` protocol. diff --git a/docs/api/field_types_reference/matrixfield.md b/docs/api/field_types_reference/matrixfield.md new file mode 100644 index 0000000000..a6c90d7246 --- /dev/null +++ b/docs/api/field_types_reference/matrixfield.md @@ -0,0 +1,209 @@ +# Matrix Field Type + +This Field represents and handles a table of rows and columns of data. + +| Name | Internal name | Expected input | +|----------|---------------|----------------| +| `Matrix` | `ezmatrix` | `array` | + +The Matrix Field Type is available via the Matrix Bundle +provided by the [ezplatform-matrix-fieldtype](https://github.com/ezsystems/ezplatform-matrix-fieldtype) package. + +## PHP API Field Type + +### Input expectations + +|Type|Description|Example| +|------|------|------| +|`array`|array of `EzSystems\EzPlatformMatrixFieldtype\FieldType\Value\Row` objects which contain column data|see below| + +Example of input: + +```php +new FieldType\Value([ + new FieldType\Value\Row(['col1' => 'Row 1, Col 1', 'col2' => 'Row 1, Col 2']), + new FieldType\Value\Row(['col1' => 'Row 2, Col 1', 'col2' => 'Row 2, Col 2']), + new FieldType\Value\Row(['col1' => 'Row 3, Col 1', 'col2' => 'Row 3, Col 2']), +]); +``` + +### Value Object + +`EzSystems\EzPlatformMatrixFieldtype\FieldType\Value` offers the following properties: + +|Property|Type|Description| +|------|------|------| +|`rows`|`RowsCollection`|Array of `Row` objects containing an array of cells (`Row::getCells()` returns array `['col1' => 'Value 1', /* ... */]`).| + +### Validation + +The minimum number of rows is set on Content Type level for each Field. + +Validation checks for empty rows. A row is considered empty if it contains only empty cells (or cells containing only spaces). Empty rows are removed. + +If, after removing empty rows, the number of rows does not fulfill the configured `Minimum number of rows`, the Field will not validate. + +For example, the following input will not validate if `Minimum number of rows` is set to 3, because the second row is empty: + +```php +new FieldType\Value([ + new FieldType\Value\Row(['col1' => 'Row 1, Col 1', 'col2' => 'Row 1, Col 2']), + new FieldType\Value\Row(['col1' => '', 'col2' => '']), + new FieldType\Value\Row(['col1' => 'Row 3, Col 1', 'col2' => 'Row 3, Col 2']), +]); +``` + +## GraphQL Field Type operations + +To get a Field of the Matrix Field Type with GraphQL, you will need to specify a Content ID, a Content Type, and a Field Type. + +The types that are returned are named after the Type and the Field: + +- `{TypeIdentifier}{FieldIdentifier}Row` + +The example below shows a GraphQL query for a Recipe Content item (belonging to a Content Type with a Matrix Field added), that has two Fields: + +- `name`: `ezstring` +- `ingredients`: `ezmatrix` with two columns: `ingredient` and `quantity` + +``` +{ + content { + recipe(id: 123) { + name + ingredients { + ingredient + quantity + } + } + } +} +``` + +The Type returned for the Matrix Field exposes columns defined in the Field definition: + +``` +{ + "data": { + "content": { + "recipe": { + "name": "Cake ingredients", + "ingredients": [ + { + "ingredient": "Butter", + "quantity": "200 grams" + }, + { + "ingredient": "Sugar", + "quantity": "100 grams" + } + ] + } + } + } +} +``` + +### Query for the Field Type and Field definition's details + +With this query you can inspect details of specific Content Type. In case of a Matrix Field, you can ask for the list of columns, their names and identifiers. + +``` +{ + content { + _types { + recipe { + ingredients { + settings { + minimumRows + columns { + name + identifier + } + } + } + } + } + } +} +``` + +The response will list the exposed Field Type settings: + +- minimumRows +- columns + - name + - identifier + +Example response: + +``` +{ + "data": { + "content": { + "_types": { + "recipe": { + "ingredients": { + "settings": { + "minimumRows": 1, + "columns": [ + { + "name": "ingredient", + "identifier": "ingredient" + }, + { + "name": "quantity", + "identifier": "quantity" + } + ] + } + } + } + } + } + } +} +``` + +### Mutation + +To create a Matrix Field Type you need to define Field Type and Field definition identifiers. +The types that are used for input are named after the Type and the Field: + +- `{TypeIdentifier}{FieldIdentifier}RowInput` e.g. `dish.nutritionFacts`, `event.agenda`: `DishNutritionFactsRowInput`, `EventAgendaRowInput` + +The example below shows how to create a Recipe Content item (belonging to a Content Type with a Matrix Field Type added) that has two Fields: + +- `name`: `"Cake Ingredient List"` +- `ingredients`: `ezmatrix` with two columns: `ingredient` and `quantity` + +``` + mutation AddRecipe { + createRecipe( + language: eng_GB + parentLocationId: 2, + input: { + name: "Cake Ingredient List", + ingredients: [ + {ingredient: "sugar", quantity: "100 grams"} + {ingredient: "butter", quantity: "200 grams"} + ] + } + ) { + name + } +} +``` + +The response will confirm creation of the new Recipe Field: + +``` +{ + "data": { + "createRecipe": { + "name": "Cake Ingredient List" + } + } +} + +``` diff --git a/docs/api/field_types_reference/mediafield.md b/docs/api/field_types_reference/mediafield.md new file mode 100644 index 0000000000..00903e059e --- /dev/null +++ b/docs/api/field_types_reference/mediafield.md @@ -0,0 +1,122 @@ +# Media Field Type + +This Field Type represents and handles a media (audio/video) binary file. + +It is capable of handling the following types of files: + +- Apple QuickTime +- Adobe Flash +- Microsoft Windows Media +- Real Media +- Silverlight +- HTML5 Video +- HTML5 Audio + +| Name | Internal name | Expected input | +|---------|---------------|----------------| +| `Media` | `ezmedia` | mixed | + +## PHP API Field Type  + +### Input expectations + +| Type | Description | Example| +|------|-------------|--------| +| `string` | Path to the media file.| `/Users/jane/butterflies.mp4` | +| `eZ\Publish\Core\FieldType\Media\Value` | Media Field Type Value Object with path to the media file as the value of `id` property. | See below. | + +### Value object + +##### Properties + +`eZ\Publish\Core\FieldType\Media\Value` offers the following properties. + +Note that both `Media` and `BinaryFile` Value and Type inherit from the `BinaryBase` abstract Field Type and share common properties. + +|Property|Type|Description|Example| +|------|------|------|------| +|`id`|string|Media file identifier. This ID depends on the [IO Handler](../../guide/clustering.md#dfs-io-handler) that is being used. With the native, default handlers (FileSystem and Legacy), the ID is the file path, relative to the binary file storage root dir (`var//storage/original` by default).|application/63cd472dd7819da7b75e8e2fee507c68.mp4| +|`fileName`|string| The human-readable file name, as exposed to the outside. Used to name the file when sending it for download.|butterflies.mp4| +|`fileSize`|int|File size, in bytes.|1077923| +|`mimeType`|string|The file's MIME type.|video/mp4| +|`uri`|string|The binary file's HTTP URI. If the URI doesn't include a host or protocol, it applies to the request domain. **The URI is not publicly readable, and must NOT be used to link to the file for download.** Use `ez_render_field` to generate a valid link to the download controller.|/var/ezdemo_site/storage/original/application/63cd472dd7819da7b75e8e2fee507c68.mp4| +|`hasController`|boolean|Whether the media has a controller when being displayed.|true| +|`autoplay`|boolean|Whether the media should be automatically played.|true| +|`loop`|boolean|Whether the media should be played in a loop.|false| +|`height`|int|Height of the media.|300| +|`width`|int|Width of the media.|400| +|`path`|string|**deprecated**|| + +### Hash format + +The hash format mostly matches the value object. It has the following keys: + +- `id` +- `path` (for backwards compatibility) +- `fileName` +- `fileSize` +- `mimeType` +- `uri` +- `hasController` +- `autoplay` +- `loop` +- `height` +- `width` + +### Validation + +The Field Type supports `FileSizeValidator`, defining maximum size of media file in bytes: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`maxFileSize`|`int`|`false`|Maximum size of the file in bytes.| + +``` php +// Example of using Media Field Type validator in PHP + +use eZ\Publish\Core\FieldType\Media\Type; + +$contentTypeService = $repository->getContentTypeService(); +$mediaFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "media", "ezmedia" ); + +// Setting maximum file size to 5 megabytes +$mediaFieldCreateStruct->validatorConfiguration = [ + "FileSizeValidator" => [ + "maxFileSize" => 5 * 1024 * 1024 + ] +]; +``` + +### Settings + +The Field Type supports the `mediaType` setting, defining how the media file should be handled in output. + +|Name|Type|Default value|Description| +|------|------|------|------| +|`mediaType`|mixed|`Type::TYPE_HTML5_VIDEO`|Type of the media, accepts one of the predefined constants.| + +List of all available `mediaType` constants is defined in the `eZ\Publish\Core\FieldType\Media\Type` class: + +|Name|Description| +|------|------| +|`TYPE_FLASH`|Adobe Flash| +|`TYPE_QUICKTIME`|Apple QuickTime| +|`TYPE_REALPLAYER`|Real Media| +|`TYPE_SILVERLIGHT`|Silverlight| +|`TYPE_WINDOWSMEDIA`|Microsoft Windows Media| +|`TYPE_HTML5_VIDEO`|HTML5 Video| +|`TYPE_HTML5_AUDIO`|HTML5 Audio| + +``` php +// Example of using Media Field Type settings in PHP + +use eZ\Publish\Core\FieldType\Media\Type; +  +$contentTypeService = $repository->getContentTypeService(); +$mediaFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "media", "ezmedia" ); + +// Setting Adobe Flash as the media type +$mediaFieldCreateStruct->fieldSettings = [ + "mediaType" => Type::TYPE_FLASH, +]; +``` diff --git a/docs/api/field_types_reference/nullfield.md b/docs/api/field_types_reference/nullfield.md new file mode 100644 index 0000000000..c13b956dc0 --- /dev/null +++ b/docs/api/field_types_reference/nullfield.md @@ -0,0 +1,39 @@ +# Null Field Type + +This Field Type is used as fallback for migration scenarios, and for testing purposes. + +| Name | Internal name | Expected input type | +|--------|---------------|---------------------| +| `Null` | variable | mixed | + +## Description + +The Null Field Type serves as an aid when migrating from eZ Publish Platform and earlier legacy versions. It is a dummy for legacy Field Types that are not implemented in [[= product_name =]]. + +Null Field Type will accept anything provided as a value and is usually combined with: + +- NullConverter: Makes it not store anything to the legacy storage engine (database), nor will it read any data. +- Unindexed: Indexable class making sure nothing is indexed to configured search engine. + +This Field Type does not have its own fixed internal name. Its identifier is instead configured as needed by passing it as an argument to the constructor. + +### Example for usage of Null Field Type + +The following example shows how an `example` Field Type could be configured as a Null Field Type: + +``` yaml +# Null Fieldtype example configuration +services: + ezpublish.fieldType.example: + class: eZ\Publish\Core\FieldType\Null\Type + autowire: true + autoconfigure: false + arguments: [example] + tags: [{name: ezplatform.field_type, alias: example}] + ezpublish.fieldType.example.converter: + class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: [{name: ezplatform.field_type.legacy_storage.converter, alias: example}] + ezpublish.fieldType.example.indexable: + class: '%ezpublish.fieldType.indexable.unindexed.class%' + tags: [{name: ezplatform.field_type.indexable, alias: example}] +``` diff --git a/docs/api/field_types_reference/pagefield.md b/docs/api/field_types_reference/pagefield.md new file mode 100644 index 0000000000..3c4bc54a3f --- /dev/null +++ b/docs/api/field_types_reference/pagefield.md @@ -0,0 +1,88 @@ +--- +edition: experience +--- + +# Page Field Type + +Page Field Type represents a Page with a layout consisting of multiple zones. Each zone can in turn contain blocks. + +Page Field Type is only used in the Page Content Type that is included in [[= product_name_exp =]]. + +| Name | Internal name | Expected input | +|----------------|-----------------|-----------------| +| `Landing page` | `ezlandingpage` | `string (JSON)` | + +!!! caution "Page Builder" + + If you create Content Type with both `ezlandingpage` and `ezuser` Field Types, + you will not be redirected to Page Builder after selecting `Edit` or `Create`. + This is caused by `ezuser` Field Type which requires separate handling. You will be redirected to the standard Back Office edit or create mode. + +## Layout and zones + +Layout defines how a Page is divided into zones. + +The placement of zones is defined in a template which is a part of the layout configuration. You can modify the template in order to define your own zone layout. + +For information on how to create and configure new blocks for the Page, see [Page layouts](../../guide/content_rendering/render_content/render_page.md#render-a-layout). + +## Blocks + +For information on how to create and configure new blocks for the Page, see [Create custom Page block](../../guide/page/create_custom_page_block.md). + +## Rendering Pages + +Page rendering takes place while editing or viewing. + +When rendering a Page, its zones are passed to the layout as a `zones` array with a `blocks` array each. You can access them using twig (e.g. `{{ zones[0].id }}` ). + +Each div that's a zone should have the `data-ez-zone-id` attribute with zone ID as a value for a zone container. + +To render a block inside the layout, use the Twig `render_esi()` function to call `EzPlatformPageFieldTypeBundle:Block:render`. + +The `renderAction` has the following parameters: + +|Parameter|Description| +|---------|-----------| +|`locationId`|ID of the Location of the Content item which can be accessed by `contentInfo.id`| +|`blockId`|ID of the block which you want to render.| +|`versionNo`|Version number of the Content item to render.| +|`languageCode`|Language code of the Content item to render.| + +Example usage: + +``` html+twig +{{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction', { + 'locationId': locationId, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo, + 'languageCode': field.languageCode +})) }} +``` + +As a whole a sample layout could look as follows: + +``` html+twig +
    + {# The required attribute for the displayed zone #} +
    + {# If a zone with [0] index contains any blocks #} + {% if zones[0].blocks %} + {# for each block #} + {% for block in blocks %} + {# create a new layer with appropriate ID #} +
    + {# render the block by using the "EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction" controller #} + {# location.id is the ID of the Location of the current Content item, block.id is the ID of the current block #} + {{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController::renderAction', { + 'locationId': locationId, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo, + 'languageCode': field.languageCode + })) }} +
    + {% endfor %} + {% endif %} +
    +
    +``` diff --git a/docs/api/field_types_reference/relationfield.md b/docs/api/field_types_reference/relationfield.md new file mode 100644 index 0000000000..e1aea55f80 --- /dev/null +++ b/docs/api/field_types_reference/relationfield.md @@ -0,0 +1,75 @@ +# Relation Field Type + +!!! caution "Deprecated" + + The Relation Field Type is deprecated since v2.0. + + Use [RelationList](relationlistfield.md) with a selection limit instead. + +This Field Type makes it possible to store and retrieve the value of a relation to another Content item. + +| Name | Internal name | Expected input | +|------------|--------------------|----------------| +| `Relation` | `ezobjectrelation` | mixed | + +## PHP API Field Type  + +### Input expectations + +|Type|Example| +|------|------| +|`string`|`"150"`| +|`integer`|`150`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property|Type| Description| +|---------|-----|-----------| +| `$destinationContentId` | `string|int|null` | This property is used to store the value provided, which represents the related content. | + +``` php +// Value object content example + +$relation->destinationContentId = $contentInfo->id; +``` + +##### Constructor + +The `Relation\Value` constructor will initialize a new Value object with the value provided. It expects a mixed value. + +``` php +// Constructor example + +// Instantiates a Relation Value object +$relationValue = new Relation\Value( $contentInfo->id ); +``` + +### Validation + +This Field Type validates whether the provided relation exists, but before that it will check that the value is either a string or an int. + +### Settings + +The Field definition of this Field Type can be configured with three options: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`selectionMethod`|`int`|`Relation\Type::SELECTION_BROWSE`| *This setting is not implemented yet, only one selection method is available.* | +|`selectionRoot`|`string`|`null`|This setting defines the selection root.| +|`selectionContentTypes`|`array`|`[]`|An array of Content Type IDs that are allowed for related Content.| + +``` php +// Relation FieldType example settings + +use eZ\Publish\Core\FieldType\Relation\Type; + +$settings = [ + "selectionMethod" => 1, + "selectionRoot" => null, + "selectionContentTypes" => [] +]; +``` diff --git a/docs/api/field_types_reference/relationlistfield.md b/docs/api/field_types_reference/relationlistfield.md new file mode 100644 index 0000000000..26c7f25f16 --- /dev/null +++ b/docs/api/field_types_reference/relationlistfield.md @@ -0,0 +1,108 @@ +# RelationList Field Type + +This Field Type makes it possible to store and retrieve values of a relation to other Content items. + +| Name | Internal name | Expected input | +|----------------|------------------------|----------------| +| `RelationList` | `ezobjectrelationlist` | `mixed` | + +## PHP API Field Type  + +### Input expectations + +|Type|Description|Example| +|------|------|------| +|`int|string`|ID of the related Content item|`42`| +|`array`|An array of related Content IDs|`[ 24, 42 ]`| +|`eZ\Publish\API\Repository\Values\Content\ContentInfo`|ContentInfo instance of the related Content|n/a| +|`eZ\Publish\Core\FieldType\RelationList\Value`|RelationList Field Type Value Object|See below.| + +### Value Object + +##### Properties + +`eZ\Publish\Core\FieldType\RelationList\Value` contains the following properties: + +|Property|Type|Description|Example| +|------|------|------|------| +|`destinationContentIds`|`array`|An array of related Content IDs|`[ 24, 42 ]`| + +``` php +// Value object content example +$relationList->destinationContentId = [ + $contentInfo1->id, + $contentInfo2->id, + 170 +]; +``` + +##### Constructor + +The `RelationList\Value` constructor will initialize a new Value object with the value provided. It expects a mixed array as value. + +``` php +//Constructor example + +// Instantiates a RelationList Value object +$relationListValue = new RelationList\Value( + [ + $contentInfo1->id, + $contentInfo2->id, + 170 + ] +); +``` + +### Validation + +This Field Type validates if: + +- the `selectionMethod` specified is `\eZ\Publish\Core\FieldType\RelationList\Type::SELECTION_BROWSE` or `\eZ\Publish\Core\FieldType\RelationList\Type::SELECTION_DROPDOWN`. A validation error is thrown if the value does not match. +- the `selectionDefaultLocation` specified is `null`, `string` or `integer`. If the type validation fails a validation error is thrown. +- the value specified in `selectionContentTypes` is an `array`. If not, a validation error in given. +- the number of Content items selected in the Field is not greater than the `selectionLimit`. + +!!! note + + The dropdown selection method is not implemented yet. + +### Settings + +The Field definition of this Field Type can be configured with the following options: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`selectionMethod`|`mixed`|`SELECTION_BROWSE`|Method of selection in the back-end interface.| +|`selectionDefaultLocation`|`string|integer`|`null`|ID of the default Location for the selection when using the back-end interface.| +|`selectionContentTypes`|`array`|`[]`|An array of Content Type IDs that are allowed for related Content.| + +Following selection methods are available: + +| Name| Description| +|-----|------------| +| `SELECTION_BROWSE` | Selection will use browse mode.| +| `SELECTION_DROPDOWN` | *Not implemented yet* | + +### Validators + +|Name|Type|Default value|Description| +|------|------|------|------| +|`RelationListValueValidator[selectionLimit]`|`integer`|`0`|The number of Content items that can be selected in the Field. When set to 0, any number can be selected.| + +``` php +// Example of using settings and validators configuration in PHP + +use eZ\Publish\Core\FieldType\RelationList\Type; + +$fieldSettings = [ + "selectionMethod" => Type::SELECTION_BROWSE, + "selectionDefaultLocation" => null, + "selectionContentTypes" => [] + ]; + +$validators = [ + "RelationListValueValidator" => [ + "selectionLimit" => 0, + ] +]; +``` diff --git a/docs/api/field_types_reference/richtextfield.md b/docs/api/field_types_reference/richtextfield.md new file mode 100644 index 0000000000..7581105445 --- /dev/null +++ b/docs/api/field_types_reference/richtextfield.md @@ -0,0 +1,290 @@ +# RichText Field Type + +The RichText Field Type is available via the RichText Field Type Bundle provided by the [ezplatform-richtext](https://github.com/ezsystems/ezplatform-richtext) package. + +This Field Type validates and stores structured rich text, and exposes it in several formats. + +|Name|Internal name|Expected input| +|------|------|------| +|`RichText`|`ezrichtext`|mixed| + +## PHP API Field Type + +### Value object + +`EzSystems\EzPlatformRichText\eZ\FieldType\RichText\Value` offers the following properties: + +|Property|Type|Description| +|------|------|------| +|`xml`|`DOMDocument`|Internal format value as an instance of `DOMDocument`.| + +### Input expectations + +|Type|Description| +|------|------| +|`string`|XML document in one of the Field Type's input formats as a string.| +|`DOMDocument`|XML document in one of the Field Type's input formats as a `DOMDocument` object.| +|`EzSystems\EzPlatformRichText\eZ\FieldType\RichText\Value`|An instance of the Field Type's `Value` object.| + +##### Input formats + +The Field Type expects an XML value as input, in the form of a string, `DOMDocument` object, or Field Type's `Value` object. +The Field Type's `Value` object must hold the value in the Field Type's [internal format](#internal-format). +For a string of a `DOMDocument` object, if the input does not conform to this format, it is converted into it. + +##### Internal format + +As its internal format, the RichText Field Type uses a [custom flavor of the DocBook format](#custom-docbook-format). + +``` xml + +
    + This is a title. + This is a paragraph. +
    +``` + +##### XHTML5 edit format + +The XHTML5 format is used by the Online Editor. + +``` xml + +
    +

    This is a title.

    +

    This is a paragraph.

    +
    +``` + +## Custom DocBook format + +!!! caution + + The custom DocBook format described below is subject to change + and is not covered by backwards compatibility promise. + +You can use the Ibexa flavor of the DocBook format in PHP API and in REST API requests +by providing the DocBook content as a string. + +The following example shows how to pass DocBook content to a [create struct](../public_php_api_creating_content.md#creating-content-item-draft): + +``` php +$contentCreateStruct = $contentService->newContentCreateStruct( $contentType, "eng-GB" ); + +$inputString = << +
    + This is a title. + This is a paragraph. +
    +DOCBOOK; + +$contentCreateStruct->setField( "description", $inputString ); +``` + +When creating RichText content with the REST API, use the `xml` key of the `fieldValue` tag: + +``` xml + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> +<title ezxhtml:level="2">This is a title.</title> +</section> + + +``` + +### DocBook elements + +The RichText format enriches [DocBook](https://docbook.org/) with the following custom elements: + +- `section` - main element of a RichText Field +- `ezembed` - holds embedded images +- `ezembedinline` - holds embedded Content items +- `eztemplate` - holds custom tags, including built-in custom tags for embedded Facebook, Twitter and YouTube content +- `eztemplateinline` - holds inline custom tags +- `ezconfig` - contains configuration for custom tags and other elements +- `ezvalue` - contains values for other elements, such as `ezconfig` or `ezembed` +- `ezattribute` - contains attributes for other elements, such as `ezconfig` or `ezembed` + +!!! note "Unsupported DocBook elements" + + Some DocBook elements are not supported by RichText. + Refer to [`ezpublish.rng`](https://github.com/ezsystems/ezplatform-richtext/blob/master/src/lib/eZ/RichText/Resources/schemas/docbook/ezpublish.rng#L120) for a full list. + +### Online Editor elements + +Elements of the Online Editor correspond to the following sample DocBook code blocks. + +#### Text formatting + +``` xml +Anchor text +Center aligned +Left aligned bold + italic + underlined + subscript + superscript + crossed out + +
    + This is a block quote. +
    +``` + +#### Heading + +``` xml +My heading +``` + +#### Code block + +``` xml + +``` + +#### Unordered list + +``` xml + + + 1st level bullet point + + + 1st level bullet point + + + 2nd level bullet point + + + 2nd level bullet point + + + + + +``` + +#### Ordered list + +``` xml + + + 1st level numbered point + + + 1st level numbered point + + + 2nd level numbered point + + + + + +``` + +#### Embedded content + +``` xml + +``` + +#### Inline embedded content + +``` xml +embed inline +``` + +#### Image + +``` xml + + + medium + + +``` + +#### Table + +``` xml + + + + + This is a merged table cell + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +#### YouTube + +``` xml + + + https://youtu.be/Y-1d5zdeg9A + false + + +``` + +#### Twitter + +``` xml + + + https://twitter.com/BBCSpringwatch/status/1401622026973032452 + light + 500 + en + true + + +``` + +#### Facebook + +``` xml + + + https://www.facebook.com/bbcnews/posts/10158930827817217?__tn__=-R + 120 + + +``` + diff --git a/docs/api/field_types_reference/selectionfield.md b/docs/api/field_types_reference/selectionfield.md new file mode 100644 index 0000000000..14a9b8d9f1 --- /dev/null +++ b/docs/api/field_types_reference/selectionfield.md @@ -0,0 +1,85 @@ +# Selection Field Type + +The Selection Field Type stores single selections or multiple choices from a list of options, by populating a hash with the list of selected values. + +| Name | Internal name | Expected input type | +|-------------|---------------|---------------------| +| `Selection` | `ezselection` | mixed | + +## PHP API Field Type + +### Input expectations + +| Type | Example | +|---------|-----------------| +| `array` | `[ 1, 2 ]` | + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|--------------|---------|------------| +| `$selection` | `int[]` | This property is used for the list of selections, which is a list of integer values, or one single integer value. | + +``` php +// Value object content examples + +// Single selection +$value->selection = 1; + +// Multiple selection +$value->selection = [ 1, 4, 5 ]; +``` + +##### Constructor + +The `Selection\Value` constructor accepts an array of selected element identifiers. + +``` php +// Constructor example + +// Instanciates a selection value with items #1 and #2 selected +$selectionValue = new Selection\Value( [ 1, 2 ] ); +``` + +##### String representation + +String representation of this Field Type is its list of selections as a string, concatenated with a comma. + +Example: `"1,2,24,42"` + +### Hash format + +Hash format of this Field Type is the same as Value object's `selection` property. + +``` php +// Example of value in hash format + +$hash = [ 1, 2 ]; +``` + +### Validation + +This Field Type validates the input, verifying if all selected options exist in the Field definition and checks if multiple selections are allowed in the Field definition. +If any of these validations fail, a `ValidationError` is thrown, specifying the error message. When option validation fails, a list with the invalid options is also presented. + +### Settings + +| Name | Type | Default value | Description| +|--------------|-----------|---------------|------------| +| `isMultiple` | `boolean` | `false` | Used to allow or prohibit multiple selection from the option list. | +| `options` | `hash` | `[]` | Stores the list of options defined in the Field definition. | + +``` php +// Selection Field Type example settings + +use eZ\Publish\Core\FieldType\Selection\Type; + +$settings = [ + "isMultiple" => true, + "options" => [1 => 'One', 2 => 'Two', 3 => 'Three'] +]; +``` diff --git a/docs/api/field_types_reference/sesexternaldata.md b/docs/api/field_types_reference/sesexternaldata.md new file mode 100644 index 0000000000..2ea56e9895 --- /dev/null +++ b/docs/api/field_types_reference/sesexternaldata.md @@ -0,0 +1,19 @@ +# SesExternalData + +Field Type `sesexternaldatatype` uses external storage to store data. +The data is stored in the `ses_externaldata` table with the following structure: + +|Field|Type|Description| +|--- |--- |--- | +|`sku`|char(40)|Unique ID of the Product category (CatalogElement).| +|`identifier`|char(40)|ID of the Field. Consists of a constant prefix (`ses`) and lowercase letters from the ERP fields.
    Example: `VENDOR_NO --> ses_vendor_no`.| +|`language_code`|char(8)|Language code, for example, `ger-DE`.| +|`ses_field_type`|char(20)|The data type used for this data.| +|`content`|longtext|Serialized data in string format.| + +## Storing data in `ses_externaldata` + +Data that is stored in the `ses_externaldata` table must be either a simple datatype: int, float, bool or a Field Type. + +Field Type data is stored in the database in serialized form by using the `toHash()` method. +Simple data types (int, float, bool) are stored in serialized form. diff --git a/docs/api/field_types_reference/sesprofiledata.md b/docs/api/field_types_reference/sesprofiledata.md new file mode 100644 index 0000000000..b2df5bf890 --- /dev/null +++ b/docs/api/field_types_reference/sesprofiledata.md @@ -0,0 +1,11 @@ +# SesProfileData + +This Field Type stores [`CustomerProfileData`](../../guide/customers/customer_api/customer_profile_data.md) in the User Content Type. + +`CustomerProfileData` must be stored as a serialized string in base64 format, +because it is impossible to store special HTML characters (`<`,`>`, `""`,`''`, `&`) in a text or text area field. + +The name of the customer (taken from the contact section) can be used for lists. +To do it, use the name pattern in the Content Type definition of the User Content Type. + +`customer_profile_data` is the identifier of the Field where the profile data is stored. diff --git a/docs/api/field_types_reference/sesselection.md b/docs/api/field_types_reference/sesselection.md new file mode 100644 index 0000000000..b3922d1b05 --- /dev/null +++ b/docs/api/field_types_reference/sesselection.md @@ -0,0 +1,23 @@ +# SesSelection + +This Field Type stores a single selection choice. + +The SesSelection Field Type is configured in a YAML file, +unlike the [Selection Field Type](selectionfield.md). +This way, you can set up SiteAccess-specific selection Field Types. + +The Field Type must be configured per attribute: + +``` yaml +siso_core.default.sesselection.news_type: + default: general + translation_context: news + options: + general: general + sports: sports + culture: culture +``` + +The `translation_context` key, which identifies the context used for translating the labels, is optional. + +To add a SesSelection Field to a Content Type, make sure the Field's identifier is the same as the configuration key (in the example above, `news_type`). diff --git a/docs/api/field_types_reference/specificationstype.md b/docs/api/field_types_reference/specificationstype.md new file mode 100644 index 0000000000..291d126dea --- /dev/null +++ b/docs/api/field_types_reference/specificationstype.md @@ -0,0 +1,40 @@ +# SpecificationsType + +This Field Type stores a structured list of attributes for products. + +!!! caution "Field naming" + + A Field of the SpecificationsType must have `ses_specifications` as its Field identifier. + +The data is stored in JSON format. + +``` json +[ + { + "name": "marketing", + "data": [ + { + "label": "Brand", + "value": "MG" + }, + { + "label": "Warranty", + "value": "2yrs" + } + ] + }, + { + "name": "technic", + "data": [ + { + "label": "Size", + "value": "12cm" + }, + { + "label": "color", + "value": "red" + } + ] + } +] +``` diff --git a/docs/api/field_types_reference/textblockfield.md b/docs/api/field_types_reference/textblockfield.md new file mode 100644 index 0000000000..4950e6a5e5 --- /dev/null +++ b/docs/api/field_types_reference/textblockfield.md @@ -0,0 +1,45 @@ +# TextBlock Field Type + +The Field Type handles a block of multiple lines of unformatted text. It is capable of handling up to 16,777,216 characters. + +| Name | Internal name | Expected input type | +|-------------|---------------|---------------------| +| `TextBlock` | `eztext` | `string` | + +## PHP API Field Type + +### Input expectations + +|Type|Example| +|----|-------| +|`string`|`"This is a block of unformatted text"`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +|Property|Type|Description| +|--------|----|-----------| +|`$text`|`string`|This property will be used for the text content.| + +##### String representation + +A TextBlock's string representation is the `$text` property's value, as a string. + +##### Constructor + +The constructor for this Value object will initialize a new Value object with the value provided. It accepts a string as argument and will import it to the `$text` attribute. + +### Validation + +This Field Type does not perform any special validation of the input value. + +### Settings + +Settings contain only one option: + +| Name | Type | Default value | Description| +|------------|-----------|---------------|------------| +| `textRows` | `integer` | `10` | Number of rows for the editing box in the back-end interface. | diff --git a/docs/api/field_types_reference/textlinefield.md b/docs/api/field_types_reference/textlinefield.md new file mode 100644 index 0000000000..6863df8529 --- /dev/null +++ b/docs/api/field_types_reference/textlinefield.md @@ -0,0 +1,40 @@ +# TextLine Field Type + +This Field Type makes possible to store and retrieve a single line of unformatted text. It is capable of handling up to 255 characters. + +| Name | Internal name | Expected input type | +|------------|---------------|---------------------| +| `TextLine` | `ezstring` | `string` | + +## PHP API Field Type + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|----------|------------| +| `$text` | `string` | This property will be used for the text content. | + +##### String representation + +A TextLine's string representation is the `$text` property's value, as a string. + +##### Constructor + +The constructor for this Value object will initialize a new Value object with the value provided. It accepts a string as argument and will import it to the `$text` attribute. + +### Validation + +The input passed into this Field Type is subject to validation by the `StringLengthValidator`. The length of the string provided must be between the minimum length defined in `minStringLength` and the maximum defined in `maxStringLength`. The default value for both properties is 0, which means that the validation is disabled by default. +To set the validation properties, the `validateValidatorConfiguration()` method needs to be inspected, which will receive an array with `minStringLength` and `maxStringLength` like in the following representation: + +``` +Array +( + [minStringLength] => 1 + [maxStringLength] => 60 +) +``` diff --git a/docs/api/field_types_reference/timefield.md b/docs/api/field_types_reference/timefield.md new file mode 100644 index 0000000000..7700c63922 --- /dev/null +++ b/docs/api/field_types_reference/timefield.md @@ -0,0 +1,93 @@ +# Time Field Type + +This Field Type represents time information. + +Date information is **not stored**. + +What is stored is the number of seconds, calculated from the beginning of the day in the given or the environment timezone. + +| Name | Internal name | Expected input type | +|--------|---------------|---------------------| +| `Time` | `eztime` | mixed | + +## PHP API Field Type + +### Input expectations + +If input value is of type `string` or `integer`, it will be passed directly to the [PHP's built-in `\DateTime` class](http://www.php.net/manual/en/datetime.construct.php) constructor, therefore the same input format expectations apply. + +It is also possible to directly pass an instance of `\DateTime`. + +|Type|Example| +|------|------| +|`string`|`"2012-08-28 12:20 Europe/Berlin"`| +|`integer`|`1346149200`| +|`\DateTime`|`new \DateTime()`| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|----------------|------------| +| `$time` | `integer|null` | Holds the time information as a number of seconds since the beginning of the day. | + +##### Constructor + +The constructor for this Value object will initialize a new Value object with the value provided. It accepts an integer representing the number of seconds since the beginning of the day. + +##### String representation + +String representation of the date value will generate the date string in the format "H:i:s" as accepted by [PHP's built-in `date()` function](http://www.php.net/manual/en/function.date.php). + +|Character|Description|Example| +|---------|----------|--------| +|H|Two digit representation of an hour, 24-hour format, range 00 to 23 |12| +|i|Two digit representation of minutes, range 00 to 59|14| +|s|Two digit representation of seconds, range 00 to 59|56| + +Example: `"12:14:56"` + +### Hash format + +Value in hash format is an integer representing a number of seconds since the beginning of the day. + +Example: `36000` + +### Validation + +This Field Type does not perform validation of the input value. + +### Settings + +The Field definition of this Field Type can be configured with several options: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`useSeconds`|`boolean`|`false`|Used to control displaying of seconds in the output.| +|`defaultType`|`Type::DEFAULT_EMPTY Type::DEFAULT_CURRENT_TIME`|`Type::DEFAULT_EMPTY`|The constant used here defines default input value when using back-end interface.| + +``` php +// Time Field Type example settings +use eZ\Publish\Core\FieldType\Time\Type; + +$settings = [ + "defaultType" => DateAndTime::DEFAULT_EMPTY +]; +``` + +## Template rendering + +The template called by [the `ez_render_field()` Twig function](../../guide/content_rendering/twig_function_reference/field_twig_functions.md#ez_render_field) while rendering a Date Field has access to the following parameters: + +| Parameter | Type | Default | Description| +|-----------|----------|---------|------------| +| `locale` | `string` |   n/a | Internal parameter set by the system based on current request locale or, if not set, calculated based on the language of the Field. | + +Example: + +``` php +{{ ez_render_field(content, 'time') }} +``` diff --git a/docs/api/field_types_reference/urlfield.md b/docs/api/field_types_reference/urlfield.md new file mode 100644 index 0000000000..b48263e1bd --- /dev/null +++ b/docs/api/field_types_reference/urlfield.md @@ -0,0 +1,68 @@ +# URL Field Type + +This Field Type makes it possible to store and retrieve a URL. It is formed by the combination of a link and the respective text. + +| Name | Internal name | Expected input | +|-------|---------------|----------------| +| `Url` | `ezurl` | `string` | + +## PHP API Field Type + +### Input expectations + +|Type|Description|Example| +|------|------|------| +|`string`|Link content provided to the value.|"http://www.ibexa.co"| +|`string`|Text content that represents the stored link.|"Ibexa"| + +### Value object + +##### Properties + +The Value class of this Field Type contains the following properties: + +| Property | Type | Description| +|----------|----------|------------| +| `$link` | `string` | This property stores the link provided to the value of this Field Type. | +| `$text` | `string` | This property stores the text to represent the stored link provided to the value of this Field Type. | + +``` php +// Value object content example + +$url->link = "http://www.ibexa.co"; +$url->text = "Ibexa"; +``` + +##### Constructor + +The `Url\Value` constructor initializes a new Value object with the provided value. It expects two comma-separated strings, corresponding to the link and text. + +``` php +// Constructor example + +// Instantiates an Url Value object +$UrlValue = new Url\Value( "http://www.ibexa.co", "Ibexa" ); +``` +### Hash format + +|Key|Type|Description|Example| +|------|------|------|------| +|`link`|`string`|Link content.|"http://ibexa.co"| +|`text`|`string`|Text content.|"Ibexa"| + +```php +// Example of the hash value in PHP +$hash = [ + "link" => "http://ibexa.co", + "text" => "Ibexa" +]; + +``` + +### Validation + +This Field Type does not perform validation. + +### Settings + +This Field Type does not have settings. diff --git a/docs/api/field_types_reference/userfield.md b/docs/api/field_types_reference/userfield.md new file mode 100644 index 0000000000..1cb9eb528f --- /dev/null +++ b/docs/api/field_types_reference/userfield.md @@ -0,0 +1,40 @@ +# User Field Type + +This Field Type validates and stores information about a user. + +| Name | Internal name | Expected input | +|--------|---------------|----------------| +| `User` | `ezuser` | ignored | + +## PHP API Field Type + +### Value Object + +|Property|Type|Description|Example| +|------|------|------|------| +|`hasStoredLogin`|`boolean`|Denotes if user has stored login.|`true`| +|`contentId`|`int|string`|ID of the Content item corresponding to the user.|`42`| +|`login`|`string`|Username.|`john`| +|`email`|`string`|The user's email address.|`john@smith.com`| +|`passwordHash`|`string`|Hash of the user's password.|`1234567890abcdef`| +|`passwordHashType`|`mixed`|Algorithm user for generating password hash as a `PASSWORD_HASH_*` constant defined in `eZ\Publish\Core\Repository\Values\User\User` class.|`User::PASSWORD_HASH_PHP_DEFAULT`| +|`maxLogin`|`int`|Maximum number of concurrent logins.|`1000`| + +##### Available password hash types + +|Constant|Description| +|------|------| +|`eZ\Publish\Core\Repository\Values\User\User::DEFAULT_PASSWORD_HASH`|Default password hash, used when none is specified, may change over time.| +|`eZ\Publish\Core\Repository\Values\User\User::PASSWORD_HASH_PHP_DEFAULT`|Passwords hashed by PHP's default algorithm, which may change over time.| +|`eZ\Publish\Core\Repository\Values\User\User::PASSWORD_HASH_BCRYPT`|Bcrypt hash of the password.| + +!!! caution + + Using the MD5-based deprecated hash types is a security risk, because if the hashes are leaked, they are too easily broken by brute-force attacks. + The plaintext type offers no security. It was only ever intended for testing, and should never be used now. + + We strongly recommend switching to one of the new hash types. If you do, it will be used for new users. + Existing users will also have their hashes updated to the new type when they log in. + (A mass update of all hashes is not possible, because this requires knowing the passwords, which only the users themselves do.) + + Removal notice: https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#password-hashes diff --git a/docs/api/field_types_reference/varianttype.md b/docs/api/field_types_reference/varianttype.md new file mode 100644 index 0000000000..adaa7e82e7 --- /dev/null +++ b/docs/api/field_types_reference/varianttype.md @@ -0,0 +1,30 @@ +# VariantType + +The VariantType Field Type offers a user interface for editing [product variants](../../guide/catalog/product_variants/product_variants.md). + +The Field Type offers a selection of preconfigured variant types. + +A variant type can be a one level or two level variant. +The variant types can be [set up in a YAML file](../../guide/catalog/product_variants/product_variants.md#adding-new-variants). + +The data is stored in JSON format: + +``` json +[ + { + "sku": { + "label": "Sku", + "value": "5515" + }, + "variantCode": { + "label": "Variant Code", + "value": "5515" + }, + "description": { + "label": "Description", + "value": "Alibaba Single Door Silver Refrigerator" + }, + // ... + }, +] +``` diff --git a/docs/api/field_types_reference/xmltextfield.md b/docs/api/field_types_reference/xmltextfield.md new file mode 100644 index 0000000000..870e1c5825 --- /dev/null +++ b/docs/api/field_types_reference/xmltextfield.md @@ -0,0 +1,131 @@ +# XmlText Field Type + +The XmlText Field Type isn't officially supported by [[= product_name =]]. It can be installed by requiring `ezsystems/ezplatform-xmltext-fieldtype`. The Back Office does not support WYSIWYG editing of Fields of this type. + +This Field Type validates and stores formatted text using the eZ Publish legacy format, eZXML.  + +| Name | Internal name | Expected input | +|-----------|---------------|----------------| +| `XmlText` | `ezxmltext` | `mixed` | + +## Input expectations + +|Type|Description|Example| +|------|------|------| +|`string`|XML document in the Field Type internal format as a string.|See the example below.| +|`eZ\Publish\Core\FieldType\XmlText\Input`|An instance of the class implementing the Field Type's abstract `Input` class.|See the example below.| +|`eZ\Publish\Core\FieldType\XmlText\Value`|An instance of the Field Type's `Value` object.|See the example below.| + +### Example of the Field Type's internal format + +``` xml + +
    + This is a paragraph. +
    +``` + +### For XHTML Input + +The XML output uses `` and `` by default, respecting the semantic XHTML notation. + +Learn more about ``, ``, ``, ``: + +- Learn more [about the semantic tags vs the presentational tags.](http://html5doctor.com/i-b-em-strong-element/) + +## Input object API + +`Input` object is intended as a vector for different input formats. It should accept input value in a foreign format and convert it to the Field Type's internal format. + +It should implement the abstract `eZ\Publish\Core\FieldType\XmlText\Input` class, which defines only one method: + +|Method|Description| +|------|------| +|`getInternalRepresentation`|The method returns the input value in the internal format.| + +At the moment there is only one implementation of the `Input` class, `eZ\Publish\Core\FieldType\XmlText\Input\EzXml`, which accepts input value in the internal format, and therefore only performs validation of the input value. + +``` php +// Example of using the Input object + +... +  +use eZ\Publish\Core\FieldType\XmlText\Input\EzXml as EzXmlInput; + +... + +$contentService = $repository->getContentService(); +$contentTypeService = $repository->getContentTypeService(); +  +$contentType = $contentTypeService->loadContentTypeByIdentifier( "article" ); +$contentCreateStruct = $contentService->newContentCreateStruct( $contentType, "eng-GB" ); + +$inputString = << +
    + This is a paragraph. +
    +EZXML; +  +$ezxmlInput = new EzXmlInput( $inputString ); + +$contentCreateStruct->setField( "description", $ezxmlInput ); +  +... +``` + +## Value object API + +`eZ\Publish\Core\FieldType\XmlText\Value` offers the following properties: + +|Property|Type|Description| +|------|------|------| +|`xml`|`DOMDocument`|Internal format value as an instance of `DOMDocument`.| + +## Validation + +Validation of the internal format is performed in the `eZ\Publish\Core\FieldType\XmlText\Input\EzXml` class. + +## Settings + +Following settings are available: + +|Name|Type|Default value|Description| +|------|------|------|------| +|`numRows`|`int`|`10`|Defines the number of rows for the online editor in the back-end interface.| +|`tagPreset`|`mixed`|`Type::TAG_PRESET_DEFAULT`|Preset of tags for the online editor in the back-end interface.| + +### Tag presets + +Following tag presets are available as constants in the `eZ\Publish\Core\FieldType\XmlText` class: + +|Constant|Description| +|------|------| +|`TAG_PRESET_DEFAULT`|Default tag preset.| +|`TAG_PRESET_SIMPLE_FORMATTING`|Preset of tags for online editor intended for simple formatting options.| + +``` php +// Example of using settings in PHP + +... +  +use eZ\Publish\Core\FieldType\XmlText\Type; + +... + +$contentTypeService = $repository->getContentTypeService(); +$xmltextFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( "description", "ezxmltext" ); + +$xmltextFieldCreateStruct->fieldSettings = [ + "numRows" => 25, + "tagPreset" => Type::TAG_PRESET_SIMPLE_FORMATTING +]; +  +... +``` diff --git a/docs/api/general_rest_usage.md b/docs/api/general_rest_usage.md deleted file mode 100644 index 46a3aba937..0000000000 --- a/docs/api/general_rest_usage.md +++ /dev/null @@ -1,519 +0,0 @@ -# General REST usage - -As explained in the [introduction](rest_api_guide.md), the REST API is based on a limited list of general principles: - -- Each resource (URI) interacts with a part of the system (content, URL aliases, User Groups, etc.). -- For each resource, one or more HTTP methods are available. Each method has a different effect (DELETE a Content item, GET a URL Alias, GET a list of user groups, etc.). -- Media-type request headers indicate what kind of data type (Content / ContentInfo) and data format (JSON or XML) are expected as a response, and what can be requested. - -## Anatomy of REST call - -### GET request - -The GET request is used to query the API for information. It is one of the two operations web browsers implement, and the one most commonly used. - -### Request - -The only requirement for this verb is usually the resource URI and the accept header. -On top of that, cache request headers can be added, like `If-None-Match`, but those are not fully implemented yet. - -**Load ContentInfo request** - -``` -GET /content/objects/23 HTTP/1.1 -Accept: application/vnd.ez.api.ContentInfo+xml -``` - -#### Response headers - -The API response contains: - -- HTTP response code -- headers -- XML representation of the ContentInfo for content with ID 23 in XML format as specified in the Accept header - -**Load ContentInfo response** - -``` -HTTP/1.1 200 OK -Accept-Patch: application/vnd.ez.api.ContentUpdate+xml;charset=utf8 -Content-Type: application/vnd.ez.api.ContentInfo+xml -``` - -###### HTTP code - -The API responded here with a standard `200 OK` HTTP response code, which is the expected response code for a typical GET request. -Some GET requests, like [getting a Content item's current version,](https://ezsystems.github.io/ezplatform-rest-reference/#managing-content-get-current-version) may return a `301 Moved permanently` or `307 Temporary redirect` code. - -Errors are indicated with HTTP error codes, e.g. `404 Not Found` or `500 Internal Server Error`. -The [REST reference](https://doc.ezplatform.com/rest-api-reference) provide the list of every HTTP response code you can expect from implemented resources. - -###### Content-Type header - -As long as a response contains an actual HTTP body, the Content Type header will be used to specify which Content Type is contained in the response. In that case: - -- ContentInfo: `Content-Type: application/vnd.ez.api.ContentInfo` -- ContentInfo in XML format: `Content-Type: application/vnd.ez.api.ContentInfo+xml` - -###### Accept-Patch header - -It tells you that the received content can be modified by patching it with a [ContentUpdateStruct](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php) in XML format: - - `Accept-Patch: application/vnd.ez.api.ContentUpdate+xml;charset=utf8` - -JSON would also work, with the proper format. - -As the example above shows, sending a PATCH `/content/objects/23` request with a [ContentUpdateStruct](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php) XML payload will update this content. - -REST will use the `Accept-Patch` header to indicate how to **modify** the returned **data**. - -###### Location header - -Depending on the resource, request and response headers will vary. - -For instance [creating Content](https://ezsystems.github.io/ezplatform-rest-reference/#managing-content-create-content-type) and [getting a Content item's current version](https://ezsystems.github.io/ezplatform-rest-reference/#managing-content-get-current-version) -will both send a **Location header** to provide you with the requested resource's ID. - -Those particular headers generally match a specific list of HTTP response codes. -Location is sent by `201 Created`, `301 Moved permanently`, `307 Temporary redirect responses`, etc. You can expect these HTTP responses to provide you with a Location header. - -###### Destination header - -This request header is the request counterpart of the Location response header. -It is used for a COPY or MOVE operation on a resource to indicate where the resource should be moved to by using the ID of the destination. -An example of such a request is [copying a Content item](https://ezsystems.github.io/ezplatform-rest-reference/#managing-content-copy-content). - -#### Response body - -Load ContentInfo response body, expand source: - -``` xml - - - - This is a title - - -
    - - - - 2012-02-12T12:30:00 - 2012-02-12T15:30:00 - eng-US - true - -``` - -The XML body is a serialized version of a [ContentInfo](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentInfo.php) struct. -Most of the REST API calls will involve exchanging XML or JSON representations of the public API. - -The example above shows that Content item 23 can be modified by sending a `vendor/application/vnd.ez.ContentUpdate+xml`. -This media type again matches a Value in the API, [ContentUpdateStruct](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php). - -The REST API data structs mostly match a PHP Public API value object. - -#### Value objects representation - -Value objects like [ContentInfo](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentInfo.php) feature two types of fields: - -- local fields (e.g. currentVersionNo, name) -- foreign field(s) references (e.g. sectionId, mainLocationId) - -Local fields will be represented in XML / JSON format with a plain type (integer, string), while foreign key references will be represented as a link to another resource. -This resource will be identified with its URI (`/content/objects/23/locations`) and the media-type that should be requested when calling that resource (`media-type="application/vnd.ez.api.LocationList+xml"`). -Depending on how much data you need, you may choose to crawl those relations or to ignore them. - -##### XSD files - -For each XML structure known to the REST API, you can find [XSD files](https://github.com/ezsystems/ezpublish-kernel/tree/master/doc/specifications/rest/xsd) in the XSD folder of the specifications. -They will allow you to validate your XML and to learn about every option these XML structures feature. - -## Request parameters - -Responses depend on: - -- URI -- request headers (e.g. ACCEPT) - -The URI parameters are used as well. -They usually serve as filters / options for the requested resource. -For instance, they can be used to customize a list's offset/limit. -To filter a list, specify which fields you want from a content, etc. -For almost all resources, these parameters must be provided as GET. - -The example request below would return the first 5 relations for version 3 of the Content item 59: - -**GET request with limit parameter** - -``` -GET /content/objects/59/versions/3/relations&limit=5 HTTP/1.1 -Accept: application/vnd.ez.api.RelationList+xml -``` - -### Working with value objects IDs - -Resources that accept a reference to another resource expect reference to be given as a REST ID, not as a Public API ID. -For example, the URI requesting a list of users assigned to the role with ID 1 is: - -``` -GET /api/ezp/v2/user/users?roleId=/api/ezp/v2/user/roles/1 -``` - -## Custom HTTP verbs - -In addition to the usual GET, POST, PUT, and DELETE HTTP verbs, the API supports a few custom ones: - -- COPY -- [MOVE](http://tools.ietf.org/html/rfc2518) -- [PATCH](http://tools.ietf.org/html/rfc5789) -- PUBLISH - -They should be recognized by most of the HTTP servers. -If the server does not recognize the custom methods you use, you can customize a standard verb (e.g. POST or PUT) with the `X-HTTP-Method-Override` header. - -**PATCH HTTP request** - -``` -POST /content/objects/59 HTTP/1.1 -X-HTTP-Method-Override: PATCH -``` - -If applicable, both methods are always mentioned in the specifications. - -### Logical operators - -When performing search endpoint (`/views`), the criteria model allows combining criteria using the following logical operators: - -- `AND` -- `OR` -- `NOT` - -By default, if multiple criteria are given, but not wrapped by any operator, the `AND` operator is used. - -When using the same criterion for multiple times, the parser wraps it with the `OR` operator. -Note that making the `AND` query for different values of the same criterion type always returns zero results. - -**Logical operators XML example** - -``` xml - - - test - - - - - folder - article - - standard - - - 10 - 0 - - ascending - - - -``` - -**Logical operators JSON example** - -``` json -{ - "ViewInput": { - "identifier": "test", - "ContentQuery": { - "Filter": { - "AND": { - "OR": { - "ContentTypeIdentifierCriterion": [ - "folder", - "article" - ] - }, - "SectionIdentifierCriterion": "standard" - } - }, - "limit": "10", - "offset": "0", - "SortClauses": { "ContentName": "ascending" } - } - } -} -``` - -!!! note - - The structure for `ContentTypeIdentifierCriterion` with multiple values is slightly - different in JSON format, because the parser expects keys to be unique. - -## Specifying SiteAccess - -One of the principles of REST is that the same resource (Content item, Location, Content Type, etc.) should be unique. -It allows caching your REST API using a reverse proxy like Varnish. -If the same resource is available in multiple locations, cache purging is noticeably more complex. - -Consequently, SiteAccess matching with REST is not enabled. -In order to specify a SiteAccess when talking to the REST API, a custom `X-Siteaccess` header has to be provided. If not, the default one is used: - -**X-Siteaccess header example** - -``` -GET / HTTP/1.1 -Host: api.example.com -Accept: application/vnd.ez.api.Root+json -X-Siteaccess: ezdemo_site_admin -``` - -## REST API authentication - -The REST API supports the following authentication methods: - -- **Session-based authentication** for AJAX operations that lets you re-use the visitor's session to execute operations with their permissions. -- **Basic authentication** for writing cross-server procedures, when one remote application executes operations on one/several [[= product_name =]] instances (remote publishing, maintenance, etc). -- [**JWT authentication**](#jwt-authentication) - -Session-based is the default authentication method as it is needed for UI. - -!!! note "Limiting anonymous access to metadata over REST API" - - Some API endpoints accessible to the Anonymous User return metadata you might not want to expose, due to insufficient permission limitations. - To prevent that, you can rely on the Symfony securing URL patterns mechanism called [access_control](https://symfony.com/doc/3.4/security/access_control.html). - The example below shows you how to block listing Content Types for the non-authenticated users. - - **security.yml** - ``` yaml - security: - access_control: - - { path: '^/api/ezp/v2/content/types', roles: ROLE_USER } - ``` - -### Session-based authentication - -This authentication method requires a Session cookie to be sent with each request. - -If this authentication method is used with a web browser, this session cookie is automatically available as soon as your visitor logs in. -Add it as a cookie to your REST requests, and the user will be authenticated. - -#### Logging in - -You can create a session for a visitor even if they are not logged in by sending the **`POST`** request to `/user/sessions`. -For logging out, use the **`DELETE`** request on the same resource. - -**Creating session — XML example** - -``` -POST /user/sessions HTTP/1.1 -Host: www.example.net -Accept: application/vnd.ez.api.Session+xml -Content-Type: application/vnd.ez.api.SessionInput+xml -``` - -```xml - - - admin - secret - -``` - -``` -HTTP/1.1 201 Created -Location: /user/sessions/go327ij2cirpo59pb6rrv2a4el2 -Set-Cookie: eZSSID=go327ij2cirpo59pb6rrv2a4el2; domain=.example.net; path=/; expires=Wed, 13-Jan-2021 22:23:01 GMT; HttpOnly -Content-Type: application/vnd.ez.api.Session+xml -``` - -```xml - - - eZSSID - go327ij2cirpo59pb6rrv2a4el2 - 23lkneri34ijajedfw39orj3j93 - - -``` - -**Logging in with active session — XML example** - -``` -POST /user/sessions HTTP/1.1 -Host: www.example.net -Accept: application/vnd.ez.api.Session+xml -Content-Type: application/vnd.ez.api.SessionInput+xml -Cookie: eZSSID=go327ij2cirpo59pb6rrv2a4el2 -X-CSRF-Token: 23lkneri34ijajedfw39orj3j93 -``` - -```xml - - - admin - secret - -``` - -``` -HTTP/1.1 200 OK -Content-Type: application/vnd.ez.api.Session+xml -``` - -```xml - - - eZSSID - go327ij2cirpo59pb6rrv2a4el2 - 23lkneri34ijajedfw39orj3j93 - - -``` - -The `csrfToken` is returned in the login response. -It is important to keep the CSRF Token for the duration of the session as it needs to be sent with requests other than GET/HEAD when auth is set to session (in most cases it is). - -For details, see [Session-based authentication](https://github.com/ezsystems/ezpublish-kernel/blob/v8.0.0-beta5/doc/specifications/rest/REST-API-V2.rst#session-based-authentication) in the REST specifications. - -### HTTP basic authentication - -To enable HTTP basic authentication, edit `config/packages/security.yaml`, and add/uncomment the following block. Note that this is enabled by default. - -!!! caution - - Until [EZP-22192](https://jira.ez.no/browse/EZP-22192) is implemented, enabling basic authentication in REST will prevent PlatformUI from working. - -``` yaml -ezpublish_rest: - pattern: ^/api/ezp/v2 - stateless: true - ezpublish_http_basic: - realm: eZ Publish REST API -``` - -Basic authentication requires the username and password to be sent *(username:password)*, based 64 encoded, with each request. -For details, see [RFC 2617](http://tools.ietf.org/html/rfc2617). - -Most HTTP client libraries as well as REST libraries support this method. - -**Raw HTTP request with basic authentication** - -``` -GET / HTTP/1.1 -Host: api.example.com -Accept: application/vnd.ez.api.Root+json -Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== -``` - -### JWT authentication - -After you [configure JWT authentication](../guide/security.md#jwt-authentication) -you can get the JWT token through the following request: - -``` -POST /user/token/jwt HTTP/1.1 -Host: -Accept: application/vnd.ez.api.JWT+xml -Content-Type: application/vnd.ez.api.JWTInput+xml -``` - -Provide the user name and password in the request body: - -``` xml - - publish - admin - -``` - -### Error handling - -Error handling in the REST API is fully based on the HTTP error codes. -The most common are: `401 Unauthorized`, `404 Not Found`, or `500 Internal Server Error`. -The REST API uses them along with a few more, to allow proper error handling. - -For the complete list of error codes and the conditions in which they apply, see the [reference documentation.](https://doc.ezplatform.com/rest-api-reference) - -### General error codes - -A few error codes apply to most resources (if they *are* applicable): - -|Error code|Error message|Description| -|----------|-----------|-------------| -|`404`|Not Found|Returned when the request failed because the request object was not found.| -|`405`|Method Not Allowed|Returned when the requested REST API resource does not support the HTTP verb that was used.| -|`406`|Not Acceptable|Returned when an accept header sent with the requested is not supported.| -|`500`|Internal Server Error|The server encountered an unexpected condition, usually an exception, which prevents it from fulfilling the request: database down, permissions or configuration error.| -|`501`|Not Implemented|Returned when the requested method has not yet been implemented. For [[= product_name =]], most of Users, User groups, Content items, Locations and Content Types have been implemented. Some of their methods, as well as other features, may return a 501.| - -#### Error handling in your REST implementation - -Depending on your client implementation, handle these codes by checking if an error code (4xx or 5xx) was returned instead of the expected 2xx or 3xx. - -## REST API countries list - -Countries list is a REST service that gives access to an [ISO-3166](http://en.wikipedia.org/wiki/ISO_3166) formatted list of world countries. It is useful when presenting a country options list from any application. - -### Obtaining list of countries - -To send a GET request to the REST API countries list, provide the Content Type header: `application/vnd.ez.api.CountriesList+xml`. - -**Countries list request** - -``` -Resource: /api/ezp/v2/services/countries -Method: GET -Content-Type: application/vnd.ez.api.CountriesList+xml -``` - -#### Usage example - -**Countries list request** - -``` -GET /api/ezp/v2/services/countries -Host: example.com -Accept: application/vnd.ez.api.CountriesList+xml -``` - -**Countries list response headers** - -``` -HTTP/1.1 200 -Content-Type: application/vnd.ez.api.CountriesList+xml -``` - -The HTTP response is a `200 OK` header and XML formatted country list with names and codes according to the ISO-3166 standard body.  - -**ISO-3166 standard** - -The **country codes** can be represented as: - -- two-letter code (alpha-2) — recommended as the general purpose code -- three-letter code (alpha-3) — related to the country name -- three-digit numeric code (numeric-3) — useful if you need to avoid using Latin script - -For details, see the [ISO-3166 glossary.](http://www.iso.org/iso/home/standards/country_codes/country_codes_glossary.htm) - -**Body XML response** - -``` xml - - - AfghanistanAF - AFG - 93 - - - ÅlandAX - ALA - 358 - - ... - -``` diff --git a/docs/api/graphql.md b/docs/api/graphql.md index 41f7a1efd1..2d41f6815c 100644 --- a/docs/api/graphql.md +++ b/docs/api/graphql.md @@ -1,3 +1,7 @@ +--- +description: GraphQL enables making concise, readable requests to Ibexa DXP APIs. +--- + # GraphQL [GraphQL](https://graphql.org/) is a query language for the API. @@ -11,7 +15,7 @@ The schema is generated automatically when installing [[= product_name =]]. When you modify Content Types in your installation, you need to regenerate the schema: ``` bash -php bin/console ezplatform:graphql:generate-schema +php bin/console ibexa:graphql:generate-schema php bin/console cache:clear ``` @@ -19,11 +23,16 @@ YAML files with the schema are located in `config/graphql/types/ezplatform`. They contain information about the domain objects and the fields you can [query](graphql_queries.md) and [operate on](graphql_operations.md). -!!! tip +### Schema generation limitations + +GraphQL schema cannot be generated for names that do not follow the [GraphQL specification](http://spec.graphql.org/June2018/#sec-Names), +for example names that start with a digit. - To make use of enhanced Location handling, you can add the beta 3.0 version of [ezplatform-graphql](https://github.com/ezsystems/ezplatform-graphql/tree/3.0) to your project. +This concerns image variations, Content Types, Content Type groups, and Field definition identifiers. - See [overview of the upcoming changes](https://github.com/ezsystems/ezplatform-graphql/pull/90). +It is recommended to rename the relevant identifiers. Failure to generate schema is registered in logs. +To find identifiers that are not included in the schema, look for "Skipped schema generation" log messages, for example: +`Skipped schema generation for Image Variation`. ## Domain schema diff --git a/docs/api/graphql_customization.md b/docs/api/graphql_customization.md index fe85edf7d4..e5aff77329 100644 --- a/docs/api/graphql_customization.md +++ b/docs/api/graphql_customization.md @@ -1,3 +1,7 @@ +--- +description: Customize your GraphQL API with a custom schema. +--- + # GraphQL customization ## Custom schema @@ -6,18 +10,22 @@ You can customize the GraphQL schema that is generated from your repository. You can use it if your application requires custom GraphQL resources, for instance for Doctrine entities. -To do so, create an `app/config/graphql/Query.types.yaml` file. It will be used as the GraphQL query root. +To do so, create a `config/graphql/types/Query.types.yaml` file. It will be used as the GraphQL query root. In that file, add new fields that use any custom type or custom logic you require, based on [overblog/GraphQLBundle](https://github.com/overblog/GraphQLBundle). +The custom schema should be created only after generating other schemas to avoid problems, especially if the custom schema depends on other schema elements. For example: +`Type "Domain" inherited by "Query" not found.`. +To avoid this problem during deployment, add the generated schemas to the repository. +Update the schema in the event of any changes related to GraphQL as well as when changing the environment, for example from `dev` to `prod`. ### Configuration You can include the [[= product_name =]] schema in two ways: either through inheritance or composition. #### Inheritance -To use inheritance, apply the following configuration in `app/config/graphql/Query.types.yaml`: +To use inheritance, apply the following configuration in `config/graphql/types/Query.types.yaml`: ``` yaml Query: @@ -33,7 +41,7 @@ Query: #### Composition To use composition, define [[= product_name =]] schema as a field in your custom schema. -For example, in `app/config/graphql/Query.types.yaml`: +For example, in `config/graphql/types/Query.types.yaml`: ``` yaml Query: @@ -49,7 +57,7 @@ Query: ### Custom mutations Custom mutations are created in the same way as custom query configuration. -An `app/config/graphql/Mutation.types.yaml` file will be used as the source for mutation definitions in your schema. +A `config/graphql/types/Mutation.types.yaml` file will be used as the source for mutation definitions in your schema. ``` yaml Mutation: @@ -79,3 +87,15 @@ SomethingPayload: type: String ``` + +## Custom field name + +You can customize the name used by GraphQL as the content field name. + +Use this setting to avoid conflicts with Field names that derive from a Content Type definition. + +``` yaml +parameters: + ezplatform_graphql.schema.content.field_name.override: + id: id_ +``` diff --git a/docs/api/graphql_operations.md b/docs/api/graphql_operations.md index 840a93e9f3..3574821477 100644 --- a/docs/api/graphql_operations.md +++ b/docs/api/graphql_operations.md @@ -1,3 +1,7 @@ +--- +description: Use GraphQL operations to create, update, and delete content. +--- + # GraphQL operations Operations on content in GraphQL are performed using [mutations](https://graphql.org/learn/queries/#mutations). diff --git a/docs/api/graphql_queries.md b/docs/api/graphql_queries.md index efe95d2a08..32e4ace8f9 100644 --- a/docs/api/graphql_queries.md +++ b/docs/api/graphql_queries.md @@ -1,3 +1,7 @@ +--- +description: Use GraphQL to query for content and Locations. +--- + # GraphQL queries ## Querying content @@ -43,6 +47,39 @@ Response: You can request any Fields of the Content item. In the example above, these are `title` and `author`. +#### Get language versions + +To get Fields of a Content item in a specific language, use the `language` argument. +The language must be configured for the current SiteAccess. + +``` +{ + content { + article(id: 57) { + title: title(language: eng_GB) + title_PL: title(language: pol_PL) + } + } +} +``` + +Response: + +``` +{ + "data": { + "content": { + "article": { + "title": "Most interesting cat breeds", + "title_PL": "Najciekawsze rasy kotów" + } + } + } +} +``` + +When you do not specify a language, the response contains the most prioritized translation. + ### Get a group of Content items To get a list of all Content items of a selected type, use the plural field, e.g. `articles`: @@ -183,12 +220,6 @@ Response: ## Querying Locations -!!! tip - - To make use of enhanced Location handling, you can add the beta 3.0 version of [ezplatform-graphql](https://github.com/ezsystems/ezplatform-graphql/tree/3.0) to your project. - - See [overview of the upcoming changes](https://github.com/ezsystems/ezplatform-graphql/pull/90). - To query a Location and its children, use the repository schema: ``` @@ -429,4 +460,4 @@ children(first: 3, after: "YXJyYXljb25uZWN0aW9uOjM=") ### Get Matrix Field Type -To get a Matrix Field Type with GraphQL, see [Matrix Field Type reference](field_type_reference.md#graphql-matrix-field-type-query). +To get a Matrix Field Type with GraphQL, see [Matrix Field Type reference](field_types_reference/matrixfield.md). diff --git a/docs/api/img/create_content_sequence.png b/docs/api/img/create_content_sequence.png index 3cc706ccbb..01bfc48fb3 100644 Binary files a/docs/api/img/create_content_sequence.png and b/docs/api/img/create_content_sequence.png differ diff --git a/docs/api/img/diagrams_source/create_content_sequence.xml b/docs/api/img/diagrams_source/create_content_sequence.xml index b6e6a78ab7..8001c41e33 100644 --- a/docs/api/img/diagrams_source/create_content_sequence.xml +++ b/docs/api/img/diagrams_source/create_content_sequence.xml @@ -1 +1 @@ -7V1bd6O2Fv41Wat9iBdCiMtjYiczczptMyc90+lTFwHF5pQgLyC3+fUVBmGQBAYsCOOZvMQIvMH723tr3yTO4PLh5V3sbje/Eh+HZ7rmv5zB1ZmuAwdC+i8bec1HbKjlA+s48IuL9gO3wVdcDLLLHgMfJ7ULU0LCNNjWBz0SRdhLa2NuHJPn+mX3JKzfdeuusTBw67mhOPpn4Keb4lfo1n78PQ7WG3ZnYDr5mTvX+2cdk8eouN+ZDu93f/npB5fRKn5osnF98lwZgldncBkTkuafHl6WOMx4y9iWf++64Wz53DGO0i5fWDrRLw/L9y/wo/ElfX4y/vf86eIcaEZO58kNHzH7IbvHTV8Zi3DkX2ScpkcRiejgpe8mG5zRBfRgkz6ExUf6KPHrF3qgLQzDYAN/Vc/e4Dh4wCmOs6vooPgzil+G/RpwxY96hwn9dvxKL3jew4UKHm8qSLGxGIduGjzV4XYLqVmX5Mo73JCAPomuFRKu2wWdQr4tRpeRSMhj7OHiW1X+84QsZ4G0/R+okQUmRzZ14zVOBbL0Q4UJ+6Ed2L2Ahz+AbwX+HOiKkM8oLQBwyj/b4bCHY2Fvo8Mo72xYCerzJkjx7db1srPP1ObXgU7SmPyDlyQk8e7b0ALOEjn0zH0QhpXxFbqyV5kkEEorSLOfClthf8Jxil9agS/OOra9QDUOIjbpVEQD6oBdVZUOy2wWhBqjW7hqWifIVQTqPAW2yFPDkeib7mjHs9QSjQ81A7fFIYnTDVmTyA2v9qOXexZrnDV6CdLcGOmoOPyruOr/OE1fCy/EfUxJxsqS+EdCtgWJ4dbpSLMDtbrVAd3mhd50GozXABsDdEEbbqm0X3geTpKLZ8pCenJJopSy8hbHTwFVgqO05Z7SYpLBjis6cr37E3Uns7y7cUHblvRvd+YYNWJ8RoLWlHO7bJY6RmmAKXD+v3hLkiAlGSEzpPe6vIvpp3X2SSkGdd5qmq0jr92SdURNgSnTedmfEhO7FybXAQ79Fb4PoiANSDQRKLZ+B01zWlAs6w1BcUQTdfPhDC2pU5oECdUKr2Kk6Ph7N/JD6qqqhAOBS/PCmAscjvl2cJRPy8Ox04Y/Xre4QVFuqRJl8bxKWFaOvjJ0GSx3NjKQ1jbB1OA6HhMA0BuCIon/jnfB2Oed/7VA1VCxjAz35472znKvpu6X5B5TfUydF9eZvWK8Lcq8+5DJanSXbHc/UqoD+aUKFcB3dF+uAOWZqRQA2W8o/+IcISrEmyREqkINrY4CPFF2LEtq1SCzOgYaHUiZqE6pIfShiLivlcu22QWJAH2/oAaKU9QMpYHpwkykweR8CksfKAsmEmShWxg8jiyIAe4cZQHNShZspEgWbGNWduFH2rwdd6AZitLmQIPKke98J2jXqmn0Q050sNzMts5WtSFWV/d4IlnSoSIjAqh7NycrMnoBYZ9SaMgJNcpDjzQbl9FhdYCKcOgS4TANBd660W9O9kI3SQKvzrS5m1KHC4agM0z4eZwEQlOKviFOoF6M3RQXybeffhZwpOKYtol7YSyrkl4MuWGwjjL4KeHMEl5mwh14bnhRnHgIfH+XyJDpVz25oUBhAJ9zY3nJatlSVmFToTAdKsGHFGY/7dRSN+xg9VI7ej3EuGpiBjmzUj1B9xx7mO4ZPOQ8IXW1fkOsBvVHmFVOK2k7Nr6HNzs4iG7Vr5gZugZ7ngIUOl0OtKycmAiEFKLbwWXorL/n2gIgqQ53dhuryjuzNASPSlkK76286AAhhfD2yz62w8tl1qFjNMHbZrf3XRQWUmsOmLxUZWhmiU2Li2Ih77R2liHeRPDdMupkCCnwiVtMxEKz0HFmYl7RJW8mDA0OhBjxhMaDuF/6qX2OX6hW63nhy6uwYYBh+AqCwhNSiK+YJnI9D2/Tz9nAKcdGkHeUZe2HI8VGSIyN6MfAp1Hp98RyfUqWi8FKSirdOKcv71xSBlpirXs05ndwNo9LP+5bZ+rpx6trS0PLlo4CBZw1uem47AY8kJdUkZZkGvWGnB2Bg3BKDs628D7jiNfkEhqDa24jFN673ogZQFUVN+aNnpYqGlOqosJgo94HqNcaAevZ5PxkI+sO5RFm1gZhch1tcGh7lA0PEFIXhJj9atW9clFlKonPMhenuuGOZm+RBbjgUNy5KLZ00kfAffTFglNUpXmGTeq89CvBvJXzMi8TaRuKnJcROsW63kh1u5ApriLaZstRtF0LuCBTJxMVOzq/tFcWvclawFXExebocXGTASzXBingIdC4uQd1tIBABQ8tFVXo5j4DuTvp6K3Z60NVrkFWdG69choXKdDYZMF29xjQLscR08fbokD0NpOUxDhfGemm7ilnAYFm2QsL8cAJ6goAGMfk2aK6fhcTDdAFdZlunrFH2u2Ar+cpXU7HCvSDivbKl9PZooOkcpoZ0s42k4mAr1aiod0vzX7ECJPAiO0vNtB7h0szwRIYoA6B0Ds9tIZNCS00a78lUbeO4AHIsvsK/b6nPK1bnOYYE1ZWnX4FlNasbdYEVPO0Hf2Apy3xpvd6ueA007HhIe98cJJDuuuZM68mM74xf/DyFge201GozrKaQL5OPdm6EVupTu/2Oe+gIPGSRPfB+jF2sw1OMq3PL6d3qn6jgzGIcRJ8de92F2RyUKRp6NXo8gytMjNAnZwk93dAxSqE+D6V2IQ0c4Cyx/aCaP3Hzhs6VxSD82l/2doY6Up2JSG4ftgCzL/yJcagkrhIlsZQsRmAM9vlenNOQZR7KBy99HNey/XQh3dodXmt6xt8hT5/eudFqy/nEhGp7SZ0vXS9TWVXISVbCh27R42gsSb9U9V4AyBXeply+w45Rv0WPb3Rqlv5o5vz0m2D3w118FJcA8xftzsksceZQctNClXoIx89Iq3bDApU1ELljO23iumAQnaaJL9N7Ro8c34L2qUi2SPf4lXBKoa5OVWAz+c5HJ7d83l8ypunpHAdgwx3wPKHShrKFlptb9+FlicThqUmWKJDuthJSW5iLuKkIVAXAqAvHHOgRBmazREzMn2cVqjEfNfpJxgBnzoyHF2Y2kcrGjYAoaJdtK0n9JSqMqbDkRi6oYBAaHR9E+Ne/BBQbl2IG1ayET94Kjehrm72cfWUwbVPCFauk3y1mT7/1VNVe8jN4KYFBK0fbdGQXBrECHsH6ipItm7qbZQnPHrvJz5iwgPyu4S8db4DaErjq0rCo7YUvDzX1xhX/ezfrU+rD5t1ZPz90V35vwe//ImtMql2MIArBW8m9t1QVcqhhBYarBRmzTrd0byrVjym74hUmQhpnDMP5EF4V3aInsrZOlZeUo2aVrWvVSzmqnxoqHPFEbK5wGhsbVPQO9sI6lyw4t+nBgYGn/z2DgKhscGSTbWCzxpjD++YddHZzf3hIR+zgzzvITOdmsBDlreciDPokvhZXfAxCaL1WXZjHrSbx7uQ6rWuXdx8OG665SbVe4BcoMmm4fLMhK8gOedTm1P6z3KwkGhvv+l3YLSK5OF2qa7TvPL2XvnjfBOr647j5FTvtmQ7j5aT58BwRXhL5njbm7V29I0XoOwtY4MtVWIJoZyLU2ylLOer2F//3WzTe85e/sGmJcn6ubG26ZWDoaBg2siX+ZgkzhkYmkHhd7vu2Aw7Sv274d3LY+1xoz787/zCrIn29FO1VBy0Jdk6dkyok4cOdfFj3P6R33zaoHcAdprD2Kt/j7Ga/zEMB/5mGp/Jl5RcLa+3qfPSKXfZzWpywi/h01zKisAcmPlC/PaLPCF1OiAHS8yI9qkqXuJ7EpdvjayuK2jNptQXIHxPSRZBrTsJdVn3qrd9ALYhqvokCz2MCUmrwkZ/1OZX4uPsin8B \ No newline at end of file +7V1bk5s4Fv41XTX7YBcCictjt92dZCez09meTTJPWzTINhMMXqBv8+tXXIRByDbGwhYdnKq0kfDhoo9P56bDlTZbv36I7M3qt9DF/pWquK9X2vxKJR+kkT9py1veYuhG3rCMPDdvAtuGB+9vXDQqReuT5+K4tmMShn7ibeqNThgE2ElqbXYUhS/13RahXz/qxl7iRsODY/vN1m+em6zyVlM1tu0fsbdc0SMD3cp7Hm3nxzIKn4LieFeqtsg+effaprKKC41Xthu+VJq02yttFoVhkn9bv86wn95betvy393t6C3PO8JB0uYHMyv4dT37+Kp9ht+Tl2f4n5cv1xOgwFzOs+0/YXoh2ekmb/QW4cC9Tu802QrCgDTeuHa8wqlcQDZWydovvpJTid6+kw1lCiGkDX9We+9x5K1xgqN0L9K4CIPkzl57foqflzD6kR7bDuKi6yF8ipz0PFZJQhBB4HZN/iMXmf6X7hBPl2G49LG98eKpE66zDifOdr1b5ILJ10I0Um+qwotrVJr3sri92K2hp7izH3BILiEigpWXLWZQIWZVgQtti7BvJ95zHXN2Ad1lKa48wn3okTNRFfqYmYUc+pBRuVREnN2m4ldVELCCDGuKlO0H1MQCnRGb2NESJw2x5EvlJmybMsQdhT5tRJ/86JsAVRD8UklTAKzyY1oMALW+AGiiw1DL2LxE1svKS/DDxs4A8EJmvzraQtLjJemJa/IgiWx7vj8L/TDKLkm7Vcg/k7THSRT+wLyenfB7xlGCX/cCsOi1THOKaiOJtAIiFYhqKqB7VVFq6LsBWRvwPaNLlY5xdHsYXQTqYwvM5thCi8M/qqWcPrRGc0YgtEhvShglq3AZBrZ/u2292Q61wkwRr16SzxAqKjb/LPb6CyfJW6Gf2k9JmIKgFP45DDeFCDmg0HnKOHEu0JT6VADaaQxHy9kxo3QgfqByqEH3k+KWpqaLndiT9PskjpxsD/1/T6lafpOPYTqCZPx2jF42duXIafNv2bjdPKSjVgoi35bp3wdCOteOg+P4+oUMHjn4jAglg/iAo2eP4KY4s+Jo9GencVmDRMh2hSrusk+TXGxMjB3II5ey51LPwimkRgGHGhxWqr88HeoUCgO6RBD8N96EsZeE6SXk5/AY0T55wago0EVuA4yFRTBEHFKLjCXCc+LSHAgu7zzsu3O88AIv8cJgAMCs9gwYnYZxQXRaEqHz4f7TFZrd4yj2YkKRTmXqJu0f7cD1cSQTLhfAtPVHHi7LngHj0tIvh8vybOXBZcaPf7xt8A7qfCC0mvrBe8FnHXdzS51DlYe7RxNBtBfBHKQPFJ8AoAsClONEPd1kpt8ze3mKqv7W0r267Xsf1nRMD1+xI3MLt94mzupuPcY8z7k8JGSvU5YIHuNNvhuPlKp8JXzGbMUvdeZyHxVF4zJX2TNgRkLmBQmJp8mNYR4ey2hGS0Y5U+AxjRfWcGO09NS1EKWjuqQdvkMCC/utstsm3SFu4O84r6DGU+JGSPIgSVlBEkjqjOpvqB0BqaMGINs5s/sBJM9NPQKSC0gkFSBNJAiQJpSKIcfcjAHkZgAFCsrNAIomHH6tj6SZteQ18iUX2hm8Y1pbWzY12tqwZwK0qgmiU0DsDpn4VHSKynACEe1AeUTMjIlK0DSTCkJVDkJ1KMCWhcfpaY5vx7HnNB0U8o6DJERg1ccYaFY3GmDB0hB0ThKAMrntnAjbCS6iWL/8o60vjjykSR3P/Jh8hZ6KJtv3lkH6UJDjpTPlTfrIe47tXxcda891M280j/rqHmq5n6EjIgVsJIuGPauJk7zcOhFc1iIn9hCXbXWjWhCAbsxfa1tvgxi9qosfWVKxYoMWLbMbLUIWd6wgcanXkJf+dCzMaOJmJQpF27cYSzeGAbGqBi4ZxCA9nwIZxDjrOPMyWG0IEgixFsp1ayabKFOAuGw2LCuvSmOSOXFZaJTpwEfTGDogSCDGjoti7ccYEzLXLLgLY/um0W06u4HeITFS0FaBLFmAzGDcbxprY7YGMkuW7NoJcUBGAkzYPWQ5VQz0DghTLrcYS5iQLp08lTBhf4SJjosg7Nf7pu+S4OQCGUtmEIJuIGuglRUkEGQ8T/+lPCq24+BN8jU9ldGfchl/isbatbzFij35UxDPn3IpLJKT8MjhRiBKAkT1nECUacFZElaWTYzceElIMqEQzWgmgfYGyRbWs5gsX2YdzAIaQOOFRcue9zK8OqPal0sLD8RFRYRFKdn1Fd3+iYdRO+cwjmmxA/Wo6ozXvnMeWA9psW0PROcjUVlg1LweSUk4KcFzkpJAP1J9FZtaW8ZWj2DnnXKP3yFnuWSZ0jqzBknrupbE1A4IEudf0o/LJD0q6lMGbdjIdtE1IPAh6SfIBma0ruBjvKSlYd8D+EQXrduTG7ojm1RuDLafwNhRO6tWfVwCzE+tVcs1Y5lQkFbdw9qetgcSvbZCl6lq0CatuqJk68lHn+IleNVS2dKfPLcTb2W5CK+iLtyrOJgqOsITkRVGQUItZ0ggYiANETmiu1OR+dafpV7tyyM4lHk13FlWtjVfCuNdQBBNaWX6Dsu+GGG0VkoPRaVlSj6IkzDCeaU8ctAxxnYpGjXMqYFYODeYFADQz5RoyhT7HdWzS8NRbVDr+bQzs6fa3Wwq4PsrNkaznDtlPgsvNmbyLD5xytlg14lJoj6x2Zao62KK3SZAD6pTj6spTKAO0xMmCaAABHUcNFbOd00EJoKmirF9/0u7pdgd4EWPK4UWlC+0HjXyy6hABkNq8IyJmNZx+TR7Q9fpSpua/8JSD/gvOD6KLWVOGdK0TO2Qz2N4hFrVn7ivILPkWk7GFqvoXPzG0vbLEci0MpW0oEeON3ZA28h1fs1z4cNoFgYLb/kU2elrBGqEXP1Fd56OcOz9bT+WJWaLaA3ZG91coXnK0MQ+iXNTBVQI28eLhEPXSWq7pFfjeMHyj8yQmUDpn7oj0oqYjBBeKR9g9ETO1As6pocJcDOwTleOy4sXPBBRXtgai70N1fFfloY+Nb4uWbE39OkDmt/cqeoK36KvXz44wfz7hIvT88+UysIu3Ey0Ld9R4U+ptXee3M1sZ1V590nfLz45VMadw7rQeMQuN2Rb9oggPI3JJzpnPXU+uo4r5vSzsWCt4A73/ulyUSNkX7rbuQ4mBPJTY4vw/ymqWAtSkBu+RzAT67xDSjtVDIhIdeSP7nHVmQ5Qk9wDVasdN0ie6ayCDYFnREQd+O8Qfi81SWQzEQAb3bIYULWPbrGhcFaSwKokPPABGk0TspBpqtTeYD1Vcv9tN28w9S1zizi9H3ewLJhWEKgjEahTOi8cT7uKyQiDKTOdF9m8OMcYbvsZw22AjWFAS23on71lv+2Ap4gVpPuWiY45LIJzWHSLEdG1rnFDUO9UKIeXL+c6vPaSTN4OJmRfzEhbXO+ZbTr/yc+q9elvn3F24vuugzRzTpzT1Lxg9qfjFNFnHSJGEdcN0JgheqtDxH9oZarclkF97sUbO3FWcr1VXGI/mghcssX1L+3TB4pQz1nFqV8r2Vv2DVKDqTovfje+zD+tlgH872d77v7u/foNG2Xc7aBrrqQBSZQiKCobiAiaKlol7VKvy+3NWtw7HqOz/2TG2qntHvD1s/6BLozFH9u+opDviLCqPLT3AZGVhlBX24wRZDIur755R8AaY7mRJQtgmKw+A3T0bbIFyRuC+kYMT/2SyEKOsIOzYTpk4be2jEcrf7TyBekmiLXyKf+ewcrn5/rL5JmbhW6aR/cUe8HyKr1kFrP3T48+mX1U5fr+k0wuAImzmgWAdsKGm8/pA+CjFjWVlNMXUddXEtXKYDKB4LTv/S2w3ksQh1cNtVXQhS/F5p/OWF/utEVgctlbk/IdsKXa29HvMwFMcld/LxXbu7puXGEjYGLS+EN5jpee8wdXpjqA47usZcCoVk9rApwKcn29y5oPUQHpnnIPjjxTFqM2dw1VGAfknDOFmIspfq7b6OLeqXIBVSqoGqIq+oJ9IbWWme/iQNkitfgUTcvGAOiQp2mVPXIj8oh6IQwDAa2VoqUap09i/4TQ0v6lw6/h9yS8nd1tEuu1Vbh0nMQEMAObgwj0jnEuxL6DkhUkjg34iJEpm2nQKYg3eBFGmFdVZG/YolUxkTGacXnmOSY3rL7KAtBXGouPZpDNKExhuOUEcmdXv4UuTvf4Pw== \ No newline at end of file diff --git a/docs/api/img/diagrams_source/field_type_overview.xml b/docs/api/img/diagrams_source/field_type_overview.xml index d56bd41c70..a954d0f1ca 100644 --- a/docs/api/img/diagrams_source/field_type_overview.xml +++ b/docs/api/img/diagrams_source/field_type_overview.xml @@ -1 +1 @@ -7Vldk5owFP01Pq4D4UN9dK277Uw749SdafsYIUK6kdgQV+2vb4AECODHumDdaR0fyElySc65N/dGe9ZktXtkcB1+oT4iPWD4u571oQeAaQPQS76Gv88QdyiBgGFfDiqAOf6NJGhIdIN9FGsDOaWE47UOejSKkMc1DDJGt/qwJSX6W9cwQDVg7kFSR79hn4cZOgSDAv+IcBCqN5vuKOtZQO85YHQTyff1gLVMP1n3CipbcqNxCH26LUHWtGdNGKU8e1rtJogk3CrasnkPB3rzdTMU8XMmyA3FfK+2jnzBhGxSxkMa0AiSaYHep9tDiQFDtEK+IuLRFI9oh/n3BO4DRzZ/yFE/Eed7qTPccCqgwvhnStfSRH39ckvJskqA3M0joivE2V4MYIhAjl90AaH0gyAfl0+dUSxeAQzps5YhFdnrnqgscMgCxOWkMqGvtBPTDfNQzY54KO2mgFK5mqVTMdaudmZZuH4qYySWlHcljaLv5mU1B7oc9nlyvNrOAfe4QNZhRyFZjkjzPUgHhjrlrnGhdicNtSfeaHgd8fKYLIXouxHWsnQ9Lg7Kk4baE9Z0pLIvkGyQyu0VqQshE363IeZovoZe0rsVdZIubswZfUYTSihLZ1tL04Fm4gFLTEgjTiNewh/Sj8SVhzUUABJ6QYyj3VFdZW8lXFRzWxRCpipfwlIRZFfZL3uCRv1RnkcNPLuEJ4ytYSSeA546b4Yle9dEcH9tqOq4i9MoGIsBwF7vik5l5QEj4j/t10iMmM8+KaNikZld/V0C1pdQkV/Qy49pHNEIVeSVECQ4iETTE6Ihgd8nYmFRjo5lxwr7fnpWNDmVfn4wykVw0mTW3eiabnNXSZHANWuOY5sNjjMYtuA3bufxaaSfhvjM6vsjB+r5HFZjLz/RyhwaDRzmEfk2EpuC792TmBN2HRIH4DSJKPLHyTU1iXkC4xh7lcyf19x9wxjqdbdpuQqYIYbFApMjI419H8ZhLs1BJrOsWMlrJ5N+iTyngTuFvbU2sCvXp4trg5FuaNRdaTCw/nrUnCf85SHUUAN0GUF2mxFUv7UeJChzrkpGueVwqepkXXpHqpXSTofx4rxd3oMS3qgwtnvp5dUAFUNmd8K4DcJkte+i3cL763T+JDrH59TcixsvuNs+ey0VHyoFWvXDFzTV0e2cvYNr+cBssyAirP97QbMXuIO+7gd5CVP2A+B05QdN96lu/ACxGMccRd7Z1/B/zxl0V7CdBlewOjsSRvX0fJO/SGvlm0pmWvmmwPZ+4jzAomgWf+Jlubj4p9Sa/gE= \ No newline at end of file +7Zpdc5s4FIZ/jS/jAcSHfZmkcbozuzOZdWe2vVRBBjUyYoUc2/vrVwIJEJAmsSGhaXzhoCNxAL0PL0c4M3C9PdwymCV/0QiRmWNFhxn4NHMc23V88UdGjmUk8IMyEDMcqUF1YI3/QypoqegORyg3BnJKCceZGQxpmqKQGzHIGN2bwzaUmEfNYIw6gXUISTf6D454UkYXTlDHPyMcJ/rItr8se77D8D5mdJeq480csCk+ZfcW6lzqQvMERnTfCIGbGbhmlPJya3u4RkTOrZ62cr/VI73VeTOU8ufsoC4o50d96SgSM6GalPGExjSF5KaOXhWXh2QCS7QSviVi0xab6ID5VxmeO55qflOjfiDOj0pnuONUhOrkf1KaqRQbmnJ9dEu1V3CLiQRpT9m9PFmY5noo3bFQjk04F2g4HrgUX+Jq5ZcckM9jSmOCYIbzeUi3RUeYF0NXmzKx2FSpPedKJe/Oo5paOT2NgJrVW0S3iDORy2KIQI4fTJCg4jGuxlW73lEsDuFY6t4BliLjaN4ROgOHLEZc7dQU9oV58mLmOnnERuNq6lCBTT9Ccu/hGbKbAM0LnFJxSlWXbNR9H3g9Ey87MLFwn4fFi/M8gukJeC1GsqimQ9knItRjt6+ko7Mw59+3ThTyyUTDKblcvI6SlVE0fOMclX9LowDA5OJkp3gy0XCA2Z4i7AGSHdIFWAu5Giip8z7BHK0zWIi3F8WsCZmU7poSyop9war49OGBCWmM22zcwAYinnNG71Ffz+SAekCMo8NPkVK9LcfQzX1dKNu6vE0aRbLbFr4JoaH6TyVe9kjsEy4nO4Op2I55cZllTE6Vob//747qjou8MAIxs5bjZoe6U2dZYUSiL8cMiRHruz900lkpQftYImyeQos8Mb3cxMvEI6UparGkQpDgOBXNUIiGRPxKioXFcuVSdWxxFBV22cezaaGMcuELVO51sbRewvivS+xFq1pxfLvDrGv3MBssBkDWH9qVOq5iFZ8+HyqWnu9Ky7b9VM+TppZWj5aVKZ0nZp//fIg5kJiVcK8jZuA8LSZKo0v5RknaL4F5jsNWHVotS+eWtTCXpjbwdeAOMSxOULp3IVoE86RCZPKKljVaq8p6sgRtiOj1aKhj51aqbutNx8mV6tJMtByvUA3Am7vIrwXg6ZbSU56O6SjukI7SfdE1eaFyfcRm5TNl+2jzAk59k9JZ6Hoj+od3PmaTR2migLj+qa/aLKeVyB4PEL8HkLGWyX/frL+Izst3skLuPBnf4q4w787zH4tAW4aukkD3uej0LYGHeSwGr4jj3e47EWb3AeS0gfSDuYlkVXA3kXS8sZDseyszGpKI5TjnKA3f06vEd8qlSaXr9VAJRjPKZbeOm8qvnZMsEo31hq56jPWGDg73i9kjaopm/Y87ZdFW/3cUuPkf \ No newline at end of file diff --git a/docs/api/img/diagrams_source/load_content_sequence.xml b/docs/api/img/diagrams_source/load_content_sequence.xml index df1120936a..70f09ba24f 100644 --- a/docs/api/img/diagrams_source/load_content_sequence.xml +++ b/docs/api/img/diagrams_source/load_content_sequence.xml @@ -1 +1 @@ -7Vzfc6M2EP5rPNM+nAckBOLRcZJL2+tM2nTmcnkjINvcYcsD2In711dg8UMSOBgDIW15CVqJBfbblXY/4UzgfP36OXS2q9+pR4IJ0LzXCbyeAKDrmsn+JJLDUYKhdhQsQ9/jgwrBg/834cJs2M73SCQMjCkNYn8rCl262RA3FmROGNIXcdiCBuJdt86SKIIH1wlU6Vffi1f8LYBVyO+Iv1xld9ZN+9jz7Lg/liHdbfj9JgAu0uPYvXYyXfxFo5Xj0ZeSCN5M4DykND6erV/nJEhsm5nteN1tTW/+3CHZxE0u+Iv8+sl0vxpPM/TH4+Pu+5MJbj7pmZ4oPmQWIR4zEG/SMF7RJd04wU0hvUrfmiR6NdZaxeuAnerslLz68WMingLEm9/4qO8kjg8cfmcXUyYqlH+hdMtVqK/FnzB5rJKAv+RnQtckDg9sQEgCJ/b3Iq4Od49lPi6/9J767BZA464MM8fljszeQNAQO+GSxPyisp3P1BPRXegSRQ87Kb1NIUpRPAtRcLzf3gl23BZzFrVMsov8zXKSuIkZMNtePYfsbJmc3e+eA99lXbP7XxR/KNBO8HlZ+TF52Dpu0vvC5gTRAxZ+EMxpQMP0WrjQkaMn8EdxSH+Qqp4F3cSZu+m8XRp3mx6nHGNPwpi8nnQN3muIyGRAvRRxr5tctirFPNLqfUmA73ysoILVn2RLIz+miXoZpjkzDXv7BxLufWb/LoHSNAyQWwWUpdtzZA8MFBgdUoaC1D0JIz9igCRQ3M4dd0VqIbtzNl5AwoEgw+AZmuawkGHtbciQNShkqI+lLTtP17Vpuspt2OPmXUmj6Lt41TuuFk3m++P61GRkdyvphQCZSkw9sACYuS6JotkLMwbr7HTOq3F/JUzEQDPZkY5TAi3v6SCAABzdnGcp+KgRtfFmSRbOWhu6SSLIc6JVDkg5gLIw0aaGYZRDJe9lM6rPnp2EPPxqrdqhr5fMi05Y98LkUtd1AVzUMCusUCR6ia41S1O7Sy/xWJ1iJFBDCaHWUEPNniJBFdYaYc1s7xxKw7bJgKgfb7DH6g1vL5wGGpXfIDmBMlv6jWHLfmM1myMG9JtscfuIfgPNUfkNxqLfWLCl32Br/PMNzNKqE35zVkp4gpeoYTJqPad5qqdrNYt4yWNAhccYWm+5HlRZo1MB6QZOFPlukxJJszBvX7/y4Esbh1Kjy+gcVzagS0yG3jY6gY0lReboglNlszJCxPP3GSMSUMfjRd1PP2f97IalIYrnsbiKT8UtXx/KIctFTuAvN4nDstsl7nWVRKnvOsGMd6x9z0sr/6qJQmQDuoh8bIso6mrkwwpvBP0FvkpsXTaj1hbZ4nxqs6O6qJ6zo6uiGsozLWg60/Zmb6TY+2bPXvDaj7ZO7K46Jgk9G3gGqDJz3jMgSWjqY+M4oMpBdZxQFGRsDX3bgV2xiaXUDVp2I0fv0bIfmD0aW4pvWQK4rSkFNcXXx5fid0ow1TrBSKCVq/7W0Cr0gZxojgDZ88iiyhpj7HjC7J2y+DJwOzwN+w1FfRO9xnkUTXVFmG+NTTU0EXbHLA1kgqIuTFuHcuujsb9IE8s0gFvHs6gIQnuqW3Z+YOPC8K65LbYqn7/2MaXxkAd54Y3HJ+jYN6toIKXS9JOSL91n7aLirPW85mkagpJxkVqNVO2Rw96SNAN0EOQZ7TMFAvFjway3A+JnLOEt7eNBjKYYa/khKWyel0mLN25G7nQXUCp1k/A0SbgMERimxGHD9w+MBrTIR89VZK8zgOR1jdcqHZxW1Lv7qqSKgtb4q3jDHt22gFFFj/xP6Yoflb5NMQ5L6RrnMS8fcu6Spxygt6yzTPyGot7nrv8U32HaHfEdsiIoF1rvz3cYnX4c89GQzXO4S5HVLW1syKIG3Mhl+cbNraWhuZpv5PIOVi456wYVm5HDbo4hta4na5/ddqZ+WV+Rh5QK/XRPrXUqoqQYHVgbWbJbv3eNgzoo/sc+K1lSdqZseDWdlWyITyvqO09AaokeEpekpvkXhIdlmaJ9Neu9w6MDCqC0bWlLBDguBOcQ4PlPLAWujSsb6iOrrN5usJM6runAwKKXGWZLyiP/8UquqL/v8H/cPR4en38D94vZty9P12gff/102UJ5RRY0JH3PB0rwV3hbfXIi7X/kPzwYYj6osnhni6UUCo2MMlRRLacoLflsC+HTinqOjvNm7r4+PRkv0EqJJaf0bWu1Ab8YZ83inycchxf/oQLe/AM= \ No newline at end of file +7VzZcqM4FP0aV808tAsQ66O3dM9WlZlMVaf7jYBiq4ORC2Q7nq8fAWIRwg7GYJM0fkjgSr6AdHR075HwCMzWr58De7P6C7vQGymS+zoC85GiyKqi03+R5ZBYDN1IDMsAuaxSbnhA/0FmlJh1i1wYchUJxh5BG97oYN+HDuFsdhDgPV/tGXv8VTf2EgqGB8f2ROtX5JJVYjUVI7d/gWi5Sq8s61ZS8mQ7L8sAb312vZECnuNPUry2U1/sQcOV7eJ9wQQWIzALMCbJ0fp1Br2obdNmS753d6Q0u+8A+qTOF/6Fv3/Sna/q94n29+Pj9sd3XVl8klM/ITmkLQJd2kDsFAdkhZfYt71Fbp3GTw0jvxI9W5G1Rw9leghfEXmMzGNFY6ffWK0fkJAD6357SzA15c7/xHjDXDxjn9zZa+RFeNrj4CW6OdsPWdED3gZO5GNFCEWIooEJ/UMfOvoTVQjHS4yXHrQ3KBw7eB0XOGFc9e45cUwPmWtNmRads6eWxLZlzRS1TcHAWvozxGtIAupYCqBnE7TjwWUzjC6zetlX7zGil1AkNp4AYNdmo4k2I+eB2MESEvalYmef6SeMm1HwQw8KT5ObYiidBSslud7O9rasLWaUOqhlGyJ/OYqwqnu0badPAT1aRkf32ycPObRocv+bAMocchFI9itE4MPGjpGwp8TEw5Dvy+R8hj0cxL7AXfyJ7MjzOLtqyIDaQxLgF1hVcit0HgXkDgYEvp6EJCtVeUSkANnnpCfrzLYqEJ4mHccwB5vzMQIEjPwDNzhEBEfuy/CY0eajT/8Agx2ibXwLgEiSOtfmVQDJSt4zQJTeIUQVEHIPgxCFFAgRBO5mtrOCR6HyxfZdDwZXgsqt5q2zIdoCVEzpbahoxlWhonURx6THcRAzjkMan95uVhSd5GUfI8QJ08u/ObknwUidmu2FTReiRBcI5YGO/onjwDCc7Glj0MK2J5pjbMGxCj+KJwuaZ6hVozgreccTjQJ6N9EYAi5EOvHdSZRv0jMf+xF9uHa4yoBQZI+UI6SxqqpFnshK6TSG6L3DoDCr9JcSWhzohT7WTnTxhWmULMscwrSa+U+FIx6qslQvIWsvkTIHZF6Sj18Hb6AEk8Z4A5I11jhXplQLcBQA9qFQbRNVCLuBpDVA8qL4SdV6BV6tHMzrDcGrWmXwGvXY8orgTWONAbwNwQv0XoHXNHnwGqAheE2j/8wL0lD7BHjPS0/ejQBaD771cxBZOhLYFWCrVMBWlTpLQoComZ+iJsezwxA5dYQLyTDZ+fyV0VB8ciicfDie6leEKJfEVbkpTymWWXKk946mRGE/1WhdtEtFWg/bLpNafvk1LacXLFQR4E8HN+EBzzMVm66LtMZMtoeWfjRq6OUijE8jqkCO7U1YwRq5biwKVlEmLxT2e2icwYGmxUNJFjkQVAwJpTsKFAX/Cye4egs7Pdbb2u50UJ74lLoTX2edrgmdvtjRB5yjcGMTZ/WTreCoxnQxn1ZhMStpAQZ6SbG4vQYLRG2+1eC2x4u3bY9xUzdLuQwwrFqjvMPuHST2yyT2viXehsEhrLHkKSbecv8S71ZV+H4jsSf4KquSjfElyJvlzKsH8DpPUa/M/AdQ1VmnSRs2ZRrVbAYq1XrDUdfrgmk4fpFYlO1lGUvaiNvOYkhKasglo/jsUDx7X3NsT0CoSbyCo5iNmY13BIA1lg0r+5jqhUR35LKmUXn/R2+zVB8wusuHRHIHLQ+QKq1cEKFQpAbFu7PaEKP6Df/6+YMGSj2siRpB1fY+0Fn2oCot0F2qjY8VTh03QFr6UdTxvhBdaRcOMLWxaUrZp+SwfsJQCujMegp4e9Qi6tuRmB0Rx09DEXpp3RXcniJqaMdDEN1K5s93ffYK4Nnxi6ycdtT5QBaVZwEyg+RY7+URq3fr6WqVoDwsQ/YPOjVWpK67DKmep1UPU0nzCJGfARS5oR6jm2846nwqGRTia4NHL005jRXisiNQFmRurxCrre65HuDVAF5ZhnMpvGRD6hu80iYbNpa2nhgrFZuqrru/RhOVULhG9LIT8c3pigC5II3G23Iax8hC7PtRulwzygP81lqI1oJc2u/e6ckkYZRyF2EDTd1JwgLmaUddB7CaKGoG0IFx0wxE0Q5RGIbOd7Jk3JooWhBNC3uxrNIyspkbzllGzn5GilunYc4+0DpN/VcFa2wP6xcxqiYPdVVvKBJnv9mQOeruDeyXL4+Hx6c/lPvnybc/v8+1Hfn66bLgaQqfcQAHZnwzai5tZcjee78GM1Z1+xBAXUsBLMfODVeFDc087ahjnjhvIv3Ztjf3BG2CFFPOuptqOld8V5ie5j8amlTPf5kVLP4HldFPD4IgFADwT8OxTaF/59T04lpzq3UkIaWhzyEt69OnAzPWpU48fjze4w8iQdXFijZlCoxLhD3WIRIijP05XvbDIA8jK7I2UCjBbNIEmXhyi57Vm2C8dRI1gNSicTGHuua5dowqBXc37QLS7drQgn9BllP5rUfBdGl0vfAmT7goyrGz79mVio7JFtqSMrh/EIkQCRSANlHVBVwOjze+S9KlLd5tztk+zJ7X6BQfyG5mim3/2fK+guK1/rV0H0xH6yfOB5PoBQ== \ No newline at end of file diff --git a/docs/api/img/diagrams_source/publish_content_sequence.xml b/docs/api/img/diagrams_source/publish_content_sequence.xml index 08fab13df7..cd66b4afe1 100644 --- a/docs/api/img/diagrams_source/publish_content_sequence.xml +++ b/docs/api/img/diagrams_source/publish_content_sequence.xml @@ -1 +1 @@ -7V3dd5s4Fv9rfM7sg3MQSHw82q4z7Zzpbna8O9s+zSGg2HSI5QOkSfrXr7ARBklgwMKQuH5og8Ay3N/91tVlYiweX36N3N3mM/FxONE1/2VifJjoOnAMg/6XjryyEcc5jKyjwM/GjgOr4AfOBrVs9CnwcVy6MCEkTIJdedAj2y32ktKYG0XkuXzZAwnLv7pz11gYWHluKI7+L/CTzWHU1q3j+EccrDfsl4GZPd+96/29jsjTNvu9iW487D+H048umyt70Hjj+uS5MGQsJ8YiIiQ5/PX4ssBhSlxGtsP3bivO5vcd4W3S6AuGffjKdzd8wuye93eWvDJq7J8Hp98AE2P+vAkSvNq5Xnr2mTIAHdskj2F2Ok4i8jdekJBE+28b9zaCSKNnHoIwLIw/2B72PDpO6FxBkrKKkV4mPkL2VN9xlOCXwlD2SL9i8oiT6JVews5qdkbfjAENlB0/H9E02DWbApI6zAbdjIPW+eRHKtI/MkLKiWoJFMQ+Za/skETJhqzJ1g2Xx9H5kcZamZ74JUi+pMM3OsoOv2ZXfcNJ8poJj/uUkJSW+eS/E7LLpqikaHpbtfSMcOgmwfeyVMhIk331jgT0J444gDIMU5ujbuJGa5xk3yqyaduJYvIUeViYaI9U/jyNwAOmIBB/4B2Jg4Skj2qGlITz+4j+tU7/WpBtgve3usLR94AKxVnSw8mIptk68mRSlZ95oDdQGL/df7JxxnIStdBBpsoIWEiQKGBKJAopECggKqm7p/swiDd/4igOyHaSqjkemhV2I2+TIhOS5L3CYjhD4uIIuKzuPk3Q4i6FJaaCkcqDxmQELT66Wz/EkVIwEJibMygDIz9zKTCQPSAY+e3Wg/E78ahKpyLz7tGwrCHRMAQ0lt/ps3wI4p2beBvFZPcd3Ye6jOz5mTJ5q2FQQHldG5LyolIS/bGtP0vDBHq0JdvU//LdeJPTvuh+benPH/wvCCEb+Fo8S+UroHeJo8wt6+5uFciDaqhzpldmOzcIQhPqNrIA/ddp5EpJpymjrKPyTBXeHaW8+1q4bJdeEAsQt3PbDFH7qUEd8ZiPHV6dj320Zr7yyYngkPDqP4W6FnVolqW6o1DvpxmRVItW9AzYKzE8UCcb0rLnPTxn2dEdCdimWZZMS+8o4ibi4TaHRBteSofvDz68ZLJ9OHo9JeklLsnTkgUuGZlKsJEiLrHhqLjEOs0l52Usj0F8RdhfySLNXWRmWwusoEtYweSNdxePGA7vGxXFhAE4EjHhEigmnwFuKiUAlGXEMpolOzuIAGznDXmhG8eB19AYjgSWKdB4P7YbLpZdO80lVRcSDRx+DCjdZnSwnNHcJwsEUKlySeqUVya6Rb2VDblhsN6mvECnTZ3aeaqqAs8NZ9mJx8D39ysRMm1ZXp1QoP74hD5wTEEfAijhLh6+LvrQ7mmBprg+A1SszxQ9DqY1O2nSxrLYlIQOEjj5kGzMku79JBfHleWiCn/ANJcjrlP1ZNTPd5aRxAtgHDQScwOoZuGc3M7+Msi1VB5DD2h0nAb+8jCpkyI7oHFF2LrllBDs7BXSifjYaUhesC9j+erUiVKryDyqEivBwaxiu6WHN+mX82653jXVqFsnJlIXLwFNaQQ8doigqUh58RMNaseA1i4Z/Cali19z6bw6B80TE7WGTv47RsUNH3E+zNgddTFmjrCH9yS+urDZ4JUvygx6MeKQsScTlbOqfTQx6BNIfV7m9VgjUlFVooCGJhe1GcxZP5GKVVEZCrS+ojalzniO9Ei0ookUGbQe1rua/hBTlMoUI8ugvWdzCPlaBj0na3voT06lCHtkyhWMOuR1AfmQuP4v3qHO8pM/0RcHlZiaxH+Sf7xfgwhZLpfRGorKXG4QVWhz/QpE0IQchU27mwBC26ifSGG8J6mFFYAZv6diw7IigRLm7s1TYRQbuacyMmmxoSJPZVw1F0BSztxa0xV4QC9lBd9EfZ0N5LLYHli9fiKVarBdQdWbtE+CkrQ6u4i8tymZSiU4YjSdenFsf0Y8f832zfzyjv03yyh7F1CyUVNWBqDEfTMa5DPeT1ZYWJVEytY3+9RhRruMyQnLUzI7+bLVl8lxQetwxgbs+LjwnR68Fg4aOyzl8mJtVFxhWmW7hjqXv+UyWTWTSqYQ17QDqpherkBjAr50ypCUkvYW8RriCucuLfLR8s22y+062Iq7oN8P/QU1akqqfoAMAKQAACZUV2qzulfnirD1p57gFaxU8nUADugGDJ+X5edRCUs7f6/elZhqNwDZZX8itUv19XLdMx3TZLucPnyGKw148dPS//hp/ucUIHNUTMFvTKLGqiNX8BPx+yBUsoXoTOwOi8qN/AgFNsWAJp/sgYJNQZYIm4pq6Dw9fpZYMC/6RrONkidtmM61eNJcUNR1363gSQszKWR+VprbX6Z8eWtpaCFmyvNxBQIE8u/UOMWyVDngGwl1kyC9lQQNlStHI/MgTrpknZ27QXPlSHQz9sEpy+xdyqoAzSkbFQREoZBGKirMSoW/ItraUnua24XrbQo9g5TsIjm3FY2gukz6UbaLxOAS2BfdRVKBUoOmgCPZW/Im3GKgVa3kttZ0/EyDtmeoJ/7lF98d+lEml5pREYKccClUeBQVhO2rs5EasXxz0aogTN2zS29ALM12eyBqwzwWuH0tjMvDu0puGQ0P8GJudaxv4hNiwIKNWEAdwO2iEDnAJ3eDjR9QLqEIQGdA2U4yNlPDirW2BaOA75fHbrk63uG/YJa+cHaJaQWDiWntYEsPAt9N8H/cdXzByIZ7ftYo6gL5sn/pEfxmky/QNGf//g2H3yzDnsqCvhV1amaeh+N49kyf9hjUqOkU3HCje5WLJDhVC/pR5TxNwZCdUCsQMkVteP7e13LZACr6VWBi8MUGSve+1jNi0Rurv/Ly+2Qr7qevjerq3dszCXqxDjrlpKjZNb82dewbx9SOn7JdbOrsdjBEFYRW/dKCgZQk3wRHNGKyAJO9X0C9BDL+KNB1QfzUcj3FwXY9kXVZ3++/9Oip2d0npSA8AOQCTQZCfuZSnaSnxpCtpCuwkijHd2jOGFFPa1925VjMGVvC7S8NdpSDCslRwvqSRYPeOhVWUFJBCWglKcZirPk+hF2Lc6f59sCKiS6ZiarAs7LgItvF/57rN6c5IMV3EfVUrymnvyWa+dbydDQLN5xhcEy9tyKoek07ElHmdgohi/PXmksyv/u5v46iFYwipoyvSVD59Z+hxVY0g8sfd5RVH0j0uCJhdKjBZgXYvKPez1J668aLnK9ye2vTeFLVUjrfd0/mpste+9afm876yP0seMj7LZuji6XsdgZxyHqHigfQR2UBAeR0Z/eFVX6mQRdW64l/+RLK29s5FU1FyhNy2Sgo8VP7qneoIKzSt38MkBD+KZZDiqWC3R6V2I8FUd1wbpzCp9zrHnbtmS8qg0uHHrbo7F7NDk++yx+UhB597fCsQEPM2OAfuyz0mG6SZDf1Dn7sOXZvvG9ZBcjglryGd1qdcTutY9GQJigj193m8TON0eb1/jb3S7wbiQpSmdJ6w8ZXBr/ArVDafrahPQJk8627gUQbSi2Ugi3wTaqgx79jTeh+3rALrQoOzytV37aaEFtcN9QSSjphwN6Xd4dohtyQhCo6DFbo2XZbj+o3I5eKGXTW7adiH3JFjyANXno5ixnxkXhQwsI06theTdjXLMykLqj8aPx3efdi/7XbRs4yXsFH72kmLdI9vn9tjmkwg6/Kjucll2yJSxfNeF8vYatA6ApKPfiNIvx7tZvKE//qhYbv5+4gTdriXvs6D2aej54/xPON/nx3K5WmOqzOfq0oB58E4sFUJP8SjI4hJj/PJSNMehgRkhQvp1pn85n4OL3i/w== \ No newline at end of file +7V1bc6M6Ev41rjr74BQCcXu0E+fMbJ3Zza53z848bRFMbGaw5QUySebXr7gIows2EGFwTB5mbAnLWP3R/XWr1Zpot9vX30Nnv/mCVl4wUZXV60S7m6j4T9fwf0nLW9YCbM3IWtahv8rbDg1L/5eXNyp567O/8iLqwhihIPb3dKOLdjvPjak2JwzRC33ZEwrob907a49rWLpOwLf+x1/Fm6zVUs1D+yfPX2/INwPDznoeHffHOkTPu/z7Jqr2lP5l3VuHjJX/0GjjrNBLqUlbTLTbEKE4e7V9vfWCZHLJtGWfu6/oLe479HZxrQ9oVvaRn07w7JF7Tu8sfiOzkf4eL/kEmGjzl40fe8u94ya9LxgAuG0Tb4O8G+EeP04Eryn47RPaxffO1g+SlhcU/kiGdnZR3rVEz2E60CaOsWwxcmb4H3y7yT/JBdHNGqF14Dl7P7px0TbtcKP00vunbGD8Mh9aV+flwfOfkN6HHwS3KEBh+pM0z109rZLbjeIQ/fBKPU/AcoxH3MNPZT67P70w9l5LTfnU/u6hrReH+H4U0qtYuZzzB0HT8/cvB1Rp5JpNCVEqzBudHMnrYvCDNPGLXKBi4ZqcJL0Vhnn+FoXxBq3RzgkWh9b5QdYKLVfv1Y+/Js03qp6//ZZf9d2L47f8IXaeY5SgoBj8D4T2+RDDwEKVWJO5OSrU0Auc2P9JqwiRfPKPPiAff8UBDIDGwtRiRBw74dqL80+Vn9mmA0XpRHIDpXApfk8tBAGD0w7/9PYo8mOU/FQjwFM4fwzxq3Xy6hZPtJfe6tILf/pYmO9SJYncSk/mffonerb7whWtUxQF3ul3Ip1S9EjRKbTwTZ3TKMAQaBRdgkIBvLF4eH4M/GjzpxdGPtpNEnPDomLpOaG7SUARoHhEhHREaHafkLA5SCwfPk/024cEERFWB4kWUIhm0G8/ObtV4IVXhYN7YM2MuQgHRY8EHOhWjzgobvc4Dv5ALrahWFGMQOgOCKbZJxA0DgiLn/i33PnR3ondjQyJs5KtQsAAJA7N+eJOKPGiR4LEVaVPifMmgHc8dqtZ4pfjdzu0SxyNlRNtCpmX/Ywd/vrM0YAQkoZv5V6sUnx8l17Y98Mtx68oyUg/IqJ3uh+WfaNDaEDV0k2A/7Vr+QzCYWioqTo9UoUbg8XvvJUu2ycXRBzOmvknGm915EBPZ4E3YqwGxlQ23KHU80xPDgT7xJg6qrfhQw8atH5rqd7SYQak33gy9Q7sDRtIEfn6pEnJJz2b7Anlag4EcYZB6yhTbansDJ3FnNEn5OC5TGr65u4113LZu7eL0HkUVIsFqhJUB6YcLV0SVC04KKiap6HazLdsHL4bNk7rO5CEb5XwqArwaLCEro2/CEfSzikMAuWBKAwmomywq5F19QUAtLYwtXprXi2UAWxG093AiSLfvSSCNBBsTIHCenntwGFaR4c5pyXRedLjbX08b1jACr3ElQYzOWRhNRvTWKJtRq7EygYmb3ICf71LAImHTVy+eaK0fdcJZnnH1l+t0qV5kfGil+uHjd8Ga5zMIiewDc48ASiAOIuhNubJ6ihtopw1AT5M1kSZChMj1sqw1dZKdeVo69wzna1I5evRElegxvUI7gFmPI6zLkjYfNZKRwTzg7iwuoCRksdnIKwDYN3OuJ6tvVhQ2Ikixtcj97BreLFXHF8uY1IfVgRQNW0KRq3dJDwQG1bpE5DWeQjQMcX68cgRcTEoPMPeyFGzNfvRW25ttxhnWW27MqWaJwaSF0oBitQI3YiTeuuXcmwJO1Cv3AYozRYwRz3TmoswGROtE3ygcWKgxvgRf49WccMHsGUjtoceH9MLPddLp3gM6/UR1tNYW6jnyqAcFBA9IzLCekVKQVerhTVyez+KIA0muqMRAZ1YPpSxqQ0oXUV3Pp6/XGB+IEbK0CWRnA5SZup+EbFb0uwUWesYKVLXFJvNTlUL2TbH38mhJAFQN8SqVh78VA5+AXJWv7nZjq3Pq4l6mxmHhCb9Df1lJEmd2lZI1h+JwCFvW4Gh8U8J6+e1Mq7qqIzOZAohI2bDaqeKoKUdH0hiXEiwyY9Dx0ih6yXcQVqvQ8Fj3hmFJmIbKfRpCj0wvWFBSRR6WKm8QLBttLHhKQFRpVaUhg+5gaALWkCslZqjSz0+kEyr1GzHwshZ2usexmaZrR0o1hcTDCUTIXzoL/FxSDGCaP6W16f4bfRuuqU9pkbTXigoiCVK7JTi3Gg1IsDjsqZUlcGlWunSkra6NClasxjzCTZCUZEiDebr5JAgk/VYgLw/pBQmb95Kby6LSdN7OpVBQdMwaa6jt97kUminqpFkIpPPFvSxnXgdDdi5tiYwexM0wdY5EWrlWDA+bWufJLArRY21xW7t7/i6eyMIpIKAs2qGIL0diFCgS0ABUS8jj+mPx7TfEsljpztrAcf0q37SPG3QDh3swiI7jkxsNPOGjnPcqXIDdIsmuglXOb5F5sJjw9N4t5g+fYFLBbjR82L16fP8zynQjUEhky1TgglMS2iyA7G1AGRik2e5+yxdrxbBHTaEGiTMQYON0UOOZ+gmjx0pCXN6M5ohVhDE0b1RLI1ydjXDHp3dMzq7TPCkbWU2ztnlRpKoBsi+xM7Wm++hCTThejPpGTbkmrgsKh3uEPmtogVnwFaXb6dL1Ea65KpXnPWBUduTDktr16fXFWed579pJI2sCl0V0wCKTRMNHfDqQRjRkEE1Kog0TwKpYuL3t467KRWXl1LOgbMEOpgbMzg5XtjhyEkzBv7rtQCEDHRozMrvWcs5VKCjxsFFY5GHAyO9CGcVKFVpcY1tDTtSr3Vcj09+V/R2tgDAgCKlVvQMG79N7BdDLQoH7AS9lcFuK6TbbIv9VSqoi4umcWql/TrABSgoo1kdgKMRIBLT+VZqF0d+hg3ZwQCRVXhmyw0E7NIFMGEtHMpDWbPYgBhlJwvUjKiqhSpm/QmA1qgixW3ISDX3pTTdIAfYs2/ILVdHIdgPGNQH3r2lrgLl/FKsv8Nv/JUTe/9y1tG1xRsYIZCjDs6wsvF3NYTfLfQVGsbsH3/1gu+mZk1F8aAlZtsz1/WiaPaCf+0h1CDteMsLqhxZw5uQAIwp6PMUuwpkGLw9en9BNDr3syD1RRedMfrxCqIdfwrLnsHxK89fPK3ifrqq4/hB/b13SvVsxc7pZTOj7eLH1LZubEM5/NH0qK7314KPVEy07IPfrzf4NOXqlfNcRhR7Ige0y9dFBKSUcLNzmZMfj3sw63SmyetpFLrpFcb/nlHGNpPZTuYaz3TFPKezXMwxmeFsfouBDqeCrxLq9Bz5u/VEdEB0WonLxV2zh8+kN/9qcsn7mdYlnRJ7Oi1BBmq1Po+JrcCtwGSOTKsbpkUke9omkyuHwrRIEtyYFiRBCQjW+Ts7+alCnBI22w1bHkMhs+y5Tm33Yk6LqlEVA51z6aICVJUZxHnBz3GnXNeqpUBFCd2aiAZ3pllMEQ1uurJQUIUbhizYmK186P0Nx63vQJQaU7FENxmnqr5OY8sDdndWXQVa+dXWUWWdWWWxSSR9KzCeGi1+PeDn5QmF2yUKwmzfL9n0yzrW13jA1ZPreFAVkvfb2SLtkYAUoLFHCImcZ+uszjM5jeas+bIXFlo5z/FntlEDHOeNrFjNqNBVrk6cjplY6qC4D4CMwWqfjcaO1Gs22vHJ7yzsQ+xDteUYNn4bmC/IrFhAga/WVbpshXSblSq4SgV12jkbFVSfCkpCRYVhA3AosFI1+8Yu/dEnicO2J5LzavHcMQCLd/jG8l7nNIzsGUVQEAPoqrxXBST4SLb3a5/HAKbJhE7dzLG7Ij9OUeCdfifiakWPDDToGpOl078jZ4+O3MWYKQPQ8GnPftiRhsh+Ok4oq/HIDxuVDfSOwWQHqjVPh9DY7ESJemc8TnFgKLHYE4GBwDgJuYqEIpR1dreOOTi15Mid7FzzNEUZz3qx+W/U2u+WI39ybk2lLaU0MOw6Ke56zmZii3DWlKOMo5kqbG+zWifHK/FNysmwKqlGX1GEr6KGvQKvMvWFUNyB+BdcOp/e8jQWrqgfN5K8uNcn7d+Lh1frv/tdaC+iJdy6zzPh5ktv68cZtZx7Tyj0RoJ5djdEpw9rIUmfZX4pOqxFxh7dCpiMqcL91LOGLTWLxp7z1JleUW4flW9zf+au9Je7aL5RXx7uhXrlGGDeES0bYVXHYjFwaJuAzo5zznAYfhuiZIPb4XJsBDZf0MpLrvg/ \ No newline at end of file diff --git a/docs/api/img/diagrams_source/update_content_sequence.xml b/docs/api/img/diagrams_source/update_content_sequence.xml index f4b6597bdf..c419bf2784 100644 --- a/docs/api/img/diagrams_source/update_content_sequence.xml +++ b/docs/api/img/diagrams_source/update_content_sequence.xml @@ -1 +1 @@ -7Vxtd6I4FP41nrP7QQ9JCC8fq21nemZmT8/07OzMfpmDEJUpggNYa3/9BgGBJFjEoLauXypJvEie+/Lcm2t7aDR//hBai9mXwCFeDyrOcw9d9yAEJkL0TzKyTkcMpKQD09B1skXFwIP7QrLBfNnSdUhUWRgHgRe7i+qgHfg+sePKmBWGwaq6bBJ41bsurCnhBh5sy+NH/3GdeJY9BdSL8Y/Enc7yOwPNTGfGlv04DYOln92vB9Fk80qn51YuK3vQaGY5wao0hG56aBQGQZy+mz+PiJfsbb5t6edua2a33zskftzkA9kDRfE6f3Ti0J3ILoMwngXTwLe8m2J0uHk8kghQ6NUsnnv0LaBvybMbf0+GBxBnlz+yVb9IHK8znK1lHNChQvjnIFhkIvjvnz1S8rVKA9nTfCDBnMThmi4IiWfF7lMVQCvTg+l23faj94FLbwGVTGeRkiGyrmpiLiG2wimJsw+VN3RPOVGwDG3CyaFvSk9TDG3gqsEappKfLG+ZPfWIGiIdWUauP+0lyGse3cXhOKTvpsm7++XYc206dXV/xyFf4JogsZq5MXlYWHYyu6JmXsV64nreKPCCcPNZNAHYAgnQURwGj0Q0Mwn8OFcskF2X1t1uXrtU4ImEMXneqQTZbB8YDAo4vVwVxgy0bMmsZMhYqdebClS7cNE4XL6SRRC5cZAIYiEZ0W2gT/pAwieX7rVMUBTFgNgWgaIDc4TN44ICWcs4IiYQduHnQNnJDTYuz6dfbDuVXBRzB7vA1HVUjT91S9Uxea6y8fbqnMrz++07V0lkpld+4Cf761jRbKvZ5e3NN1EZqKpa3sjt7D0JXfotSZiB0z5slLQP71C+A6NL3zQruq9qzcKCQJAxMDWleKkVuRpuFLVaRBtonivEZbNA+lnBDnVzgCsA6Q35QANRDbGmiFjr0rJFsiA6UBtQ7v3laMOWN7K0MXl//Vy+WL8Ri1f1arTbRr99gWcFATYeHhV3nttwuO/FWHbQxRqCWQv8PvQQVPfUzC5LqgEFqoFkUBHUIFYetoUGHCNN20EHJWwh1E+3g6pc51OEIswGotqNKtOuc4s5TOrTmmoghQldqJnjaeFWVD6V3YWo7VlR5NpVHM89IAA2/UG4HS5QM3YLOmZAUBGH3HLhWDHJ0to//uSApB4m3uXBMnstO69syPLcqZ/gTwUnvHCY+CvXtryrbGLuOs4mdRO5zGo6J8EHAlwl9duoUlIoJFAoKMEHYlVgMWlpwXGf8tqCHRIKxXVoTeLbMJgnmOSrooXl58synNIJeufyXGm4JPj9goo0WAVVQxyoalegNsix9qsH1VRzGGJgmre3o5GISqjXpqlI2tltIMp31mjGGVQJO2t0VOcu5ytAdo0nJxatyIb0Go+5H+sSxui68pip63Vp/2awSAE3V6/mgJUd086KBrDEWWVZVVMaYHJyBqZaKgx1Vgla+9O/yR3WTOPh2xf16zUgwy99oMhQj7yuOqhWVhHKr8W1gOTirVUEoVmNMwgYA0M3i1c7rTBUhRFrHlcPcg7UfXJ2sGfIw+2ZaARb6GmduHGlJzaCHjNBqNESCd7irVk4MIyWeLKUlBUkz6bHH34PV14fvyy9+fzj8Kd/5/zoCyL/A6WdV7ZNouhqRQGgk1KPMetoK3tYWaWxGn1t1nE0djtTqzN7ZH2nO8UUo7Nf7eTyzt5YotTWrRrndAAjVgW+GNORIbL5Y5JBigxvRF+yDI9t6RBUW4Ql584MD3O7fTGlrz6qgiEo/3dV+RJj0eBc6q3zib7BlhtbFpD1V+Sc3I8BJIppaR1ysS1avizodk6CcN6fxfGib1v2jJSqlYvaWmU3LVXFqdvxWqpMtqQmICNYPyIZAYgPQZQvRG5EPWJCC29HKUw5X/xo+dRthYdhxGHhmNBRoQQmKQEjfMJWxBqMRGcHx2SM77HfQ2NMsWnjLy8IczzzhOl7jQbx1Oe4GlSrDeX6ufi75yeWZ6I3QGEqdjpLkpoqDlDQ2WcoQND9cvNEQbx2o4UV09BwYChgHPk2EOwOEUcK1zrXynXyUGB0ZMjtWml268yZWKyBqyi2LygwgsD5OfrcN3XXrHZzqyt4xFvudlyC3fGHIs1KB2ZndrdnC9ubzFcNhhEhrLUObK9I2ttQaiqFWHyfAupUomQb45PdkNhks/FXSaa0aeLJ8qVNL88mYnL68m6KSwYTJhHkzRWIFBcydEeivcqurJ6kaRco0GAY4jH7dmu29tTZaO3Gvs5Mzi6XyKnSodTkTeQSaldZqJxWgzNRCrYw0VonuArHKdsKajTiAo4BOFrVtlUIQJYWs5K6olXZfTqmVXyl4bJpFVA0NvonjOPUzIqvQJC5G18oRpj5kQgU9EgDUfu5jFPuu8efjws8XX36/Gt8/zT7V/n147uw+ef+rodHlWOd/DiHjks50WFIMAZD7Uo9l1M3xHSUHLOMJ8ZIlJ38/2MQCeaYE4ocaVPwYxBN4C5RV0jvlyy9SYKjMgemELTsm9Qw3C2o475J0P2vpccGVrGyw93VdWpJMA4Nszg1qyV05wYvgP1zm57/EHDvX5Ey3cmcIHnG8ayO+38N+1/1Tz8nRP82//z4+5uIVxS8b0gmQUgujv0hnSXoIHdZxyDoYpzg+zcqtkUYYEZEU6Pie30YQZIyalRzH1kZtVgTGtTAL7rRnPtPLW07OVSN+7dBxyu10cvi30+my4v/8Ylu/gM= \ No newline at end of file +7V1bc5s4FP41ntl9sAchro++JG2m7U6mme22+9IhWLZpMHIBx0l/fcXNgCQSTAArKX5IQcIHw/k45zsX0RGcbx/e+dZu8wkvkTuSpeXDCC5GMvmokPwTjTwmI7qmJwNr31kmQyAfuHF+oXRQSkf3zhIFpQNDjN3Q2ZUHbex5yA5LY5bv40P5sBV2y2fdWWvEDNzYlsuO/ucsw00yash6Pv4eOetNdmagmcnMrWXfrX2899LzjWS4ij/J9NbKZKUXGmysJT4UhuDFCM59jMNka/swR250b7PblnzvsmL2+Lt95IV1vpBeUBA+ZpeOluROpLvYDzd4jT3LvchHZ/HloUiARPY24dYlm4Bsogcn/BoNT2Q13f2WHvUDheFjqmdrH2IylAv/iPEuFbHCXnhpbR03As4B+3fRj7O8IJ26wXvfjmRswpBAgeBsSv6Qq4v+RAcEkzXGaxdZOyeY2HgbT9hBfOjlKhFMNlPRqjwrCk+vWmJvYnpfo3tTGEhv6TuEtyj0iWDJR64VOvdlFFkpGNfH445fvcYOOYUspQ8OlNJzP5Yfh0xCaPlrFKZfKmr1RDlBfBsZOWSjcDX5UIyZCsDJieR7y92nVz0n1oCM7APHW48i+GkuuYuzW59sraOt6/2t69hkanp9xcAvB1cEh8PGCdHNzop1fiC2pgy4otay/Tl2sR/Lgpfx55yocly39HsUHUAyHoQ+vkO8mUro3SM/RA9Pgi+dHQOD0r6a7B5ySwa09JBNwYqpUjVeSxB5Cg8ag4fPaIcDJ8SRIBoKc3JzyZXeIP/eIRpoDQyS+GCQJGWhLnhgOM60AAaZtgQ9YkGWu3AuoOhZJrGf8cgPO05FO/nc2/A7QXb6gsVNfEF5rD3/VFvHOvO8s0r3ltOIk5E9D3uRkpdWsDk+1kUdZ5qUJoqiFLV5nL1GvkN+JfILT7m4intWIYXnUH3iMXwhrxibZskKKFo9QsARZExMTco/SkmuptbiKw14hmwOOKtpIKAuFPZk3ZyoJZToNeloDVE1AUdgYT0WDttFBwQvhCTMnHE7kDzGTnToFG0vHoo7j68Ck4LgT9HLDOjIiE5FHy0I0BypV/CxPJsB32ns+eRQRWz0nRIvgbJizXS3gE+Zg0/YBkeGNfjTS/RYI8p4K3qU9fOpUWnXF+T0RKXJidjaKgYlovEQKivSmANDiaIzsJ4faGDlFTa79hSsbNcKAsdm0yLiAkYQcAA6TQLVZuCQNeNpQX2SBAUy8NnvllaI0rTbX38zaCK2Nizjp+w4UstV9DLpkOU6ay8CIREcRU2zyHI7tuVO04mts1zGKR6eByunfcTGbH2XBNRy3H1kGgVUQw6q5RZckqpwbEeSf10691kC1vYRwcPCt1bhpY+3ETCyo4Kd5WWHpWBJJsiZi3OF4YLgAVmdIgtqchlZGmSQpXSFrBq5mJMz99xMPRWTAGOqzbgxSTbzVtR7JCeZeo16ZFZpQb1GRzXhYl4DvMnUfMZ4G7Hg1lPz5mkxCZc8VpVWTF2vSpTGg3m+Kt57HQmrkto0ofgpHdsqdMxRl5+ajJyJqRTy+Z0l8B+99b/oStVM4+bLJ+XzAqDZpzGQ2sBoVhiclEuDEGb7/OxptPO6svyigNEscw8IjImhm/mnGTQNRaLEmv2CMSPn3Sdx3oaNzHigILCk8/ONEzxMxYBmVX3G8BVQbcFuig00QUBF2zpgGA1BRQdstKD2rNvtu5+zgztWf+3d7fb97Lt3tfw25rDBGxKUTW0bBcH0QBRAJttux6pqv6LbtARox5peAKApvODyOFOJ1RMSQufrxuKj4rQE85/WViGIEaJJe1PHZojUPsHHI5ux7sgKnfz0i43U+iaIbg7mpKS5ZdLOTJDKqHwoUvTa/wDLiODUzbuqUfABUaO1ZeC2LbiVsUFXpxoWPfVn5JzdrQDI4zlJ2Wp3rHH92pHbucL+dhzBYmxb9gYVilu7ytLWsEzhpTbIpOsdHGKs6j0SYwBZJkK4a+AExCdFodHlPIFHFjO9tzziOPyO2IqYMVONglwL2FDPuJypAhu80nqfUdPQLNyRS9QoO1R35SQrSGVirTMmEStgzDLvfmEsNiSLlV3+Dcz6qwQBL5CoCopOc/S66AUSFD5VADit0xf3RIkLJ9hZIXHOLTjjV0TUFH12seA64+NMC85YZxZBnN0ZGx1ZsVfcev30AyOIuTLUMpSapzUpQUA8V5sZ5mG5zsvzl0ybQL0EptmZBTpx8ceQsGpsMSh2DlWtMb95RtLJJqOicqPyz5PjLZHYsrVhs10+slF846dRyiJu+k4TF3Hvd0ycGNAOefZWDRdFnaDMGi7Ae3pkinq3aLlaL7f9sasPgSQbVMjU5wLECv2eO1Eltnafp8zCRfgZh38pZ34VEb7SVYLqDTVkCoJMOnHaGJhMBvaczZcVsGyhQC26Phmq3bSrG8h0vEZL6opqp+fpmGqzSciBagtAxjSajEFNY9hY32ybzVSirRMOQDkfUFRq3b/MWWcJeOto22iHu7r7frdT14cPH3/cXt9v/pd+fPvK7dO+vhqp81L3QdZ1QMZbazx4RbWOfhoPINV322etg48NXrg+LOl/K7Yo45UZ3DJ1F5f0axyHBbuC22nZgyGv3ZRsK1SHkwwaLjLSVPlpQR0vMgLtv5ZueDUEZSY0lQZLvTRjd15p6NfuKcdCaz57xc/JL6miFjUygtozEw/K7fif2fiz/uH7Culfth/vfn7hcdw8EJqhFfbREA6d5a1COh02g8yD9BE288EiD+alp6XOdLhDiahrXtiGbUpQSxk/WHGetjJ+fDjWKJn+wSU1QbDMvKu5aSesojFvL++vHkF28/8JKjk8/++24MVv \ No newline at end of file diff --git a/docs/extending/img/extending_field_type_create.png b/docs/api/img/extending_field_type_create.png similarity index 100% rename from docs/extending/img/extending_field_type_create.png rename to docs/api/img/extending_field_type_create.png diff --git a/docs/extending/img/extending_field_type_definition.png b/docs/api/img/extending_field_type_definition.png similarity index 100% rename from docs/extending/img/extending_field_type_definition.png rename to docs/api/img/extending_field_type_definition.png diff --git a/docs/extending/img/extending_field_type_hello_world.png b/docs/api/img/extending_field_type_hello_world.png similarity index 100% rename from docs/extending/img/extending_field_type_hello_world.png rename to docs/api/img/extending_field_type_hello_world.png diff --git a/docs/api/img/field_type_overview.png b/docs/api/img/field_type_overview.png index 7ebbdad030..01b4e2975e 100644 Binary files a/docs/api/img/field_type_overview.png and b/docs/api/img/field_type_overview.png differ diff --git a/docs/api/img/load_content_sequence.png b/docs/api/img/load_content_sequence.png index 014eb40179..7b24e65cc6 100644 Binary files a/docs/api/img/load_content_sequence.png and b/docs/api/img/load_content_sequence.png differ diff --git a/docs/api/img/publish_content_sequence.png b/docs/api/img/publish_content_sequence.png index 929664eb30..55ed80f158 100644 Binary files a/docs/api/img/publish_content_sequence.png and b/docs/api/img/publish_content_sequence.png differ diff --git a/docs/api/img/update_content_sequence.png b/docs/api/img/update_content_sequence.png index 350d3bb9c1..5d1271b3e2 100644 Binary files a/docs/api/img/update_content_sequence.png and b/docs/api/img/update_content_sequence.png differ diff --git a/docs/api/making_cross_origin_http_requests.md b/docs/api/making_cross_origin_http_requests.md deleted file mode 100644 index 53800908a4..0000000000 --- a/docs/api/making_cross_origin_http_requests.md +++ /dev/null @@ -1,56 +0,0 @@ -# Making cross-origin HTTP requests - -[[= product_name =]] ships with [NelmioCorsBundle](https://github.com/nelmio/NelmioCorsBundle), -an open-source Symfony bundle that provides support for [CORS (Cross-Origin Resource Sharing)](http://www.w3.org/TR/cors/). -The REST API is pre-configured to respond to such requests, as long as you customize the allowed origins as explained below. - -## What is CORS? - -Supported by most modern browsers, this W3C specification defines a set of custom headers -that, under specific circumstances, allow HTTP requests between different hosts. -The main use-case is execution of AJAX code from one site towards another. - -!!! tip "More information about CORS" - - - [W3C specification](http://www.w3.org/TR/cors/) - - [Tutorial about CORS on html5rocks.com](http://www.html5rocks.com/en/tutorials/cors/) - - [Overview of CORS on developer.mozilla.org](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS) - -## Configuration - -Since CORS support is provided by a third party bundle, we re-use the semantic configuration it provides. -You can read more about it in [NelmioCorsBundle's README](https://github.com/nelmio/NelmioCorsBundle/blob/master/README.md). - -The origin of a request is one of the main criteria for allowing or blocking a cross-origin request. -Such requests will come with an Origin HTTP header, automatically added by the browser, -that gets approved/blocked by the server. By default, all cross-origin requests will be blocked. - -#### Granting an origin default access - -To allow a specific host to execute cross-origin requests, you need to add this host to the `nelmio_cors.default.allow_origin` configuration array in `config/packages/nelmio_cors.yaml`. -As an example, in order to allow requests from `http://example.com` you would add those lines to `nelmio_cors.yaml`: - -``` yaml -nelmio_cors: - defaults: - allow_origin: [ 'http://example.com' ] -``` - -#### Changing configuration of NelmioCorsBundle for [[= product_name =]] REST - -The default configuration of NelmioCorsBundle for [[= product_name =]] REST paths is set in the [nelmio_cors.yaml](https://github.com/ezsystems/ezplatform-rest/blob/master/src/bundle/Resources/config/nelmio_cors.yml) file. -To adapt these settings to your own needs you have to overwrite them in the `nelmio_cors.yaml` file under the same configuration path, for instance: - -```yaml -nelmio_cors: - paths: - '^/api/ezp/v2/': - max_age: 3600 - allow_credentials: false - allow_origin: ['http://ez.no'] -``` - -### Granting CORS access to your own HTTP resources - -NelmioCorsBundle is perfectly safe to use for any non-eZ HTTP resource you would like to expose. -Follow the instructions in [NelmioCorsBundle's configuration chapter](https://github.com/nelmio/NelmioCorsBundle/blob/master/README.md#configuration). diff --git a/docs/api/public_php_api.md b/docs/api/public_php_api.md index 5f59257941..fc310186d4 100644 --- a/docs/api/public_php_api.md +++ b/docs/api/public_php_api.md @@ -1,6 +1,10 @@ -# Public PHP API +--- +description: Public PHP API exposes the Repository in a number of services and allows creating, reading, updating, managing, and deleting objects. +--- -The Public PHP API enables you to interact with [[= product_name =]]'s Repository and content model from your PHP code. +# PHP API + +The public PHP API enables you to interact with [[= product_name =]]'s Repository and content model from your PHP code. You can use it to create, read, update, manage, and delete all objects available in [[= product_name =]], namely content and related objects such as Sections, Locations, Content Types, languages, etc. @@ -8,10 +12,6 @@ content and related objects such as Sections, Locations, Content Types, language The PHP API is built on top of a layered architecture, including a persistence SPI that abstracts storage. Using the API ensures that your code will be forward compatible with future releases based on other storage engines. -!!! tip - - For more information see a [presentation about [[= product_name =]] API.](https://alongosz.github.io/ezconf2018-api/) - ## Using API services You can access the PHP API by injecting relevant services into your code. @@ -64,6 +64,12 @@ These objects provide you with lower-level information. For instance, `ContentInfo` contains `currentVersionNo` or `remoteId`, while `Content` enables you to retrieve Fields, Content Type, or previous versions. +!!! note + + The Public API value objects should not be serialized. + + Serialization of value objects, for example, `eZ\Publish\API\Repository\Values\Content\ContentInfo` / `eZ\Publish\API\Repository\Values\Content\VersionInfo` + or `eZ\Publish\API\Repository\Values\Content\Location` results in memory limit exceeded error. ## Authentication One of the responsibilities of the Repository is user authentication. Every action is executed *as* a user. @@ -109,8 +115,7 @@ In order to identify as a different user, you need to use the `UserService` toge (in the example `admin` is the login of the administrator user): ``` php -$user = $userService->loadUserByLogin('admin'); -$permissionResolver->setCurrentUserReference($user); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentCommand.php', 50, 52) =]] ``` !!! tip @@ -148,3 +153,18 @@ try { $output->writeln("Permission denied on content with id $contentId"); } ``` + +## Service container + +[[= product_name =]] uses the [Symfony service container]([[=symfony_doc=]]/service_container.html) for dependency resolution. + +[Symfony dependency injection]([[=symfony_doc=]]/components/dependency_injection.html) ensures that any required services are available in your custom code +(for example, controllers) when you inject them into the constructor. + +Symfony service container uses service tags to dedicate services to a specific purpose. They are usually used for extension points. + +[[= product_name =]] exposes multiple features using service tags, for example, Field Types. + +!!! tip + + For a list of all service tags exposed by Symfony, see its [reference documentation]([[=symfony_doc=]]/reference/dic_tags.html). diff --git a/docs/api/public_php_api_browsing.md b/docs/api/public_php_api_browsing.md index 63a3d6a581..1fea8c7b10 100644 --- a/docs/api/public_php_api_browsing.md +++ b/docs/api/public_php_api_browsing.md @@ -1,12 +1,16 @@ +--- +description: Use PHP API to get Content items and their information, as well as content Fields, Location, and others. +--- + # Browsing and viewing content -To retrieve a Content item and its information, you need to make use of the [`ContentService`.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php) +To retrieve a Content item and its information, you need to make use of the [`ContentService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php). -The service should be [injected into the constructor of your command or controller.](https://symfony.com/doc/5.0/service_container.html) +The service should be [injected into the constructor of your command or controller](../api/public_php_api.md#service-container). !!! tip "Console commands" - To learn more about commands in Symfony, refer to [Console Commands.](https://symfony.com/doc/5.0/console.html) + To learn more about commands in Symfony, refer to [Console Commands]([[= symfony_doc =]]/console.html). ## Viewing content metadata @@ -18,50 +22,30 @@ as well as methods for retrieving selected properties. You can also use it to request other Content-related value objects from various services: -``` php hl_lines="13" -//... -use eZ\Publish\API\Repository\ContentService; - -class ViewContentMetaDataCommand extends Command -{ - //... - protected function execute(InputInterface $input, OutputInterface $output) - { - $contentId = $input->getArgument('contentId'); - - try - { - $contentInfo = $this->contentService->loadContentInfo($contentId); - - $output->writeln("Name: $contentInfo->name"); - $output->writeln("Last modified: " . $contentInfo->modificationDate->format('Y-m-d')); - $output->writeln("Published: ". $contentInfo->publishedDate->format('Y-m-d')); - $output->writeln("RemoteId: $contentInfo->remoteId"); - $output->writeln("Main Language: " . $contentInfo->getMainLanguage()->name); - $output->writeln("Always available: " . ($contentInfo->alwaysAvailable ? 'Yes' : 'No')); - } catch //.. - } -} +``` php hl_lines="9" +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 8, 9) =]] +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 16, 17) =]] +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 50, 52) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 58, 59) =]] +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 60, 66) =]] ``` -`ContentInfo` is loaded from the [`ContentService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php) (line 13). +`ContentInfo` is loaded from the [`ContentService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php) (line 9). It provides you with basic content metadata such as modification and publication dates or main language code. !!! note "Retrieving content information in a controller" To retrieve content information in a controller, you also make use of the `ContentService`, but rendering specific elements (e.g. content information or Field values) - is relegated to [templates](../guide/templates.md). + is relegated to [templates](../guide/content_rendering/templates/templates.md). ### Locations To get the Locations of a Content item you need to make use of the [`LocationService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php): ``` php -$locations = $this->locationService->loadLocations($contentInfo); -foreach ($locations as $location) { - $output->writeln($location->pathString); -} +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 68, 72) =]] ``` [`LocationService::loadLocations`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php#L94) @@ -72,17 +56,13 @@ For each Location, the code above prints out its `pathString` (the internal repr #### URL Aliases The [`URLAliasService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/URLAliasService.php) -additionally enables you to retrieve the human-readable URL alias of each Location. +additionally enables you to retrieve the human-readable [URL alias](../guide/url_management.md#url-aliases) of each Location. [`URLAliasService::reverseLookup`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/URLAliasService.php#L146) gets the Location's main [URL alias](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/URLAlias.php): ``` php -$locations = $this->locationService->loadLocations($contentInfo); -foreach ($locations as $location) { - $urlAlias = $this->urlAliasService->reverseLookup($location); - $output->writeln($location->pathString ($urlAlias->path)); -} +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 68, 71) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 72, 75) =]] ``` ### Content Type @@ -91,8 +71,7 @@ You can retrieve the Content Type of a Content item through the [`getContentType`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentInfo.php#L188) method of the ContentInfo object: ``` php -$content = $this->contentService->loadContent($contentId); -$output->writeln("Content Type: " . $contentInfo->getContentType()->identifier); +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 77, 79) =]] ``` ### Versions @@ -101,19 +80,14 @@ To iterate over the versions of a Content item, use the [`ContentService::loadVersions`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L360) method, which returns an array of `VersionInfo` value objects. ``` php -$versionInfos = $this->contentService->loadVersions($contentInfo); -foreach ($versionInfos as $versionInfo) { - $output->write("Version $versionInfo->versionNo "); - $output->write(" by " . $versionInfo->getCreator()->getName()); - $output->writeln(" in " . $versionInfo->getInitialLanguage()->name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 81, 87) =]] ``` You can additionally provide the `loadVersions` method with the version status to get only versions of a specific status, e.g.: ``` php -$versionInfoArray = $this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_DRAFT); +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 88, 89) =]] ``` !!! note @@ -129,12 +103,7 @@ you need to pass a `VersionInfo` object to the [`ContentService::loadRelations`] You can get the current version's `VersionInfo` using [`ContentService::loadVersionInfo`.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L82) ``` php -$versionInfo = $this->contentService->loadVersionInfo($contentInfo); -$relations = $this->contentService->loadRelations($versionInfo); -foreach ($relations as $relation) { - $name = $relation->destinationContentInfo->name; - $output->write('Relation to content ' . $name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 100, 106) =]] ``` You can also specify the version number as the second argument to get Relations for a specific version: @@ -153,7 +122,7 @@ and the optional Field this relation is made with. You can use the `getOwner` method of the `ContentInfo` object to load the Content item's owner as a `User` value object. ``` php -$output->writeln("Owner: " . $contentInfo->getOwner()->getName()); +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 108, 109) =]] ``` To get the creator of the current version and not the Content item's owner, @@ -166,7 +135,7 @@ the [`getSection`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ of the ContentInfo object: ``` php -$output->writeln("Section: " . $contentInfo->getSection()->name); +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 111, 112) =]] ``` !!! note @@ -182,45 +151,16 @@ You need to provide it with the Object state group. All Object state groups can be retrieved through [`loadObjectStateGroups`.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ObjectStateService.php#L59) ``` php -$stateGroups = $this->objectStateService->loadObjectStateGroups(); -foreach ($stateGroups as $stateGroup) { - $state = $this->objectStateService->getContentState($contentInfo, $stateGroup); - $output->writeln('Object state: ' . $state->identifier); -} +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 114, 119) =]] ``` ## Viewing content with Fields To retrieve the Fields of the selected Content item, you can use the following command: -```php hl_lines="16 17 19 20 21 22 23 24" -//... -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\FieldTypeService; - -class ViewContentCommand extends Command -{ - // ... - - protected function execute(InputInterface $input, OutputInterface $output) - { - - $contentId = $input->getArgument('contentId'); - - try { - $content = $this->contentService->loadContent($contentId); - $contentType = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId); - - foreach ($contentType->fieldDefinitions as $fieldDefinition) { - $output->writeln("" . $fieldDefinition->identifier . ""); - $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->fieldTypeIdentifier); - $field = $content->getFieldValue($fieldDefinition->identifier); - $valueHash = $fieldType->toHash($field); - $output->writeln($valueHash); - } - } catch //... - } +```php hl_lines="11-12 14-21" +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentCommand.php', 8, 14) =]] // ... +[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentCommand.php', 38, 57) =]] } ``` @@ -251,34 +191,8 @@ $contentService->loadContent($content->id, Language::ALL); To go through all the Content items contained in a subtree, you need to use the [`LocationService`.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php) -``` php hl_lines="14 23" -//... -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Location; - -class BrowseContentCommand extends Command - - //... - - protected function execute(InputInterface $input, OutputInterface $output) - { - $locationId = $input->getArgument('locationId'); - - try { - $location = $this->locationService->loadLocation($locationId); - $this->browseLocation($location, $output); - } catch //... - } - - private function browseLocation(Location $location, OutputInterface $output, $depth = 0) - { - $output->writeln($location->contentInfo->name); - - $childLocations = $this->locationService->loadLocationChildren($location); - foreach ($childLocations->locations as $childLocation) { - $this->browseLocation($childLocation, $output, $depth + 1); - } - } +``` php hl_lines="5 15" +[[= include_file('code_samples/api/public_php_api/src/Command/BrowseLocationsCommand.php', 30, 49) =]] ``` `loadLocation` (line 14) returns a value object, here a `Location`. diff --git a/docs/api/public_php_api_creating_content.md b/docs/api/public_php_api_creating_content.md index 13b21342a5..cb82cc2103 100644 --- a/docs/api/public_php_api_creating_content.md +++ b/docs/api/public_php_api_creating_content.md @@ -1,3 +1,7 @@ +--- +description: Create, publish, update and translate Content items by using the PHP API. +--- + # Creating content !!! note @@ -12,34 +16,8 @@ Value objects such as Content items are read-only, so to create or modify them y [`ContentService::newContentCreateStruct`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L526) returns a new [`ContentCreateStruct`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentCreateStruct.php) object. -``` php hl_lines="17 18 21" -//... -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\LocationService; - -class CreateContentCommand extends Command -{ - //... - protected function execute(InputInterface $input, OutputInterface $output) - { - $parentLocationId = $input->getArgument('parentLocationId'); - $contentTypeIdentifier = $input->getArgument('contentType'); - $title = $input->getArgument('title'); - - try { - $contentType = $this->contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); - $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, 'eng-GB'); - $contentCreateStruct->setField('title', $title); - $locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocationId); - - $draft = $this->contentService->createContent($contentCreateStruct, [$locationCreateStruct]); - - $output->writeln("Created a draft of " . $contentType->getName() . " with name " . $draft->getName()); - - } catch //.. - } -} +``` php hl_lines="2-3 5" +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentCommand.php', 57, 66) =]] ``` This command creates a draft using [`ContentService::createContent`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L206) (line 21). @@ -60,21 +38,7 @@ Therefore, when creating a Content item of the Image type (or any other Content the `ContentCreateStruct` is slightly more complex than in the previous example: ``` php -$file = '/path/to/image.png'; -$name = 'Image name'; - -$contentType = $this->contentTypeService->loadContentTypeByIdentifier('image'); -$contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, 'eng-GB'); -$contentCreateStruct->setField('name', $name); -$imageValue = new \eZ\Publish\Core\FieldType\Image\Value( - array( - 'path' => $file, - 'fileSize' => filesize($file), - 'fileName' => basename($file), - 'alternativeText' => $name - ) -); -$contentCreateStruct->setField('image', $imageValue); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateImageCommand.php', 56, 68) =]] ``` Value of the Image Field Type contains the path to the image file, as well as other basic information @@ -82,15 +46,16 @@ based on the input file. ### Creating content with RichText -The RichText Field accepts values in [[= product_name =]]'s variant of the [Docbook](https://github.com/docbook/wiki/wiki) format. -You can see more information about this format in [Field Types reference](field_type_reference.md#example-of-the-field-types-internal-format). - +The RichText Field accepts values in a custom flavor of [Docbook](https://github.com/docbook/wiki/wiki) format. For example, to add a simple RichText paragraph, provide the following as input: ``` xml
    Description of your Content item.
    ``` +To learn more about the format and how it represents different elements of rich text, see +[RichText Field Type reference](field_types_reference/richtextfield.md#custom-docbook-format). + ## Publishing a draft [`ContentService::createContent`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L206) creates a Content item with only one draft version. @@ -98,7 +63,7 @@ To publish it, use [`ContentService::publishVersion`](https://github.com/ezsyste This method must get the [`VersionInfo`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/VersionInfo.php) object of a draft version. ``` php -$content = $this->contentService->publishVersion($draft->versionInfo); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentCommand.php', 68, 69) =]] ``` ## Updating content @@ -108,50 +73,25 @@ and pass it to [`ContentService::updateContent`.](https://github.com/ezsystems/e This method works on a draft, so to publish your changes you need to use [`ContentService::publishVersion`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentService.php#L336) as well: ``` php -try { - $contentDraft = $this->contentService->createContentDraft($contentInfo); - $newName = 'New content name'; - - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentUpdateStruct->initialLanguageCode = 'eng-GB'; - $contentUpdateStruct->setField('name', $newName); - - $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); - $this->contentService->publishVersion($contentDraft->versionInfo); - -} catch //... +[[= include_file('code_samples/api/public_php_api/src/Command/UpdateContentCommand.php', 47, 55) =]] ``` ## Translating content -Content translations are created per version. By default every version contains all existing translations. +Content [translations](../guide/internationalization.md#language-versions) are created per version. By default every version contains all existing translations. To translate a Content item to a new language, you need to update it and provide a new `initialLanguageCode`: ``` php -$contentDraft = $this->contentService->createContentDraft($contentInfo); -$newLanguage = 'ger-DE'; -$translatedName = 'Name in German'; - -$contentUpdateStruct = $this->contentService->newContentUpdateStruct(); -$contentUpdateStruct->initialLanguageCode = $newLanguage; -$contentUpdateStruct->setField('name', $translatedName); - -$contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); -$this->contentService->publishVersion($contentDraft->versionInfo); +[[= include_file('code_samples/api/public_php_api/src/Command/TranslateContentCommand.php', 52, 57) =]] +[[= include_file('code_samples/api/public_php_api/src/Command/TranslateContentCommand.php', 62, 64) =]] ``` You can also update content in multiple languages at once using the `setField` method's third argument. Only one language can still be set as a version's initial language: ``` php -$anotherLanguagee = 'fre-FR'; -$newNameInAnotherLanguage = "Name in French"; - -$contentUpdateStruct = $this->contentService->newContentUpdateStruct(); -$contentUpdateStruct->initialLanguageCode = $newLanguage; -$contentUpdateStruct->setField('name', $newName); -$contentUpdateStruct->setField('name', $newNameInAnotherLanguage, $anotherLanguage); +[[= include_file('code_samples/api/public_php_api/src/Command/TranslateContentCommand.php', 59, 60) =]] ``` ### Deleting a translation diff --git a/docs/api/public_php_api_managing_content.md b/docs/api/public_php_api_managing_content.md index 2addd47f21..c93c2e8838 100644 --- a/docs/api/public_php_api_managing_content.md +++ b/docs/api/public_php_api_managing_content.md @@ -1,3 +1,7 @@ +--- +description: PHP API enables managing content Locations, Content Types, as well as content in Trash and Calendar events. +--- + # Managing content ## Locations @@ -19,9 +23,8 @@ a [`LocationCreateStruct`](https://github.com/ezsystems/ezplatform-kernel/blob/v and pass it to the [`LocationService::createLocation`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php#L140) method: ``` php -$locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocationId); -$contentInfo = $this->contentService->loadContentInfo($contentId); -$newLocation = $this->locationService->createLocation($contentInfo, $locationCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php', 51, 52) =]] +[[= include_file('code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php', 56, 58) =]] ``` `LocationCreateStruct` must receive the parent Location ID. @@ -30,8 +33,7 @@ It sets the `parentLocationId` property of the new Location. You can also provide other properties for the Location, otherwise they will be set to their defaults: ``` php -$locationCreateStruct->priority = 500; -$locationCreateStruct->hidden = true; +[[= include_file('code_samples/api/public_php_api/src/Command/AddLocationToContentCommand.php', 53, 55) =]] ``` ### Changing the main Location @@ -42,10 +44,7 @@ by updating the `ContentInfo` with a [`ContentUpdateStruct`](https://github.com/ that sets the new main Location: ``` php -$contentUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); -$contentUpdateStruct->mainLocationId = $locationId; - -$this->contentService->updateContentMetadata($contentInfo, $contentUpdateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/SetMainLocationCommand.php', 48, 52) =]] ``` ### Hiding and revealing Locations @@ -55,8 +54,7 @@ To hide or reveal (unhide) a Location you need to make use of or [`LocationService::unhideLocation`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php#L188) ``` php -$hiddenLocation = $locationService->hideLocation($location); -$visibleLocation = $locationService->unhideLocation($location); +[[= include_file('code_samples/api/public_php_api/src/Command/HideLocationCommand.php', 46, 47) =]][[= include_file('code_samples/api/public_php_api/src/Command/HideLocationCommand.php', 49, 50) =]] ``` See [Location visibility](../guide/content_management/#location-visibility) for detailed information @@ -75,7 +73,7 @@ If you delete the [main Location](#changing-the-main-location) of a Content item another Location will become the main one. ``` php -$this->locationService->deleteLocation($location); +[[= include_file('code_samples/api/public_php_api/src/Command/DeleteContentCommand.php', 44, 45) =]] ``` To send the Location and its subtree to Trash, @@ -83,7 +81,7 @@ use [`TrashService::trash`.](https://github.com/ezsystems/ezplatform-kernel/blob Items in Trash can be later [restored, or deleted permanently](#trash). ``` php -$this->trashService->trash($location); +[[= include_file('code_samples/api/public_php_api/src/Command/TrashContentCommand.php', 54, 55) =]] ``` ### Moving and copying a subtree @@ -91,9 +89,7 @@ $this->trashService->trash($location); You can move a Location with its whole subtree using [`LocationService::moveSubtree`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php#L203) ``` php -$sourceLocation = $this->locationService->loadLocation($sourceLocationId); -$targetLocation = $this->locationService->loadLocation($targetLocationId); -$this->locationService->moveSubtree($sourceLocation, $targetLocation); +[[= include_file('code_samples/api/public_php_api/src/Command/MoveContentCommand.php', 47, 50) =]] ``` [`LocationService::copySubtree`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LocationService.php#L37) is used in the same way, @@ -102,7 +98,7 @@ but it copies the Location and its subtree instead of moving it. !!! tip To copy a subtree you can also make use of the built-in `copy-subtree` command: - `bin/console ezplatform:copy-subtree `. + `bin/console ibexa:copy-subtree `. !!! note @@ -119,7 +115,7 @@ You must provide the method with the ID of the object in Trash. Trash Location is identical to the origin Location of the object. ``` php -$this->trashService->recover($trashItem); +[[= include_file('code_samples/api/public_php_api/src/Command/TrashContentCommand.php', 64, 65) =]] ``` The Content item will be restored under its previous Location. @@ -135,7 +131,7 @@ For more information, see [Searching in trash](public_php_api_search.md#searchin ## Content Types -### Creating Content Types +### Adding Content Types To operate on Content Types, you need to make use of [`ContentTypeService`.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentTypeService.php) @@ -145,40 +141,13 @@ In this case you use [`ContentTypeCreateStruct`.](https://github.com/ezsystems/e A Content Type must have at least one name, in the main language, and at least one Field definition. ``` php -$contentTypeCreateStruct = $this->contentTypeService->newContentTypeCreateStruct($contentTypeIdentifier); -$contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; -$contentTypeCreateStruct->nameSchema = ''; - -$contentTypeCreateStruct->names = [ - 'eng-GB' => $contentTypeIdentifier, -]; - -$titleFieldCreateStruct = $this->contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); -$titleFieldCreateStruct->names = ['eng-GB' => 'Title']; -$contentTypeCreateStruct->addFieldDefinition($titleFieldCreateStruct); - -try { - $contentTypeDraft = $this->contentTypeService->createContentType( - $contentTypeCreateStruct, - [$contentTypeGroup] - ); - $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); - $output->writeln("Content type '$contentTypeIdentifier' with ID $contentTypeDraft->id created"); -} catch //... +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php', 58, 68) =]][[= include_file('code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php', 75, 84) =]] ``` You can specify more details of the Field definition in the create struct, for example: ``` php -$titleFieldCreateStruct = $this->contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); -$titleFieldCreateStruct->names = ['eng-GB' => 'Title']; -$titleFieldCreateStruct->descriptions = ['eng-GB' => 'The Title']; -$titleFieldCreateStruct->fieldGroup = 'content'; -$titleFieldCreateStruct->position = 10; -$titleFieldCreateStruct->isTranslatable = true; -$titleFieldCreateStruct->isRequired = true; -$titleFieldCreateStruct->isSearchable = true; -$contentTypeCreateStruct->addFieldDefinition($titleFieldCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php', 66, 76) =]] ``` ### Copying Content Types @@ -186,7 +155,7 @@ $contentTypeCreateStruct->addFieldDefinition($titleFieldCreateStruct); To copy a Content Type, use [`ContentTypeService::copyContentType`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ContentTypeService.php#L241) ``` php -$copy = $this->contentTypeService->copyContentType($contentType); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php', 89, 90) =]] ``` The copy will automatically be given an identifier based on the original Content Type identifier @@ -195,11 +164,7 @@ and the copy's ID, for example: `copy_of_folder_21`. To change the identifier of the copy, use a [`ContentTypeUpdateStruct`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/ContentType/ContentTypeUpdateStruct.php) ``` php -$copyDraft = $this->contentTypeService->createContentTypeDraft($copiedContentType); -$copyUpdateStruct = $this->contentTypeService->newContentTypeUpdateStruct(); -$copyUpdateStruct->identifier = $newIdentifier; -$this->contentTypeService->updateContentTypeDraft($copyDraft, $copyUpdateStruct); -$this->contentTypeService->publishContentTypeDraft($copyDraft); +[[= include_file('code_samples/api/public_php_api/src/Command/CreateContentTypeCommand.php', 90, 96) =]] ``` ## Calendar events @@ -212,34 +177,20 @@ To get a list of events for a specified time period, use the `CalendarServiceInt You need to provide the method with an EventQuery, which takes a date range and a count as the minimum of parameters: ``` php -$dateFrom = new \DateTimeImmutable('2020-08-01T10:00:00+00:00'); -$dateTo = new \DateTimeImmutable('2020-10-31T10:0:00+00:00'); -$dateRange = new Calendar\DateRange($dateFrom, $dateTo); - -$eventQuery = new Calendar\EventQuery($dateRange, 10); - -$eventList = $this->calendarService->getEvents($eventQuery); - -foreach ($eventList as $event) { - $output->writeln($event->getName() . '; date: ' . $event->getDateTime()->format('T Y-m-d H:i:s') ); -} +[[= include_file('code_samples/api/public_php_api/src/Command/CalendarCommand.php', 39, 50) =]] ``` You can also get the first and last event in the list by using the `first()` and `last()` methods of an `EventCollection` (`EzSystems\EzPlatformCalendar\Calendar\EventCollection`) respectively: ``` php -$eventCollection = $eventList->getEvents(); -$output->writeln('First event: ' . $eventCollection->first()->getName() . '; date: ' . $eventCollection->first()->getDateTime()->format('T Y-m-d H:i:s') ); +[[= include_file('code_samples/api/public_php_api/src/Command/CalendarCommand.php', 51, 53) =]] ``` You can process the events in a collection using the `find(Closure $predicate)`, `filter(Closure $predicate)`, `map(Closure $callback)` or `slice(int $offset, ?int $length = null)` methods of `EventCollection`, for example: ``` php -$newCollection = $eventCollection->slice(3, 5); -foreach ($newCollection as $event) { - $output->writeln('New collection: '. $event->getName() . '; date: ' . $event->getDateTime()->format('T Y-m-d H:i:s') ); -} +[[= include_file('code_samples/api/public_php_api/src/Command/CalendarCommand.php', 54, 57) =]] ``` ### Performing calendar actions @@ -249,8 +200,7 @@ You must pass an `EzSystems\EzPlatformCalendar\Calendar\EventAction\EventActionC `EventActionContext` defines events on which the action is performed, as well as action-specific parameters e.g. a new date: ``` php -$newDate = new \DateTimeImmutable('2020-08-12T10:10:01+00:00'); -$context = new RescheduleEventActionContext($eventCollection, $newDate); +[[= include_file('code_samples/api/public_php_api/src/Command/CalendarCommand.php', 59, 61) =]] ``` ``` php diff --git a/docs/api/public_php_api_managing_forms.md b/docs/api/public_php_api_managing_forms.md new file mode 100644 index 0000000000..0175e563dc --- /dev/null +++ b/docs/api/public_php_api_managing_forms.md @@ -0,0 +1,49 @@ +--- +description: You can use PHP API to get, create and delete form submissions. +edition: experience +--- + +# Form API + +## Form submissions + +To manage form submissions created in the [Form Builder](../guide/form_builder/forms.md), use `FormSubmissionServiceInterface`. + +### Getting form submissions + +To get existing form submissions, use `FormSubmissionServiceInterface::loadByContent()` +(which takes a `ContentInfo` object as parameter), or `FormSubmissionServiceInterface::loadById()`. + +``` php +[[= include_file('code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php', 54, 55) =]] +``` + +Through this object, you can get information about submissions, such as their total number, +and submission contents. + +``` php +[[= include_file('code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php', 55, 66) =]] +``` + +### Creating form submissions + +To create a form submission, use the `FormSubmissionServiceInterface::create()` method. + +This method takes: + +- the `ContentInfo` object of the Content item containing the form +- the language code +- the value of the Field containing the form +- the array of form field values + +``` php +[[= include_file('code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php', 40, 53) =]] +``` + +### Deleting form submissions + +You can delete a form submission by using the `FormSubmissionServiceInterface::delete()` method. + +``` php +[[= include_file('code_samples/api/public_php_api/src/Command/FormSubmissionCommand.php', 66, 68) =]] +``` diff --git a/docs/api/public_php_api_managing_migrations.md b/docs/api/public_php_api_managing_migrations.md new file mode 100644 index 0000000000..0929caa234 --- /dev/null +++ b/docs/api/public_php_api_managing_migrations.md @@ -0,0 +1,40 @@ +--- +description: You can use the PHP API to run data migrations, add new migration files, or get information about available migrations. +--- + +# Data migration API + +You can use the PHP API to manage and run [data migrations](../guide/data_migration/data_migration.md). + +## Getting migration information + +To list all migration files available in the directory defined in configuration (by default, `src/Migrations/Ibexa`), +use the `MigrationService:listMigrations()` method: + +``` php +[[= include_file('code_samples/api/migration/src/Command/MigrationCommand.php', 25, 28) =]] +``` + +To get a single migration file by its name, use the `MigrationService:findOneByName()` method: + +``` php +[[= include_file('code_samples/api/migration/src/Command/MigrationCommand.php', 30, 31) =]] +``` + +## Running migration files + +To run migration file(s), use either `MigrationService:executeOne()` or `MigrationService:executeAll()`: + +``` php +[[= include_file('code_samples/api/migration/src/Command/MigrationCommand.php', 32, 33) =]] +``` + +Both `executeOne()` and `executeAll()` can take an optional parameter: the login of the User that you want to execute the migrations as. + +## Adding new migrations + +To add a new migration file, use the `MigrationService:add()` method: + +``` php +[[= include_file('code_samples/api/migration/src/Command/MigrationCommand.php', 18, 24) =]] +``` diff --git a/docs/api/public_php_api_managing_repository.md b/docs/api/public_php_api_managing_repository.md index 0e658eb0b5..f0e9d07601 100644 --- a/docs/api/public_php_api_managing_repository.md +++ b/docs/api/public_php_api_managing_repository.md @@ -1,4 +1,8 @@ -# Managing the Repository +--- +description: PHP API enables you to manage Sections, Object states, workflows, bookmarks and languages in the system. +--- + +# Repository API ## Sections @@ -11,10 +15,7 @@ To create a new Section, you need to make use of the [`SectionCreateStruct`](htt and pass it to the [`SectionService::createSection`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/SectionService.php#L32) method: ``` php -$sectionCreateStruct = $this->sectionService->newSectionCreateStruct(); -$sectionCreateStruct->name = 'New section'; -$sectionCreateStruct->identifier = 'newsection'; -$this->sectionService->createSection($sectionCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/SectionCommand.php', 58, 62) =]] ``` ### Getting Section information @@ -22,7 +23,7 @@ $this->sectionService->createSection($sectionCreateStruct); You can use `SectionService` to retrieve Section information such as whether it is in use: ``` php -$output->writeln(($this->sectionService->isSectionUsed($section) ? 'This section is in use.' : 'This section is not in use.')); +[[= include_file('code_samples/api/public_php_api/src/Command/SectionCommand.php', 76, 80) =]] ``` ### Listing content in a Section @@ -32,17 +33,7 @@ for content belonging to this section, by applying the [`SearchService`.](https: You can also use the query to get the total number of assigned Content items: ``` php -$query = new LocationQuery(); -$query->filter = new Criterion\SectionId([ - $section->id, -]); - -$result = $this->searchService->findContentInfo($query); -$output->writeln('Number of Content items in this section: ' . $result->totalCount); - -foreach ($result->searchHits as $seachResult) { - $output->writeln($seachResult->valueObject->name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/SectionCommand.php', 69, 75) =]][[= include_file('code_samples/api/public_php_api/src/Command/SectionCommand.php', 82, 85) =]] ``` ### Assigning Section to content @@ -52,9 +43,7 @@ You need to provide it with the `ContentInfo` object of the Content item, and the [`Section`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Section.php) object: ``` php -$contentInfo = $this->contentService->loadContentInfo($contentId); -$section = $this->sectionService->loadSectionByIdentifier($sectionIdentifier); -$this->sectionService->assignSection($contentInfo, $section); +[[= include_file('code_samples/api/public_php_api/src/Command/SectionCommand.php', 64, 67) =]] ``` Note that assigning a Section to content does not automatically assign it to the Content item's children. @@ -70,11 +59,7 @@ You can use the [`ObjectStateService`](https://github.com/ezsystems/ezplatform-k to get information about Object state groups or Object states. ``` php -$objectStateGroup = $this->objectStateService->loadObjectStateGroupByIdentifier('ez_lock'); -$output->writeln($objectStateGroup->getName()); - -$objectState = $this->objectStateService->loadObjectStateByIdentifier($objectStateGroup, 'locked'); -$output->writeln($objectState->getName()); +[[= include_file('code_samples/api/public_php_api/src/Command/ObjectStateCommand.php', 48, 53) =]] ``` ### Creating Object states @@ -83,10 +68,7 @@ To create an Object state group and add Object states to it, you need to make use of the [`ObjectStateService`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ObjectStateService.php) ``` php -$objectStateGroupStruct = $this->objectStateService->newObjectStateGroupCreateStruct('rank'); -$objectStateGroupStruct->defaultLanguageCode = 'eng-GB'; -$objectStateGroupStruct->names = ['eng-GB' => 'rank']; -$this->objectStateService->createObjectStateGroup($objectStateGroupStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/ObjectStateCommand.php', 57, 61) =]] ``` [`ObjectStateService::createObjectStateGroup`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/ObjectStateService.php#L36) @@ -98,17 +80,7 @@ use [`ObjectStateService::newObjectStateCreateStruct`](https://github.com/ezsyst and provide it with an `ObjectStateCreateStruct`: ``` php -$objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); - -$stateRegularStruct = $this->objectStateService->newObjectStateCreateStruct('regular'); -$stateRegularStruct->defaultLanguageCode = 'eng-GB'; -$stateRegularStruct->names = ['eng-GB' => 'regular']; -$this->objectStateService->createObjectState($objectStateGroup, $stateRegularStruct); - -$stateSpecialStruct = $this->objectStateService->newObjectStateCreateStruct('special'); -$stateSpecialStruct->defaultLanguageCode = 'eng-GB'; -$stateSpecialStruct->names = ['eng-GB' => 'special']; -$this->objectStateService->createObjectState($objectStateGroup, $stateSpecialStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/ObjectStateCommand.php', 64, 68) =]] ``` ### Assigning Object state @@ -118,35 +90,28 @@ use [`ObjectStateService::setContentState`.](https://github.com/ezsystems/ezplat Provide it with a `ContentInfo` object of the Content item, the Object state group and the Object state: ``` php -$contentInfo = $this->contentService->loadContentInfo($contentId); -$objectStateGroup = $this->objectStateService->loadObjectStateGroup($objectStateGroupId); -$objectState = $this->objectStateService->loadObjectState($objectStateId); - -$this->objectStateService->setContentState($contentInfo, $objectStateGroup, $objectState); +[[= include_file('code_samples/api/public_php_api/src/Command/ObjectStateCommand.php', 78, 83) =]] ``` ## Workflow ### Getting workflow information -To get information about a specific workflow for a Content item, use `WorkflowServiceInterface::loadWorkflowMetadataForContent`: +To get information about a specific [workflow](../guide/workflow/workflow.md) for a Content item, use `WorkflowServiceInterface::loadWorkflowMetadataForContent`: ``` php -$workflowMetadata = $this->workflowService->loadWorkflowMetadataForContent($content, $workflowName); -foreach ($workflowMetadata->markings as $marking) { - $output->writeln($content->getName() . ' is in stage ' . $marking->name . ' in workflow ' . $workflowMetadata->workflow->getName()); -} +[[= include_file('code_samples/api/public_php_api/src/Command/WorkflowCommand.php', 53, 57) =]] ``` !!! tip - `marking`, a term from [Symfony Workflow,](https://symfony.com/doc/5.0/components/workflow.html) + `marking`, a term from [Symfony Workflow,]([[= symfony_doc =]]/components/workflow.html) refers to a state in a workflow. To get a list of all workflows that can be used for a given Content item, use `WorkflowRegistry`: ``` php -$supportedWorkflows = $this->workflowRegistry->getSupportedWorkflows($content); +[[= include_file('code_samples/api/public_php_api/src/Command/WorkflowCommand.php', 47, 48) =]] ``` ### Applying workflow transitions @@ -154,18 +119,23 @@ $supportedWorkflows = $this->workflowRegistry->getSupportedWorkflows($content); To place a Content item in a workflow, use `WorkflowService::start`: ``` php -$this->workflowService->start($content, $workflowName); +[[= include_file('code_samples/api/public_php_api/src/Command/WorkflowCommand.php', 52, 53) =]] ``` -To apply a transition to a Content item, use `WorkflowService::apply`. +To apply a transition to a Content item, use `Workflow::apply`. Additionally, you can check if the transition is possible for the given object using `WorkflowService::can`: ``` php -if ($this->workflowService->can($workflowMetadata, $transitionName)) { - $this->workflowService->apply($workflowMetadata, $transitionName, 'Please review'); -} +[[= include_file('code_samples/api/public_php_api/src/Command/WorkflowCommand.php', 59, 62) =]] } ``` +!!! tip + + `\EzSystems\EzPlatformWorkflow\Value\WorkflowMetadata` object contains all + information about a workflow, such as ID, name, transitions and current stage. + `\EzSystems\EzPlatformWorkflow\Value\WorkflowMetadata::$workflow` gives you direct + access to native Symfony Workflow object. + ## Bookmarks [`BookmarkService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/BookmarkService.php) @@ -174,28 +144,20 @@ enables you to read, add and remove bookmarks from content. To view a list of all bookmarks, use [`BookmarkService::loadBookmarks`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/BookmarkService.php#L54) ``` php -$bookmarkList = $this->bookmarkService->loadBookmarks(); - -$output->writeln('Total bookmarks: ' . $bookmarkList->totalCount); - -foreach ($bookmarkList->items as $bookmark) { - $output->writeln($bookmark->getContentInfo()->name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/BookmarkCommand.php', 43, 50) =]] ``` You can add a bookmark to a Content item by providing its Location object to the [`BookmarkService::createBookmark`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/BookmarkService.php#L31) method: ``` php -$location = $this->locationService->loadLocation($locationId); - -$this->bookmarkService->createBookmark($location); +[[= include_file('code_samples/api/public_php_api/src/Command/BookmarkCommand.php', 37, 40) =]] ``` You can remove a bookmark from a Location with [`BookmarkService::deleteBookmark`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/BookmarkService.php#L42) ``` php -$this->bookmarkService->deleteBookmark($location); +[[= include_file('code_samples/api/public_php_api/src/Command/BookmarkCommand.php', 52, 53) =]] ``` ## Languages @@ -205,11 +167,7 @@ $this->bookmarkService->deleteBookmark($location); To get a list of all languages in the system use [`LanguageService::loadLanguages`:](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LanguageService.php#L81) ``` php -$languageList = $this->languageService->loadLanguages(); - -foreach ($languageList as $language) { - $output->writeln($language->languageCode . ': ' . $language->name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/AddLanguageCommand.php', 37, 42) =]] ``` ### Creating a language @@ -219,8 +177,5 @@ and provide it with the language code and language name. Then, use [`LanguageService::createLanguage`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/LanguageService.php#L29) and pass the `LanguageCreateStruct` to it: ``` php -$languageCreateStruct = $this->languageService->newLanguageCreateStruct(); -$languageCreateStruct->languageCode = 'ger-DE'; -$languageCreateStruct->name = 'German'; -$this->languageService->createLanguage($languageCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/AddLanguageCommand.php', 43, 47) =]] ``` diff --git a/docs/api/public_php_api_managing_users.md b/docs/api/public_php_api_managing_users.md index 3c2c55141b..632dbba51a 100644 --- a/docs/api/public_php_api_managing_users.md +++ b/docs/api/public_php_api_managing_users.md @@ -1,4 +1,10 @@ -# Managing Users [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: You can use PHP API to get Segment information, create and manage Segments, and assign users to them. +--- + +# User API + +The user API enables you to operate on user-related part of the Repository, such a segments. ## Segments @@ -8,23 +14,17 @@ To manage Segments, use the `SegmentationService`. ### Getting Segment information -To load a Segment Group, use `SegmentationService::loadSegmentGroup()`. +To load a Segment Group, use `SegmentationService::loadSegmentGroupByIdentifier()`. Get all Segments assigned to the group with `SegmentationService::loadSegmentsAssignedToGroup()`: ``` php -$segmentGroup = $this->segmentationService->loadSegmentGroup(1); - -$segments = $this->segmentationService->loadSegmentsAssignedToGroup($segmentGroup); - -foreach ($segments as $segment) { - $output->writeln($segment->name); -} +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 53, 60) =]] ``` -Similarly, you can load a Segment in a group by using `SegmentationService::loadSegmentGroup()`: +Similarly, you can load a Segment in a group by using `SegmentationService::loadSegmentIdentifier()`: ``` php -$segment = $this->segmentationService->loadSegment(12); +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 62, 63) =]] ``` ### Checking assignment @@ -32,10 +32,7 @@ $segment = $this->segmentationService->loadSegment(12); You can check whether a User is assigned to a Segment with `SegmentationService::isUserAssignedToSegment()`: ``` php -$user = $this->userService->loadUserByLogin('admin'); -$segment = $this->segmentationService->loadSegment(12); - -$isAssigned = $this->segmentationService->isUserAssignedToSegment($user, $segment); +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 65, 69) =]] ``` ### Assigning Users @@ -43,10 +40,7 @@ $isAssigned = $this->segmentationService->isUserAssignedToSegment($user, $segmen To assign a User to a Segment, use `SegmentationService::assignUserToSegment()`: ``` php -$user = $this->userService->loadUserByLogin('admin'); -$segment = $this->segmentationService->loadSegment(12); - -$this->segmentationService->assignUserToSegment($user, $segment); +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 61, 64) =]] ``` ### Creating Segments @@ -57,26 +51,14 @@ To create a Segment Group, use `SegmentationService::createSegmentGroup()` and provide it with a `SegmentGroupCreateStruct`: ``` php -$segmentGroupCreateStruct = new SegmentGroupCreateStruct([ - 'name' => 'Custom Group', - 'identifier' => 'custom_group', - 'createSegments' => [] -]); - -$newSegmentGroup = $this->segmentationService->createSegmentGroup($segmentGroupCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 37, 44) =]] ``` -To create a Segment, use `SegmentationService::createSegment()` +To add a Segment, use `SegmentationService::createSegment()` and provide it with a `SegmentCreateStruct`, which takes an existing group as one of the parameters: ``` php -$segmentCreateStruct = new SegmentCreateStruct([ - 'name' => 'Segment 1', - 'identifier' => 'segment_1', - 'group' => $segmentGroup, -]); - -$newSegment = $this->segmentationService->createSegment($segmentCreateStruct); +[[= include_file('code_samples/api/public_php_api/src/Command/SegmentCommand.php', 45, 52) =]] ``` ### Updating Segments diff --git a/docs/api/public_php_api_search.md b/docs/api/public_php_api_search.md index e6eb493cc8..6a37db3632 100644 --- a/docs/api/public_php_api_search.md +++ b/docs/api/public_php_api_search.md @@ -1,4 +1,8 @@ -# Content search +--- +description: You can search for content, Locations and products by using the PHP API. Fine-tune the search with Search Criteria, Sort Clauses and Aggregations. +--- + +# Search API You can search for content with the PHP API in two ways. @@ -9,7 +13,7 @@ To do this, you can use the [`SearchService`](#searchservice) or [Repository fil [`SearchService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/SearchService.php) enables you to perform search queries using the PHP API. -The service should be [injected into the constructor of your command or controller.](https://symfony.com/doc/5.0/service_container.html) +The service should be [injected into the constructor of your command or controller](../api/public_php_api.md#service-container). !!! tip "SearchService in the Back Office" @@ -27,28 +31,9 @@ For example, to search for all content of a selected Content Type, use one Crite The following command takes the Content Type identifier as an argument and lists all results: ``` php hl_lines="14 16" -//... -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -class FindContentCommand extends Command -{ - // ... - protected function execute(InputInterface $input, OutputInterface $output) - { - $contentTypeId = $input->getArgument('contentTypeId'); - - $query = new LocationQuery(); - $query->filter = new Criterion\ContentTypeIdentifier($contentTypeId); - - $result = $this->searchService->findContentInfo($query); - $output->writeln('Found ' . $result->totalCount . ' items'); - foreach ($result->searchHits as $searchHit) { - $output->writeln($searchHit->valueObject->name); - } - } -} +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 8, 14) =]] // ... +[[= include_file('code_samples/api/public_php_api/src/Command/FindContentCommand.php', 31, 47) =]] ``` [`SearchService::findContentInfo`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/SearchService.php#L144) (line 16) @@ -66,7 +51,7 @@ $output->writeln($result->getName()); !!! tip - For full list and details of available Search Criteria, see [Search Criteria reference](../guide/search/search_criteria_reference.md). + For full list and details of available Search Criteria, see [Search Criteria reference](../guide/search/criteria_reference/search_criteria_reference.md). !!! note "Search result limit" @@ -84,9 +69,39 @@ In contrast to `filter`, `query` has an effect of search scoring (relevancy). It affects default sorting if no Sort Clause is used. As such, `query` is recommended when the search is based on user input. -The difference between `query` and `filter` is only relevant when using Solr search engine. +The difference between `query` and `filter` is only relevant when using Solr or Elasticsearch search engine. With the Legacy search engine both properties will give identical results. +#### Processing large result sets + +To process a large result set, use `eZ\Publish\API\Repository\Iterator\BatchIterator`. +`BatchIterator` divides the results of search or filtering into smaller batches. +This enables iterating over results that are too large to handle due to memory constraints. + +`BatchIterator` takes one of the available adapters (`\eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter` ) and optional batch size. For example: + +``` php +$query = new LocationQuery; + +$iterator = new BatchIterator(new BatchIteratorAdapter\LocationSearchAdapter($this->searchService, $query)); + +foreach ($iterator as $result) { + $output->writeln($result->valueObject->getContentInfo()->name); +} +``` + +You can also define the batch size by setting `$iterator->setBatchSize()`. + +The following BatchIterator adapters are available, for both `query` and `filter` searches: + +| Adapter | Method | +|----------------------------|-------------------------------------------------------------| +| `ContentFilteringAdapter` | `\eZ\Publish\API\Repository\ContentService::find` | +| `ContentInfoSearchAdapter` | `\eZ\Publish\API\Repository\SearchService::findContentInfo` | +| `ContentSearchAdapter` | `\eZ\Publish\API\Repository\SearchService::findContent` | +| `LocationFilteringAdapter` | `\eZ\Publish\API\Repository\LocationService::find` | +| `LocationSearchAdapter` | `\eZ\Publish\API\Repository\SearchService::findLocations` | + ## Repository filtering You can use the `ContentService::find(Filter)` method to find Content items or @@ -102,71 +117,19 @@ Filtering differs from search. It does not use the `SearchService` and is not ba For example, the following command lists all Content items under the specified parent Location and sorts them by name in descending order: -``` php hl_lines="15 16 17 18" +``` php hl_lines="16-19" // ... -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -class FilterCommand extends Command -{ - // ... - protected function execute(InputInterface $input, OutputInterface $output): int - { - $parentLocationId = (int)$input->getArgument('parent-location-id'); - - $filter = new Filter(); - $filter - ->withCriterion(new Criterion\ParentLocationId($parentLocationId)) - ->withSortClause(new SortClause\ContentName(Query::SORT_DESC)); - - $result = $this->contentService->find($filter, []); - - $output->writeln('Found ' . $result->getTotalCount() . ' items'); - - foreach ($result as $content) { - $output->writeln($content->getName()); - } - - return self::SUCCESS; - } -} +[[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 8, 16) =]] +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/FilterCommand.php', 32, 52) =]] ``` The same Filter can be applied to find Locations instead of Content items, for example: -``` php hl_lines="19" +``` php hl_lines="20" // ... -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -class FilterCommand extends Command -{ - // ... - protected function execute(InputInterface $input, OutputInterface $output): int - { - $parentLocationId = (int)$input->getArgument('parent-location-id'); - $filter = new Filter(); - $filter - ->withCriterion(new Criterion\ParentLocationId($parentLocationId)) - ->withSortClause(new SortClause\ContentName(Query::SORT_DESC)); - - $result = $this->locationService->find($filter, []); - - $output->writeln('Found ' . $result->totalCount . ' items'); - - foreach ($result as $location) { - $output->writeln($location->getContent()->getName()); - } - - return self::SUCCESS; - } -} +[[= include_file('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 8, 16) =]]// ... +[[= include_file('code_samples/api/public_php_api/src/Command/FilterLocationCommand.php', 32, 52) =]] ``` Notice that the total number of items is retrieved differently for `ContentList` and `LocationList`. @@ -209,8 +172,8 @@ $filter and Sort Clauses implementing [`FilteringSortClause`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.1.0/eZ/Publish/SPI/Repository/Values/Filter/FilteringSortClause.php) are supported. - See [Search Criteria](../guide/search/search_criteria_reference.md) - and [Sort Clause reference](../guide/search/sort_clause_reference.md) for details. + See [Search Criteria](../guide/search/criteria_reference/search_criteria_reference.md) + and [Sort Clause reference](../guide/search/sort_clause_reference/sort_clause_reference.md) for details. !!! tip @@ -222,63 +185,20 @@ $filter You can use the `SearchService` or Repository filtering in a controller, as long as you provide the required parameters. For example, in the code below, `locationId` is provided to list all children of a Location by using the `SearchService`. -``` php hl_lines="20 21 22" -//... -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -class CustomController extends Controller -{ - //... - public function showContentAction($locationId) - { - $query = new LocationQuery(); - $query->filter = new Criterion\ParentLocationId($locationId); - - $results = $this->searchService->findContentInfo($query); - $items = []; - foreach ($results->searchHits as $searchHit) { - $items[] = $searchHit; - } - - return $this->render('custom.html.twig', [ - 'items' => $items, - ]); - } -} +``` php hl_lines="21-23" +// ... +[[= include_file('code_samples/api/public_php_api/src/Controller/CustomController.php', 4, 11) =]] // ... +[[= include_file('code_samples/api/public_php_api/src/Controller/CustomController.php', 18, 34) =]] ``` -The rendering of results is then relegated to [templates](../guide/templates.md) (lines 20-22). +The rendering of results is then relegated to [templates](../guide/content_rendering/templates/templates.md) (lines 20-22). When using Repository filtering, provide the results of `ContentService::find()` as parameters to the view: ``` php hl_lines="19" // ... -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; - -class CustomController extends Controller -{ - // ... - public function showChildrenAction(ContentView $view): ContentView - { - $filter = new Filter(); - $filter - ->withCriterion(new ParentLocationId($view->getLocation()->id)) - - $view->setParameters( - [ - 'items' => $this->contentService->find($filter), - ] - ); - - return $view; - } -} +[[= include_file('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 4, 12) =]] // ... +[[= include_file('code_samples/api/public_php_api/src/Controller/CustomFilterController.php', 19, 34) =]] ``` ### Paginating search results @@ -286,47 +206,38 @@ class CustomController extends Controller To paginate search or filtering results, it is recommended to use the [Pagerfanta library](https://github.com/whiteoctober/Pagerfanta) and [[[= product_name =]]'s adapters for it.](https://github.com/ezsystems/ezplatform-kernel/tree/v1.0.0/eZ/Publish/Core/Pagination/Pagerfanta) ``` php -//... -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter; -use Symfony\Component\HttpFoundation\Request; -use Pagerfanta\Pagerfanta; - -class CustomController extends Controller -{ - //... - public function showContentAction(Request $request, $locationId) - { - // formulate a $query - - $pager = new Pagerfanta( - new ContentSearchAdapter($query, $this->searchService) - ); - $pager->setMaxPerPage(3); - $pager->setCurrentPage($request->get('page', 1)); - - return $this->render('custom.html.twig', [ - 'totalItemCount' => $pager->getNbResults(), - 'pagerItems' => $pager, - ] - ); - } -} +// ... +[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 8, 14) =]] // ... +[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 21, 31) =]] +[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 35, 42) =]] ``` Pagination can then be rendered for example using the following template: ``` html+twig -{% for item in pagerItems %} - <h2><a href={{ ez_path(item.valueObject) }}>{{ ez_content_name(item) }}</a></h2> -{% endfor %} - -{% if pagerItems.haveToPaginate() %} - {{ pagerfanta( pagerItems, 'ez') }} -{% endif %} +[[= include_file('code_samples/api/public_php_api/templates/custom_pagination.html.twig') =]] ``` For more information and examples, see [PagerFanta documentation.](https://github.com/whiteoctober/Pagerfanta/blob/master/README.md) +#### Additional search result data + +You can access the following additional search result data from PagerFanta: + +- Aggregation results +- Max. score +- Computation time +- Timeout flag + +``` php +[[= include_file('code_samples/api/public_php_api/src/Controller/PaginationController.php', 32, 34) =]] +``` + +``` html+twig +<p>Max score: {{ pagerItems.maxScore }}</p> +<p>Time: {{ pagerItems.time }}</p> +``` + #### Pagerfanta adapters |Adapter class name|Description| @@ -343,21 +254,9 @@ For more information and examples, see [PagerFanta documentation.](https://githu For more complex searches, you need to combine multiple Criteria. You can do it using logical operators: `LogicalAnd`, `LogicalOr`, and `LogicalNot`. -``` php hl_lines="6 7 8 11" -$query = new LocationQuery; -$criterion1 = new Criterion\Subtree($this->locationService->loadLocation($locationId)->pathString); -$criterion2 = new Criterion\ContentTypeId($contentTypeId); -$criterion3 = new Criterion\FullText($text); - -$query->query = new Criterion\LogicalAnd( - [$criterion1, $criterion2, $criterion3] -); - -$result = $this->searchService->findContentInfo($query); -$output->writeln('Found ' . $result->totalCount . ' items'); -foreach ($result->searchHits as $searchHit) { - $output->writeln($searchHit->valueObject->name); -} +``` php +[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 43, 49) =]][[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 52, 53) =]] +[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 59, 64) =]] ``` This example takes three parameters from a command — `$text`, `$contentTypeId`, and `$locationId`. @@ -367,17 +266,11 @@ that belong to a specific subtree, have the chosen Content Type and contain the This also shows that you can get the total number of search results using the `totalCount` property of search results (line 11). You can also nest different operators to construct more complex queries. -The example below uses the `LogicalNot` operator to search for all children of the selected parent -that do not belong to the provided Content Type: +The example below uses the `LogicalNot` operator to search for all content containing a given phrase +that does not belong to the provided Section: ``` php -$query->filter = new Criterion\LogicalAnd([ - new Criterion\ParentLocationId($locationId), - new Criterion\LogicalNot( - new Criterion\ContentTypeIdentifier($contentTypeId) - ) - ] -); +[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 45, 46) =]][[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 48, 53) =]] ``` ### Combining independent Criteria @@ -405,46 +298,32 @@ Even though the Location B is hidden, the query will find the content because bo ## Sorting results -To sort the results of a query, use one of more [Sort Clauses](../guide/search/sort_clause_reference.md). +To sort the results of a query, use one of more [Sort Clauses](../guide/search/sort_clause_reference/sort_clause_reference.md). For example, to order search results by their publicationg date, from oldest to newest, and then alphabetically by content name, add the following Sort Clauses to the query: ``` php -$query->sortClauses = [ - new SortClause\DatePublished(LocationQuery::SORT_ASC), - new SortClause\ContentName(LocationQuery::SORT_DESC), -]; +[[= include_file('code_samples/api/public_php_api/src/Command/FindComplexCommand.php', 54, 58) =]] ``` !!! tip - For the full list and details of available Sort Clauses, see [Sort Clause reference](../guide/search/sort_clause_reference.md). + For the full list and details of available Sort Clauses, see [Sort Clause reference](../guide/search/sort_clause_reference/sort_clause_reference.md). ## Searching in trash In the user interface, on the Trash screen, you can search for Content items, and then sort the results based on different criteria. To search the trash with the API, use the `TrashService::findInTrash` method to submit a query for Content items that are held in trash. Searching in trash supports a limited set of Criteria and Sort Clauses. -For a list of supported Criteria and Sort Clauses, see [Searching in trash reference](../guide/search/search_in_trash_reference.md). +For a list of supported Criteria and Sort Clauses, see [Search in trash reference](../guide/search/search_in_trash_reference.md). !!! note Searching through the trashed Content items operates directly on the database, therefore you cannot use external search engines, such as Solr or Elasticsearch, and it is impossible to reindex the data. ``` php -use eZ\Publish\API\Repository\Values\Content\Query; - - // ... - - $query = new Query(); - // find trashed folders - $query->filter = new Query\Criterion\ContentTypeId([1]); - $results = $this->trashService->findTrashItems($query); - foreach ($results->items as $trashedLocation) { -     /** @var \eZ\Publish\API\Repository\Values\Content\TrashItem[] $trashedLocation */ -     // ... - } +[[= include_file('code_samples/api/public_php_api/src/Command/FindInTrashCommand.php', 34, 41) =]] ``` !!! caution @@ -463,7 +342,7 @@ With aggregations you can find the count of search results or other result infor To do this, you use of the query's `$aggregations` property: ``` php -$query->aggregations[] = new ContentTypeTermAggregation('content_type'); +[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 34, 35) =]] ``` The name of the aggregation must be unique in the given query. @@ -471,15 +350,13 @@ The name of the aggregation must be unique in the given query. Access the results by using the `get()` method of the aggregation: ``` php -$contentByType = $results->aggregations->get('content_type'); +[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 39, 40) =]] ``` Aggregation results contain the name of the result and the count of found items: ``` php -foreach ($contentByType as $contentType => $count) { - $output->writeln($contentType->getName() . ': ' . $count); -} +[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 45, 48) =]] ``` With field aggregations you can group search results according to the value of a specific Field. @@ -489,7 +366,7 @@ The following example creates an aggregation named `selection` that groups resul according to the value of the `topic` Field in the `article` Content Type: ``` php -$query->aggregations[] = new SelectionTermAggregation('selection', 'article', 'topic'); +[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 35, 36) =]] ``` With term aggregation you can define additional limits to the results. @@ -497,8 +374,7 @@ The following example limits the number of terms returned to 5 and only considers terms that have 10 or more results: ``` php -$query->aggregations[0]->setLimit(5); -$query->aggregations[0]->setMinCount(10); +[[= include_file('code_samples/api/public_php_api/src/Command/FindWithAggregationCommand.php', 42, 44) =]] ``` To use a range aggregation, you must provide a `ranges` array containing a set of `Range` objects @@ -521,45 +397,12 @@ $query->aggregations[] = new IntegerRangeAggregation('range', 'person', 'age', `null` means that a range does not have an end. In the example all values above (and including) 60 are included in the last range. -See [Agrregation reference](../guide/search/aggregation_reference.md) for details of all available aggregations. +See [Agrregation reference](../guide/search/aggregation_reference/aggregation_reference.md) for details of all available aggregations. ## Faceted search !!! caution "Deprecated" Search Facets are deprecated since version v3.2. - -!!! tip "Checking feature support per search engine" - - Faceted search is available only for the Solr search engine. - - To find out if a given search engine supports any of the advanced search capabilities, - use the [`eZ\Publish\API\Repository\SearchService::supports`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/SearchService.php#L188-L199) method: - - ``` php - $facetSupport = $this->searchService->supports(SearchService::CAPABILITY_FACETS); - ``` - -Faceted search enables you to find the count of search results for each Facet value. - -To do this, you need to make use of the query's `$facetBuilders` property: - -``` php -$query->facetBuilders[] = new FacetBuilderUserFacetBuilder( - [ - 'name' => 'User', - 'type' => FacetBuilder\UserFacetBuilder::OWNER, - 'minCount' => 2, - 'limit' => 5 - ] -); - -$result = $this->searchService->findContentInfo($query); - -$output->writeln("Number of results per facet value: "); -foreach ($result->facets[0]->entries as $facetEntry) { - $output->writeln("* " . $facetEntry); -} -``` - -See [Search Facet reference](../guide/search/search.md#search-facet-reference) for details of all available Facets. + + Use [Aggregation API](#aggregation) instead. diff --git a/docs/api/public_php_api_url_service.md b/docs/api/public_php_api_url_service.md index 688d588254..5458b4f0b0 100644 --- a/docs/api/public_php_api_url_service.md +++ b/docs/api/public_php_api_url_service.md @@ -1,4 +1,8 @@ -# URLService +--- +description: The PHP API URLService enables searching for external URLs used in tech text and URL Fields. +--- + +# URL API [`URLService`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/URLService.php) enables you to find, load and update external URLs used in RichText and URL Fields. @@ -14,54 +18,13 @@ in which you need to specify: - query limit. If value is `0`, search query will not return any search hits ```php -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; - -# ... - -$urlQuery = new URLQuery(); -$urlQuery->filter = new Criterion\LogicalAnd( - [ - new Criterion\SectionIdentifier(['standard']), - new Criterion\Validity(true), - ] -); -$urlQuery->sortClauses = [ - new SortClause\URL(SortClause::SORT_DESC) -]; -$urlQuery->offset = 0; -$urlQuery->limit = 25; - -$results = $urlService->findUrls($urlQuery); +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 9, 12) =]] +// ... +[[= include_file('code_samples/api/public_php_api/src/Command/FindUrlCommand.php', 43, 58) =]] ``` -## URL criteria reference - -|URL criteria|URL based on| -|------------|------------| -|[LogicalAnd](url_reference/logicaland_criterion.md)|Implements a logical AND Criterion. It matches if ALL of the provided Criteria match.| -|[LogicalNot](url_reference/logicalnot_criterion.md)|Implements a logical NOT Criterion. It matches if the provided Criterion doesn't match.| -|[LogicalOr](url_reference/logicalor_criterion.md)|Implements a logical OR Criterion. It matches if at least one of the provided Criteria match.| -|[MatchAll](url_reference/matchall_criterion.md)|Returns all URL results.| -|[MatchNone](url_reference/matchnone_criterion.md)|Returns no URL results.| -|[Pattern](url_reference/pattern_criterion.md)|Matches URLs that contain a pattern.| -|[SectionId](url_reference/sectionid_criterion.md)|Matches URLs from content placed in the Section with the specified ID.| -|[SectionIdentifier](url_reference/sectionidentifier_criterion.md)|Matches URLs from content placed in Sections with the specified identifiers.| -|[Validity](url_reference/validity_criterion.md)|Matches URLs based on validity flag.| -|[VisibleOnly](url_reference/visibleonly_criterion.md)|Matches URLs from published content.| - -## URL Sort Clauses reference - -Sort Clauses are the sorting options for URLs. - -All Sort Clauses can take the following optional argument: - -- `sortDirection` - the direction of the sorting, either `\eZ\Publish\API\Repository\Values\URL\Query\SortClause::SORT_ASC` (default) or `\eZ\Publish\API\Repository\Values\URL\Query\SortClause::SORT_DESC` - -#### Sort Clauses +## URL search reference -| Sort Clause | Sorting based on | -|-----|-----| -|[Id](url_reference/id_sort_clause.md)|URL ID| -|[URL](url_reference/url_sort_clause.md)|URL address| +For the reference of Search Criteria and Sort Clauses you can use in URL search, +see [URL search reference](../guide/search/url_search_reference/url_search_reference.md). diff --git a/docs/api/rest_api_authentication.md b/docs/api/rest_api_authentication.md index ec27084fb7..22567c8603 100644 --- a/docs/api/rest_api_authentication.md +++ b/docs/api/rest_api_authentication.md @@ -1,53 +1,235 @@ +--- +description: To authenticate REST API communication you can use session (default), JWT, basic, OAuth and client certificate (SSL) authentication. +--- + # REST API authentication -This page refers to [REST API reference](https://doc.ezplatform.com/rest-api-reference), where you can find detailed information about +This page refers to [REST API reference](rest_api_reference/rest_api_reference.html), where you can find detailed information about REST API resources and endpoints. -Using HTTPS for authenticated (REST) traffic is highly recommended. +Five authentication methods are currently supported: session (default), JWT, basic, OAuth and client certificate (SSL). -## Basic authentication +You can only use one of those methods at the same time. -For more information, see [HTTP Authentication: Basic and Digest Access Authentication.](http://tools.ietf.org/html/rfc2617) +Using HTTPS for authenticated traffic is highly recommended. -## OAuth +For other security related subjects, see: -For more information, see [OAuth 2.0 protocol for authorization.](https://oauth.net/2/) +- [Cross-origin requests](rest_api_responses.md#cross-origin) +- [`access_control`]([[= symfony_doc =]]/security/access_control.html) ## Session-based authentication -Sessions are created to re-authenticate the user only (and perform authorization), not to hold session state in the service. -Because of that, we regard this method as supporting AJAX-based applications even if it violates the principles of RESTful services. +This authentication method requires a session cookie to be sent with each request. -For more information, see [REST API authentication](general_rest_usage.md#rest-api-authentication). +If you use this authentication method with a web browser, this session cookie is automatically available as soon as your visitor logs in. +Add it as a cookie to your REST requests, and the user will be authenticated. -### Session cookie +Sessions are created to re-authenticate the user only (and perform authorization), not to hold session state in the service. +Because of that, you can use this method as supporting AJAX-based applications even if it violates the principles of RESTful services. -If activated, the user must log in, and the client must send the session cookie in every request, using standard Cookie header. -The name (`sessionName`) and value (`sessionID`) of the header are defined in a `/user/sessions` POST response. +### Configuration -Example request header: `Cookie: <SessionName> : <sessionID>`. +Session is the default method and is already enabled, so no configuration required. +Enabling any other method disables session. -### CSRF token +### Usage examples -A CSRF token must be sent in every request that uses unsafe methods (not GET or HEAD or OPTIONS), when a session has been established. -It should be sent with an `X-CSRF-Token` header. -The token (`csrfToken`) is defined in a response during logging in through the POST `/user/sessions`. +You can create a session for a visitor even if they are not logged in by sending the **`POST`** request to `/user/sessions`. +To log out, use the **`DELETE`** request on the same resource. + +#### Establishing session + +##### Creating session + +To create a session, execute the following REST request: + +=== "XML" + + ``` + POST /user/sessions HTTP/1.1 + Host: www.example.net + Accept: application/vnd.ez.api.Session+xml + Content-Type: application/vnd.ez.api.SessionInput+xml + ``` + + ```xml + <?xml version="1.0" encoding="UTF-8"?> + <SessionInput> + <login>admin</login> + <password>publish</password> + </SessionInput> + ``` + + ``` + HTTP/1.1 201 Created + Location: /user/sessions/go327ij2cirpo59pb6rrv2a4el2 + Set-Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2; domain=.example.net; path=/; expires=Wed, 13-Jan-2021 22:23:01 GMT; HttpOnly + Content-Type: application/vnd.ez.api.Session+xml + ``` + + ```xml + <?xml version="1.0" encoding="UTF-8"?> + <Session href="/user/sessions/sessionID" media-type="application/vnd.ez.api.Session+xml"> + <name>eZSESSID98defd6ee70dfb1dea416</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lk.neri34ijajedfw39orj-3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/> + </Session> + ``` + +=== "JSON" + + ``` + POST /user/sessions HTTP/1.1 + Host: www.example.net + Accept: application/vnd.ez.api.Session+json + Content-Type: application/vnd.ez.api.SessionInput+json + ``` + + ```json + { + "SessionInput": { + "login": "admin", + "password": "publish" + } + } + ``` + + ``` + HTTP/1.1 201 Created + Location: /user/sessions/go327ij2cirpo59pb6rrv2a4el2 + Set-Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2; domain=.example.net; path=/; expires=Wed, 13-Jan-2021 22:23:01 GMT; HttpOnly + Content-Type: application/vnd.ez.api.Session+xml + ``` + + ```json + { + "Session": { + "_media-type": "application\/vnd.ez.api.Session+json", + "_href": "\/api\/ibexa\/v2\/user\/sessions\/jg1nhinvepsb9ivd10hbjbdp4l", + "name": "eZSESSID98defd6ee70dfb1dea416", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lk.neri34ijajedfw39orj-3j93", + "User": { + "_media-type": "application\/vnd.ez.api.User+json", + "_href": "\/api\/ibexa\/v2\/user\/users\/14" + } + } + } + ``` + +##### Logging in with active session + +Logging in is very similar to session creation, with one important detail: +the CSRF token obtained in the previous step is added to the new request through the `X-CSRF-Token` header. + +=== "XML" + + ``` + POST /user/sessions HTTP/1.1 + Host: www.example.net + Accept: application/vnd.ez.api.Session+xml + Content-Type: application/vnd.ez.api.SessionInput+xml + Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2 + X-CSRF-Token: 23lk.neri34ijajedfw39orj-3j93 + ``` + + ```xml + <?xml version="1.0" encoding="UTF-8"?> + <SessionInput> + <login>admin</login> + <password>publish</password> + </SessionInput> + ``` + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.ez.api.Session+xml + ``` + + ```xml + <?xml version="1.0" encoding="UTF-8"?> + <Session href="user/sessions/go327ij2cirpo59pb6rrv2a4el2/refresh" media-type="application/vnd.ez.api.Session+xml"> + <name>eZSESSID98defd6ee70dfb1dea416</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lk.neri34ijajedfw39orj-3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/> + </Session> + ``` -Example request headers: +=== "JSON" + + ``` + POST /user/sessions HTTP/1.1 + Host: www.example.net + Accept: application/vnd.ez.api.Session+json + Content-Type: application/vnd.ez.api.SessionInput+json + Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2 + X-CSRF-Token: 23lk.neri34ijajedfw39orj-3j93 + ``` + + ```xml + { + "SessionInput": { + "login": "admin", + "password": "publish" + } + } + ``` + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.ez.api.Session+json + ``` + + ```xml + { + "Session": { + "_media-type": "application\/vnd.ez.api.Session+json", + "_href": "\/api\/ibexa\/v2\/user\/sessions\/jg1nhinvepsb9ivd10hbjbdp4l", + "name": "eZSESSID98defd6ee70dfb1dea416", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lk.neri34ijajedfw39orj-3j93", + "User": { + "_media-type": "application\/vnd.ez.api.User+json", + "_href": "\/api\/ibexa\/v2\/user\/users\/14" + } + } + } + ``` + +#### Using session + +##### Session cookie + +You can now add the previously set cookie to requests to be executed with the logged-in user. ``` -DELETE /content/types/32 HTTP/1.1 -X-CSRF-Token: <csrfToken> +GET /content/locations/1/5 HTTP/1.1 +Host: www.example.net +Accept: Accept: application/vnd.ez.api.Location+xml +Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2 ``` +##### CSRF token + +It can be important to keep the CSRF token (`csrfToken`) for the duration of the session, +because you must send this token in every request that uses [unsafe HTTP methods](rest_api_requests.md#request-method) (others than the safe GET or HEAD or OPTIONS) when a session has been established. +It should be sent with an `X-CSRF-Token` header. + +Only three built-in routes can accept unsafe methods without CSRF, the sessions routes starting with `/user/sessions` to create, refresh or delete a session. + ``` -DELETE /user/sessions/<sessionID> -X-CSRF-Token: <csrfToken> +DELETE /content/types/32 HTTP/1.1 +Host: www.example.net +Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2 +X-CSRF-Token: 23lk.neri34ijajedfw39orj-3j93 ``` If an unsafe request is missing the CSRF token, or the token has incorrect value, an error is returned: `401 Unauthorized`. -### Rich client application security concerns +##### Rich client application security concerns The purpose of CSRF protection is to prevent users from accidentally running harmful operations by being tricked into executing an HTTP(S) request against a web applications they are logged into. In browsers this action will be blocked by lack of CSRF token. @@ -62,13 +244,153 @@ However, if you develop a rich client application (JavaScript, JAVA, iOS, Androi Then, you have to make sure to confirm with the user if they want to perform an unsafe operation. -Example: +Example: A rich JavaScript/web application is using `navigator.registerProtocolHandler()` to register "web+ez:" links to go against REST API. It uses a session-based authentication, and it is in widespread use across the net, or/and it is used by everyone within a company. -A person with minimal insight into this application and the company can easily send out the following link to all employees in that company in email: +A person with minimal insight into this application and the company can easily send out the following link to all employees in that company in email: `<a href="web+ez:DELETE /content/locations/1/2">latest reports</a>`. +#### Logging out from session + +To log out is to `DELETE` the session using its ID (like in the cookie). As this is an unsafe method, the CSRF token must be presented. + +``` +DELETE /user/sessions/go327ij2cirpo59pb6rrv2a4el2 HTTP/1.1 +Host: www.example.net +Cookie: eZSESSID98defd6ee70dfb1dea416=go327ij2cirpo59pb6rrv2a4el2 +X-CSRF-Token: 23lk.neri34ijajedfw39orj-3j93 +``` + +## JWT authentication + +### Configuration + +See [JWT authentication](../guide/security.md#jwt-authentication) to lear how to enable JWT for REST and/or GraphQL. + +### Usage example + +After you [configure JWT authentication](../guide/security.md#jwt-authentication) at least for REST, +you can get the JWT token through the following request: + +=== "XML" + + ``` + POST /user/token/jwt HTTP/1.1 + Host: <yourdomain> + Accept: application/vnd.ez.api.JWT+xml + Content-Type: application/vnd.ez.api.JWTInput+xml + ``` + + Provide the username and password in the request body: + + ```xml + <JWTInput> + <username>admin</username> + <password>publish</password> + </JWTInput> + ``` + + If credentials are valid, the server response contains a token: + + ```xml + <JWT media-type="application/vnd.ez.api.JWT+xml" token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…-QBE4-6eKNjg"/> + ``` + + You can then use this token in your request instead of username and password. + + ``` + GET /content/locations/1/5/children + Host: <yourdomain> + Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…-QBE4-6eKNjg + Accept: application/vnd.ez.api.LocationList+xml + ``` + +=== "JSON" + + ``` + POST /user/token/jwt HTTP/1.1 + Host: <yourdomain> + Accept: application/vnd.ez.api.JWT+json + Content-Type: application/vnd.ez.api.JWTInput+json + ``` + + Provide the username and password in the request body: + + ```json + { + "JWTInput": { + "username": "admin", + "password": "publish" + } + } + ``` + + If credentials are valid, the server response contains a token: + + ```json + { + "JWT": { + "_media-type": "application/vnd.ez.api.JWT+xml", + "_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…-QBE4-6eKNjg" + } + } + ``` + + You can then use this token in your request instead of username and password. + + ``` + GET /content/locations/1/5/children + Host: <yourdomain> + Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9…-QBE4-6eKNjg + Accept: application/vnd.ez.api.LocationList+json + ``` + +## HTTP basic authentication + +For more information, see [HTTP Authentication: Basic and Digest Access Authentication](http://tools.ietf.org/html/rfc2617). + +### Configuration + +If the installation has a dedicated host for REST, you can enable HTTP basic authentication only on this host by setting a firewall like in the following example before the `ibexa_front` one: + +```yaml + ibexa_rest: + host: ^api\.example\.com$ + http_basic: + realm: Ibexa DXP REST API +``` + +!!! caution "Back Office uses REST API" + + Back Office uses the REST API too (for some parts like the Location tree or the Calendar) on its own domain. + + * If the Back Office SiteAccess matches `admin.example.com`, it will call the REST API under `//admin.example.com/api/ezp/v2`; + * If the Back Office SiteAccess matches localhost/admin, it will call the REST API under `//localhost/api/ezp/v2`. + + If basic authentication is used only for REST API, it is better to have a dedicated domain even on a development environment. + +### Usage example + +Basic authentication requires the username and password to be sent *(username:password)*, base64 encoded, with each request. +For details, see [RFC 2617](http://tools.ietf.org/html/rfc2617). + +Most HTTP client libraries as well as REST libraries support this method. +[Creating content with binary attachments](rest_api_requests.md#creating-content-with-binary-attachments) has an example using basic authentication with [cURL](https://www.php.net/manual/en/book.curl.php) and its `CURLOPT_USERPWD`. + +**Raw HTTP request with basic authentication** + +``` +GET / HTTP/1.1 +Host: api.example.com +Accept: application/vnd.ez.api.Root+json +Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== +``` + +## OAuth + +For more information, see [OAuth 2.0 protocol for authorization.](https://oauth.net/2/) + ## SSL client authentication The REST API provides authentication of a user by a subject in a client certificate delivered by the web server configured as SSL endpoint. diff --git a/docs/api/rest_api_best_practices.md b/docs/api/rest_api_best_practices.md deleted file mode 100644 index 0dde300218..0000000000 --- a/docs/api/rest_api_best_practices.md +++ /dev/null @@ -1,127 +0,0 @@ -# REST API best practices - -This page refers to [REST API reference](https://doc.ezplatform.com/rest-api-reference), where you can find detailed information about -REST API resources and endpoints. - -## Specifying SiteAccess - -In order to specify a SiteAccess when communicating with the REST API, provide a custom `X-Siteaccess` header. -If it is not provided, the default SiteAccess is be used. - -Example: - -``` -GET / HTTP/1.1 -Host: api.example.com -Accept: application/vnd.ez.api.Root+json -X-Siteaccess: admin -``` - -## Media types - -The methods on resources provide multiple media types in their responses. -A media type can be selected in the `Accept` header. -Each XML media type has a unique name, e.g. `application/vnd.ez.api.User+xml`. -The returned XML response conforms with the complex type definition with a name, e.g. `vnd.ez.api.User` in the `user.xsd` XML schema definition file (see `User_`). - -To derive the implicit schema of the JSON from the XML schema a uniform transformation from XML to JSON is performed as shown below. - -```xml -<test attr1="attr1"> - <value attr2="attr2">value</value> - <simpleValue>45</simpleValue> - <fields> - <field>1</field> - <field>2</field> - </fields> -</test> -``` - -Transforms to: - -```json -{ - "test":{ - "_attr1":"attr1", - "value":{ - "_attr2":"attr2", - "#text":"value" - }, - "simpleValue":"45", - "fields": { - "field": [ 1, 2 ] - } - } -} -``` - -Different schemas that induce different media types on resource can be used to allow making specific representations optimized for purposes of clients. -It is possible to make e.g. a new schema for mobile devices for retrieving an article. - -```xml -<?xml version="1.0" encoding="UTF-8"?> -<xsd:schema version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns="http://ez.no/API/Values" targetNamespace="http://ez.no/API/Values"> - <xsd:include schemaLocation="CommonDefinitions.xsd" /> - <xsd:complexType name="vnd.ez.api.MobileContent"> - <xsd:complexContent> - <xsd:extension base="ref"> - <xsd:all> - <xsd:element name="Title" type="xsd:string" /> - <xsd:element name="Summary" type="xsd:string" /> - </xsd:all> - </xsd:extension> - </xsd:complexContent> - </xsd:complexType> - <xsd:element name="MobileContent" type="vnd.ez.api.MobileContent"/> -</xsd:schema> -``` - -So that: - -``` -GET /content/objects/23 HTTP/1.1 -Accept: application/vnd.ez.api.MobileContent+xml -``` - -Returns: - -```xml -<?xml version="1.0" encoding="UTF-8"?> -<MobileContent href="/content/objects/23" media-type="application/vnd.ez.api.MobileContent+xml"> - <Title>Title - This is a summary - -``` - -In this specification, only the standard schemas and media types are defined (see `InputOutput_`). -If there is only one media type defined for XML or JSON, it is also possible to specify `application/xml` or `application/json`. - -## URIs - -The REST API is designed in such a way that the client doesn't need to construct any URIs to resources. -Starting from the root resources (`ListRoot_`) every response includes further links to related resources. -The URIs should be used directly as identifiers on the client side and the client should not construct any URIs by using an ID. - -### URIs prefix - -In [eZ Platform REST reference](https://doc.ezplatform.com/rest-api-reference), for the sake of readability, there are no prefixes used in the URIs. -In practice, the `/api/ezp/v2` prefixes are all REST hrefs. - -Remember that the URIs to REST resources should never be generated manually, but obtained from earlier REST calls. - -### OPTIONS requests - -Any URI resource that the REST API responds to will respond to an OPTIONS request. - -The response contains an `Allow` header, that as specified in [chapter 14.7 of RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7) lists the methods accepted by the resource. - -``` -OPTIONS /content/objects/1 HTTP/1.1 -Host: api.example.net -``` - -``` -HTTP/1.1 200 OK -Allow: PATCH,GET,DELETE,COPY -``` diff --git a/docs/api/rest_api_extension_media_type.md b/docs/api/rest_api_extension_media_type.md new file mode 100644 index 0000000000..d34e6baa80 --- /dev/null +++ b/docs/api/rest_api_extension_media_type.md @@ -0,0 +1,96 @@ +--- +description: Add a custom media type to REST API request headers. +--- + +# Adding custom media type + +In this example case, you pass a new media type in the `Accept` header of a GET request to `/content/locations/{locationPath}` route and its controller action (`Controller/Location::loadLocation`). + +By default, this resource takes an `application/vnd.ez.api.Location+xml` (or `+json`) `Accept` header. +The following example adds the handling of a new media type `application/app.api.Location+xml` (or `+json`) `Accept` header to obtain a different response using the same controller. + +You need the following elements: +* A `ValueObjectVisitor` to create the new response corresponding to the new media type; +* A `ValueObjectVisitorDispatcher` to have this `ValueObjectVisitor` used to visit the default controller result; +* An `Output\Visitor` service associating this new `ValueObjectVisitorDispatcher` with the new media type. + +!!! note + + You can change the vendor name (from default `vnd.ez.api` to new `app.api` like in this example), or you can create a new media type in the default vendor (like `vnd.ez.api.Greeting` in the [Creating a new REST resource](#creating-a-new-rest-resource) example). + To do so, tag your new ValueObjectVisitor with `ezpublish_rest.output.value_object_visitor` to add it to the existing `ValueObjectVisitorDispatcher`, and a new one will not be needed. + This way, the `media-type` attribute is also easier to create, because the default `Output\Generator` uses this default vendor. + This example presents creating a new vendor as a good practice, to highlight that this is custom extensions that isn't available in a regular Ibexa DXP installation. + +## New `RestLocation` `ValueObjectVisitor` + +The controller action returns a `Values\RestLocation` object wrapped in `Values\CachedValue`. +The new `ValueObjectVisitor` has to visit `Values\RestLocation` to prepare the new `Response`. + +To be accepted by the `ValueObjectVisitorDispatcher`, all new `ValueObjectVisitor` need to extend the abstract class `Output\ValueObjectVisitor`. +In this example, this new `ValueObjectVisitor` extends the built-in `RestLocation` visitor to reuse it. +This way, the abstract class is implicitly inherited. + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/ValueObjectVisitor/RestLocation.php') =]] +``` + +This new `ValueObjectVisitor` receives a new tag `app.rest.output.value_object.visitor` to be associated to the new `ValueObjectVisitorDispatcher` in the next step. +This tag has a `type` property to associate the new `ValueObjectVisitor` with the type of value is made for. + +``` yaml +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 28, 35) =]] +``` + +## New `ValueObjectVisitorDispatcher` + +The new `ValueObjectVisitorDispatcher` receives the `ValueObjectVisitor`s tagged `app.rest.output.value_object.visitor`. +As not all value FQCNs are handled, the new `ValueObjectVisitorDispatcher` also receives the default one as a fallback. + +``` yaml +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 22, 27) =]] +``` + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php') =]] +``` + +## New `Output\Visitor` service + +The following new pair of `Ouput\Visitor` entries associates `Accept` headers starting with `application/app.api.` to the new `ValueObjectVisitorDispatcher` for both XML and JSON. +A priority is set higher than other `ezpublish_rest.output.visitor` tagged built-in services. + +``` yaml +parameters: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 1, 3) =]] +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 6, 21) =]] +``` + +## Testing the new media-type + +In the following example, `curl` and `diff` commands are used to compare the default media type (`application/vnd.ez.api.Location+xml`) with the new `application/app.api.Location+xml`. + +```shell +diff --ignore-space-change \ + <(curl --silent https://api.example.com/api/ezp/v2/content/locations/1/2) \ + <(curl --silent https://api.example.com/api/ezp/v2/content/locations/1/2 --header 'Accept: application/app.api.Location+xml'); +``` + +``` diff +2c2,3 +< +--- +> +> +37a39,42 +> +> +> +> +``` diff --git a/docs/api/rest_api_extension_resource.md b/docs/api/rest_api_extension_resource.md new file mode 100644 index 0000000000..0345f178e8 --- /dev/null +++ b/docs/api/rest_api_extension_resource.md @@ -0,0 +1,210 @@ +--- +description: Extend REST API by creating a new resource. +--- + +# Creating new REST resource + +To create a new REST resource, you need to prepare: + +* The REST route leading to a controller action. +* The controller and its action. +* Optionally, one or several `InputParser` objects if the controller needs to receive a payload to treat, one or several value classes to represent this payload and potentially one or several new media types to type this payload in the `Content-Type` header. +* Optionally, one or several new value classes to represent the controller action result, their `ValueObjectVisitor` to help the generator to turn this into XML or JSON and potentially one or several new media types to claim in the `Accept` header the desired value. +* Optionally, the addition of this resource route to the REST root. + +In the following example, you add a greeting resource to the REST API. +It is available through `GET` and `POST` methods. `GET` sets default values while `POST` allows inputting custom values. + +## Route + +New REST routes should use the [REST URI prefix](rest_api_usage.md#uri-prefix) for consistency. +To ensure that they do, in the `config/routes.yaml` file, while importing a REST routing file, use `ezpublish_rest.path_prefix` parameter as a `prefix`. + +``` yaml +[[= include_file('code_samples/api/rest_api/config/routes.yaml', 0, 3) =]] +``` + +The `config/routes_rest.yaml` file imported above is created with the following configuration: + +``` yaml +[[= include_file('code_samples/api/rest_api/config/routes_rest.yaml', 0, 3) =]] methods: [GET] +``` + +### CSRF protection + +If a REST route is designed to be used with [unsafe methods](rest_api_requests.md#request-method), the CSRF protection is enabled by default like for built-in routes. +You can disable it by using the route parameter `csrf_protection`. + +``` yaml +[[= include_file('code_samples/api/rest_api/config/routes_rest.yaml') =]] +``` + +## Controller + +### Controller service + +You can use the following configuration to have all controllers from the `App\Rest\Controller\` namespace (files in the `src/Rest/Controller/` folder) to be set as REST controller services. + +``` yaml +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 36, 42) =]] +``` + +Having the REST controllers set as services enables using features such as the `InputDispatcher` service in the [Controller action](#controller-action). + +### Controller action + +A REST controller should: + +- return a value object and have a `Generator` and `ValueObjectVisitor`s producing the XML or JSON output; +- extend `Ibexa\Rest\Server\Controller` to inherit utils methods and properties like `InputDispatcher` or `RequestParser`. + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/Controller/DefaultController.php') =]] +``` + +If the returned value was depending on a Location, it could have been wrapped in a `CachedValue` to be cached by the reverse proxy (like Varnish) for future calls. + +`CachedValue` is used in the following way: + +```php +return new CachedValue( + new MyValue($args…), + ['locationId'=> $locationId] +); +``` + +## Value and ValueObjectVisitor + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/Values/Greeting.php') =]] +``` + +A `ValueObjectVisitor` must implement the `visit` method. + +| Argument | Description | +|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| `$visitor` | The output visitor.
    Can be used to set custom response headers (`setHeader`), HTTP status code ( `setStatus`) | +| `$generator` | The actual response generator. It provides you with a DOM-like API. | +| `$data` | The visited data. The exact object that you returned from the controller.
    It can't have a type declaration because the method signature is shared. | + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/ValueObjectVisitor/Greeting.php') =]] +``` + +The `Values/Greeting` class is linked to its `ValueObjectVisitor` through the service tag. + +``` yaml +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 43, 48) =]] +``` + +Here, the media type is `application/vnd.ez.api.Greeting` plus a format. To have a different vendor than the default, you could create a new `Output\Generator` or hard-code it in the `ValueObjectVisitor` like in the [`RestLocation` example](rest_api_extension_media_type.md#new-restlocation-valueobjectvisitor). + +## InputParser + +A REST resource could use route parameters to handle input, but this example illustrates the usage of an input parser. + +For this example, the structure is a `GreetingInput` root node with two leaf nodes, `Salutation` and `Recipient`. + +``` php +[[= include_file('code_samples/api/rest_api/src/Rest/InputParser/GreetingInput.php') =]] +``` + +Here, this `InputParser` directly returns the right value object. +In other cases, it could return whatever object is needed to represent the input for the controller to perform its action, like arguments to use with a Repository service. + +``` yaml +services: + #… +[[= include_file('code_samples/api/rest_api/config/services.yaml', 48, 53) =]] +``` + +## Testing the new resource + +Now you can test both `GET` and `POST` methods, and both `XML` and `JSON` format for inputs and outputs. + +```shell +curl https://api.example.com/api/ezp/v2/greet --include; +curl https://api.example.com/api/ezp/v2/greet --include --request POST \ + --header 'Content-Type: application/vnd.ez.api.GreetingInput+xml' \ + --data 'Good morning'; +curl https://api.example.com/api/ezp/v2/greet --include --request POST \ + --header 'Content-Type: application/vnd.ez.api.GreetingInput+json' \ + --data '{"GreetingInput": {"Salutation": "Good day", "Recipient": "Earth"}}' \ + --header 'Accept: application/vnd.ez.api.Greeting+json'; +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.greeting+xml + + + + Hello + World + Hello World + + +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.greeting+xml + + + + Good morning + World + Good morning World + + +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.greeting+json + +{ + "Greeting": { + "_media-type": "application\/vnd.ez.api.Greeting+json", + "_href": "\/api\/ezp\/v2\/greet", + "Salutation": "Good day", + "Recipient": "Earth", + "Sentence": "Good day Earth" + } +} +``` + +## Registering resources in REST root + +You can add the new resource to the [root resource](rest_api_usage.md#rest-root) through a configuration with the following pattern: + +```yaml +ez_publish_rest: + system: + : + rest_root_resources: + : + mediaType: + href: 'router.generate("", {routeParameter: value})' +``` + +The `router.generate` renders a URI based on the name of the route and its parameters. The parameter values can be a real value or a placeholder. For example, `'router.generate("ibexa.rest.load_location", {locationPath: "1/2"})'` results in `/api/ezp/v2/content/locations/1/2` while `'router.generate("ibexa.rest.load_location", {locationPath: "{locationPath}"})'` gives `/api/ezp/v2/content/locations/{locationPath}`. +This syntax is based on Symfony's [expression language]([[= symfony_doc =]]/components/expression_language/index.html), an extensible component that allows limited/readable scripting to be used outside the code context. + +In this example, `app.rest.greeting` is available in every SiteAccess (`default`): + +```yaml +ibexa_rest: + system: + default: + rest_root_resources: + greeting: + mediaType: Greeting + href: 'router.generate("app.rest.greeting")' +``` + +You can place this configuration in any regular config file, like the existing `config/packages/ibexa.yaml`, or a new `config/packages/ibexa_rest.yaml` file. + +The above example adds the following entry to the root XML output: + +```xml + +``` diff --git a/docs/api/rest_api_guide.md b/docs/api/rest_api_guide.md deleted file mode 100644 index be8fe18da7..0000000000 --- a/docs/api/rest_api_guide.md +++ /dev/null @@ -1,125 +0,0 @@ -# REST API Guide - -The REST API v2 introduced in [[= product_name =]] allows you to interact with an [[= product_name =]] installation using the HTTP protocol, following a [REST](http://en.wikipedia.org/wiki/Representational_state_transfer) interaction model. - -## REST API reference - -See [REST API reference](https://doc.ezplatform.com/rest-api-reference) for detailed information about -REST API resources and endpoints. - -!!! tip - - For more information see a [presentation about [[= product_name =]] APIs](https://alongosz.github.io/ezconf2018-api/). - -## Accessing the REST API - -The REST API is available at the URI `/api/ezp/v2` . HTTPS is available as long as your server is properly configured. Refer to the [Getting started with the REST API](#getting-started-with-the-rest-api) section below to start using the API. - -## Basics - -REST (REpresentational State Transfer) is a web services architecture that follows the HTTP Protocol very closely. The [[= product_name =]] REST API supports both [JSON](http://www.json.org/) and [XML](http://www.w3.org/XML/) in terms of format. - -### Resources - -The API provides a set of URIs, each of them identifying and providing access to operations on a certain resource. For instance, the URI `/content/objects/59` will allow you to interact with the Content item with ID 59, while `/content/types/1` will allow you to interact with the Content Type with ID 1. - -### HTTP methods - -It uses HTTP methods ( **`GET`** , **`POST`** , **`PUT`** , **`DELETE`** , etc.), as well as HTTP headers to specify the type of request. Depending on the used HTTP verb, different actions will be possible. Example: - -|Action|Description| -|------|-----------| -|`GET /content/objects/2`| Provides you with data about Content item \#2| -|`PATCH /content/objects/2`| Updates the Content item \#2's metadata (section, main language, main location...)| -|`DELETE /content/objects/2`| Deletes Content item \#2| -|`COPY /content/objects/2`| Creates a copy of this Content| - -!!! note "Caution with custom HTTP verbs" - - Using custom HTTP verbs, those besides the standard (GET, POST, PUT, DELETE, OPTIONS, TRACE), can cause issues with several HTTP proxies, network firewall/security solutions and simpler web servers. To avoid issues with this REST API allows you to set these using a HTTP header instead using HTTP verb POST. Example: `X-HTTP-Method-Override: PUBLISH` - -### Media type headers - -On top of methods, HTTP request headers will allow you to personalize the request's behavior. On every resource, you can use the Accept header to indicate which format you want to communicate in, JSON or XML. This header is also used to specify the response type you want the server to send when multiple ones are available. - -- `Accept: application/vnd.ez.api.Content+xml` to get **Content** (full data, fields included) as **XML** -- `Accept: application/vnd.ez.api.ContentInfo+json` to get **ContentInfo** (metadata only) as **JSON** - -!!! note "More information" - - [REST specifications chapter "Media Types"](https://github.com/ezsystems/ezpublish-kernel/blob/v8.0.0-beta5/doc/specifications/rest/REST-API-V2.rst#media-types) - -### Other headers - -Other headers will be used in HTTP requests for specifying: the siteaccess to interact with and [authentication credentials](general_rest_usage.md#rest-api-authentication). - -Responses returned by the API will also use custom headers to indicate information about the executed operation. - -## Getting started with the REST API - -### Installation - -No special preparations are necessary to use the REST API. As long as your [[= product_name =]] is correctly configured, the REST API is available on your site using the URI `/api/ezp/v2/`. If you have installed [[= product_name =]] in a subfolder, prepend the path with this subfolder: `http://example.com/sub/folder/ezpublish/api/ezp/v2/`. - -!!! note - - Please note that the `/api/ezp/v2` prefix will be used in all REST hrefs, but not in URIs. - -### Configuration - -#### Authentication - -As explained in more detail in the [authentication page](general_rest_usage.md#rest-api-authentication), two authentication methods are currently supported: session and basic. By default, session authentication is the active mode, it uses a session cookie. The alternative, basic auth authentication requires a login / password to be sent using basic HTTP authentication. - -To enable basic auth based authentication, you need to edit `config/packages/security.yaml` and uncomment the configuration block about REST - -**security.yaml** - -```yaml -security: - # ... - firewalls: - # ... - ezpublish_rest: - pattern: ^/api/ezp/v2 - ezpublish_http_basic: - realm: eZ Platform REST API -``` - -### Testing the API - -A standard web browser is not sufficient to fully test the API. You can, however, try opening the root resource with it, using the session authentication: `http://example.com/api/ezp/v2/`. Depending on how your browser understands XML, it will either download the XML file, or open it in the browser. - -To test further, you can use browser extensions, like [Advanced REST client for Chrome](https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo) or [RESTClient for Firefox](https://addons.mozilla.org/firefox/addon/restclient/), or dedicated tools. For command line users, [HTTPie](https://github.com/jkbr/httpie) is a good tool. - -#### JavaScript example - -One of the main reasons for this API is to help implement JavaScript / AJAX interaction. You can see here an example of an AJAX call that retrieves ContentInfo (e.g. metadata) for a Content item: - -**REST API with JavaScript** - -```javascript -
    
    -
    -```
    -
    -In order to test it, just save this code to some test.html file in the web folder of your [[= product_name =]] installation. If you use the rewrite rules, don't forget to allow this file to be served directly.
    -
    -If necessary, substitute `59` with the Content item ID of an item from your database. You will get the ContentInfo for item 59 in JSON encoding.
    -
    -Note that by default, session authentication is used. This means that the session cookie will be transparently sent together with the request, and every AJAX call will have the same permissions as the currently logged in user.
    diff --git a/docs/api/rest_api_reference/assets/css/bootstrap-toc.min.css b/docs/api/rest_api_reference/assets/css/bootstrap-toc.min.css
    new file mode 100644
    index 0000000000..3c6210780b
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/css/bootstrap-toc.min.css
    @@ -0,0 +1,4 @@
    +/*!
    + * Bootstrap Table of Contents v1.0.1 (http://afeld.github.io/bootstrap-toc/)
    + * Copyright 2015 Aidan Feldman
    + * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */nav[data-toggle=toc] .nav>li>a{display:block;padding:4px 20px;font-size:13px;font-weight:500;color:#767676}nav[data-toggle=toc] .nav>li>a:focus,nav[data-toggle=toc] .nav>li>a:hover{padding-left:19px;color:#563d7c;text-decoration:none;background-color:transparent;border-left:1px solid #563d7c}nav[data-toggle=toc] .nav-link.active,nav[data-toggle=toc] .nav-link.active:focus,nav[data-toggle=toc] .nav-link.active:hover{padding-left:18px;font-weight:700;color:#563d7c;background-color:transparent;border-left:2px solid #563d7c}nav[data-toggle=toc] .nav-link+ul{display:none;padding-bottom:10px}nav[data-toggle=toc] .nav .nav>li>a{padding-top:1px;padding-bottom:1px;padding-left:30px;font-size:12px;font-weight:400}nav[data-toggle=toc] .nav .nav>li>a:focus,nav[data-toggle=toc] .nav .nav>li>a:hover{padding-left:29px}nav[data-toggle=toc] .nav .nav>li>.active,nav[data-toggle=toc] .nav .nav>li>.active:focus,nav[data-toggle=toc] .nav .nav>li>.active:hover{padding-left:28px;font-weight:500}nav[data-toggle=toc] .nav-link.active+ul{display:block}
    \ No newline at end of file
    diff --git a/docs/api/rest_api_reference/assets/css/bootstrap.min.css b/docs/api/rest_api_reference/assets/css/bootstrap.min.css
    new file mode 100644
    index 0000000000..613d28aab8
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/css/bootstrap.min.css
    @@ -0,0 +1,6 @@
    +/*!
    + * Bootstrap v4.4.1 (https://getbootstrap.com/)
    + * Copyright 2011-2019 The Bootstrap Authors
    + * Copyright 2011-2019 Twitter, Inc.
    + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
    + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]){color:inherit;text-decoration:none}a:not([href]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 0%;flex:1 1 0%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal .list-group-item.active{margin-top:0}.list-group-horizontal .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm .list-group-item.active{margin-top:0}.list-group-horizontal-sm .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md .list-group-item.active{margin-top:0}.list-group-horizontal-md .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg .list-group-item.active{margin-top:0}.list-group-horizontal-lg .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl .list-group-item.active{margin-top:0}.list-group-horizontal-xl .list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl .list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush .list-group-item{border-right-width:0;border-left-width:0;border-radius:0}.list-group-flush .list-group-item:first-child{border-top-width:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
    diff --git a/docs/api/rest_api_reference/assets/css/github-gist.css b/docs/api/rest_api_reference/assets/css/github-gist.css
    new file mode 100644
    index 0000000000..18240c8882
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/css/github-gist.css
    @@ -0,0 +1,79 @@
    +/**
    + * GitHub Gist Theme
    + * Author : Anthony Attard - https://github.com/AnthonyAttard
    + * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro
    + */
    +
    +.hljs {
    +  display: block;
    +  background: white;
    +  padding: 0.5em;
    +  color: #333333;
    +  overflow-x: auto;
    +}
    +
    +.hljs-comment,
    +.hljs-meta {
    +  color: #969896;
    +}
    +
    +.hljs-variable,
    +.hljs-template-variable,
    +.hljs-strong,
    +.hljs-emphasis,
    +.hljs-quote {
    +  color: #df5000;
    +}
    +
    +.hljs-keyword,
    +.hljs-selector-tag,
    +.hljs-type {
    +  color: #d73a49;
    +}
    +
    +.hljs-literal,
    +.hljs-symbol,
    +.hljs-bullet,
    +.hljs-attribute {
    +  color: #0086b3;
    +}
    +
    +.hljs-section,
    +.hljs-name {
    +  color: #63a35c;
    +}
    +
    +.hljs-tag {
    +  color: #333333;
    +}
    +
    +.hljs-title,
    +.hljs-attr,
    +.hljs-selector-id,
    +.hljs-selector-class,
    +.hljs-selector-attr,
    +.hljs-selector-pseudo {
    +  color: #6f42c1;
    +}
    +
    +.hljs-addition {
    +  color: #55a532;
    +  background-color: #eaffea;
    +}
    +
    +.hljs-deletion {
    +  color: #bd2c00;
    +  background-color: #ffecec;
    +}
    +
    +.hljs-link {
    +  text-decoration: underline;
    +}
    +
    +.hljs-number {
    +  color: #005cc5;
    +}
    +
    +.hljs-string {
    +  color: #032f62;
    +}
    diff --git a/docs/api/rest_api_reference/assets/css/style.css b/docs/api/rest_api_reference/assets/css/style.css
    new file mode 100644
    index 0000000000..ae5f2e397b
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/css/style.css
    @@ -0,0 +1,388 @@
    +body {
    +    font-family: "Maven Pro";
    +    position: relative;
    +}
    +::-webkit-scrollbar {
    +    width: 5px;
    +    height: 5px;
    +}
    +::-webkit-scrollbar-track {
    +    background: #dee2e6;
    +}
    +::-webkit-scrollbar-thumb {
    +    background: #c0c0c0;
    +}
    +::-webkit-scrollbar-thumb:hover {
    +    background: #f15a22;
    +}
    +.heading:hover .heading__link {
    +    display: inline-block;
    +    color: #00000042;
    +    text-decoration: none;
    +}
    +.heading:hover .heading__link:hover {
    +    color: #f15a22;
    +}
    +.heading__link {
    +    display: none;
    +    text-decoration: none;
    +}
    +.header {
    +    height: 3rem;
    +}
    +.nav-brand {
    +    margin-top: 5px;
    +}
    +.nav-brand img {
    +    width: 100%;
    +    max-width: 40px;
    +    padding-top: .9rem;
    +}
    +.page-title {
    +    margin-top: 3px;
    +    margin-left: 12px;
    +    font-size: 20px;
    +    line-height: 33px;
    +    font-weight: 600;
    +}
    +.dropdown--menu {
    +    z-index: 1031;
    +    position: fixed;
    +    top: 0;
    +    left: 50px;
    +}
    +.sidebar-menu, .input--search  {
    +    background-color: #fcfcfc;
    +}
    +.sidebar__header {
    +    position: absolute;
    +    top: 0;
    +    left: 0;
    +    right: 0;
    +    z-index: 3;
    +    background-color: transparent;
    +    padding-left: 15px;
    +}
    +.icon {
    +    cursor: pointer;
    +}
    +.icon--search {
    +    position: absolute;
    +    left: 15px;
    +}
    +.icon--copy-clipboard {
    +    position: absolute;
    +    top: 10px;
    +    right: 15px;
    +    opacity: 0.1;
    +}
    +.example:hover .icon--copy-clipboard,
    +.tab-pane:hover .icon--copy-clipboard {
    +    color: #f15a22!important;
    +    opacity: 1;
    +}
    +.input--search {
    +    padding-left: 30px;
    +}
    +#search-results {
    +    position: fixed;
    +    top: 0;
    +    right: 0;
    +    z-index: 1032;
    +    background-color: #f9f9fa;
    +    height: 100%;
    +    overflow-y: scroll;
    +    overflow-x: hidden;
    +}
    +.search-result__close {
    +    cursor: pointer;
    +}
    +.search__link {
    +    color: #212529;
    +}
    +.search__link:hover {
    +    text-decoration: none;
    +    background-color: #f9f9fa;
    +    opacity: 1;
    +}
    +.sidebar-menu {
    +    position: fixed;
    +    left: 0;
    +    top: 0;
    +    height: 100%;
    +    z-index: 1;
    +    margin-top: 3rem;
    +    padding-bottom: 3rem;
    +}
    +.sidebar__nav {
    +    overflow-y: scroll;
    +    height: 100%;
    +    width: 100%;
    +    position: absolute;
    +    top: 43px;
    +    left: 0;
    +    align-items: start;
    +}
    +.sidebar__nav > .navbar-nav {
    +    padding-bottom: 100px;
    +}
    +.navbar-nav {
    +    width: 100%;
    +}
    +.sidebar__nav .nav .nav {
    +    padding-left: 1rem;
    +}
    +.sidebar__nav .nav > li > .nav-link,
    +.sidebar__nav .nav .nav > li > .nav-link {
    +    padding: .5rem 0;
    +    color: #2b2b2b;
    +    font-size: 15px;
    +    display: flex;
    +}
    +.sidebar__nav .nav > li > .nav-link.active,
    +.sidebar__nav .nav .nav > li > .nav-link.active {
    +    color: #f15a22;
    +    border-left: none;
    +    padding-left: 0;
    +}
    +.nav__link--toggler {
    +    font-size: 16px!important;
    +    padding-top: 4px;
    +    padding-left: 5px;
    +}
    +.nav-link.active  .nav__link--toggler, .toggler--rotated {
    +    padding-top: 3px;
    +    padding-right: 5px;
    +}
    +.sidebar__nav .nav > li > .nav-link.active .nav__link--toggler,
    +.toggler--rotated {
    +    transform: rotate(180deg);
    +}
    +.sidebar__nav .nav > li > .nav-link.active .nav__link--toggler.toggler--rotated-0,
    +.toggler--rotated-0 {
    +    transform: rotate(0deg);
    +}
    +.sidebar__nav .nav > li > .nav-link:hover,
    +.sidebar__nav .nav .nav > li > .nav-link:hover,
    +.sidebar__nav .nav > li > .nav-link:focus,
    +.sidebar__nav .nav .nav > li > .nav-link:focus {
    +    padding-left: 0;
    +    border-left: none;
    +    color: #f15a22;
    +}
    +.sidebar__nav .nav .nav > li > .nav-link.active:hover {
    +    padding-left: 0;
    +}
    +.menu-list-item__link.current {
    +    color: darkred;
    +}
    +.text-orange {
    +    color: #f15a22!important;
    +}
    +.text-ocean-blue {
    +    color: #2a9dc7;
    +}
    +h2 {
    +    font-size: 34px;
    +}
    +h3 {
    +    font-size: 28px;
    +}
    +h4 {
    +    font-size: 22px;
    +}
    +h5 {
    +    font-size: 20px;
    +}
    +h6 {
    +    font-size: 18px;
    +}
    +.text-black {
    +    color: #212529;
    +}
    +.text-gray {
    +    color: #63605d;
    +}
    +.font-weight-medium {
    +    font-weight: 600;
    +}
    +.table-responsive {
    +    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12), 0 3px 1px -2px rgba(0,0,0,.2);
    +}
    +.table {
    +    max-width: 100%;
    +    border-radius: .2rem;
    +    margin-bottom: 0;
    +}
    +.table th {
    +    background-color: #f9f9fa;
    +    color: #808080;
    +    vertical-align: top;
    +}
    +.table td, .table th {
    +    padding: .25rem;
    +}
    +.table:not([class]) td {
    +    border-top: .1rem solid rgba(0,0,0,.07);
    +    vertical-align: top;
    +}
    +code {
    +    background-color: #ededed;
    +    padding: 1px 0px;
    +    margin: 0;
    +    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.125);
    +    color: #2b2b2b;
    +    font-family: monospace;
    +    font-size: 14px;
    +}
    +.badge {
    +    text-transform: uppercase;
    +    font-weight: 700;
    +    align-items: center;
    +    justify-content: center;
    +    min-width: 85px;
    +    color: #ffffff;
    +}
    +.badge--sm {
    +    min-width: auto;
    +}
    +.badge i {
    +    margin-right: 0.2rem;
    +    font-size: 1rem;
    +}
    +.badge--get {
    +    background-color: #2196f3!important;
    +}
    +
    +.badge--post {
    +    background-color: #4caf50!important;
    +}
    +
    +.badge--delete {
    +    background-color: #bd2c00!important;
    +}
    +
    +.badge--put {
    +    background-color: #ff9800!important;
    +}
    +
    +.badge--patch {
    +    background-color: #673ab7!important;
    +}
    +
    +.badge--head {
    +    background-color: #222222!important;
    +}
    +
    +.badge--options {
    +    background-color: #616161!important;
    +}
    +.badge--copy {
    +    background-color: #26a69a!important;
    +}
    +.background--gray {
    +    background-color: #fcfcfc;
    +}
    +.badge--publish {
    +    background-color: #df5000;
    +}
    +.badge--swap {
    +    background-color: #005cc5;
    +}
    +.badge--move {
    +    background-color: #032f62;
    +}
    +.example {
    +    border-radius: 10px;
    +}
    +.example__code-wrapper {
    +    max-height: 200px;
    +    overflow: hidden;
    +}
    +.btn--example-view-more {
    +    position: absolute;
    +    bottom: 15px;
    +    left: 0;
    +    right: 0;
    +    color: #2b2b2b;
    +    background-color: #ededed;
    +    margin: 0 auto;
    +    width: 120px;
    +}
    +.btn--view-more:hover {
    +    background-color: #dee2e6;
    +}
    +.examples code {
    +    background-color: transparent;
    +    box-shadow: none;
    +}
    +.hljs-section,
    +.hljs-name {
    +    color: #0086b3;
    +}
    +.highlight {
    +    background-color: #ededed;
    +}
    +.sticky {
    +    position: -webkit-sticky;
    +    position: sticky;
    +    top: 48px;
    +    background-color: #ffffff;
    +    z-index: 1030;
    +}
    +.sticky:before,
    +.sticky:after {
    +    content: "";
    +    display: table;
    +}
    +h5[data-field="name"]:before,
    +.response__code:before {
    +    display: block;
    +    margin-top: -6.9rem;
    +    padding-top: 7.9rem;
    +    content: "";
    +}
    +.search-container {
    +    margin-bottom: 80px;
    +}
    +.modal-title {
    +    font-size: 20px;
    +}
    +
    +@media (max-width: 991px) {
    +    .sidebar__header {
    +        padding-right: 15px;
    +    }
    +    .sidebar-menu {
    +        display: none;
    +        z-index: 1032;
    +    }
    +    .sticky__body {
    +        overflow-x: scroll;
    +    }
    +    #search-results {
    +        top: 100px;
    +    }
    +    .hljs {
    +        overflow-x: scroll;
    +    }
    +}
    +
    +@media (min-width: 992px) {
    +    .navbar-toggler-icon {
    +        display: none;
    +    }
    +    .page-wrapper {
    +        margin-left: 16.666667%;
    +    }
    +}
    +
    +.mobile-menu-expanded .navbar-toggler-icon {
    +    transform: rotate(90deg);
    +}
    +.mobile-menu-expanded .sidebar-menu {
    +    display: block;
    +}
    +.navbar-toggler-icon {
    +    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
    +}
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenPro-Black.ttf b/docs/api/rest_api_reference/assets/fonts/MavenPro-Black.ttf
    new file mode 100644
    index 0000000000..f808a1bfb8
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenPro-Black.ttf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenPro-Bold.ttf b/docs/api/rest_api_reference/assets/fonts/MavenPro-Bold.ttf
    new file mode 100644
    index 0000000000..40c3359261
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenPro-Bold.ttf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenPro-Medium.ttf b/docs/api/rest_api_reference/assets/fonts/MavenPro-Medium.ttf
    new file mode 100644
    index 0000000000..75a388e2ac
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenPro-Medium.ttf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenPro-Regular.ttf b/docs/api/rest_api_reference/assets/fonts/MavenPro-Regular.ttf
    new file mode 100644
    index 0000000000..6442c2b226
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenPro-Regular.ttf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenPro.css b/docs/api/rest_api_reference/assets/fonts/MavenPro.css
    new file mode 100644
    index 0000000000..0f62d75e9e
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/fonts/MavenPro.css
    @@ -0,0 +1,48 @@
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenPro-Regular.ttf") format("truetype");
    +    font-weight: normal;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenProLight-100.otf") format("truetype");
    +    font-weight: 100;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenProLight-200.otf") format("truetype");
    +    font-weight: 200;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenProLight-300.otf") format("truetype");
    +    font-weight: 300;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenPro-Medium.ttf") format("truetype");
    +    font-weight: 600;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenPro-Bold.ttf") format("truetype");
    +    font-weight: 700;
    +    font-style: normal;
    +}
    +
    +@font-face {
    +    font-family: "Maven Pro";
    +    src: url("MavenPro-Black.ttf") format("truetype");
    +    font-weight: 800;
    +    font-style: normal;
    +}
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenProLight-100.otf b/docs/api/rest_api_reference/assets/fonts/MavenProLight-100.otf
    new file mode 100644
    index 0000000000..8e23189fff
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenProLight-100.otf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenProLight-200.otf b/docs/api/rest_api_reference/assets/fonts/MavenProLight-200.otf
    new file mode 100644
    index 0000000000..9df40f4126
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenProLight-200.otf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/MavenProLight-300.otf b/docs/api/rest_api_reference/assets/fonts/MavenProLight-300.otf
    new file mode 100644
    index 0000000000..928d32c3fc
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/MavenProLight-300.otf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/ez-icons.ttf b/docs/api/rest_api_reference/assets/fonts/ez-icons.ttf
    new file mode 100644
    index 0000000000..d612ecf53d
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/ez-icons.ttf differ
    diff --git a/docs/api/rest_api_reference/assets/fonts/ez-icons.woff b/docs/api/rest_api_reference/assets/fonts/ez-icons.woff
    new file mode 100644
    index 0000000000..aab87b0d1b
    Binary files /dev/null and b/docs/api/rest_api_reference/assets/fonts/ez-icons.woff differ
    diff --git a/docs/api/rest_api_reference/assets/js/bootstrap-toc.js b/docs/api/rest_api_reference/assets/js/bootstrap-toc.js
    new file mode 100644
    index 0000000000..409632acd9
    --- /dev/null
    +++ b/docs/api/rest_api_reference/assets/js/bootstrap-toc.js
    @@ -0,0 +1,178 @@
    +/*!
    + * Bootstrap Table of Contents v1.0.1 (http://afeld.github.io/bootstrap-toc/)
    + * Copyright 2015 Aidan Feldman
    + * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
    +(function($) {
    +  "use strict";
    +
    +  window.Toc = {
    +    helpers: {
    +      // return all matching elements in the set, or their descendants
    +      findOrFilter: function($el, selector) {
    +        // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
    +        // http://stackoverflow.com/a/12731439/358804
    +        var $descendants = $el.find(selector);
    +        return $el
    +          .filter(selector)
    +          .add($descendants)
    +          .filter(":not([data-toc-skip])");
    +      },
    +
    +      generateUniqueIdBase: function(el) {
    +        var text = $(el).text();
    +
    +        // adapted from
    +        // https://github.com/bryanbraun/anchorjs/blob/65fede08d0e4a705f72f1e7e6284f643d5ad3cf3/anchor.js#L237-L257
    +
    +        // Regex for finding the non-safe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ (newlines, tabs, backspace, & vertical tabs)
    +        var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\\n\t\b\v]/g,
    +          urlText;
    +
    +        // Note: we trim hyphens after truncating because truncating can cause dangling hyphens.
    +        // Example string:                      // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
    +        urlText = text
    +          .trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
    +          .replace(/\'/gi, "") // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
    +          .replace(nonsafeChars, "-") // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-"
    +          .replace(/-{2,}/g, "-") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-"
    +          .substring(0, 64) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-"
    +          .replace(/^-+|-+$/gm, "") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated"
    +          .toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated"
    +
    +        return urlText || el.tagName.toLowerCase();
    +      },
    +
    +      generateUniqueId: function(el) {
    +        var anchorBase = this.generateUniqueIdBase(el);
    +        for (var i = 0; ; i++) {
    +          var anchor = anchorBase;
    +          if (i > 0) {
    +            // add suffix
    +            anchor += "-" + i;
    +          }
    +          // check if ID already exists
    +          if (!document.getElementById(anchor)) {
    +            return anchor;
    +          }
    +        }
    +      },
    +
    +      generateAnchor: function(el) {
    +        if (el.id) {
    +          return el.id;
    +        } else {
    +          var anchor = this.generateUniqueId(el);
    +          el.id = anchor;
    +          return anchor;
    +        }
    +      },
    +
    +      createNavList: function() {
    +        return $('');
    +      },
    +
    +      createChildNavList: function($parent) {
    +        var $childList = this.createNavList();
    +        $parent.append($childList);
    +        return $childList;
    +      },
    +      generateNavEl: function(anchor, text) {
    +        var $a = $('');
    +        $a.attr("href", "#" + anchor);
    +        $a.text(text.replace(/[.¶]/g, ''));
    +        var $li = $("
  • "); + $li.append($a); + return $li; + }, + + generateNavItem: function(headingEl) { + var anchor = this.generateAnchor(headingEl); + var $heading = $(headingEl); + var text = $heading.data("toc-text") || $heading.text(); + return this.generateNavEl(anchor, text); + }, + + // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). + getTopLevel: function($scope) { + for (var i = 1; i <= 6; i++) { + var $headings = this.findOrFilter($scope, "h" + i); + if ($headings.length > 1) { + return i; + } + } + + return 1; + }, + + // returns the elements for the top level, and the next below it + getHeadings: function($scope, topLevel) { + var topSelector = "h" + topLevel; + var secondaryLevel = 5; + var secondarySelector = "h" + secondaryLevel; + + return this.findOrFilter($scope, topSelector + "," + secondarySelector); + }, + + getNavLevel: function(el) { + return parseInt(el.tagName.charAt(1), 10); + }, + + populateNav: function($topContext, topLevel, $headings) { + var $context = $topContext; + var $prevNav; + + var helpers = this; + $headings.each(function(i, el) { + var $newNav = helpers.generateNavItem(el); + var navLevel = helpers.getNavLevel(el); + + // determine the proper $context + if (navLevel === topLevel) { + // use top level + $context = $topContext; + } else if ($prevNav && $context === $topContext) { + // create a new level of the tree and switch to it + $context = helpers.createChildNavList($prevNav); + } // else use the current $context + + $context.append($newNav); + + $prevNav = $newNav; + }); + }, + + parseOps: function(arg) { + var opts; + if (arg.jquery) { + opts = { + $nav: arg + }; + } else { + opts = arg; + } + opts.$scope = opts.$scope || $(document.body); + return opts; + } + }, + + // accepts a jQuery object, or an options object + init: function(opts) { + opts = this.helpers.parseOps(opts); + + // ensure that the data attribute is in place for styling + opts.$nav.attr("data-toggle", "toc"); + + var $topContext = this.helpers.createChildNavList(opts.$nav); + var topLevel = this.helpers.getTopLevel(opts.$scope); + var $headings = this.helpers.getHeadings(opts.$scope, topLevel); + this.helpers.populateNav($topContext, topLevel, $headings); + } + }; + + $(function() { + $('nav[data-toggle="toc"]').each(function(i, el) { + var $nav = $(el); + Toc.init($nav); + }); + }); +})(jQuery); diff --git a/docs/api/rest_api_reference/assets/js/bootstrap.bundle.min.js b/docs/api/rest_api_reference/assets/js/bootstrap.bundle.min.js new file mode 100644 index 0000000000..95516469ad --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/bootstrap.bundle.min.js @@ -0,0 +1,6 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t((e=e||self).bootstrap={},e.jQuery)}(this,function(e,p){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)p(this._element).one(V.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(h[t],l[e]-("right"===e?h.width:h.height))),Ye({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";h=ze({},h,u[t](e))}),e.offsets.popper=h,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,o=e.placement.split("-")[0],r=Math.floor,s=-1!==["top","bottom"].indexOf(o),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]r(i[a])&&(e.offsets.popper[l]=r(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!gt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var o=e.placement.split("-")[0],r=e.offsets,s=r.popper,a=r.reference,l=-1!==["left","right"].indexOf(o),c=l?"height":"width",h=l?"Top":"Left",u=h.toLowerCase(),f=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[u]+=a[u]+p-s[d]),e.offsets.popper=Xe(e.offsets.popper);var m=a[u]+a[c]/2-p/2,g=ke(e.instance.popper),_=parseFloat(g["margin"+h],10),v=parseFloat(g["border"+h+"Width"],10),y=m-e.offsets.popper[u]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ye(n={},u,Math.round(y)),Ye(n,f,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(m,g){if(at(m.instance.modifiers,"inner"))return m;if(m.flipped&&m.placement===m.originalPlacement)return m;var _=Ze(m.instance.popper,m.instance.reference,g.padding,g.boundariesElement,m.positionFixed),v=m.placement.split("-")[0],y=it(v),E=m.placement.split("-")[1]||"",b=[];switch(g.behavior){case Et:b=[v,y];break;case bt:b=yt(v);break;case wt:b=yt(v,!0);break;default:b=g.behavior}return b.forEach(function(e,t){if(v!==e||b.length===t+1)return m;v=m.placement.split("-")[0],y=it(v);var n=m.offsets.popper,i=m.offsets.reference,o=Math.floor,r="left"===v&&o(n.right)>o(i.left)||"right"===v&&o(n.left)o(i.top)||"bottom"===v&&o(n.top)o(_.right),l=o(n.top)o(_.bottom),h="left"===v&&s||"right"===v&&a||"top"===v&&l||"bottom"===v&&c,u=-1!==["top","bottom"].indexOf(v),f=!!g.flipVariations&&(u&&"start"===E&&s||u&&"end"===E&&a||!u&&"start"===E&&l||!u&&"end"===E&&c),d=!!g.flipVariationsByContent&&(u&&"start"===E&&a||u&&"end"===E&&s||!u&&"start"===E&&c||!u&&"end"===E&&l),p=f||d;(r||h||p)&&(m.flipped=!0,(r||h)&&(v=b[t+1]),p&&(E=function(e){return"end"===e?"start":"start"===e?"end":e}(E)),m.placement=v+(E?"-"+E:""),m.offsets.popper=ze({},m.offsets.popper,ot(m.instance.popper,m.offsets.reference,m.placement)),m=st(m.instance.modifiers,m,"flip"))}),m},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,o=i.popper,r=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return o[s?"left":"top"]=r[n]-(a?o[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Xe(o),e}},hide:{order:800,enabled:!0,fn:function(e){if(!gt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=rt(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right

    ',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Cn,popperConfig:null},Fn="show",Mn="out",Wn={HIDE:"hide"+Nn,HIDDEN:"hidden"+Nn,SHOW:"show"+Nn,SHOWN:"shown"+Nn,INSERTED:"inserted"+Nn,CLICK:"click"+Nn,FOCUSIN:"focusin"+Nn,FOCUSOUT:"focusout"+Nn,MOUSEENTER:"mouseenter"+Nn,MOUSELEAVE:"mouseleave"+Nn},Un="fade",Bn="show",qn=".tooltip-inner",Kn=".arrow",Qn="hover",Vn="focus",Yn="click",zn="manual",Xn=function(){function i(e,t){if("undefined"==typeof St)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=p(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(Bn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var e=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(e);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=m.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&p(o).addClass(Un);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();p(o).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(o).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new St(this.element,o,this._getPopperConfig(a)),p(o).addClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,p(t.element).trigger(t.constructor.Event.SHOWN),e===Mn&&t._leave(null,t)};if(p(this.tip).hasClass(Un)){var h=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(e){function t(){n._hoverState!==Fn&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),p(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),e&&e()}var n=this,i=this.getTipElement(),o=p.Event(this.constructor.Event.HIDE);if(p(this.element).trigger(o),!o.isDefaultPrevented()){if(p(i).removeClass(Bn),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Yn]=!1,this._activeTrigger[Vn]=!1,this._activeTrigger[Qn]=!1,p(this.tip).hasClass(Un)){var r=m.getTransitionDurationFromElement(i);p(i).one(m.TRANSITION_END,t).emulateTransitionEnd(r)}else t();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){p(this.getTipElement()).addClass(Ln+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(p(e.querySelectorAll(qn)),this.getTitle()),p(e).removeClass(Un+" "+Bn)},e.setElementContent=function(e,t){"object"!=typeof t||!t.nodeType&&!t.jquery?this.config.html?(this.config.sanitize&&(t=In(t,this.config.whiteList,this.config.sanitizeFn)),e.html(t)):e.text(t):this.config.html?p(t).parent().is(e)||e.empty().append(t):e.text(p(t).text())},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e=e||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},e._getPopperConfig=function(e){var t=this;return l({},{placement:e,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Kn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){return t._handlePopperPlacementChange(e)}},{},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,{},t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},e._getAttachment=function(e){return Hn[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==zn){var t=e===Qn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Qn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}}),this._hideModalHandler=function(){i.element&&i.hide()},p(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==e||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Vn:Qn]=!0),p(t.getTipElement()).hasClass(Bn)||t._hoverState===Fn?t._hoverState=Fn:(clearTimeout(t._timeout),t._hoverState=Fn,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===Fn&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Vn:Qn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Mn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Mn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){var t=p(this.element).data();return Object.keys(t).forEach(function(e){-1!==xn.indexOf(e)&&delete t[e]}),"number"==typeof(e=l({},this.constructor.Default,{},t,{},"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),m.typeCheckConfig(An,e,this.constructor.DefaultType),e.sanitize&&(e.template=In(e.template,e.whiteList,e.sanitizeFn)),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(Pn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(p(e).removeClass(Un),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=p(this).data(On),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),p(this).data(On,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return Rn}},{key:"NAME",get:function(){return An}},{key:"DATA_KEY",get:function(){return On}},{key:"Event",get:function(){return Wn}},{key:"EVENT_KEY",get:function(){return Nn}},{key:"DefaultType",get:function(){return jn}}]),i}();p.fn[An]=Xn._jQueryInterface,p.fn[An].Constructor=Xn,p.fn[An].noConflict=function(){return p.fn[An]=kn,Xn._jQueryInterface};var Gn="popover",$n="bs.popover",Jn="."+$n,Zn=p.fn[Gn],ei="bs-popover",ti=new RegExp("(^|\\s)"+ei+"\\S+","g"),ni=l({},Xn.Default,{placement:"right",trigger:"click",content:"",template:''}),ii=l({},Xn.DefaultType,{content:"(string|element|function)"}),oi="fade",ri="show",si=".popover-header",ai=".popover-body",li={HIDE:"hide"+Jn,HIDDEN:"hidden"+Jn,SHOW:"show"+Jn,SHOWN:"shown"+Jn,INSERTED:"inserted"+Jn,CLICK:"click"+Jn,FOCUSIN:"focusin"+Jn,FOCUSOUT:"focusout"+Jn,MOUSEENTER:"mouseenter"+Jn,MOUSELEAVE:"mouseleave"+Jn},ci=function(e){function i(){return e.apply(this,arguments)||this}!function(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}(i,e);var t=i.prototype;return t.isWithContent=function(){return this.getTitle()||this._getContent()},t.addAttachmentClass=function(e){p(this.getTipElement()).addClass(ei+"-"+e)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var e=p(this.getTipElement());this.setElementContent(e.find(si),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(ai),t),e.removeClass(oi+" "+ri)},t._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},t._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(ti);null!==t&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||e { + e.clearSelection(); + const trigger = e.trigger; + const originalTitle = trigger.dataset.originalTitle; + trigger.dataset.originalTitle = 'Copied'; + $(trigger).tooltip('show'); + $(trigger).on('hidden.bs.tooltip', (setTitle(trigger, originalTitle))); +}); + +function setTitle(element, title) { + element.dataset.originalTitle = title; +} + +const currentYearElement = document.querySelector('.current-year'); +const date = new Date(); +currentYearElement.textContent = date.getFullYear(); + +const mobileNavbarBtn = document.querySelector('.navbar-toggler-icon'); +mobileNavbarBtn.addEventListener('click', event => { + document.body.classList.add('overflow-hidden'); + document.body.classList.toggle('mobile-menu-expanded'); + + const menu = document.querySelector('.sidebar__nav'); + const menuItems = menu.querySelectorAll('.nav-link'); + const activeItems = menu.querySelectorAll('.nav-link.active'); + const activeItem = activeItems[activeItems.length - 1]; + + menu.scrollTop = activeItem.offsetTop; + + menuItems.forEach(item => { + item.addEventListener('click', itemEvent => { + if (!itemEvent.target.classList.contains('nav__link--toggler') + && document.body.classList.contains('mobile-menu-expanded')) { + document.body.classList.remove('mobile-menu-expanded') + } + }); + }); +}); diff --git a/docs/api/rest_api_reference/assets/js/highlight.js b/docs/api/rest_api_reference/assets/js/highlight.js new file mode 100644 index 0000000000..9b2bf55d5b --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/highlight.js @@ -0,0 +1,230 @@ +/* + * jQuery Highlight plugin + * + * Based on highlight v3 by Johann Burkard + * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html + * + * Code a little bit refactored and cleaned (in my humble opinion). + * Most important changes: + * - has an option to highlight only entire words (wordsOnly - false by default), + * - has an option to be case sensitive (caseSensitive - false by default) + * - highlight element tag and class names can be specified in options + * + * Usage: + * // wrap every occurrence of text 'lorem' in content + * // with (default options) + * $('#content').highlight('lorem'); + * + * // search for and highlight more terms at once + * // so you can save some time on traversing DOM + * $('#content').highlight(['lorem', 'ipsum']); + * $('#content').highlight('lorem ipsum'); + * + * // search only for entire word 'lorem' + * $('#content').highlight('lorem', { wordsOnly: true }); + * + * // search only for the entire word 'C#' + * // and make sure that the word boundary can also + * // be a 'non-word' character, as well as a regex latin1 only boundary: + * $('#content').highlight('C#', { wordsOnly: true , wordsBoundary: '[\\b\\W]' }); + * + * // don't ignore case during search of term 'lorem' + * $('#content').highlight('lorem', { caseSensitive: true }); + * + * // wrap every occurrence of term 'ipsum' in content + * // with + * $('#content').highlight('ipsum', { element: 'em', className: 'important' }); + * + * // remove default highlight + * $('#content').unhighlight(); + * + * // remove custom highlight + * $('#content').unhighlight({ element: 'em', className: 'important' }); + * + * + * Copyright (c) 2009 Bartek Szopka + * + * Licensed under MIT license. + * + */ + +(function(factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +})(function(jQuery) { + jQuery.extend({ + highlight: function( + node, + re, + nodeName, + className, + callback, + ignoreDiacritics + ) { + if (node.nodeType === 3) { + var subject = ignoreDiacritics + ? jQuery.removeDiacritcs(node.data) + : node.data; + var match = subject.match(re); + if (match) { + // The new highlight Element Node + var highlight = document.createElement(nodeName || 'span'); + highlight.className = className || 'highlight'; + // Note that we use the captured value to find the real index + // of the match. This is because we do not want to include the matching word boundaries + var capturePos = node.data.indexOf(match[1], match.index); + + // Split the node and replace the matching wordnode + // with the highlighted node + var wordNode = node.splitText(capturePos); + wordNode.splitText(match[1].length); + + var wordClone = wordNode.cloneNode(true); + highlight.appendChild(wordClone); + wordNode.parentNode.replaceChild(highlight, wordNode); + if (typeof callback == 'function') { + callback(highlight); + } + return 1; //skip added node in parent + } + } else if ( + node.nodeType === 1 && + node.childNodes && // only element nodes that have children + !/(script|style)/i.test(node.tagName) && // ignore script and style nodes + !( + node.tagName === nodeName.toUpperCase() && + node.className === className + ) + ) { + // skip if already highlighted + for (var i = 0; i < node.childNodes.length; i++) { + i += jQuery.highlight( + node.childNodes[i], + re, + nodeName, + className, + callback, + ignoreDiacritics + ); + } + } + return 0; + }, + + removeDiacritcs: function(word) { + return word + .replace(/[\u00c0-\u00c6]/g, 'A') + .replace(/[\u00e0-\u00e6]/g, 'a') + .replace(/[\u00c7]/g, 'C') + .replace(/[\u00e7]/g, 'c') + .replace(/[\u00c8-\u00cb]/g, 'E') + .replace(/[\u00e8-\u00eb]/g, 'e') + .replace(/[\u00cc-\u00cf]/g, 'I') + .replace(/[\u00ec-\u00ef]/g, 'i') + .replace(/[\u00d1|\u0147]/g, 'N') + .replace(/[\u00f1|\u0148]/g, 'n') + .replace(/[\u00d2-\u00d8|\u0150]/g, 'O') + .replace(/[\u00f2-\u00f8|\u0151]/g, 'o') + .replace(/[\u0160]/g, 'S') + .replace(/[\u0161]/g, 's') + .replace(/[\u00d9-\u00dc]/g, 'U') + .replace(/[\u00f9-\u00fc]/g, 'u') + .replace(/[\u00dd]/g, 'Y') + .replace(/[\u00fd]/g, 'y'); + }, + + // https://github.com/knownasilya/jquery-highlight/issues/13 + normalize: function (node) { + if (!node) { + return; + } + if (node.nodeType == 3) { + while (node.nextSibling && node.nextSibling.nodeType == 3) { + node.nodeValue += node.nextSibling.nodeValue; + node.parentNode.removeChild(node.nextSibling); + } + } else { + jQuery.normalize(node.firstChild); + } + jQuery.normalize(node.nextSibling); + } + }); + + jQuery.fn.unhighlight = function(options) { + var settings = { + className: 'highlight', + element: 'span' + }; + + jQuery.extend(settings, options); + + return this.find(settings.element + '.' + settings.className) + .each(function() { + var parent = this.parentNode; + parent.replaceChild(this.firstChild, this); + jQuery.normalize(parent); + }) + .end(); + }; + + jQuery.fn.highlight = function(words, options, callback) { + var settings = { + className: 'highlight', + element: 'span', + caseSensitive: false, + wordsOnly: false, + wordsBoundary: '\\b', + ignoreDiacritics: false + }; + + jQuery.extend(settings, options); + + if (typeof words === 'string') { + words = [words]; + } + words = jQuery.grep(words, function(word) { + return word != ''; + }); + words = jQuery.map(words, function(word) { + if (settings.ignoreDiacritics) { + word = jQuery.removeDiacritcs(word); + } + return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + }); + + if (words.length === 0) { + return this; + } + + var flag = settings.caseSensitive ? '' : 'i'; + // The capture parenthesis will make sure we can match + // only the matching word + var pattern = '(' + words.join('|') + ')'; + if (settings.wordsOnly) { + pattern = + (settings.wordsBoundaryStart || settings.wordsBoundary) + + pattern + + (settings.wordsBoundaryEnd || settings.wordsBoundary); + } + var re = new RegExp(pattern, flag); + + return this.each(function() { + jQuery.highlight( + this, + re, + settings.element, + settings.className, + callback, + settings.ignoreDiacritics + ); + }); + }; +}); diff --git a/docs/api/rest_api_reference/assets/js/jquery.min.js b/docs/api/rest_api_reference/assets/js/jquery.min.js new file mode 100644 index 0000000000..a1c07fd803 --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0 0) { + var tokenMetadata = lunr.utils.clone(metadata) || {} + tokenMetadata["position"] = [sliceStart, sliceLength] + tokenMetadata["index"] = tokens.length + + tokens.push( + new lunr.Token ( + str.slice(sliceStart, sliceEnd), + tokenMetadata + ) + ) + } + + sliceStart = sliceEnd + 1 + } + + } + + return tokens +} + +/** + * The separator used to split a string into tokens. Override this property to change the behaviour of + * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens. + * + * @static + * @see lunr.tokenizer + */ +lunr.tokenizer.separator = /[\s\-]+/ +/*! + * lunr.Pipeline + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * lunr.Pipelines maintain an ordered list of functions to be applied to all + * tokens in documents entering the search index and queries being ran against + * the index. + * + * An instance of lunr.Index created with the lunr shortcut will contain a + * pipeline with a stop word filter and an English language stemmer. Extra + * functions can be added before or after either of these functions or these + * default functions can be removed. + * + * When run the pipeline will call each function in turn, passing a token, the + * index of that token in the original list of all tokens and finally a list of + * all the original tokens. + * + * The output of functions in the pipeline will be passed to the next function + * in the pipeline. To exclude a token from entering the index the function + * should return undefined, the rest of the pipeline will not be called with + * this token. + * + * For serialisation of pipelines to work, all functions used in an instance of + * a pipeline should be registered with lunr.Pipeline. Registered functions can + * then be loaded. If trying to load a serialised pipeline that uses functions + * that are not registered an error will be thrown. + * + * If not planning on serialising the pipeline then registering pipeline functions + * is not necessary. + * + * @constructor + */ +lunr.Pipeline = function () { + this._stack = [] +} + +lunr.Pipeline.registeredFunctions = Object.create(null) + +/** + * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token + * string as well as all known metadata. A pipeline function can mutate the token string + * or mutate (or add) metadata for a given token. + * + * A pipeline function can indicate that the passed token should be discarded by returning + * null, undefined or an empty string. This token will not be passed to any downstream pipeline + * functions and will not be added to the index. + * + * Multiple tokens can be returned by returning an array of tokens. Each token will be passed + * to any downstream pipeline functions and all will returned tokens will be added to the index. + * + * Any number of pipeline functions may be chained together using a lunr.Pipeline. + * + * @interface lunr.PipelineFunction + * @param {lunr.Token} token - A token from the document being processed. + * @param {number} i - The index of this token in the complete list of tokens for this document/field. + * @param {lunr.Token[]} tokens - All tokens for this document/field. + * @returns {(?lunr.Token|lunr.Token[])} + */ + +/** + * Register a function with the pipeline. + * + * Functions that are used in the pipeline should be registered if the pipeline + * needs to be serialised, or a serialised pipeline needs to be loaded. + * + * Registering a function does not add it to a pipeline, functions must still be + * added to instances of the pipeline for them to be used when running a pipeline. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @param {String} label - The label to register this function with + */ +lunr.Pipeline.registerFunction = function (fn, label) { + if (label in this.registeredFunctions) { + lunr.utils.warn('Overwriting existing registered function: ' + label) + } + + fn.label = label + lunr.Pipeline.registeredFunctions[fn.label] = fn +} + +/** + * Warns if the function is not registered as a Pipeline function. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @private + */ +lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) { + var isRegistered = fn.label && (fn.label in this.registeredFunctions) + + if (!isRegistered) { + lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn) + } +} + +/** + * Loads a previously serialised pipeline. + * + * All functions to be loaded must already be registered with lunr.Pipeline. + * If any function from the serialised data has not been registered then an + * error will be thrown. + * + * @param {Object} serialised - The serialised pipeline to load. + * @returns {lunr.Pipeline} + */ +lunr.Pipeline.load = function (serialised) { + var pipeline = new lunr.Pipeline + + serialised.forEach(function (fnName) { + var fn = lunr.Pipeline.registeredFunctions[fnName] + + if (fn) { + pipeline.add(fn) + } else { + throw new Error('Cannot load unregistered function: ' + fnName) + } + }) + + return pipeline +} + +/** + * Adds new functions to the end of the pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline. + */ +lunr.Pipeline.prototype.add = function () { + var fns = Array.prototype.slice.call(arguments) + + fns.forEach(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + this._stack.push(fn) + }, this) +} + +/** + * Adds a single function after a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.after = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + pos = pos + 1 + this._stack.splice(pos, 0, newFn) +} + +/** + * Adds a single function before a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.before = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + this._stack.splice(pos, 0, newFn) +} + +/** + * Removes a function from the pipeline. + * + * @param {lunr.PipelineFunction} fn The function to remove from the pipeline. + */ +lunr.Pipeline.prototype.remove = function (fn) { + var pos = this._stack.indexOf(fn) + if (pos == -1) { + return + } + + this._stack.splice(pos, 1) +} + +/** + * Runs the current list of functions that make up the pipeline against the + * passed tokens. + * + * @param {Array} tokens The tokens to run through the pipeline. + * @returns {Array} + */ +lunr.Pipeline.prototype.run = function (tokens) { + var stackLength = this._stack.length + + for (var i = 0; i < stackLength; i++) { + var fn = this._stack[i] + var memo = [] + + for (var j = 0; j < tokens.length; j++) { + var result = fn(tokens[j], j, tokens) + + if (result === null || result === void 0 || result === '') continue + + if (Array.isArray(result)) { + for (var k = 0; k < result.length; k++) { + memo.push(result[k]) + } + } else { + memo.push(result) + } + } + + tokens = memo + } + + return tokens +} + +/** + * Convenience method for passing a string through a pipeline and getting + * strings out. This method takes care of wrapping the passed string in a + * token and mapping the resulting tokens back to strings. + * + * @param {string} str - The string to pass through the pipeline. + * @param {?object} metadata - Optional metadata to associate with the token + * passed to the pipeline. + * @returns {string[]} + */ +lunr.Pipeline.prototype.runString = function (str, metadata) { + var token = new lunr.Token (str, metadata) + + return this.run([token]).map(function (t) { + return t.toString() + }) +} + +/** + * Resets the pipeline by removing any existing processors. + * + */ +lunr.Pipeline.prototype.reset = function () { + this._stack = [] +} + +/** + * Returns a representation of the pipeline ready for serialisation. + * + * Logs a warning if the function has not been registered. + * + * @returns {Array} + */ +lunr.Pipeline.prototype.toJSON = function () { + return this._stack.map(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + + return fn.label + }) +} +/*! + * lunr.Vector + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * A vector is used to construct the vector space of documents and queries. These + * vectors support operations to determine the similarity between two documents or + * a document and a query. + * + * Normally no parameters are required for initializing a vector, but in the case of + * loading a previously dumped vector the raw elements can be provided to the constructor. + * + * For performance reasons vectors are implemented with a flat array, where an elements + * index is immediately followed by its value. E.g. [index, value, index, value]. This + * allows the underlying array to be as sparse as possible and still offer decent + * performance when being used for vector calculations. + * + * @constructor + * @param {Number[]} [elements] - The flat list of element index and element value pairs. + */ +lunr.Vector = function (elements) { + this._magnitude = 0 + this.elements = elements || [] +} + + +/** + * Calculates the position within the vector to insert a given index. + * + * This is used internally by insert and upsert. If there are duplicate indexes then + * the position is returned as if the value for that index were to be updated, but it + * is the callers responsibility to check whether there is a duplicate at that index + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @returns {Number} + */ +lunr.Vector.prototype.positionForIndex = function (index) { + // For an empty vector the tuple can be inserted at the beginning + if (this.elements.length == 0) { + return 0 + } + + var start = 0, + end = this.elements.length / 2, + sliceLength = end - start, + pivotPoint = Math.floor(sliceLength / 2), + pivotIndex = this.elements[pivotPoint * 2] + + while (sliceLength > 1) { + if (pivotIndex < index) { + start = pivotPoint + } + + if (pivotIndex > index) { + end = pivotPoint + } + + if (pivotIndex == index) { + break + } + + sliceLength = end - start + pivotPoint = start + Math.floor(sliceLength / 2) + pivotIndex = this.elements[pivotPoint * 2] + } + + if (pivotIndex == index) { + return pivotPoint * 2 + } + + if (pivotIndex > index) { + return pivotPoint * 2 + } + + if (pivotIndex < index) { + return (pivotPoint + 1) * 2 + } +} + +/** + * Inserts an element at an index within the vector. + * + * Does not allow duplicates, will throw an error if there is already an entry + * for this index. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + */ +lunr.Vector.prototype.insert = function (insertIdx, val) { + this.upsert(insertIdx, val, function () { + throw "duplicate index" + }) +} + +/** + * Inserts or updates an existing index within the vector. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + * @param {function} fn - A function that is called for updates, the existing value and the + * requested value are passed as arguments + */ +lunr.Vector.prototype.upsert = function (insertIdx, val, fn) { + this._magnitude = 0 + var position = this.positionForIndex(insertIdx) + + if (this.elements[position] == insertIdx) { + this.elements[position + 1] = fn(this.elements[position + 1], val) + } else { + this.elements.splice(position, 0, insertIdx, val) + } +} + +/** + * Calculates the magnitude of this vector. + * + * @returns {Number} + */ +lunr.Vector.prototype.magnitude = function () { + if (this._magnitude) return this._magnitude + + var sumOfSquares = 0, + elementsLength = this.elements.length + + for (var i = 1; i < elementsLength; i += 2) { + var val = this.elements[i] + sumOfSquares += val * val + } + + return this._magnitude = Math.sqrt(sumOfSquares) +} + +/** + * Calculates the dot product of this vector and another vector. + * + * @param {lunr.Vector} otherVector - The vector to compute the dot product with. + * @returns {Number} + */ +lunr.Vector.prototype.dot = function (otherVector) { + var dotProduct = 0, + a = this.elements, b = otherVector.elements, + aLen = a.length, bLen = b.length, + aVal = 0, bVal = 0, + i = 0, j = 0 + + while (i < aLen && j < bLen) { + aVal = a[i], bVal = b[j] + if (aVal < bVal) { + i += 2 + } else if (aVal > bVal) { + j += 2 + } else if (aVal == bVal) { + dotProduct += a[i + 1] * b[j + 1] + i += 2 + j += 2 + } + } + + return dotProduct +} + +/** + * Calculates the similarity between this vector and another vector. + * + * @param {lunr.Vector} otherVector - The other vector to calculate the + * similarity with. + * @returns {Number} + */ +lunr.Vector.prototype.similarity = function (otherVector) { + return this.dot(otherVector) / this.magnitude() || 0 +} + +/** + * Converts the vector to an array of the elements within the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toArray = function () { + var output = new Array (this.elements.length / 2) + + for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) { + output[j] = this.elements[i] + } + + return output +} + +/** + * A JSON serializable representation of the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toJSON = function () { + return this.elements +} +/* eslint-disable */ +/*! + * lunr.stemmer + * Copyright (C) 2019 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + */ + +/** + * lunr.stemmer is an english language stemmer, this is a JavaScript + * implementation of the PorterStemmer taken from http://tartarus.org/~martin + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token - The string to stem + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + * @function + */ +lunr.stemmer = (function(){ + var step2list = { + "ational" : "ate", + "tional" : "tion", + "enci" : "ence", + "anci" : "ance", + "izer" : "ize", + "bli" : "ble", + "alli" : "al", + "entli" : "ent", + "eli" : "e", + "ousli" : "ous", + "ization" : "ize", + "ation" : "ate", + "ator" : "ate", + "alism" : "al", + "iveness" : "ive", + "fulness" : "ful", + "ousness" : "ous", + "aliti" : "al", + "iviti" : "ive", + "biliti" : "ble", + "logi" : "log" + }, + + step3list = { + "icate" : "ic", + "ative" : "", + "alize" : "al", + "iciti" : "ic", + "ical" : "ic", + "ful" : "", + "ness" : "" + }, + + c = "[^aeiou]", // consonant + v = "[aeiouy]", // vowel + C = c + "[^aeiouy]*", // consonant sequence + V = v + "[aeiou]*", // vowel sequence + + mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0 + meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1 + mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1 + s_v = "^(" + C + ")?" + v; // vowel in stem + + var re_mgr0 = new RegExp(mgr0); + var re_mgr1 = new RegExp(mgr1); + var re_meq1 = new RegExp(meq1); + var re_s_v = new RegExp(s_v); + + var re_1a = /^(.+?)(ss|i)es$/; + var re2_1a = /^(.+?)([^s])s$/; + var re_1b = /^(.+?)eed$/; + var re2_1b = /^(.+?)(ed|ing)$/; + var re_1b_2 = /.$/; + var re2_1b_2 = /(at|bl|iz)$/; + var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$"); + var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var re_1c = /^(.+?[^aeiou])y$/; + var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + + var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + + var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + var re2_4 = /^(.+?)(s|t)(ion)$/; + + var re_5 = /^(.+?)e$/; + var re_5_1 = /ll$/; + var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var porterStemmer = function porterStemmer(w) { + var stem, + suffix, + firstch, + re, + re2, + re3, + re4; + + if (w.length < 3) { return w; } + + firstch = w.substr(0,1); + if (firstch == "y") { + w = firstch.toUpperCase() + w.substr(1); + } + + // Step 1a + re = re_1a + re2 = re2_1a; + + if (re.test(w)) { w = w.replace(re,"$1$2"); } + else if (re2.test(w)) { w = w.replace(re2,"$1$2"); } + + // Step 1b + re = re_1b; + re2 = re2_1b; + if (re.test(w)) { + var fp = re.exec(w); + re = re_mgr0; + if (re.test(fp[1])) { + re = re_1b_2; + w = w.replace(re,""); + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = re_s_v; + if (re2.test(stem)) { + w = stem; + re2 = re2_1b_2; + re3 = re3_1b_2; + re4 = re4_1b_2; + if (re2.test(w)) { w = w + "e"; } + else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); } + else if (re4.test(w)) { w = w + "e"; } + } + } + + // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say) + re = re_1c; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "i"; + } + + // Step 2 + re = re_2; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step2list[suffix]; + } + } + + // Step 3 + re = re_3; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step3list[suffix]; + } + } + + // Step 4 + re = re_4; + re2 = re2_4; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + if (re.test(stem)) { + w = stem; + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = re_mgr1; + if (re2.test(stem)) { + w = stem; + } + } + + // Step 5 + re = re_5; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + re2 = re_meq1; + re3 = re3_5; + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) { + w = stem; + } + } + + re = re_5_1; + re2 = re_mgr1; + if (re.test(w) && re2.test(w)) { + re = re_1b_2; + w = w.replace(re,""); + } + + // and turn initial Y back to y + + if (firstch == "y") { + w = firstch.toLowerCase() + w.substr(1); + } + + return w; + }; + + return function (token) { + return token.update(porterStemmer); + } +})(); + +lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer') +/*! + * lunr.stopWordFilter + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * lunr.generateStopWordFilter builds a stopWordFilter function from the provided + * list of stop words. + * + * The built in lunr.stopWordFilter is built using this generator and can be used + * to generate custom stopWordFilters for applications or non English languages. + * + * @function + * @param {Array} token The token to pass through the filter + * @returns {lunr.PipelineFunction} + * @see lunr.Pipeline + * @see lunr.stopWordFilter + */ +lunr.generateStopWordFilter = function (stopWords) { + var words = stopWords.reduce(function (memo, stopWord) { + memo[stopWord] = stopWord + return memo + }, {}) + + return function (token) { + if (token && words[token.toString()] !== token.toString()) return token + } +} + +/** + * lunr.stopWordFilter is an English language stop word list filter, any words + * contained in the list will not be passed through the filter. + * + * This is intended to be used in the Pipeline. If the token does not pass the + * filter then undefined will be returned. + * + * @function + * @implements {lunr.PipelineFunction} + * @params {lunr.Token} token - A token to check for being a stop word. + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + */ +lunr.stopWordFilter = lunr.generateStopWordFilter([ + 'a', + 'able', + 'about', + 'across', + 'after', + 'all', + 'almost', + 'also', + 'am', + 'among', + 'an', + 'and', + 'any', + 'are', + 'as', + 'at', + 'be', + 'because', + 'been', + 'but', + 'by', + 'can', + 'cannot', + 'could', + 'dear', + 'did', + 'do', + 'does', + 'either', + 'else', + 'ever', + 'every', + 'for', + 'from', + 'get', + 'got', + 'had', + 'has', + 'have', + 'he', + 'her', + 'hers', + 'him', + 'his', + 'how', + 'however', + 'i', + 'if', + 'in', + 'into', + 'is', + 'it', + 'its', + 'just', + 'least', + 'let', + 'like', + 'likely', + 'may', + 'me', + 'might', + 'most', + 'must', + 'my', + 'neither', + 'no', + 'nor', + 'not', + 'of', + 'off', + 'often', + 'on', + 'only', + 'or', + 'other', + 'our', + 'own', + 'rather', + 'said', + 'say', + 'says', + 'she', + 'should', + 'since', + 'so', + 'some', + 'than', + 'that', + 'the', + 'their', + 'them', + 'then', + 'there', + 'these', + 'they', + 'this', + 'tis', + 'to', + 'too', + 'twas', + 'us', + 'wants', + 'was', + 'we', + 'were', + 'what', + 'when', + 'where', + 'which', + 'while', + 'who', + 'whom', + 'why', + 'will', + 'with', + 'would', + 'yet', + 'you', + 'your' +]) + +lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter') +/*! + * lunr.trimmer + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * lunr.trimmer is a pipeline function for trimming non word + * characters from the beginning and end of tokens before they + * enter the index. + * + * This implementation may not work correctly for non latin + * characters and should either be removed or adapted for use + * with languages with non-latin characters. + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token The token to pass through the filter + * @returns {lunr.Token} + * @see lunr.Pipeline + */ +lunr.trimmer = function (token) { + return token.update(function (s) { + return s.replace(/^\W+/, '').replace(/\W+$/, '') + }) +} + +lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer') +/*! + * lunr.TokenSet + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * A token set is used to store the unique list of all tokens + * within an index. Token sets are also used to represent an + * incoming query to the index, this query token set and index + * token set are then intersected to find which tokens to look + * up in the inverted index. + * + * A token set can hold multiple tokens, as in the case of the + * index token set, or it can hold a single token as in the + * case of a simple query token set. + * + * Additionally token sets are used to perform wildcard matching. + * Leading, contained and trailing wildcards are supported, and + * from this edit distance matching can also be provided. + * + * Token sets are implemented as a minimal finite state automata, + * where both common prefixes and suffixes are shared between tokens. + * This helps to reduce the space used for storing the token set. + * + * @constructor + */ +lunr.TokenSet = function () { + this.final = false + this.edges = {} + this.id = lunr.TokenSet._nextId + lunr.TokenSet._nextId += 1 +} + +/** + * Keeps track of the next, auto increment, identifier to assign + * to a new tokenSet. + * + * TokenSets require a unique identifier to be correctly minimised. + * + * @private + */ +lunr.TokenSet._nextId = 1 + +/** + * Creates a TokenSet instance from the given sorted array of words. + * + * @param {String[]} arr - A sorted array of strings to create the set from. + * @returns {lunr.TokenSet} + * @throws Will throw an error if the input array is not sorted. + */ +lunr.TokenSet.fromArray = function (arr) { + var builder = new lunr.TokenSet.Builder + + for (var i = 0, len = arr.length; i < len; i++) { + builder.insert(arr[i]) + } + + builder.finish() + return builder.root +} + +/** + * Creates a token set from a query clause. + * + * @private + * @param {Object} clause - A single clause from lunr.Query. + * @param {string} clause.term - The query clause term. + * @param {number} [clause.editDistance] - The optional edit distance for the term. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromClause = function (clause) { + if ('editDistance' in clause) { + return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance) + } else { + return lunr.TokenSet.fromString(clause.term) + } +} + +/** + * Creates a token set representing a single string with a specified + * edit distance. + * + * Insertions, deletions, substitutions and transpositions are each + * treated as an edit distance of 1. + * + * Increasing the allowed edit distance will have a dramatic impact + * on the performance of both creating and intersecting these TokenSets. + * It is advised to keep the edit distance less than 3. + * + * @param {string} str - The string to create the token set from. + * @param {number} editDistance - The allowed edit distance to match. + * @returns {lunr.Vector} + */ +lunr.TokenSet.fromFuzzyString = function (str, editDistance) { + var root = new lunr.TokenSet + + var stack = [{ + node: root, + editsRemaining: editDistance, + str: str + }] + + while (stack.length) { + var frame = stack.pop() + + // no edit + if (frame.str.length > 0) { + var char = frame.str.charAt(0), + noEditNode + + if (char in frame.node.edges) { + noEditNode = frame.node.edges[char] + } else { + noEditNode = new lunr.TokenSet + frame.node.edges[char] = noEditNode + } + + if (frame.str.length == 1) { + noEditNode.final = true + } + + stack.push({ + node: noEditNode, + editsRemaining: frame.editsRemaining, + str: frame.str.slice(1) + }) + } + + if (frame.editsRemaining == 0) { + continue + } + + // insertion + if ("*" in frame.node.edges) { + var insertionNode = frame.node.edges["*"] + } else { + var insertionNode = new lunr.TokenSet + frame.node.edges["*"] = insertionNode + } + + if (frame.str.length == 0) { + insertionNode.final = true + } + + stack.push({ + node: insertionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str + }) + + // deletion + // can only do a deletion if we have enough edits remaining + // and if there are characters left to delete in the string + if (frame.str.length > 1) { + stack.push({ + node: frame.node, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // deletion + // just removing the last character from the str + if (frame.str.length == 1) { + frame.node.final = true + } + + // substitution + // can only do a substitution if we have enough edits remaining + // and if there are characters left to substitute + if (frame.str.length >= 1) { + if ("*" in frame.node.edges) { + var substitutionNode = frame.node.edges["*"] + } else { + var substitutionNode = new lunr.TokenSet + frame.node.edges["*"] = substitutionNode + } + + if (frame.str.length == 1) { + substitutionNode.final = true + } + + stack.push({ + node: substitutionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // transposition + // can only do a transposition if there are edits remaining + // and there are enough characters to transpose + if (frame.str.length > 1) { + var charA = frame.str.charAt(0), + charB = frame.str.charAt(1), + transposeNode + + if (charB in frame.node.edges) { + transposeNode = frame.node.edges[charB] + } else { + transposeNode = new lunr.TokenSet + frame.node.edges[charB] = transposeNode + } + + if (frame.str.length == 1) { + transposeNode.final = true + } + + stack.push({ + node: transposeNode, + editsRemaining: frame.editsRemaining - 1, + str: charA + frame.str.slice(2) + }) + } + } + + return root +} + +/** + * Creates a TokenSet from a string. + * + * The string may contain one or more wildcard characters (*) + * that will allow wildcard matching when intersecting with + * another TokenSet. + * + * @param {string} str - The string to create a TokenSet from. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromString = function (str) { + var node = new lunr.TokenSet, + root = node + + /* + * Iterates through all characters within the passed string + * appending a node for each character. + * + * When a wildcard character is found then a self + * referencing edge is introduced to continually match + * any number of any characters. + */ + for (var i = 0, len = str.length; i < len; i++) { + var char = str[i], + final = (i == len - 1) + + if (char == "*") { + node.edges[char] = node + node.final = final + + } else { + var next = new lunr.TokenSet + next.final = final + + node.edges[char] = next + node = next + } + } + + return root +} + +/** + * Converts this TokenSet into an array of strings + * contained within the TokenSet. + * + * This is not intended to be used on a TokenSet that + * contains wildcards, in these cases the results are + * undefined and are likely to cause an infinite loop. + * + * @returns {string[]} + */ +lunr.TokenSet.prototype.toArray = function () { + var words = [] + + var stack = [{ + prefix: "", + node: this + }] + + while (stack.length) { + var frame = stack.pop(), + edges = Object.keys(frame.node.edges), + len = edges.length + + if (frame.node.final) { + /* In Safari, at this point the prefix is sometimes corrupted, see: + * https://github.com/olivernn/lunr.js/issues/279 Calling any + * String.prototype method forces Safari to "cast" this string to what + * it's supposed to be, fixing the bug. */ + frame.prefix.charAt(0) + words.push(frame.prefix) + } + + for (var i = 0; i < len; i++) { + var edge = edges[i] + + stack.push({ + prefix: frame.prefix.concat(edge), + node: frame.node.edges[edge] + }) + } + } + + return words +} + +/** + * Generates a string representation of a TokenSet. + * + * This is intended to allow TokenSets to be used as keys + * in objects, largely to aid the construction and minimisation + * of a TokenSet. As such it is not designed to be a human + * friendly representation of the TokenSet. + * + * @returns {string} + */ +lunr.TokenSet.prototype.toString = function () { + // NOTE: Using Object.keys here as this.edges is very likely + // to enter 'hash-mode' with many keys being added + // + // avoiding a for-in loop here as it leads to the function + // being de-optimised (at least in V8). From some simple + // benchmarks the performance is comparable, but allowing + // V8 to optimize may mean easy performance wins in the future. + + if (this._str) { + return this._str + } + + var str = this.final ? '1' : '0', + labels = Object.keys(this.edges).sort(), + len = labels.length + + for (var i = 0; i < len; i++) { + var label = labels[i], + node = this.edges[label] + + str = str + label + node.id + } + + return str +} + +/** + * Returns a new TokenSet that is the intersection of + * this TokenSet and the passed TokenSet. + * + * This intersection will take into account any wildcards + * contained within the TokenSet. + * + * @param {lunr.TokenSet} b - An other TokenSet to intersect with. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.prototype.intersect = function (b) { + var output = new lunr.TokenSet, + frame = undefined + + var stack = [{ + qNode: b, + output: output, + node: this + }] + + while (stack.length) { + frame = stack.pop() + + // NOTE: As with the #toString method, we are using + // Object.keys and a for loop instead of a for-in loop + // as both of these objects enter 'hash' mode, causing + // the function to be de-optimised in V8 + var qEdges = Object.keys(frame.qNode.edges), + qLen = qEdges.length, + nEdges = Object.keys(frame.node.edges), + nLen = nEdges.length + + for (var q = 0; q < qLen; q++) { + var qEdge = qEdges[q] + + for (var n = 0; n < nLen; n++) { + var nEdge = nEdges[n] + + if (nEdge == qEdge || qEdge == '*') { + var node = frame.node.edges[nEdge], + qNode = frame.qNode.edges[qEdge], + final = node.final && qNode.final, + next = undefined + + if (nEdge in frame.output.edges) { + // an edge already exists for this character + // no need to create a new node, just set the finality + // bit unless this node is already final + next = frame.output.edges[nEdge] + next.final = next.final || final + + } else { + // no edge exists yet, must create one + // set the finality bit and insert it + // into the output + next = new lunr.TokenSet + next.final = final + frame.output.edges[nEdge] = next + } + + stack.push({ + qNode: qNode, + output: next, + node: node + }) + } + } + } + } + + return output +} +lunr.TokenSet.Builder = function () { + this.previousWord = "" + this.root = new lunr.TokenSet + this.uncheckedNodes = [] + this.minimizedNodes = {} +} + +lunr.TokenSet.Builder.prototype.insert = function (word) { + var node, + commonPrefix = 0 + + if (word < this.previousWord) { + throw new Error ("Out of order word insertion") + } + + for (var i = 0; i < word.length && i < this.previousWord.length; i++) { + if (word[i] != this.previousWord[i]) break + commonPrefix++ + } + + this.minimize(commonPrefix) + + if (this.uncheckedNodes.length == 0) { + node = this.root + } else { + node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child + } + + for (var i = commonPrefix; i < word.length; i++) { + var nextNode = new lunr.TokenSet, + char = word[i] + + node.edges[char] = nextNode + + this.uncheckedNodes.push({ + parent: node, + char: char, + child: nextNode + }) + + node = nextNode + } + + node.final = true + this.previousWord = word +} + +lunr.TokenSet.Builder.prototype.finish = function () { + this.minimize(0) +} + +lunr.TokenSet.Builder.prototype.minimize = function (downTo) { + for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) { + var node = this.uncheckedNodes[i], + childKey = node.child.toString() + + if (childKey in this.minimizedNodes) { + node.parent.edges[node.char] = this.minimizedNodes[childKey] + } else { + // Cache the key for this node since + // we know it can't change anymore + node.child._str = childKey + + this.minimizedNodes[childKey] = node.child + } + + this.uncheckedNodes.pop() + } +} +/*! + * lunr.Index + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * An index contains the built index of all documents and provides a query interface + * to the index. + * + * Usually instances of lunr.Index will not be created using this constructor, instead + * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be + * used to load previously built and serialized indexes. + * + * @constructor + * @param {Object} attrs - The attributes of the built search index. + * @param {Object} attrs.invertedIndex - An index of term/field to document reference. + * @param {Object} attrs.fieldVectors - Field vectors + * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens. + * @param {string[]} attrs.fields - The names of indexed document fields. + * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms. + */ +lunr.Index = function (attrs) { + this.invertedIndex = attrs.invertedIndex + this.fieldVectors = attrs.fieldVectors + this.tokenSet = attrs.tokenSet + this.fields = attrs.fields + this.pipeline = attrs.pipeline +} + +/** + * A result contains details of a document matching a search query. + * @typedef {Object} lunr.Index~Result + * @property {string} ref - The reference of the document this result represents. + * @property {number} score - A number between 0 and 1 representing how similar this document is to the query. + * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match. + */ + +/** + * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple + * query language which itself is parsed into an instance of lunr.Query. + * + * For programmatically building queries it is advised to directly use lunr.Query, the query language + * is best used for human entered text rather than program generated text. + * + * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported + * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello' + * or 'world', though those that contain both will rank higher in the results. + * + * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can + * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding + * wildcards will increase the number of documents that will be found but can also have a negative + * impact on query performance, especially with wildcards at the beginning of a term. + * + * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term + * hello in the title field will match this query. Using a field not present in the index will lead + * to an error being thrown. + * + * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term + * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported + * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2. + * Avoid large values for edit distance to improve query performance. + * + * Each term also supports a presence modifier. By default a term's presence in document is optional, however + * this can be changed to either required or prohibited. For a term's presence to be required in a document the + * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and + * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not + * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'. + * + * To escape special characters the backslash character '\' can be used, this allows searches to include + * characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead + * of attempting to apply a boost of 2 to the search term "foo". + * + * @typedef {string} lunr.Index~QueryString + * @example Simple single term query + * hello + * @example Multiple term query + * hello world + * @example term scoped to a field + * title:hello + * @example term with a boost of 10 + * hello^10 + * @example term with an edit distance of 2 + * hello~2 + * @example terms with presence modifiers + * -foo +bar baz + */ + +/** + * Performs a search against the index using lunr query syntax. + * + * Results will be returned sorted by their score, the most relevant results + * will be returned first. For details on how the score is calculated, please see + * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}. + * + * For more programmatic querying use lunr.Index#query. + * + * @param {lunr.Index~QueryString} queryString - A string containing a lunr query. + * @throws {lunr.QueryParseError} If the passed query string cannot be parsed. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.search = function (queryString) { + return this.query(function (query) { + var parser = new lunr.QueryParser(queryString, query) + parser.parse() + }) +} + +/** + * A query builder callback provides a query object to be used to express + * the query to perform on the index. + * + * @callback lunr.Index~queryBuilder + * @param {lunr.Query} query - The query object to build up. + * @this lunr.Query + */ + +/** + * Performs a query against the index using the yielded lunr.Query object. + * + * If performing programmatic queries against the index, this method is preferred + * over lunr.Index#search so as to avoid the additional query parsing overhead. + * + * A query object is yielded to the supplied function which should be used to + * express the query to be run against the index. + * + * Note that although this function takes a callback parameter it is _not_ an + * asynchronous operation, the callback is just yielded a query object to be + * customized. + * + * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.query = function (fn) { + // for each query clause + // * process terms + // * expand terms from token set + // * find matching documents and metadata + // * get document vectors + // * score documents + + var query = new lunr.Query(this.fields), + matchingFields = Object.create(null), + queryVectors = Object.create(null), + termFieldCache = Object.create(null), + requiredMatches = Object.create(null), + prohibitedMatches = Object.create(null) + + /* + * To support field level boosts a query vector is created per + * field. An empty vector is eagerly created to support negated + * queries. + */ + for (var i = 0; i < this.fields.length; i++) { + queryVectors[this.fields[i]] = new lunr.Vector + } + + fn.call(query, query) + + for (var i = 0; i < query.clauses.length; i++) { + /* + * Unless the pipeline has been disabled for this term, which is + * the case for terms with wildcards, we need to pass the clause + * term through the search pipeline. A pipeline returns an array + * of processed terms. Pipeline functions may expand the passed + * term, which means we may end up performing multiple index lookups + * for a single query term. + */ + var clause = query.clauses[i], + terms = null, + clauseMatches = lunr.Set.complete + + if (clause.usePipeline) { + terms = this.pipeline.runString(clause.term, { + fields: clause.fields + }) + } else { + terms = [clause.term] + } + + for (var m = 0; m < terms.length; m++) { + var term = terms[m] + + /* + * Each term returned from the pipeline needs to use the same query + * clause object, e.g. the same boost and or edit distance. The + * simplest way to do this is to re-use the clause object but mutate + * its term property. + */ + clause.term = term + + /* + * From the term in the clause we create a token set which will then + * be used to intersect the indexes token set to get a list of terms + * to lookup in the inverted index + */ + var termTokenSet = lunr.TokenSet.fromClause(clause), + expandedTerms = this.tokenSet.intersect(termTokenSet).toArray() + + /* + * If a term marked as required does not exist in the tokenSet it is + * impossible for the search to return any matches. We set all the field + * scoped required matches set to empty and stop examining any further + * clauses. + */ + if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = lunr.Set.empty + } + + break + } + + for (var j = 0; j < expandedTerms.length; j++) { + /* + * For each term get the posting and termIndex, this is required for + * building the query vector. + */ + var expandedTerm = expandedTerms[j], + posting = this.invertedIndex[expandedTerm], + termIndex = posting._index + + for (var k = 0; k < clause.fields.length; k++) { + /* + * For each field that this query term is scoped by (by default + * all fields are in scope) we need to get all the document refs + * that have this term in that field. + * + * The posting is the entry in the invertedIndex for the matching + * term from above. + */ + var field = clause.fields[k], + fieldPosting = posting[field], + matchingDocumentRefs = Object.keys(fieldPosting), + termField = expandedTerm + "/" + field, + matchingDocumentsSet = new lunr.Set(matchingDocumentRefs) + + /* + * if the presence of this term is required ensure that the matching + * documents are added to the set of required matches for this clause. + * + */ + if (clause.presence == lunr.Query.presence.REQUIRED) { + clauseMatches = clauseMatches.union(matchingDocumentsSet) + + if (requiredMatches[field] === undefined) { + requiredMatches[field] = lunr.Set.complete + } + } + + /* + * if the presence of this term is prohibited ensure that the matching + * documents are added to the set of prohibited matches for this field, + * creating that set if it does not yet exist. + */ + if (clause.presence == lunr.Query.presence.PROHIBITED) { + if (prohibitedMatches[field] === undefined) { + prohibitedMatches[field] = lunr.Set.empty + } + + prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet) + + /* + * Prohibited matches should not be part of the query vector used for + * similarity scoring and no metadata should be extracted so we continue + * to the next field + */ + continue + } + + /* + * The query field vector is populated using the termIndex found for + * the term and a unit value with the appropriate boost applied. + * Using upsert because there could already be an entry in the vector + * for the term we are working with. In that case we just add the scores + * together. + */ + queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b }) + + /** + * If we've already seen this term, field combo then we've already collected + * the matching documents and metadata, no need to go through all that again + */ + if (termFieldCache[termField]) { + continue + } + + for (var l = 0; l < matchingDocumentRefs.length; l++) { + /* + * All metadata for this term/field/document triple + * are then extracted and collected into an instance + * of lunr.MatchData ready to be returned in the query + * results + */ + var matchingDocumentRef = matchingDocumentRefs[l], + matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field), + metadata = fieldPosting[matchingDocumentRef], + fieldMatch + + if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) { + matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata) + } else { + fieldMatch.add(expandedTerm, field, metadata) + } + + } + + termFieldCache[termField] = true + } + } + } + + /** + * If the presence was required we need to update the requiredMatches field sets. + * We do this after all fields for the term have collected their matches because + * the clause terms presence is required in _any_ of the fields not _all_ of the + * fields. + */ + if (clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = requiredMatches[field].intersect(clauseMatches) + } + } + } + + /** + * Need to combine the field scoped required and prohibited + * matching documents into a global set of required and prohibited + * matches + */ + var allRequiredMatches = lunr.Set.complete, + allProhibitedMatches = lunr.Set.empty + + for (var i = 0; i < this.fields.length; i++) { + var field = this.fields[i] + + if (requiredMatches[field]) { + allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field]) + } + + if (prohibitedMatches[field]) { + allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field]) + } + } + + var matchingFieldRefs = Object.keys(matchingFields), + results = [], + matches = Object.create(null) + + /* + * If the query is negated (contains only prohibited terms) + * we need to get _all_ fieldRefs currently existing in the + * index. This is only done when we know that the query is + * entirely prohibited terms to avoid any cost of getting all + * fieldRefs unnecessarily. + * + * Additionally, blank MatchData must be created to correctly + * populate the results. + */ + if (query.isNegated()) { + matchingFieldRefs = Object.keys(this.fieldVectors) + + for (var i = 0; i < matchingFieldRefs.length; i++) { + var matchingFieldRef = matchingFieldRefs[i] + var fieldRef = lunr.FieldRef.fromString(matchingFieldRef) + matchingFields[matchingFieldRef] = new lunr.MatchData + } + } + + for (var i = 0; i < matchingFieldRefs.length; i++) { + /* + * Currently we have document fields that match the query, but we + * need to return documents. The matchData and scores are combined + * from multiple fields belonging to the same document. + * + * Scores are calculated by field, using the query vectors created + * above, and combined into a final document score using addition. + */ + var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), + docRef = fieldRef.docRef + + if (!allRequiredMatches.contains(docRef)) { + continue + } + + if (allProhibitedMatches.contains(docRef)) { + continue + } + + var fieldVector = this.fieldVectors[fieldRef], + score = queryVectors[fieldRef.fieldName].similarity(fieldVector), + docMatch + + if ((docMatch = matches[docRef]) !== undefined) { + docMatch.score += score + docMatch.matchData.combine(matchingFields[fieldRef]) + } else { + var match = { + ref: docRef, + score: score, + matchData: matchingFields[fieldRef] + } + matches[docRef] = match + results.push(match) + } + } + + /* + * Sort the results objects by score, highest first. + */ + return results.sort(function (a, b) { + return b.score - a.score + }) +} + +/** + * Prepares the index for JSON serialization. + * + * The schema for this JSON blob will be described in a + * separate JSON schema file. + * + * @returns {Object} + */ +lunr.Index.prototype.toJSON = function () { + var invertedIndex = Object.keys(this.invertedIndex) + .sort() + .map(function (term) { + return [term, this.invertedIndex[term]] + }, this) + + var fieldVectors = Object.keys(this.fieldVectors) + .map(function (ref) { + return [ref, this.fieldVectors[ref].toJSON()] + }, this) + + return { + version: lunr.version, + fields: this.fields, + fieldVectors: fieldVectors, + invertedIndex: invertedIndex, + pipeline: this.pipeline.toJSON() + } +} + +/** + * Loads a previously serialized lunr.Index + * + * @param {Object} serializedIndex - A previously serialized lunr.Index + * @returns {lunr.Index} + */ +lunr.Index.load = function (serializedIndex) { + var attrs = {}, + fieldVectors = {}, + serializedVectors = serializedIndex.fieldVectors, + invertedIndex = Object.create(null), + serializedInvertedIndex = serializedIndex.invertedIndex, + tokenSetBuilder = new lunr.TokenSet.Builder, + pipeline = lunr.Pipeline.load(serializedIndex.pipeline) + + if (serializedIndex.version != lunr.version) { + lunr.utils.warn("Version mismatch when loading serialised index. Current version of lunr '" + lunr.version + "' does not match serialized index '" + serializedIndex.version + "'") + } + + for (var i = 0; i < serializedVectors.length; i++) { + var tuple = serializedVectors[i], + ref = tuple[0], + elements = tuple[1] + + fieldVectors[ref] = new lunr.Vector(elements) + } + + for (var i = 0; i < serializedInvertedIndex.length; i++) { + var tuple = serializedInvertedIndex[i], + term = tuple[0], + posting = tuple[1] + + tokenSetBuilder.insert(term) + invertedIndex[term] = posting + } + + tokenSetBuilder.finish() + + attrs.fields = serializedIndex.fields + + attrs.fieldVectors = fieldVectors + attrs.invertedIndex = invertedIndex + attrs.tokenSet = tokenSetBuilder.root + attrs.pipeline = pipeline + + return new lunr.Index(attrs) +} +/*! + * lunr.Builder + * Copyright (C) 2019 Oliver Nightingale + */ + +/** + * lunr.Builder performs indexing on a set of documents and + * returns instances of lunr.Index ready for querying. + * + * All configuration of the index is done via the builder, the + * fields to index, the document reference, the text processing + * pipeline and document scoring parameters are all set on the + * builder before indexing. + * + * @constructor + * @property {string} _ref - Internal reference to the document reference field. + * @property {string[]} _fields - Internal reference to the document fields to index. + * @property {object} invertedIndex - The inverted index maps terms to document fields. + * @property {object} documentTermFrequencies - Keeps track of document term frequencies. + * @property {object} documentLengths - Keeps track of the length of documents added to the index. + * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing. + * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing. + * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index. + * @property {number} documentCount - Keeps track of the total number of documents indexed. + * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75. + * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2. + * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space. + * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index. + */ +lunr.Builder = function () { + this._ref = "id" + this._fields = Object.create(null) + this._documents = Object.create(null) + this.invertedIndex = Object.create(null) + this.fieldTermFrequencies = {} + this.fieldLengths = {} + this.tokenizer = lunr.tokenizer + this.pipeline = new lunr.Pipeline + this.searchPipeline = new lunr.Pipeline + this.documentCount = 0 + this._b = 0.75 + this._k1 = 1.2 + this.termIndex = 0 + this.metadataWhitelist = [] +} + +/** + * Sets the document field used as the document reference. Every document must have this field. + * The type of this field in the document should be a string, if it is not a string it will be + * coerced into a string by calling toString. + * + * The default ref is 'id'. + * + * The ref should _not_ be changed during indexing, it should be set before any documents are + * added to the index. Changing it during indexing can lead to inconsistent results. + * + * @param {string} ref - The name of the reference field in the document. + */ +lunr.Builder.prototype.ref = function (ref) { + this._ref = ref +} + +/** + * A function that is used to extract a field from a document. + * + * Lunr expects a field to be at the top level of a document, if however the field + * is deeply nested within a document an extractor function can be used to extract + * the right field for indexing. + * + * @callback fieldExtractor + * @param {object} doc - The document being added to the index. + * @returns {?(string|object|object[])} obj - The object that will be indexed for this field. + * @example Extracting a nested field + * function (doc) { return doc.nested.field } + */ + +/** + * Adds a field to the list of document fields that will be indexed. Every document being + * indexed should have this field. Null values for this field in indexed documents will + * not cause errors but will limit the chance of that document being retrieved by searches. + * + * All fields should be added before adding documents to the index. Adding fields after + * a document has been indexed will have no effect on already indexed documents. + * + * Fields can be boosted at build time. This allows terms within that field to have more + * importance when ranking search results. Use a field boost to specify that matches within + * one field are more important than other fields. + * + * @param {string} fieldName - The name of a field to index in all documents. + * @param {object} attributes - Optional attributes associated with this field. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this field. + * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document. + * @throws {RangeError} fieldName cannot contain unsupported characters '/' + */ +lunr.Builder.prototype.field = function (fieldName, attributes) { + if (/\//.test(fieldName)) { + throw new RangeError ("Field '" + fieldName + "' contains illegal character '/'") + } + + this._fields[fieldName] = attributes || {} +} + +/** + * A parameter to tune the amount of field length normalisation that is applied when + * calculating relevance scores. A value of 0 will completely disable any normalisation + * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b + * will be clamped to the range 0 - 1. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.b = function (number) { + if (number < 0) { + this._b = 0 + } else if (number > 1) { + this._b = 1 + } else { + this._b = number + } +} + +/** + * A parameter that controls the speed at which a rise in term frequency results in term + * frequency saturation. The default value is 1.2. Setting this to a higher value will give + * slower saturation levels, a lower value will result in quicker saturation. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.k1 = function (number) { + this._k1 = number +} + +/** + * Adds a document to the index. + * + * Before adding fields to the index the index should have been fully setup, with the document + * ref and all fields to index already having been specified. + * + * The document must have a field name as specified by the ref (by default this is 'id') and + * it should have all fields defined for indexing, though null or undefined values will not + * cause errors. + * + * Entire documents can be boosted at build time. Applying a boost to a document indicates that + * this document should rank higher in search results than other documents. + * + * @param {object} doc - The document to add to the index. + * @param {object} attributes - Optional attributes associated with this document. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this document. + */ +lunr.Builder.prototype.add = function (doc, attributes) { + var docRef = doc[this._ref], + fields = Object.keys(this._fields) + + this._documents[docRef] = attributes || {} + this.documentCount += 1 + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i], + extractor = this._fields[fieldName].extractor, + field = extractor ? extractor(doc) : doc[fieldName], + tokens = this.tokenizer(field, { + fields: [fieldName] + }), + terms = this.pipeline.run(tokens), + fieldRef = new lunr.FieldRef (docRef, fieldName), + fieldTerms = Object.create(null) + + this.fieldTermFrequencies[fieldRef] = fieldTerms + this.fieldLengths[fieldRef] = 0 + + // store the length of this field for this document + this.fieldLengths[fieldRef] += terms.length + + // calculate term frequencies for this field + for (var j = 0; j < terms.length; j++) { + var term = terms[j] + + if (fieldTerms[term] == undefined) { + fieldTerms[term] = 0 + } + + fieldTerms[term] += 1 + + // add to inverted index + // create an initial posting if one doesn't exist + if (this.invertedIndex[term] == undefined) { + var posting = Object.create(null) + posting["_index"] = this.termIndex + this.termIndex += 1 + + for (var k = 0; k < fields.length; k++) { + posting[fields[k]] = Object.create(null) + } + + this.invertedIndex[term] = posting + } + + // add an entry for this term/fieldName/docRef to the invertedIndex + if (this.invertedIndex[term][fieldName][docRef] == undefined) { + this.invertedIndex[term][fieldName][docRef] = Object.create(null) + } + + // store all whitelisted metadata about this token in the + // inverted index + for (var l = 0; l < this.metadataWhitelist.length; l++) { + var metadataKey = this.metadataWhitelist[l], + metadata = term.metadata[metadataKey] + + if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) { + this.invertedIndex[term][fieldName][docRef][metadataKey] = [] + } + + this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata) + } + } + + } +} + +/** + * Calculates the average document length for this index + * + * @private + */ +lunr.Builder.prototype.calculateAverageFieldLengths = function () { + + var fieldRefs = Object.keys(this.fieldLengths), + numberOfFields = fieldRefs.length, + accumulator = {}, + documentsWithField = {} + + for (var i = 0; i < numberOfFields; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + field = fieldRef.fieldName + + documentsWithField[field] || (documentsWithField[field] = 0) + documentsWithField[field] += 1 + + accumulator[field] || (accumulator[field] = 0) + accumulator[field] += this.fieldLengths[fieldRef] + } + + var fields = Object.keys(this._fields) + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i] + accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName] + } + + this.averageFieldLength = accumulator +} + +/** + * Builds a vector space model of every document using lunr.Vector + * + * @private + */ +lunr.Builder.prototype.createFieldVectors = function () { + var fieldVectors = {}, + fieldRefs = Object.keys(this.fieldTermFrequencies), + fieldRefsLength = fieldRefs.length, + termIdfCache = Object.create(null) + + for (var i = 0; i < fieldRefsLength; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + fieldName = fieldRef.fieldName, + fieldLength = this.fieldLengths[fieldRef], + fieldVector = new lunr.Vector, + termFrequencies = this.fieldTermFrequencies[fieldRef], + terms = Object.keys(termFrequencies), + termsLength = terms.length + + + var fieldBoost = this._fields[fieldName].boost || 1, + docBoost = this._documents[fieldRef.docRef].boost || 1 + + for (var j = 0; j < termsLength; j++) { + var term = terms[j], + tf = termFrequencies[term], + termIndex = this.invertedIndex[term]._index, + idf, score, scoreWithPrecision + + if (termIdfCache[term] === undefined) { + idf = lunr.idf(this.invertedIndex[term], this.documentCount) + termIdfCache[term] = idf + } else { + idf = termIdfCache[term] + } + + score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf) + score *= fieldBoost + score *= docBoost + scoreWithPrecision = Math.round(score * 1000) / 1000 + // Converts 1.23456789 to 1.234. + // Reducing the precision so that the vectors take up less + // space when serialised. Doing it now so that they behave + // the same before and after serialisation. Also, this is + // the fastest approach to reducing a number's precision in + // JavaScript. + + fieldVector.insert(termIndex, scoreWithPrecision) + } + + fieldVectors[fieldRef] = fieldVector + } + + this.fieldVectors = fieldVectors +} + +/** + * Creates a token set of all tokens in the index using lunr.TokenSet + * + * @private + */ +lunr.Builder.prototype.createTokenSet = function () { + this.tokenSet = lunr.TokenSet.fromArray( + Object.keys(this.invertedIndex).sort() + ) +} + +/** + * Builds the index, creating an instance of lunr.Index. + * + * This completes the indexing process and should only be called + * once all documents have been added to the index. + * + * @returns {lunr.Index} + */ +lunr.Builder.prototype.build = function () { + this.calculateAverageFieldLengths() + this.createFieldVectors() + this.createTokenSet() + + return new lunr.Index({ + invertedIndex: this.invertedIndex, + fieldVectors: this.fieldVectors, + tokenSet: this.tokenSet, + fields: Object.keys(this._fields), + pipeline: this.searchPipeline + }) +} + +/** + * Applies a plugin to the index builder. + * + * A plugin is a function that is called with the index builder as its context. + * Plugins can be used to customise or extend the behaviour of the index + * in some way. A plugin is just a function, that encapsulated the custom + * behaviour that should be applied when building the index. + * + * The plugin function will be called with the index builder as its argument, additional + * arguments can also be passed when calling use. The function will be called + * with the index builder as its context. + * + * @param {Function} plugin The plugin to apply. + */ +lunr.Builder.prototype.use = function (fn) { + var args = Array.prototype.slice.call(arguments, 1) + args.unshift(this) + fn.apply(this, args) +} +/** + * Contains and collects metadata about a matching document. + * A single instance of lunr.MatchData is returned as part of every + * lunr.Index~Result. + * + * @constructor + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + * @property {object} metadata - A cloned collection of metadata associated with this document. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData = function (term, field, metadata) { + var clonedMetadata = Object.create(null), + metadataKeys = Object.keys(metadata || {}) + + // Cloning the metadata to prevent the original + // being mutated during match data combination. + // Metadata is kept in an array within the inverted + // index so cloning the data can be done with + // Array#slice + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + clonedMetadata[key] = metadata[key].slice() + } + + this.metadata = Object.create(null) + + if (term !== undefined) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = clonedMetadata + } +} + +/** + * An instance of lunr.MatchData will be created for every term that matches a + * document. However only one instance is required in a lunr.Index~Result. This + * method combines metadata from another instance of lunr.MatchData with this + * objects metadata. + * + * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData.prototype.combine = function (otherMatchData) { + var terms = Object.keys(otherMatchData.metadata) + + for (var i = 0; i < terms.length; i++) { + var term = terms[i], + fields = Object.keys(otherMatchData.metadata[term]) + + if (this.metadata[term] == undefined) { + this.metadata[term] = Object.create(null) + } + + for (var j = 0; j < fields.length; j++) { + var field = fields[j], + keys = Object.keys(otherMatchData.metadata[term][field]) + + if (this.metadata[term][field] == undefined) { + this.metadata[term][field] = Object.create(null) + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k] + + if (this.metadata[term][field][key] == undefined) { + this.metadata[term][field][key] = otherMatchData.metadata[term][field][key] + } else { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key]) + } + + } + } + } +} + +/** + * Add metadata for a term/field pair to this instance of match data. + * + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + */ +lunr.MatchData.prototype.add = function (term, field, metadata) { + if (!(term in this.metadata)) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = metadata + return + } + + if (!(field in this.metadata[term])) { + this.metadata[term][field] = metadata + return + } + + var metadataKeys = Object.keys(metadata) + + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + + if (key in this.metadata[term][field]) { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key]) + } else { + this.metadata[term][field][key] = metadata[key] + } + } +} +/** + * A lunr.Query provides a programmatic way of defining queries to be performed + * against a {@link lunr.Index}. + * + * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method + * so the query object is pre-initialized with the right index fields. + * + * @constructor + * @property {lunr.Query~Clause[]} clauses - An array of query clauses. + * @property {string[]} allFields - An array of all available fields in a lunr.Index. + */ +lunr.Query = function (allFields) { + this.clauses = [] + this.allFields = allFields +} + +/** + * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause. + * + * This allows wildcards to be added to the beginning and end of a term without having to manually do any string + * concatenation. + * + * The wildcard constants can be bitwise combined to select both leading and trailing wildcards. + * + * @constant + * @default + * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour + * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists + * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with trailing wildcard + * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING }) + * @example query term with leading and trailing wildcard + * query.term('foo', { + * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + * }) + */ + +lunr.Query.wildcard = new String ("*") +lunr.Query.wildcard.NONE = 0 +lunr.Query.wildcard.LEADING = 1 +lunr.Query.wildcard.TRAILING = 2 + +/** + * Constants for indicating what kind of presence a term must have in matching documents. + * + * @constant + * @enum {number} + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with required presence + * query.term('foo', { presence: lunr.Query.presence.REQUIRED }) + */ +lunr.Query.presence = { + /** + * Term's presence in a document is optional, this is the default value. + */ + OPTIONAL: 1, + + /** + * Term's presence in a document is required, documents that do not contain + * this term will not be returned. + */ + REQUIRED: 2, + + /** + * Term's presence in a document is prohibited, documents that do contain + * this term will not be returned. + */ + PROHIBITED: 3 +} + +/** + * A single clause in a {@link lunr.Query} contains a term and details on how to + * match that term against a {@link lunr.Index}. + * + * @typedef {Object} lunr.Query~Clause + * @property {string[]} fields - The fields in an index this clause should be matched against. + * @property {number} [boost=1] - Any boost that should be applied when matching this clause. + * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be. + * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline. + * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended. + * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents. + */ + +/** + * Adds a {@link lunr.Query~Clause} to this query. + * + * Unless the clause contains the fields to be matched all fields will be matched. In addition + * a default boost of 1 is applied to the clause. + * + * @param {lunr.Query~Clause} clause - The clause to add to this query. + * @see lunr.Query~Clause + * @returns {lunr.Query} + */ +lunr.Query.prototype.clause = function (clause) { + if (!('fields' in clause)) { + clause.fields = this.allFields + } + + if (!('boost' in clause)) { + clause.boost = 1 + } + + if (!('usePipeline' in clause)) { + clause.usePipeline = true + } + + if (!('wildcard' in clause)) { + clause.wildcard = lunr.Query.wildcard.NONE + } + + if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) { + clause.term = "*" + clause.term + } + + if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) { + clause.term = "" + clause.term + "*" + } + + if (!('presence' in clause)) { + clause.presence = lunr.Query.presence.OPTIONAL + } + + this.clauses.push(clause) + + return this +} + +/** + * A negated query is one in which every clause has a presence of + * prohibited. These queries require some special processing to return + * the expected results. + * + * @returns boolean + */ +lunr.Query.prototype.isNegated = function () { + for (var i = 0; i < this.clauses.length; i++) { + if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) { + return false + } + } + + return true +} + +/** + * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause} + * to the list of clauses that make up this query. + * + * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion + * to a token or token-like string should be done before calling this method. + * + * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an + * array, each term in the array will share the same options. + * + * @param {object|object[]} term - The term(s) to add to the query. + * @param {object} [options] - Any additional properties to add to the query clause. + * @returns {lunr.Query} + * @see lunr.Query#clause + * @see lunr.Query~Clause + * @example adding a single term to a query + * query.term("foo") + * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard + * query.term("foo", { + * fields: ["title"], + * boost: 10, + * wildcard: lunr.Query.wildcard.TRAILING + * }) + * @example using lunr.tokenizer to convert a string to tokens before using them as terms + * query.term(lunr.tokenizer("foo bar")) + */ +lunr.Query.prototype.term = function (term, options) { + if (Array.isArray(term)) { + term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this) + return this + } + + var clause = options || {} + clause.term = term.toString() + + this.clause(clause) + + return this +} +lunr.QueryParseError = function (message, start, end) { + this.name = "QueryParseError" + this.message = message + this.start = start + this.end = end +} + +lunr.QueryParseError.prototype = new Error +lunr.QueryLexer = function (str) { + this.lexemes = [] + this.str = str + this.length = str.length + this.pos = 0 + this.start = 0 + this.escapeCharPositions = [] +} + +lunr.QueryLexer.prototype.run = function () { + var state = lunr.QueryLexer.lexText + + while (state) { + state = state(this) + } +} + +lunr.QueryLexer.prototype.sliceString = function () { + var subSlices = [], + sliceStart = this.start, + sliceEnd = this.pos + + for (var i = 0; i < this.escapeCharPositions.length; i++) { + sliceEnd = this.escapeCharPositions[i] + subSlices.push(this.str.slice(sliceStart, sliceEnd)) + sliceStart = sliceEnd + 1 + } + + subSlices.push(this.str.slice(sliceStart, this.pos)) + this.escapeCharPositions.length = 0 + + return subSlices.join('') +} + +lunr.QueryLexer.prototype.emit = function (type) { + this.lexemes.push({ + type: type, + str: this.sliceString(), + start: this.start, + end: this.pos + }) + + this.start = this.pos +} + +lunr.QueryLexer.prototype.escapeCharacter = function () { + this.escapeCharPositions.push(this.pos - 1) + this.pos += 1 +} + +lunr.QueryLexer.prototype.next = function () { + if (this.pos >= this.length) { + return lunr.QueryLexer.EOS + } + + var char = this.str.charAt(this.pos) + this.pos += 1 + return char +} + +lunr.QueryLexer.prototype.width = function () { + return this.pos - this.start +} + +lunr.QueryLexer.prototype.ignore = function () { + if (this.start == this.pos) { + this.pos += 1 + } + + this.start = this.pos +} + +lunr.QueryLexer.prototype.backup = function () { + this.pos -= 1 +} + +lunr.QueryLexer.prototype.acceptDigitRun = function () { + var char, charCode + + do { + char = this.next() + charCode = char.charCodeAt(0) + } while (charCode > 47 && charCode < 58) + + if (char != lunr.QueryLexer.EOS) { + this.backup() + } +} + +lunr.QueryLexer.prototype.more = function () { + return this.pos < this.length +} + +lunr.QueryLexer.EOS = 'EOS' +lunr.QueryLexer.FIELD = 'FIELD' +lunr.QueryLexer.TERM = 'TERM' +lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE' +lunr.QueryLexer.BOOST = 'BOOST' +lunr.QueryLexer.PRESENCE = 'PRESENCE' + +lunr.QueryLexer.lexField = function (lexer) { + lexer.backup() + lexer.emit(lunr.QueryLexer.FIELD) + lexer.ignore() + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexTerm = function (lexer) { + if (lexer.width() > 1) { + lexer.backup() + lexer.emit(lunr.QueryLexer.TERM) + } + + lexer.ignore() + + if (lexer.more()) { + return lunr.QueryLexer.lexText + } +} + +lunr.QueryLexer.lexEditDistance = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.EDIT_DISTANCE) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexBoost = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.BOOST) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexEOS = function (lexer) { + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } +} + +// This matches the separator used when tokenising fields +// within a document. These should match otherwise it is +// not possible to search for some tokens within a document. +// +// It is possible for the user to change the separator on the +// tokenizer so it _might_ clash with any other of the special +// characters already used within the search string, e.g. :. +// +// This means that it is possible to change the separator in +// such a way that makes some words unsearchable using a search +// string. +lunr.QueryLexer.termSeparator = lunr.tokenizer.separator + +lunr.QueryLexer.lexText = function (lexer) { + while (true) { + var char = lexer.next() + + if (char == lunr.QueryLexer.EOS) { + return lunr.QueryLexer.lexEOS + } + + // Escape character is '\' + if (char.charCodeAt(0) == 92) { + lexer.escapeCharacter() + continue + } + + if (char == ":") { + return lunr.QueryLexer.lexField + } + + if (char == "~") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexEditDistance + } + + if (char == "^") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexBoost + } + + // "+" indicates term presence is required + // checking for length to ensure that only + // leading "+" are considered + if (char == "+" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + // "-" indicates term presence is prohibited + // checking for length to ensure that only + // leading "-" are considered + if (char == "-" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + if (char.match(lunr.QueryLexer.termSeparator)) { + return lunr.QueryLexer.lexTerm + } + } +} + +lunr.QueryParser = function (str, query) { + this.lexer = new lunr.QueryLexer (str) + this.query = query + this.currentClause = {} + this.lexemeIdx = 0 +} + +lunr.QueryParser.prototype.parse = function () { + this.lexer.run() + this.lexemes = this.lexer.lexemes + + var state = lunr.QueryParser.parseClause + + while (state) { + state = state(this) + } + + return this.query +} + +lunr.QueryParser.prototype.peekLexeme = function () { + return this.lexemes[this.lexemeIdx] +} + +lunr.QueryParser.prototype.consumeLexeme = function () { + var lexeme = this.peekLexeme() + this.lexemeIdx += 1 + return lexeme +} + +lunr.QueryParser.prototype.nextClause = function () { + var completedClause = this.currentClause + this.query.clause(completedClause) + this.currentClause = {} +} + +lunr.QueryParser.parseClause = function (parser) { + var lexeme = parser.peekLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.type) { + case lunr.QueryLexer.PRESENCE: + return lunr.QueryParser.parsePresence + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expected either a field or a term, found " + lexeme.type + + if (lexeme.str.length >= 1) { + errorMessage += " with value '" + lexeme.str + "'" + } + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } +} + +lunr.QueryParser.parsePresence = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.str) { + case "-": + parser.currentClause.presence = lunr.Query.presence.PROHIBITED + break + case "+": + parser.currentClause.presence = lunr.Query.presence.REQUIRED + break + default: + var errorMessage = "unrecognised presence operator'" + lexeme.str + "'" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term or field, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseField = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + if (parser.query.allFields.indexOf(lexeme.str) == -1) { + var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '), + errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.fields = [lexeme.str] + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseTerm = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + parser.currentClause.term = lexeme.str.toLowerCase() + + if (lexeme.str.indexOf("*") != -1) { + parser.currentClause.usePipeline = false + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseEditDistance = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var editDistance = parseInt(lexeme.str, 10) + + if (isNaN(editDistance)) { + var errorMessage = "edit distance must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.editDistance = editDistance + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseBoost = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var boost = parseInt(lexeme.str, 10) + + if (isNaN(boost)) { + var errorMessage = "boost must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.boost = boost + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + + /** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ + ;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like enviroments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + root.lunr = factory() + } + }(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + return lunr + })) +})(); diff --git a/docs/api/rest_api_reference/assets/js/search.js b/docs/api/rest_api_reference/assets/js/search.js new file mode 100644 index 0000000000..052d5824e1 --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/search.js @@ -0,0 +1,216 @@ +'use strict'; + +let index = lunr.Index; +const builder = new lunr.Builder; +const searchSections = document.querySelectorAll('.search-container'); +const searchResults = document.getElementById('search-results'); +const searchInput = document.getElementById('search-input'); +const dropdownMenu = document.getElementById('dropdownMenuLink'); + +configureIndexBuilder(); +addIndexes(); +index = builder.build(); + +searchInput.addEventListener('keyup', search); + +document.addEventListener('keyup', event => { + if (event.keyCode === 27) { + hideResultsBlock(); + this.value = ''; + } +}); + +document.addEventListener('click', event => { + if (event.target !== searchResults) { + hideResultsBlock(); + } + + if (event.target === searchInput && event.target.value.trim()) { + showResultsBlock(); + } +}); + +dropdownMenu.addEventListener('click', event => { + hideResultsBlock(); +}); + +function configureIndexBuilder() { + builder.ref('id'); + builder.field('name'); + builder.field('body'); + builder.field('root'); + builder.field('url'); +} + +function addIndexes() { + searchSections.forEach(searchSection => { + const name = getEndpointName(searchSection); + const body = getEndpointBody(searchSection); + const url = getEndpointUrl(searchSection); + + addIndex( + name.id, + name.textContent.trim(), + body ? body.textContent : '', + searchSection.dataset.parent, + `/${url.textContent.slice(1)}` + ); + }); +} + +function addIndex(id, name, body, root, url) { + builder.add({ + id: id, + name: name, + body: body, + root: root, + url: url + }); +} + +function search(event) { + if (this.value.length > 0 && this.value.trim()) { + showResultsBlock(); + showResults( + getResults(this.value) + ); + } else { + hideResultsBlock(); + } +} + +function getResults(searchValue) { + const results = executeQuery(searchValue, buildQuery(searchValue)); + + if (results.length === 0 && isLogicalAnd(searchValue)) { + return executeQuery(searchValue, getLogicalAndQueryWithLeadingAndTrailingWildcards()); + } + + if (results.length === 0 && !isLogicalAnd(searchValue)) { + return executeQuery(searchValue, getQueryWithLeadingAndTrailingWildcards()); + } + + return results; +} + +function executeQuery(searchValue, options) { + return index.query(query => { + query.term(lunr.tokenizer(parseSearchValue(searchValue)), options); + }); +} + +function parseSearchValue(searchValue) { + if (!isLogicalAnd(searchValue)) { + return searchValue; + } + + return searchValue.slice(1,-1); +} + +function isLogicalAnd(searchValue) { + return searchValue.charAt(0) === '"' + && searchValue.charAt(searchValue.length-1) === '"'; +} + +function buildLogicalAndQuery() { + return { + presence: lunr.Query.presence.REQUIRED + }; +} + +function buildQuery(searchValue) { + if (isLogicalAnd(searchValue)) { + return buildLogicalAndQuery(); + } + + return { + wildcard: lunr.Query.wildcard.TRAILING + } +} + +function getQueryWithLeadingAndTrailingWildcards() { + return { + wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + } +} + +function getLogicalAndQueryWithLeadingAndTrailingWildcards() { + return { + presence: lunr.Query.presence.REQUIRED, + wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + } +} + +function showResults(results) { + searchResults.innerHTML = 'close'; + + if (results.length > 0) { + results.forEach(result => { + const endpointSection = document.getElementById(`${result.ref}-section`); + let resultRow = document.createElement('div'); + resultRow.classList.add('border-bottom'); + + const name = getEndpointName(endpointSection); + const body = getEndpointBody(endpointSection); + + resultRow.innerHTML = ` +

    + ${name.textContent.replace(/[.¶]/g, '')} - ${endpointSection.dataset.parent} +

    +

    + ${getEndpointMethod(endpointSection).outerHTML} + ${getEndpointUrl(endpointSection).outerHTML} +

    +

    ${body.textContent.trim()}

    +
    `; + + searchResults.append(resultRow); + highlight(); + }); + + const searchLinks = document.querySelectorAll('.search__link'); + + searchLinks.forEach(link => { + link.addEventListener('click', event =>{ + if (document.body.classList.contains('mobile-menu-expanded')) { + document.body.classList.remove('mobile-menu-expanded') + } + + hideResultsBlock(); + }); + }); + } else { + searchResults.innerHTML += '

    No results

    '; + } +} + +function highlight() { + let words = searchInput.value.replace(/[.*"]/g, '').split(' '); + $(searchResults).highlight(words); +} + +function showResultsBlock() { + document.body.classList.add('overflow-hidden'); + searchResults.classList.remove('d-none'); +} + +function hideResultsBlock() { + document.body.classList.remove('overflow-hidden'); + searchResults.classList.add('d-none'); +} + +function getEndpointName(section) { + return section.querySelector('[data-field="name"]'); +} + +function getEndpointBody(section) { + return section.querySelector('[data-field="body"]'); +} + +function getEndpointUrl(section) { + return section.querySelector('[data-field="url"]'); +} + +function getEndpointMethod(section) { + return section.querySelector('[data-field="method"]'); +} diff --git a/docs/api/rest_api_reference/assets/js/stickyfill.js b/docs/api/rest_api_reference/assets/js/stickyfill.js new file mode 100644 index 0000000000..9b8c6c0e3f --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/stickyfill.js @@ -0,0 +1,546 @@ +/*! + * Stickyfill – `position: sticky` polyfill + * v. 2.1.0 | https://github.com/wilddeer/stickyfill + * MIT License + */ + +;(function(window, document) { + 'use strict'; + + /* + * 1. Check if the browser supports `position: sticky` natively or is too old to run the polyfill. + * If either of these is the case set `seppuku` flag. It will be checked later to disable key features + * of the polyfill, but the API will remain functional to avoid breaking things. + */ + + var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + + var seppuku = false; + + var isWindowDefined = typeof window !== 'undefined'; + + // The polyfill can’t function properly without `window` or `window.getComputedStyle`. + if (!isWindowDefined || !window.getComputedStyle) seppuku = true; + // Dont’t get in a way if the browser supports `position: sticky` natively. + else { + (function () { + var testNode = document.createElement('div'); + + if (['', '-webkit-', '-moz-', '-ms-'].some(function (prefix) { + try { + testNode.style.position = prefix + 'sticky'; + } catch (e) {} + + return testNode.style.position != ''; + })) seppuku = true; + })(); + } + + /* + * 2. “Global” vars used across the polyfill + */ + var isInitialized = false; + + // Check if Shadow Root constructor exists to make further checks simpler + var shadowRootExists = typeof ShadowRoot !== 'undefined'; + + // Last saved scroll position + var scroll = { + top: null, + left: null + }; + + // Array of created Sticky instances + var stickies = []; + + /* + * 3. Utility functions + */ + function extend(targetObj, sourceObject) { + for (var key in sourceObject) { + if (sourceObject.hasOwnProperty(key)) { + targetObj[key] = sourceObject[key]; + } + } + } + + function parseNumeric(val) { + return parseFloat(val) || 0; + } + + function getDocOffsetTop(node) { + var docOffsetTop = 0; + + while (node) { + docOffsetTop += node.offsetTop; + node = node.offsetParent; + } + + return docOffsetTop; + } + + /* + * 4. Sticky class + */ + + var Sticky = function () { + function Sticky(node) { + _classCallCheck(this, Sticky); + + if (!(node instanceof HTMLElement)) throw new Error('First argument must be HTMLElement'); + if (stickies.some(function (sticky) { + return sticky._node === node; + })) throw new Error('Stickyfill is already applied to this node'); + + this._node = node; + this._stickyMode = null; + this._active = false; + + stickies.push(this); + + this.refresh(); + } + + _createClass(Sticky, [{ + key: 'refresh', + value: function refresh() { + if (seppuku || this._removed) return; + if (this._active) this._deactivate(); + + var node = this._node; + + /* + * 1. Save node computed props + */ + var nodeComputedStyle = getComputedStyle(node); + var nodeComputedProps = { + position: nodeComputedStyle.position, + top: nodeComputedStyle.top, + display: nodeComputedStyle.display, + marginTop: nodeComputedStyle.marginTop, + marginBottom: nodeComputedStyle.marginBottom, + marginLeft: nodeComputedStyle.marginLeft, + marginRight: nodeComputedStyle.marginRight, + cssFloat: nodeComputedStyle.cssFloat + }; + + /* + * 2. Check if the node can be activated + */ + if (isNaN(parseFloat(nodeComputedProps.top)) || nodeComputedProps.display == 'table-cell' || nodeComputedProps.display == 'none') return; + + this._active = true; + + /* + * 3. Check if the current node position is `sticky`. If it is, it means that the browser supports sticky positioning, + * but the polyfill was force-enabled. We set the node’s position to `static` before continuing, so that the node + * is in it’s initial position when we gather its params. + */ + var originalPosition = node.style.position; + if (nodeComputedStyle.position == 'sticky' || nodeComputedStyle.position == '-webkit-sticky') node.style.position = 'static'; + + /* + * 4. Get necessary node parameters + */ + var referenceNode = node.parentNode; + var parentNode = shadowRootExists && referenceNode instanceof ShadowRoot ? referenceNode.host : referenceNode; + var nodeWinOffset = node.getBoundingClientRect(); + var parentWinOffset = parentNode.getBoundingClientRect(); + var parentComputedStyle = getComputedStyle(parentNode); + + this._parent = { + node: parentNode, + styles: { + position: parentNode.style.position + }, + offsetHeight: parentNode.offsetHeight + }; + this._offsetToWindow = { + left: nodeWinOffset.left, + right: document.documentElement.clientWidth - nodeWinOffset.right + }; + this._offsetToParent = { + top: nodeWinOffset.top - parentWinOffset.top - parseNumeric(parentComputedStyle.borderTopWidth), + left: nodeWinOffset.left - parentWinOffset.left - parseNumeric(parentComputedStyle.borderLeftWidth), + right: -nodeWinOffset.right + parentWinOffset.right - parseNumeric(parentComputedStyle.borderRightWidth) + }; + this._styles = { + position: originalPosition, + top: node.style.top, + bottom: node.style.bottom, + left: node.style.left, + right: node.style.right, + width: node.style.width, + marginTop: node.style.marginTop, + marginLeft: node.style.marginLeft, + marginRight: node.style.marginRight + }; + + var nodeTopValue = parseNumeric(nodeComputedProps.top); + this._limits = { + start: nodeWinOffset.top + window.pageYOffset - nodeTopValue, + end: parentWinOffset.top + window.pageYOffset + parentNode.offsetHeight - parseNumeric(parentComputedStyle.borderBottomWidth) - node.offsetHeight - nodeTopValue - parseNumeric(nodeComputedProps.marginBottom) + }; + + /* + * 5. Ensure that the node will be positioned relatively to the parent node + */ + var parentPosition = parentComputedStyle.position; + + if (parentPosition != 'absolute' && parentPosition != 'relative') { + parentNode.style.position = 'relative'; + } + + /* + * 6. Recalc node position. + * It’s important to do this before clone injection to avoid scrolling bug in Chrome. + */ + this._recalcPosition(); + + /* + * 7. Create a clone + */ + var clone = this._clone = {}; + clone.node = document.createElement('div'); + + // Apply styles to the clone + extend(clone.node.style, { + width: nodeWinOffset.right - nodeWinOffset.left + 'px', + height: nodeWinOffset.bottom - nodeWinOffset.top + 'px', + marginTop: nodeComputedProps.marginTop, + marginBottom: nodeComputedProps.marginBottom, + marginLeft: nodeComputedProps.marginLeft, + marginRight: nodeComputedProps.marginRight, + cssFloat: nodeComputedProps.cssFloat, + padding: 0, + border: 0, + borderSpacing: 0, + fontSize: '1em', + position: 'static' + }); + + referenceNode.insertBefore(clone.node, node); + clone.docOffsetTop = getDocOffsetTop(clone.node); + } + }, { + key: '_recalcPosition', + value: function _recalcPosition() { + if (!this._active || this._removed) return; + + var stickyMode = scroll.top <= this._limits.start ? 'start' : scroll.top >= this._limits.end ? 'end' : 'middle'; + + if (this._stickyMode == stickyMode) return; + + switch (stickyMode) { + case 'start': + extend(this._node.style, { + position: 'absolute', + left: this._offsetToParent.left + 'px', + right: this._offsetToParent.right + 'px', + top: this._offsetToParent.top + 'px', + bottom: 'auto', + width: 'auto', + marginLeft: 0, + marginRight: 0, + marginTop: 0 + }); + break; + + case 'middle': + extend(this._node.style, { + position: 'fixed', + left: this._offsetToWindow.left + 'px', + right: this._offsetToWindow.right + 'px', + top: this._styles.top, + bottom: 'auto', + width: 'auto', + marginLeft: 0, + marginRight: 0, + marginTop: 0 + }); + break; + + case 'end': + extend(this._node.style, { + position: 'absolute', + left: this._offsetToParent.left + 'px', + right: this._offsetToParent.right + 'px', + top: 'auto', + bottom: 0, + width: 'auto', + marginLeft: 0, + marginRight: 0 + }); + break; + } + + this._stickyMode = stickyMode; + } + }, { + key: '_fastCheck', + value: function _fastCheck() { + if (!this._active || this._removed) return; + + if (Math.abs(getDocOffsetTop(this._clone.node) - this._clone.docOffsetTop) > 1 || Math.abs(this._parent.node.offsetHeight - this._parent.offsetHeight) > 1) this.refresh(); + } + }, { + key: '_deactivate', + value: function _deactivate() { + var _this = this; + + if (!this._active || this._removed) return; + + this._clone.node.parentNode.removeChild(this._clone.node); + delete this._clone; + + extend(this._node.style, this._styles); + delete this._styles; + + // Check whether element’s parent node is used by other stickies. + // If not, restore parent node’s styles. + if (!stickies.some(function (sticky) { + return sticky !== _this && sticky._parent && sticky._parent.node === _this._parent.node; + })) { + extend(this._parent.node.style, this._parent.styles); + } + delete this._parent; + + this._stickyMode = null; + this._active = false; + + delete this._offsetToWindow; + delete this._offsetToParent; + delete this._limits; + } + }, { + key: 'remove', + value: function remove() { + var _this2 = this; + + this._deactivate(); + + stickies.some(function (sticky, index) { + if (sticky._node === _this2._node) { + stickies.splice(index, 1); + return true; + } + }); + + this._removed = true; + } + }]); + + return Sticky; + }(); + + /* + * 5. Stickyfill API + */ + + + var Stickyfill = { + stickies: stickies, + Sticky: Sticky, + + forceSticky: function forceSticky() { + seppuku = false; + init(); + + this.refreshAll(); + }, + addOne: function addOne(node) { + // Check whether it’s a node + if (!(node instanceof HTMLElement)) { + // Maybe it’s a node list of some sort? + // Take first node from the list then + if (node.length && node[0]) node = node[0];else return; + } + + // Check if Stickyfill is already applied to the node + // and return existing sticky + for (var i = 0; i < stickies.length; i++) { + if (stickies[i]._node === node) return stickies[i]; + } + + // Create and return new sticky + return new Sticky(node); + }, + add: function add(nodeList) { + // If it’s a node make an array of one node + if (nodeList instanceof HTMLElement) nodeList = [nodeList]; + // Check if the argument is an iterable of some sort + if (!nodeList.length) return; + + // Add every element as a sticky and return an array of created Sticky instances + var addedStickies = []; + + var _loop = function _loop(i) { + var node = nodeList[i]; + + // If it’s not an HTMLElement – create an empty element to preserve 1-to-1 + // correlation with input list + if (!(node instanceof HTMLElement)) { + addedStickies.push(void 0); + return 'continue'; + } + + // If Stickyfill is already applied to the node + // add existing sticky + if (stickies.some(function (sticky) { + if (sticky._node === node) { + addedStickies.push(sticky); + return true; + } + })) return 'continue'; + + // Create and add new sticky + addedStickies.push(new Sticky(node)); + }; + + for (var i = 0; i < nodeList.length; i++) { + var _ret2 = _loop(i); + + if (_ret2 === 'continue') continue; + } + + return addedStickies; + }, + refreshAll: function refreshAll() { + stickies.forEach(function (sticky) { + return sticky.refresh(); + }); + }, + removeOne: function removeOne(node) { + // Check whether it’s a node + if (!(node instanceof HTMLElement)) { + // Maybe it’s a node list of some sort? + // Take first node from the list then + if (node.length && node[0]) node = node[0];else return; + } + + // Remove the stickies bound to the nodes in the list + stickies.some(function (sticky) { + if (sticky._node === node) { + sticky.remove(); + return true; + } + }); + }, + remove: function remove(nodeList) { + // If it’s a node make an array of one node + if (nodeList instanceof HTMLElement) nodeList = [nodeList]; + // Check if the argument is an iterable of some sort + if (!nodeList.length) return; + + // Remove the stickies bound to the nodes in the list + + var _loop2 = function _loop2(i) { + var node = nodeList[i]; + + stickies.some(function (sticky) { + if (sticky._node === node) { + sticky.remove(); + return true; + } + }); + }; + + for (var i = 0; i < nodeList.length; i++) { + _loop2(i); + } + }, + removeAll: function removeAll() { + while (stickies.length) { + stickies[0].remove(); + } + } + }; + + /* + * 6. Setup events (unless the polyfill was disabled) + */ + function init() { + if (isInitialized) { + return; + } + + isInitialized = true; + + // Watch for scroll position changes and trigger recalc/refresh if needed + function checkScroll() { + if (window.pageXOffset != scroll.left) { + scroll.top = window.pageYOffset; + scroll.left = window.pageXOffset; + + Stickyfill.refreshAll(); + } else if (window.pageYOffset != scroll.top) { + scroll.top = window.pageYOffset; + scroll.left = window.pageXOffset; + + // recalc position for all stickies + stickies.forEach(function (sticky) { + return sticky._recalcPosition(); + }); + } + } + + checkScroll(); + window.addEventListener('scroll', checkScroll); + + // Watch for window resizes and device orientation changes and trigger refresh + window.addEventListener('resize', Stickyfill.refreshAll); + window.addEventListener('orientationchange', Stickyfill.refreshAll); + + //Fast dirty check for layout changes every 500ms + var fastCheckTimer = void 0; + + function startFastCheckTimer() { + fastCheckTimer = setInterval(function () { + stickies.forEach(function (sticky) { + return sticky._fastCheck(); + }); + }, 500); + } + + function stopFastCheckTimer() { + clearInterval(fastCheckTimer); + } + + var docHiddenKey = void 0; + var visibilityChangeEventName = void 0; + + if ('hidden' in document) { + docHiddenKey = 'hidden'; + visibilityChangeEventName = 'visibilitychange'; + } else if ('webkitHidden' in document) { + docHiddenKey = 'webkitHidden'; + visibilityChangeEventName = 'webkitvisibilitychange'; + } + + if (visibilityChangeEventName) { + if (!document[docHiddenKey]) startFastCheckTimer(); + + document.addEventListener(visibilityChangeEventName, function () { + if (document[docHiddenKey]) { + stopFastCheckTimer(); + } else { + startFastCheckTimer(); + } + }); + } else startFastCheckTimer(); + } + + if (!seppuku) init(); + + /* + * 7. Expose Stickyfill + */ + if (typeof module != 'undefined' && module.exports) { + module.exports = Stickyfill; + } else if (isWindowDefined) { + window.Stickyfill = Stickyfill; + } + +})(window, document); diff --git a/docs/api/rest_api_reference/assets/js/toc.js b/docs/api/rest_api_reference/assets/js/toc.js new file mode 100644 index 0000000000..3a04fc4739 --- /dev/null +++ b/docs/api/rest_api_reference/assets/js/toc.js @@ -0,0 +1,62 @@ +$(function () { + const tocRootPositions = document.querySelectorAll('#toc > .navbar-nav > li > .nav-link'); + + tocRootPositions.forEach(rootPosition => { + const arrow = document.createElement('i'); + arrow.classList.add('material-icons', 'nav__link--toggler'); + arrow.innerHTML = 'keyboard_arrow_down'; + + rootPosition.append(arrow); + }); + + const navToggler = document.querySelectorAll('.nav__link--toggler'); + navToggler.forEach(toggler => { + toggler.addEventListener('click', event => { + event.preventDefault(); + + const parentAnchor = event.target.parentNode; + const subMenu = parentAnchor.nextElementSibling; + + if (!event.target.classList.contains('toggler--rotated') + && parentAnchor.classList.contains('active')) { + event.target.classList.toggle('toggler--rotated-0'); + event.target.classList.remove('toggler--rotated'); + } else if(event.target.classList.contains('toggler--rotated') + && parentAnchor.classList.contains('active')) { + event.target.classList.add('toggler--rotated-0'); + event.target.classList.remove('toggler--rotated'); + } else { + event.target.classList.toggle('toggler--rotated'); + } + + if (parentAnchor.classList.contains('active')) { + subMenu.classList.remove('d-block'); + subMenu.classList.toggle('d-none'); + } else { + subMenu.classList.remove('d-none'); + subMenu.classList.toggle('d-block'); + } + }); + }); + + $(window).on('activate.bs.scrollspy', function (e, obj) { + const navBar = document.querySelector('.sidebar__nav'); + const link = navBar.querySelector(`.nav-link[href="${obj.relatedTarget}"]`); + const activeRootNode = navBar.querySelector('.nav-link.active'); + + activeRootNode.childNodes.forEach(childNode => { + if (typeof childNode.classList !== "undefined" + && childNode.classList.contains('nav__link--toggler')) { + childNode.classList.remove('toggler--rotated-0'); + } + }); + + if (activeRootNode.nextElementSibling.classList.contains('d-none')) { + activeRootNode.nextElementSibling.classList.remove('d-none'); + } + + navBar.scrollTop = link.offsetTop; + }); + + $('body').scrollspy({ target: '#toc' }); +}) diff --git a/docs/api/rest_api_reference/favicon.ico b/docs/api/rest_api_reference/favicon.ico new file mode 100644 index 0000000000..80d6b5f199 Binary files /dev/null and b/docs/api/rest_api_reference/favicon.ico differ diff --git a/docs/api/rest_api_reference/input/.editorconfig b/docs/api/rest_api_reference/input/.editorconfig new file mode 100644 index 0000000000..3f64c2264d --- /dev/null +++ b/docs/api/rest_api_reference/input/.editorconfig @@ -0,0 +1,4 @@ +root = true +[*.raml] +indent_size = 4 +indent_style = space diff --git a/docs/api/rest_api_reference/input/examples/GET/Root.json.example b/docs/api/rest_api_reference/input/examples/GET/Root.json.example new file mode 100644 index 0000000000..5ea998214f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/GET/Root.json.example @@ -0,0 +1,93 @@ +{ + "Root": { + "_media-type": "application/vnd.ez.api.Root+json", + "content": { + "_href": "/api/ezp/v2/content/objects", + "_media-type": "" + }, + "contentByRemoteId": { + "_href": "/api/ezp/v2/content/objects{?remoteId}", + "_media-type": "" + }, + "contentTypeByIdentifier": { + "_href": "/api/ezp/v2/content/types{?identifier}", + "_media-type": "" + }, + "contentTypeGroupByIdentifier": { + "_href": "/api/ezp/v2/content/typegroups{?identifier}", + "_media-type": "" + }, + "contentTypeGroups": { + "_href": "/api/ezp/v2/content/typegroups", + "_media-type": "application/vnd.ez.api.ContentTypeGroupList+json" + }, + "contentTypes": { + "_href": "/api/ezp/v2/content/types", + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json" + }, + "createSession": { + "_href": "/api/ezp/v2/user/sessions", + "_media-type": "application/vnd.ez.api.UserSession+json" + }, + "globalUrlAliases": { + "_href": "/api/ezp/v2/content/urlaliases", + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json" + }, + "locationByPath": { + "_href": "/api/ezp/v2/content/locations{?locationPath}", + "_media-type": "" + }, + "locationByRemoteId": { + "_href": "/api/ezp/v2/content/locations{?remoteId}", + "_media-type": "" + }, + "objectStateGroups": { + "_href": "/api/ezp/v2/content/objectstategroups", + "_media-type": "application/vnd.ez.api.ObjectStateGroupList+json" + }, + "objectStates": { + "_href": "/api/ezp/v2/content/objectstategroups/{objectStateGroupId}/objectstates", + "_media-type": "application/vnd.ez.api.ObjectStateList+json" + }, + "roles": { + "_href": "/api/ezp/v2/user/roles", + "_media-type": "application/vnd.ez.api.RoleList+json" + }, + "rootLocation": { + "_href": "/api/ezp/v2/content/locations/1/2", + "_media-type": "application/vnd.ez.api.Location+json" + }, + "rootMediaFolder": { + "_href": "/api/ezp/v2/content/locations/1/43", + "_media-type": "application/vnd.ez.api.Location+json" + }, + "rootUserGroup": { + "_href": "/api/ezp/v2/user/groups/1/5", + "_media-type": "application/vnd.ez.api.UserGroup+json" + }, + "sections": { + "_href": "/api/ezp/v2/content/sections", + "_media-type": "application/vnd.ez.api.SectionList+json" + }, + "trash": { + "_href": "/api/ezp/v2/content/trash", + "_media-type": "application/vnd.ez.api.Trash+json" + }, + "urlWildcards": { + "_href": "/api/ezp/v2/content/urlwildcards", + "_media-type": "application/vnd.ez.api.UrlWildcardList+json" + }, + "users": { + "_href": "/api/ezp/v2/user/users", + "_media-type": "application/vnd.ez.api.UserRefList+json" + }, + "views": { + "_href": "/api/ezp/v2/views", + "_media-type": "application/vnd.ez.api.RefList+json" + }, + "refreshSession": { + "_media-type": "application\/vnd.ez.api.UserSession+json", + "_href": "\/api\/ezp\/v2\/user\/sessions\/{sessionId}\/refresh" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/GET/Root.xml.example b/docs/api/rest_api_reference/input/examples/GET/Root.xml.example new file mode 100644 index 0000000000..03223be248 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/GET/Root.xml.example @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.json.example b/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.json.example new file mode 100644 index 0000000000..793075ff3b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.json.example @@ -0,0 +1,254 @@ +{ + "BookmarkList": { + "_media-type": "application/vnd.ez.api.BookmarkList+json", + "count": 3, + "items": [ + { + "_media-type": "application/vnd.ez.api.Bookmark+json", + "__href": "/api/ezp/v2/bookmark/65", + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/57/65", + "id": 65, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/65/", + "depth": 3, + "childCount": 0, + "remoteId": "aa538e305aea472eb221ce23d1cc4b50", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/57/65/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/63" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/57/65/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/63", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/63", + "_remoteId": "211e99934c8fef5900e4813b96ec5c87", + "_id": 63, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2" + }, + "Name": "Art3", + "TranslatedName": "Art3", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/63/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/63/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/63/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:34:17+00:00", + "publishedDate": "2021-06-28T11:34:17+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/63/objectstates" + } + } + } + } + }, + { + "_media-type": "application/vnd.ez.api.Bookmark+json", + "__href": "/api/ezp/v2/bookmark/68", + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/68", + "id": 68, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/58" + }, + "pathString": "/1/2/58/68/", + "depth": 3, + "childCount": 0, + "remoteId": "b8cc4627dbc3ca693c85b6b06a8f7492", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/68/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/67" + }, + "sortField": "PUBLISHED", + "sortOrder": "DESC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/68/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/67", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/67", + "_remoteId": "180adb876af5755d65c1a362fcd619c8", + "_id": 67, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/45" + }, + "Name": "Tip1", + "TranslatedName": "Tip1", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/67/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/67/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/67/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:36:21+00:00", + "publishedDate": "2021-06-28T11:36:21+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/67/objectstates" + } + } + } + } + }, + { + "_media-type": "application/vnd.ez.api.Bookmark+json", + "__href": "/api/ezp/v2/bookmark/59", + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/59", + "id": 59, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "pathString": "/1/2/59/", + "depth": 2, + "childCount": 3, + "remoteId": "fd949fc2eed1fff847d73021ff1a6ea9", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/58" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/58", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/58", + "_remoteId": "00b513aae54036a16e00fb3365c4b5f3", + "_id": 58, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "Dog Breed Catalog", + "TranslatedName": "Dog Breed Catalog", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/58/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/58/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/58/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:23:10+00:00", + "publishedDate": "2021-06-28T11:23:10+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/58/objectstates" + } + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.xml.example b/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.xml.example new file mode 100644 index 0000000000..bade5477ca --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/bookmark/GET/BookmarkList.xml.example @@ -0,0 +1,121 @@ + + + 3 + + + 65 + 0 + false + false + false + + /1/2/57/65/ + 3 + 0 + aa538e305aea472eb221ce23d1cc4b50 + + + PATH + ASC + + + + + Art3 + Art3 + + +
    + + + 2021-06-28T11:34:17+00:00 + 2021-06-28T11:34:17+00:00 + eng-GB + 1 + false + false + PUBLISHED + + + + + + + + 68 + 0 + false + false + false + + /1/2/58/68/ + 3 + 0 + b8cc4627dbc3ca693c85b6b06a8f7492 + + + PUBLISHED + DESC + + + + + Tip1 + Tip1 + + +
    + + + 2021-06-28T11:36:21+00:00 + 2021-06-28T11:36:21+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + + + + + 59 + 0 + false + false + false + + /1/2/59/ + 2 + 3 + fd949fc2eed1fff847d73021ff1a6ea9 + + + PATH + ASC + + + + + Dog Breed Catalog + Dog Breed Catalog + + +
    + + + 2021-06-28T11:23:10+00:00 + 2021-06-28T11:23:10+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.json.example b/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.json.example new file mode 100644 index 0000000000..14ec3aec27 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.json.example @@ -0,0 +1,8 @@ +{ + "EventList": { + "_media-type": "application/vnd.ez.api.EventList+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597104000&end=1598745600&count=10", + "totalCount": 5, + "events": [] + } +} diff --git a/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.xml.example b/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.xml.example new file mode 100644 index 0000000000..88a440a64b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/calendar/event/GET/ContentInfo.xml.example @@ -0,0 +1,4 @@ + + + 5 + diff --git a/docs/api/rest_api_reference/input/examples/calendar/event/eventType/POST/UnscheduleActionInput.json.example b/docs/api/rest_api_reference/input/examples/calendar/event/eventType/POST/UnscheduleActionInput.json.example new file mode 100644 index 0000000000..c1222a2355 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/calendar/event/eventType/POST/UnscheduleActionInput.json.example @@ -0,0 +1,7 @@ +{ + "UnscheduleAction": { + "events": [ + "future_publication:2" + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.json.example b/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.json.example new file mode 100644 index 0000000000..df715b3adf --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.json.example @@ -0,0 +1,216 @@ +{ + "EventGroupList": { + "_media-type": "application/vnd.ez.api.EventGroupList+json", + "groups": [ + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597104000&end=1597190400&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597104000", + "endDate": "1597190400" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597190400&end=1597276800&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597190400", + "endDate": "1597276800" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597276800&end=1597363200&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597276800", + "endDate": "1597363200" + }, + "totalCount": 1, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597363200&end=1597449600&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597363200", + "endDate": "1597449600" + }, + "totalCount": 1, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597449600&end=1597536000&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597449600", + "endDate": "1597536000" + }, + "totalCount": 1, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597536000&end=1597622400&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597536000", + "endDate": "1597622400" + }, + "totalCount": 1, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597622400&end=1597708800&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597622400", + "endDate": "1597708800" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597708800&end=1597795200&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597708800", + "endDate": "1597795200" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597795200&end=1597881600&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597795200", + "endDate": "1597881600" + }, + "totalCount": 1, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597881600&end=1597968000&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597881600", + "endDate": "1597968000" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1597968000&end=1598054400&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1597968000", + "endDate": "1598054400" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598054400&end=1598140800&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598054400", + "endDate": "1598140800" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598140800&end=1598227200&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598140800", + "endDate": "1598227200" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598227200&end=1598313600&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598227200", + "endDate": "1598313600" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598313600&end=1598400000&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598313600", + "endDate": "1598400000" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598400000&end=1598486400&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598400000", + "endDate": "1598486400" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598486400&end=1598572800&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598486400", + "endDate": "1598572800" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598572800&end=1598659200&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598572800", + "endDate": "1598659200" + }, + "totalCount": 0, + "events": [] + }, + { + "_media-type": "application/vnd.ez.api.EventGroup+json", + "_currentPage": "/api/ezp/v2/calendar/event?start=1598659200&end=1598745600&count=10", + "DateRange": { + "_media-type": "application/vnd.ez.api.DateRange+json", + "startDate": "1598659200", + "endDate": "1598745600" + }, + "totalCount": 0, + "events": [] + } + ] + } +} diff --git a/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.xml.example b/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.xml.example new file mode 100644 index 0000000000..20c04aab28 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/calendar/event/grouped-by-day/GET/ContentInfo.xml.example @@ -0,0 +1,136 @@ + + + + + 1597104000 + 1597190400 + + 0 + + + + 1597190400 + 1597276800 + + 0 + + + + 1597276800 + 1597363200 + + 1 + + + + 1597363200 + 1597449600 + + 1 + + + + 1597449600 + 1597536000 + + 1 + + + + 1597536000 + 1597622400 + + 1 + + + + 1597622400 + 1597708800 + + 0 + + + + 1597708800 + 1597795200 + + 0 + + + + 1597795200 + 1597881600 + + 1 + + + + 1597881600 + 1597968000 + + 0 + + + + 1597968000 + 1598054400 + + 0 + + + + 1598054400 + 1598140800 + + 0 + + + + 1598140800 + 1598227200 + + 0 + + + + 1598227200 + 1598313600 + + 0 + + + + 1598313600 + 1598400000 + + 0 + + + + 1598400000 + 1598486400 + + 0 + + + + 1598486400 + 1598572800 + + 0 + + + + 1598572800 + 1598659200 + + 0 + + + + 1598659200 + 1598745600 + + 0 + + diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/Basket.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/Basket.json.example new file mode 100644 index 0000000000..aa8f68f085 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/Basket.json.example @@ -0,0 +1,212 @@ +{ + "Basket": { + "_media-type": "application\/vnd.ez.api.Basket+json", + "basketId": 1, + "originId": null, + "erpOrderId": null, + "guid": null, + "state": "new", + "type": "basket", + "erpFailCounter": null, + "erpFailErrorLog": null, + "sessionId": null, + "userId": 14, + "basketName": null, + "invoiceParty": null, + "deliveryParty": null, + "buyerParty": null, + "remark": null, + "dateCreated": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 10:33:26" + }, + "dateLastModified": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 10:35:22" + }, + "shop": "MAIN", + "requirePriceUpdate": false, + "totals": { + "lines": { + "_media-type": "application\/vnd.ez.api.BasketTotals+json", + "name": "lines", + "totalNet": 0, + "totalGross": 0, + "vatList": { + "19": 0 + }, + "groupType": "order", + "currency": "EUR" + }, + "additionalLines": { + "_media-type": "application\/vnd.ez.api.BasketTotals+json", + "name": "additionalLines", + "totalNet": 0, + "totalGross": 0, + "vatList": {}, + "groupType": "order", + "currency": "EUR" + } + }, + "totalsSum": { + "_media-type": "application\/vnd.ez.api.BasketTotals+json", + "name": "", + "totalNet": 0, + "totalGross": 0, + "vatList": { + "19": 0 + }, + "groupType": "order", + "currency": "EUR" + }, + "currency": "EUR", + "totalsSumNet": 0, + "totalsSumGross": 0, + "additionalLines": {}, + "lines": [ + { + "_media-type": "application\/vnd.ez.api.BasketLine+json", + "basketLineId": 2, + "lineNumber": 1, + "sku": "test", + "variantCode": null, + "productType": "OrderableProductNode", + "quantity": 9, + "unit": null, + "price": 0, + "priceNet": 0, + "priceGross": 0, + "linePriceAmountNet": 0, + "linePriceAmountGross": 0, + "vat": 19, + "isIncVat": true, + "currency": "EUR", + "catalogElement": { + "_media-type": "application\/vnd.ez.api.OrderableProductNode+json", + "seoUrl": "\/product-catalog\/test", + "permanentUrl": "\/Products\/test", + "dataMap": { + "discontinued": { + "_media-type": "application\/vnd.ez.api.BoolField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\BoolField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + } + }, + "subtitle": null, + "displayInSearch": false, + "displayInProductList": false, + "sku": "test", + "manufacturerSku": "", + "ean": "", + "type": "", + "isOrderable": null, + "price": { + "_media-type": "application\/vnd.ez.api.PriceField+json", + "price": { + "_media-type": "application\/vnd.ez.api.Price+json", + "price": 0, + "priceInclVat": 0, + "priceExclVat": 0, + "isVatPrice": true, + "vatPercent": 19, + "currency": "EUR", + "discount": null, + "timestamp": "1615545324", + "campaign": null, + "source": "eZ", + "quantityDiscount": null + } + }, + "customerPrice": null, + "scaledPrices": null, + "stock": { + "_media-type": "application\/vnd.ez.api.StockField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\StockField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + }, + "shortDescription": { + "_media-type": "application\/vnd.ez.api.TextBlockField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\TextBlockField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + }, + "longDescription": { + "_media-type": "application\/vnd.ez.api.TextBlockField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\TextBlockField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + }, + "specifications": null, + "imageList": null, + "minOrderQuantity": null, + "maxOrderQuantity": null, + "allowedQuantity": "#^[0-9]+$#", + "packagingUnit": null, + "unit": "", + "vatCode": "VATNORMAL", + "name": "test", + "section": null, + "text": "test", + "image": { + "_media-type": "application\/vnd.ez.api.ImageField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\ImageField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + }, + "path": null, + "url": "\/product-catalog\/test", + "parentElementIdentifier": "126", + "identifier": "130", + "cacheIdentifier": null + }, + "groupCalc": null, + "groupOrder": null, + "remark": null, + "remoteDataMap": { + "lineRemark": null, + "scaledPrices": null, + "special_offers": { + "0": "Buy 3 pay 2", + "1": "Customer price" + }, + "stockSign": "", + "stock": { + "_media-type": "application\/vnd.ez.api.StockField+json", + "_exception": { + "Name": "EzSystems\\EzPlatformRest\\Output\\Exceptions\\NoVisitorFoundException", + "Message": "No visitor found for Silversolutions\\Bundle\\EshopBundle\\Content\\Fields\\StockField, Silversolutions\\Bundle\\EshopBundle\\Content\\AbstractField, Silversolutions\\Bundle\\EshopBundle\\Content\\ValueObject." + } + }, + "isPriceValid": true + }, + "variantCharacteristics": null, + "assignedLines": null + } + ], + "dateLastPriceCalculation": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 10:35:22" + }, + "shippingMethod": null, + "paymentMethod": null, + "paymentTransactionId": null, + "confirmationEmail": null, + "salesConfirmationEmail": null, + "allProductsAvailable": false, + "dataMap": { + "pricesValid": true, + "priceResponseSourceType": "ShopMaster", + "dateLastCatalogUpdate": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 10:35:22" + } + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/POST/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/POST/ValidationResponse.json.example new file mode 100644 index 0000000000..85bc9422bd --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/POST/ValidationResponse.json.example @@ -0,0 +1,10 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": { + "_error": [ + "msg.duplicate_basket_name_not_allowed" + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/GET/Basket.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/GET/Basket.json.example new file mode 100644 index 0000000000..d9dac11cdc --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/GET/Basket.json.example @@ -0,0 +1,50 @@ +{ + "Basket": { + "_media-type": "application\/vnd.ez.api.Basket+json", + "basketId": 7, + "originId": null, + "erpOrderId": null, + "guid": null, + "state": "new", + "type": "storedBasket", + "erpFailCounter": null, + "erpFailErrorLog": null, + "sessionId": "sbnla08g8seeoua51v46jns3vc", + "userId": 14, + "basketName": "foo", + "invoiceParty": null, + "deliveryParty": null, + "buyerParty": null, + "remark": null, + "dateCreated": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 07:40:00" + }, + "dateLastModified": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 07:40:00" + }, + "shop": null, + "requirePriceUpdate": false, + "totals": null, + "totalsSum": null, + "currency": null, + "totalsSumNet": null, + "totalsSumGross": null, + "additionalLines": null, + "lines": [], + "dateLastPriceCalculation": null, + "shippingMethod": null, + "paymentMethod": null, + "paymentTransactionId": null, + "confirmationEmail": null, + "salesConfirmationEmail": null, + "allProductsAvailable": true, + "dataMap": { + "dateLastCatalogUpdate": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "string": "2021-03-12 07:40:00" + } + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/_mode_/POST/BasketHeaderData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/_mode_/POST/BasketHeaderData.json.example new file mode 100644 index 0000000000..107cd23eee --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/_mode_/POST/BasketHeaderData.json.example @@ -0,0 +1,5 @@ +{ + "BasketHeaderData": { + "value": "foobar" + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/POST/BasketLinesData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/POST/BasketLinesData.json.example new file mode 100644 index 0000000000..5efe1bde55 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/POST/BasketLinesData.json.example @@ -0,0 +1,11 @@ +{ + "BasketLinesData": [ + { + "basketLineId": 1, + "lineNumber": 1, + "sku": "test", + "quantity": "3", + "dataMap": [] + } + ] +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/_line_id_/DELETE/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/_line_id_/DELETE/ValidationResponse.json.example new file mode 100644 index 0000000000..03a1d79e02 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/_id_/line/_line_id_/DELETE/ValidationResponse.json.example @@ -0,0 +1,10 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": { + "_error": [ + "msg.invalid_basket_line_data" + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/copyfrom/_basket_id_/POST/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/copyfrom/_basket_id_/POST/ValidationResponse.json.example new file mode 100644 index 0000000000..b0cc7414b6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/copyfrom/_basket_id_/POST/ValidationResponse.json.example @@ -0,0 +1,10 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": { + "_error": [ + "Access denied" + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/BasketLineData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/BasketLineData.json.example new file mode 100644 index 0000000000..26d869de03 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/BasketLineData.json.example @@ -0,0 +1,10 @@ +{ + "BasketLineData": [ + { + "sku":"1000", + "quantity":"3", + "isVariant":"0", + "variantCode":"" + } + ] +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/ValidationResponse.json.example new file mode 100644 index 0000000000..b0c479671e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/lines/POST/ValidationResponse.json.example @@ -0,0 +1,10 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": { + "_error": [ + "Product with the sku: foo not found" + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/party/invoice/PATCH/PartyInvoice.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/party/invoice/PATCH/PartyInvoice.json.example new file mode 100644 index 0000000000..37a37a88cf --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/party/invoice/PATCH/PartyInvoice.json.example @@ -0,0 +1,58 @@ +{ + "PartyData": { + "_data-type":"invoice", + "_media-type":"application/vnd.ez.api.Party+json", + "PartyIdentification":[], + "PartyName":[ + { + "_media-type":"application/vnd.ez.api.PartyPartyName+json", + "Name":"Testerin" + }, + { + "_media-type":"application/vnd.ez.api.PartyPartyName+json", + "Name":"" + } + ], + "PostalAddress":{ + "_media-type":"application/vnd.ez.api.PartyPostalAddress+json", + "StreetName":"Teststr. 11", + "AdditionalStreetName":"", + "BuildingNumber":null, + "CityName":"Testingen", + "PostalZone":"1111", + "CountrySubentity":"Dummy", + "CountrySubentityCode":null, + "AddressLine":[], + "Country":{ + "_media-type":"application/vnd.ez.api.PartyPostalAddressCountry+json", + "IdentificationCode":"NO", + "Name":null + }, + "Department":null, + "SesExtension":{} + }, + "Contact":{ + "_media-type":"application/vnd.ez.api.Contact+json", + "ID":null, + "Name":null, + "Telephone":"", + "Telefax":null, + "ElectronicMail":"contact@silversolutions.de", + "OtherCommunication":null, + "Note":null, + "SesExtension":{} + }, + "Person":{ + "_media-type":"application/vnd.ez.api.PartyPerson+json", + "FirstName":null, + "FamilyName":null, + "Title":null, + "MiddleName":null, + "SesExtension":{} + }, + "SesExtension":{ + "forceInvoice":false, + "customer_type":"private" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/paymentmethod/PATCH/PaymentMethodData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/paymentmethod/PATCH/PaymentMethodData.json.example new file mode 100644 index 0000000000..59affaa2ba --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/paymentmethod/PATCH/PaymentMethodData.json.example @@ -0,0 +1,5 @@ +{ + "PaymentMethodData": { + "paymentMethod": "invoice" + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/shippingmethod/PATCH/ShippingMethodData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/shippingmethod/PATCH/ShippingMethodData.json.example new file mode 100644 index 0000000000..dd7960039b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/shippingmethod/PATCH/ShippingMethodData.json.example @@ -0,0 +1,5 @@ +{ + "ShippingMethodData": { + "shippingMethod": "mail" + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/current/voucher/PATCH/VoucherData.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/current/voucher/PATCH/VoucherData.json.example new file mode 100644 index 0000000000..9a31c3bee3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/current/voucher/PATCH/VoucherData.json.example @@ -0,0 +1,5 @@ +{ + "VoucherData": { + "voucherCode": "1234567" + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/deleteLine/PATCH/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/deleteLine/PATCH/ValidationResponse.json.example new file mode 100644 index 0000000000..02ad859bff --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/deleteLine/PATCH/ValidationResponse.json.example @@ -0,0 +1,6 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": [] + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basket/headers/GET/BasketListResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basket/headers/GET/BasketListResponse.json.example new file mode 100644 index 0000000000..c74ce50ce1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basket/headers/GET/BasketListResponse.json.example @@ -0,0 +1,45 @@ +{ + "BasketListResponse": { + "_media-type": "application\/vnd.ez.api.BasketListResponse+json", + "basketList": [ + { + "_media-type": "application\/vnd.ez.api.stdClass+json", + "basketId": 5, + "state": "new", + "type": "storedBasket", + "sessionId": "mrt02u8u2rbh6aeeibm7rj0i1j", + "userId": 14, + "basketName": "test", + "dateCreated": { + "_media-type": "application\/vnd.ez.api.DateTime+json" + }, + "dateLastModified": { + "_media-type": "application\/vnd.ez.api.DateTime+json" + }, + "totals": [ + { + "_media-type": "application\/vnd.ez.api.BasketTotals+json" + }, + { + "_media-type": "application\/vnd.ez.api.BasketTotals+json" + } + ], + "totalsSum": { + "_media-type": "application\/vnd.ez.api.BasketTotals+json" + }, + "dateLastPriceCalculation": { + "_media-type": "application\/vnd.ez.api.DateTime+json" + }, + "allProductsAvailable": true, + "dataMap": { + "dateLastCatalogUpdate": { + "_media-type": "application\/vnd.ez.api.DateTime+json", + "date": "2021-03-10 11:11:06.291444", + "timezone_type": 3, + "timezone": "UTC" + } + } + } + ] + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/basketline/_id_/_basketLineId_/DELETE/ValidationResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/basketline/_id_/_basketLineId_/DELETE/ValidationResponse.json.example new file mode 100644 index 0000000000..f1563e0ae6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/basketline/_id_/_basketLineId_/DELETE/ValidationResponse.json.example @@ -0,0 +1,14 @@ +{ + "ValidationResponse": { + "_media-type": "application\/vnd.ez.api.ValidationResponse+json", + "messages": { + "CustomerPrice": { + "_errors": [ + { + "_message": "msg.invalid_customer_price_request" + } + ] + } + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/checkout/payment-methods/GET/PaymentMethodDataResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/checkout/payment-methods/GET/PaymentMethodDataResponse.json.example new file mode 100644 index 0000000000..412050e53e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/checkout/payment-methods/GET/PaymentMethodDataResponse.json.example @@ -0,0 +1,10 @@ +{ + "PaymentMethodDataResponse": { + "_media-type": "application\/vnd.ez.api.PaymentMethodDataResponse+json", + "paymentMethods": { + "paypal": "paypal", + "invoice": "invoice" + }, + "defaultMethod": "invoice" + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/checkout/shipping-methods/GET/ShippingMethodDataResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/checkout/shipping-methods/GET/ShippingMethodDataResponse.json.example new file mode 100644 index 0000000000..e722968924 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/checkout/shipping-methods/GET/ShippingMethodDataResponse.json.example @@ -0,0 +1,11 @@ +{ + "ShippingMethodDataResponse": { + "_media-type": "application\/vnd.ez.api.ShippingMethodDataResponse+json", + "shippingMethods": { + "standard": "standard", + "expressDel": "express_delivery" + }, + "defaultMethod": "standard", + "shippingMethodsSorted": [] + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/country-selection/GET/CountrySelectionResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/country-selection/GET/CountrySelectionResponse.json.example new file mode 100644 index 0000000000..5d5a2e64a7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/country-selection/GET/CountrySelectionResponse.json.example @@ -0,0 +1,63 @@ +{ + "CountrySelectionResponse": { + "_media-type": "application\/vnd.ez.api.CountrySelectionResponse+json", + "countryOptions": { + "DZ": "Algeria", + "AU": "Australia", + "AT": "Austria", + "BE": "Belgium", + "BR": "Brazil", + "BG": "Bulgaria", + "CA": "Canada", + "CY": "Cyprus", + "CZ": "Czechia", + "DK": "Denmark", + "EE": "Estonia", + "SZ": "Eswatini", + "FI": "Finland", + "FR": "France", + "DE": "Germany", + "GR": "Greece", + "HU": "Hungary", + "IS": "Iceland", + "IN": "India", + "ID": "Indonesia", + "IE": "Ireland", + "IT": "Italy", + "KE": "Kenya", + "LV": "Latvia", + "LT": "Lithuania", + "MO": "Macao SAR China", + "MY": "Malaysia", + "MT": "Malta", + "MX": "Mexico", + "MZ": "Mozambique", + "NL": "Netherlands", + "NZ": "New Zealand", + "NG": "Nigeria", + "NO": "Norway", + "PH": "Philippines", + "PL": "Poland", + "PT": "Portugal", + "RO": "Romania", + "RU": "Russia", + "SG": "Singapore", + "SK": "Slovakia", + "SI": "Slovenia", + "ZA": "South Africa", + "ES": "Spain", + "SE": "Sweden", + "CH": "Switzerland", + "TZ": "Tanzania", + "TH": "Thailand", + "TN": "Tunisia", + "TR": "Turkey", + "UG": "Uganda", + "AE": "United Arab Emirates", + "GB": "United Kingdom", + "US": "United States" + }, + "defaultCountry": "DE", + "countryCodeStates": [] + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/customer/addresses/shipping/GET/ShippingAddressesResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/customer/addresses/shipping/GET/ShippingAddressesResponse.json.example new file mode 100644 index 0000000000..b69fd5a3c6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/customer/addresses/shipping/GET/ShippingAddressesResponse.json.example @@ -0,0 +1,50 @@ +{ + "ShippingAddressesResponse": { + "_media-type": "application\/vnd.ez.api.ShippingAddressesResponse+json", + "Parties": [ + { + "_media-type": "application\/vnd.ez.api.Party+json", + "PartyIdentification": [], + "PartyName": [], + "PostalAddress": { + "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json", + "StreetName": null, + "AdditionalStreetName": null, + "BuildingNumber": null, + "CityName": null, + "PostalZone": null, + "CountrySubentity": null, + "CountrySubentityCode": null, + "AddressLine": [], + "Country": { + "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json", + "IdentificationCode": null, + "Name": null + }, + "Department": null, + "SesExtension": {} + }, + "Contact": { + "_media-type": "application\/vnd.ez.api.Contact+json", + "ID": null, + "Name": null, + "Telephone": null, + "Telefax": null, + "ElectronicMail": null, + "OtherCommunication": null, + "Note": null, + "SesExtension": {} + }, + "Person": { + "_media-type": "application\/vnd.ez.api.PartyPerson+json", + "FirstName": null, + "FamilyName": null, + "Title": null, + "MiddleName": null, + "SesExtension": {} + }, + "SesExtension": {} + } + ] + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/CustomerPriceData.json.example b/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/CustomerPriceData.json.example new file mode 100644 index 0000000000..340bd148fb --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/CustomerPriceData.json.example @@ -0,0 +1,8 @@ +{ + "PriceResponse": { + "_media-type": "application\/vnd.ez.api.PriceResponse+json", + "priceResponse": { + "_media-type": "application\/vnd.ez.api.priceResponse+json" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/PriceResponse.json.example b/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/PriceResponse.json.example new file mode 100644 index 0000000000..340bd148fb --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/commerce/customerprice/POST/PriceResponse.json.example @@ -0,0 +1,8 @@ +{ + "PriceResponse": { + "_media-type": "application\/vnd.ez.api.PriceResponse+json", + "priceResponse": { + "_media-type": "application\/vnd.ez.api.priceResponse+json" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example b/docs/api/rest_api_reference/input/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example new file mode 100644 index 0000000000..23d1b81179 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example @@ -0,0 +1,15 @@ + + + https://images.unsplash.com/photo-1544568100-847a948585b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1MDk5MH0 + UtrE5DcgEyg + unsplash + + medium-coated brown dog during daytime + 5184 + 3888 + 2018-12-11T17:46:12-05:00 + 2020-10-23T23:08:44-04:00 + Jamie Street + https://unsplash.com/@jamie452?utm_source=Ibexa+Platform+DAM+Connector&utm_medium=referral&utm_campaign=api-credit + + diff --git a/docs/api/rest_api_reference/input/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example b/docs/api/rest_api_reference/input/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example new file mode 100644 index 0000000000..f0b6ae0739 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example @@ -0,0 +1,8 @@ + + + http://127.0.0.1:8000/var/site/storage/images/_aliases/large/6/1/2/0/216-1-eng-GB/ez-logo-small.png + image/png + 20 + 20 + 1709 + diff --git a/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.json.example b/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.json.example new file mode 100644 index 0000000000..b8c47a483a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/58", + "id": 58, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "pathString": "/1/2/58/", + "depth": 2, + "childCount": 3, + "remoteId": "0cfe62f27753448d79aaa8acd8d01699", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/57" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57", + "_remoteId": "782f5afa9d6587804daa911e88ff5bb9", + "_id": 57, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "All Tips", + "TranslatedName": "All Tips", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/57/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/57/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/57/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:22:52+00:00", + "publishedDate": "2021-06-28T11:22:52+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/57/objectstates" + } + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.xml.example new file mode 100644 index 0000000000..fa4dfd02d7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/GET/LocationList.xml.example @@ -0,0 +1,38 @@ + + + 58 + 0 + false + false + false + + /1/2/58/ + 2 + 3 + 0cfe62f27753448d79aaa8acd8d01699 + + + PATH + ASC + + + + + All Tips + All Tips + + +
    + + + 2021-06-28T11:22:52+00:00 + 2021-06-28T11:22:52+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.json.example b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.json.example new file mode 100644 index 0000000000..63f8ba8c60 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/59", + "id": 59, + "priority": 3, + "hidden": true, + "invisible": true, + "explicitlyHidden": true, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "pathString": "/1/2/59/", + "depth": 2, + "childCount": 5, + "remoteId": "remoteId-qwert999", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/58" + }, + "sortField": "NAME", + "sortOrder": "DESC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/58", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/58", + "_remoteId": "00b513aae54036a16e00fb3365c4b5f3", + "_id": 58, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "Dog Breed Catalog", + "TranslatedName": "Dog Breed Catalog", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/58/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/58/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/58/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:23:10+00:00", + "publishedDate": "2021-06-28T11:23:10+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/58/objectstates" + } + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.xml.example new file mode 100644 index 0000000000..50da4b6676 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/Location.xml.example @@ -0,0 +1,38 @@ + + + 58 + 0 + false + false + false + + /1/2/58/ + 2 + 3 + 0cfe62f27753448d79aaa8acd8d01699 + + + PATH + ASC + + + + + All Tips + All Tips + + +
    + + + 2021-06-28T11:22:52+00:00 + 2021-06-28T11:22:52+00:00 + eng-GB + 1 + true + false + PUBLISHED + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.json.example b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.json.example new file mode 100644 index 0000000000..e7a29a87ae --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.json.example @@ -0,0 +1,9 @@ +{ + "LocationUpdate": { + "priority": "3", + "hidden": true, + "remoteId": "remoteId-qwert999", + "sortField": "NAME", + "sortOrder": "DESC" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example new file mode 100644 index 0000000000..c4ffb31f0f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/location_id/PATCH/LocationUpdate.xml.example @@ -0,0 +1,8 @@ + + + 3 + true + remoteId-qwert999 + CLASS + DESC + diff --git a/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.json.example b/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.json.example new file mode 100644 index 0000000000..b8c47a483a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/58", + "id": 58, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "pathString": "/1/2/58/", + "depth": 2, + "childCount": 3, + "remoteId": "0cfe62f27753448d79aaa8acd8d01699", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/57" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/58/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57", + "_remoteId": "782f5afa9d6587804daa911e88ff5bb9", + "_id": 57, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "All Tips", + "TranslatedName": "All Tips", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/57/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/57/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/57/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:22:52+00:00", + "publishedDate": "2021-06-28T11:22:52+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/57/objectstates" + } + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.xml.example new file mode 100644 index 0000000000..5946dd9edd --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/path/GET/Location.xml.example @@ -0,0 +1,34 @@ + + + 61 + 0 + false + false + + /1/2/61/ + 2 + 0 + 2cfa66027e3806b113d994c9c26d3a66 + + + NAME + ASC + + + + + 666 + + +
    + + + 2018-11-09T14:49:52+01:00 + 2018-11-09T14:49:52+01:00 + eng-GB + 1 + false + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/locations/path/children/GET/LocationList.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/path/children/GET/LocationList.xml.example new file mode 100644 index 0000000000..4072bcb254 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/path/children/GET/LocationList.xml.example @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example b/docs/api/rest_api_reference/input/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example new file mode 100644 index 0000000000..751b7b3f83 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.json.example b/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.json.example new file mode 100644 index 0000000000..d8fac1d440 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.json.example @@ -0,0 +1,153 @@ +{ + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/57", + "_remoteId": "055e5ed41cba5e8ec81d8846a4d1c673", + "_id": 57, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2" + }, + "Name": "draft article", + "TranslatedName": "draft article", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/57/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/57/currentversion", + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/57/versions/1", + "VersionInfo": { + "id": 517, + "versionNo": 1, + "status": "DRAFT", + "modificationDate": "2021-08-11T08:59:50+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-08-11T08:59:50+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "draft article" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57" + } + }, + "Fields": { + "field": [ + { + "id": 264, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "draft article" + }, + { + "id": 265, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 266, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [] + }, + { + "id": 267, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    draft draft
    \n", + "xhtml5edit": "\n

    draft draft

    \n" + } + }, + { + "id": 268, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    draft draft draft
    \n", + "xhtml5edit": "\n

    draft draft draft

    \n" + } + }, + { + "id": 269, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 270, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezobjectrelation", + "fieldValue": { + "destinationContentId": null + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/57/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/57/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": true, + "isHidden": false, + "status": "DRAFT", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/57/objectstates" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.xml.example new file mode 100644 index 0000000000..b2d3cb50d3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/POST/Content.xml.example @@ -0,0 +1,104 @@ + + + + draft article + + + + + 547 + 1 + DRAFT + 2019-02-19T15:55:37+01:00 + + 2019-02-19T15:55:37+01:00 + eng-GB + eng-GB + + + eng-GB + + + + draft article + + + + + + 446 + title + eng-GB + ezstring + draft article + + + 447 + short_title + eng-GB + ezstring + + + + 448 + author + eng-GB + ezauthor + + + + 449 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>draft draft</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>draft draft</p></section> + + + + + 450 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>draft draft draft </para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>draft draft draft </p></section> + + + + + 451 + enable_comments + eng-GB + ezboolean + false + + + 452 + image + eng-GB + ezobjectrelation + + + + + + + + +
    + + + eng-GB + 1 + true + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.json.example b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.json.example new file mode 100644 index 0000000000..fe5916d690 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.json.example @@ -0,0 +1,48 @@ +{ + "ContentCreate": { + "ContentType": { + "_href": "/api/ezp/v2/content/types/2" + }, + "mainLanguageCode": "eng-GB", + "LocationCreate": { + "ParentLocation": { + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "priority": "0", + "hidden": "false", + "sortField": "PATH", + "sortOrder": "ASC" + }, + "Section": { + "_href": "/api/ezp/v2/content/sections/1" + }, + "alwaysAvailable": "true", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "draft article" + }, + { + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": + { + "xml": "

    draft draft

    " + } + }, + { + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "

    draft draft draft

    " + } + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.xml.example new file mode 100644 index 0000000000..73ddb91571 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentCreate.xml.example @@ -0,0 +1,36 @@ + + + + eng-GB + + + 0 + false + PATH + ASC + +
    + true + + + + title + eng-GB + draft article + + + intro + eng-GB + +

    draft draft

    ]]> + + + + body + eng-GB + +

    draft draft draft

    ]]> + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentInfo.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentInfo.xml.example new file mode 100644 index 0000000000..d897c47bd4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/POST/ContentInfo.xml.example @@ -0,0 +1,1213 @@ + + + + 1114 + 5 + PUBLISHED + 2019-03-16T11:19:51+01:00 + + 2019-03-16T11:18:45+01:00 + eng-GB + eng-GB,fre-FR,ger-DE,nor-NO + + + eng-GB + + + fre-FR + + + ger-DE + + + nor-NO + + + + Spanish seafood paella + Paella de fruits de mer espagnols + Spanische Meeresfrüchte Paella + Spansk sjømat paella + + + + + + 1041 + title + eng-GB + ezstring + Spanish seafood paella + + + 1056 + title + fre-FR + ezstring + Paella de fruits de mer espagnols + + + 1071 + title + ger-DE + ezstring + Spanische Meeresfrüchte Paella + + + 1086 + title + nor-NO + ezstring + Spansk sjømat paella + + + 1042 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>A real taste of Britain’s most acknowledged culinary export of a take-away food and early example of food fusion. A popular lunch meal that is conquering the world.</para><para> </para><ezembed xlink:href="ezcontent://196" view="embed" ezxhtml:class="ez-embed-type-image"><ezconfig><ezvalue key="size">medium</ezvalue></ezconfig></ezembed><ezembed xlink:href="ezcontent://70" view="embed"/></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>A real taste of Britain’s most acknowledged culinary export of a take-away food and early example of food fusion. A popular lunch meal that is conquering the world.</p><p> </p><div data-ezelement="ezembed" data-href="ezcontent://196" data-ezview="embed" class="ez-embed-type-image"><span data-ezelement="ezconfig"><span data-ezelement="ezvalue" data-ezvalue-key="size">medium</span></span></div><div data-ezelement="ezembed" data-href="ezcontent://70" data-ezview="embed"/></section> + + + + + 1057 + description + fre-FR + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> + <para> Un vrai avant-go&#xFB;t de l'exportation culinaire la plus reconnue en Grande-Bretagne d'un plat &#xE0; emporter et de + l'exemple pr&#xE9;coce de la fusion des aliments. Un repas de midi populaire qui conquiert le monde. + </para> +</section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> + <p> Un vrai avant-goût de l'exportation culinaire la plus reconnue en Grande-Bretagne d'un plat à emporter et de + l'exemple précoce de la fusion des aliments. Un repas de midi populaire qui conquiert le monde. + </p> +</section> + + + + + 1072 + description + ger-DE + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> + <para> Ein echter Geschmack von Gro&#xDF;britanniens anerkanntem kulinarischen Export von Speisen zum Mitnehmen und einem + fr&#xFC;hen Beispiel der Fusion von Lebensmitteln. Ein beliebtes Mittagessen, das die Welt erobert. + </para> +</section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> + <p> Ein echter Geschmack von Großbritanniens anerkanntem kulinarischen Export von Speisen zum Mitnehmen und einem + frühen Beispiel der Fusion von Lebensmitteln. Ein beliebtes Mittagessen, das die Welt erobert. + </p> +</section> + + + + + 1087 + description + nor-NO + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> + <para> En ekte smak av Storbritannias mest anerkjente kulinariske eksport av en take-away mat og tidlig eksempel p&#xE5; + matfusjon. Et popul&#xE6;rt lunsjm&#xE5;ltid som erobre verden. + </para> +</section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> + <p> En ekte smak av Storbritannias mest anerkjente kulinariske eksport av en take-away mat og tidlig eksempel på + matfusjon. Et populært lunsjmåltid som erobre verden. + </p> +</section> + + + + + 1043 + cooking_instructions + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <para>Make a seafood broth:</para> <para>For the Canadian lobster - In a medium pot, bring 8 cups of water to boil. Add one bay leaf, chopped onion and salt and pepper according to the weight of the Canadian lobster. Boiling time will be based on weight.</para> <para>Remove cooked Canadian lobster, strain broth and let sit off.</para> <para>For the mussels - Warm the butter or olive oil over medium-high heat in the pot (if using butter, let it melt completely). Add the minced onion and garlic. Sauté until the onions are translucent and the garlic is fragrant, 1 to 2 minutes. Pour the water (2 cups) and half cup of white wine, bring it to boil. Pour all of the mussels into the pot. Remove the opened mussels and discard any mussels that haven’t opened. Strain the broth and let sit off.</para> <para>Mix both broths and let them sit off.</para> <para>Saute the seafood:</para> <para>In a 16-inch paella pan, heat the oil just until it is hot. Sauté the shrimp, scallops, and calamari (if using) until just cooked through, about 2 minutes.</para> <para>Make the sofrito:</para> <para>Reduce the heat to medium and sauté the garlic. Add the tomato and pimenton and cook, stirring often, until the mixture has darkened to a deep burgundy and is thick like a compote, 5 to 10 min.</para> <para>Add the rice and cook:</para> <para>Bring the broth back to a simmer. Add the rice to the pan, and cook for 1 to 2 min. Raise the heat to medium-high. Pour in 7 cups of the seafood broth (reserve the rest) and stir or shake the pan to evenly distribute the rice. From this point on, do not stir the rice. Once broth is boiling, count 4 minutes.</para> <para>After the 4 minutes, reduce the heat to medium-low and count 10 minutes.</para> <para>Continue to simmer more gently, rotating the pan as necessary. Arrange the shrimp and Canadian lobster in the pan.</para> <para>Create the socarrat:</para> <para>Increase the heat to medium-high and, rotating the pan, cook for about 2 min., until the bottom layer of rice starts to caramelize, creating the socarrat. The rice will crackle, but if it starts to smell burned, remove the pan from the heat immediately.</para> <para>Let the paella rest:</para> <para>Remove the pan from the heat and let the paella rest for 5 min. to even the cooking and let the flavors meld.</para> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <p>Make a seafood broth:</p> <p>For the Canadian lobster - In a medium pot, bring 8 cups of water to boil. Add one bay leaf, chopped onion and salt and pepper according to the weight of the Canadian lobster. Boiling time will be based on weight.</p> <p>Remove cooked Canadian lobster, strain broth and let sit off.</p> <p>For the mussels - Warm the butter or olive oil over medium-high heat in the pot (if using butter, let it melt completely). Add the minced onion and garlic. Sauté until the onions are translucent and the garlic is fragrant, 1 to 2 minutes. Pour the water (2 cups) and half cup of white wine, bring it to boil. Pour all of the mussels into the pot. Remove the opened mussels and discard any mussels that haven’t opened. Strain the broth and let sit off.</p> <p>Mix both broths and let them sit off.</p> <p>Saute the seafood:</p> <p>In a 16-inch paella pan, heat the oil just until it is hot. Sauté the shrimp, scallops, and calamari (if using) until just cooked through, about 2 minutes.</p> <p>Make the sofrito:</p> <p>Reduce the heat to medium and sauté the garlic. Add the tomato and pimenton and cook, stirring often, until the mixture has darkened to a deep burgundy and is thick like a compote, 5 to 10 min.</p> <p>Add the rice and cook:</p> <p>Bring the broth back to a simmer. Add the rice to the pan, and cook for 1 to 2 min. Raise the heat to medium-high. Pour in 7 cups of the seafood broth (reserve the rest) and stir or shake the pan to evenly distribute the rice. From this point on, do not stir the rice. Once broth is boiling, count 4 minutes.</p> <p>After the 4 minutes, reduce the heat to medium-low and count 10 minutes.</p> <p>Continue to simmer more gently, rotating the pan as necessary. Arrange the shrimp and Canadian lobster in the pan.</p> <p>Create the socarrat:</p> <p>Increase the heat to medium-high and, rotating the pan, cook for about 2 min., until the bottom layer of rice starts to caramelize, creating the socarrat. The rice will crackle, but if it starts to smell burned, remove the pan from the heat immediately.</p> <p>Let the paella rest:</p> <p>Remove the pan from the heat and let the paella rest for 5 min. to even the cooking and let the flavors meld.</p> </section> + + + + + 1058 + cooking_instructions + fre-FR + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <para> Faire un bouillon de fruits de mer:</para> <para> Pour le homard canadien - Dans une casserole moyenne, porter &#xE0; &#xE9;bullition 8 tasses d'eau. Ajouter une feuille de laurier, de l'oignon hach&#xE9;, du sel et du poivre selon le poids du homard canadien. Le temps d'&#xE9;bullition sera bas&#xE9; sur le poids. </para> <para> Retirer le homard canadien cuit, filtrer le bouillon et laisser reposer.</para> <para> Pour les moules - Chauffer le beurre ou l'huile d'olive &#xE0; feu moyen-vif dans le pot (si vous utilisez du beurre, laissez-le fondre compl&#xE8;tement). Ajouter l'oignon &#xE9;minc&#xE9; et l'ail. Faire sauter jusqu'&#xE0; ce que les oignons soient translucides et que l'ail soit parfum&#xE9;, 1 &#xE0; 2 minutes. Verser l'eau (2 tasses) et la moiti&#xE9; de la tasse de vin blanc, porter &#xE0; &#xE9;bullition. Verser toutes les moules dans le pot. Retirez les moules ouvertes et jetez les moules qui ne se sont pas ouvertes. Passer le bouillon et laisser reposer. </para> <para> M&#xE9;langer les deux bouillons et les laisser reposer.</para> <para> Saute les fruits de mer:</para> <para> Dans une po&#xEA;le &#xE0; paella de 16 pouces, chauffer l'huile jusqu'&#xE0; ce qu'elle soit chaude. Faire sauter les crevettes, les p&#xE9;toncles et les calamars (si vous utilisez) jusqu'&#xE0; ce qu'ils soient bien cuits, environ 2 minutes. </para> <para> Faites le sofrito:</para> <para> R&#xE9;duire le feu &#xE0; moyen et faire revenir l'ail. Ajouter la tomate et le pimenton et cuire, en remuant souvent, jusqu'&#xE0; ce que le m&#xE9;lange noircisse et soit &#xE9;pais comme une compote, de 5 &#xE0; 10 min. </para> <para> Ajouter le riz et cuire:</para> <para> Ramener le bouillon &#xE0; &#xE9;bullition. Ajouter le riz dans la po&#xEA;le et cuire de 1 &#xE0; 2 minutes. Augmenter le feu &#xE0; moyen-&#xE9;lev&#xE9;. Verser 7 tasses de bouillon de fruits de mer (r&#xE9;server le reste) et m&#xE9;langer ou agiter la po&#xEA;le pour r&#xE9;partir uniform&#xE9;ment le riz. &#xC0; partir de ce moment, ne pas remuer le riz. Une fois que le bouillon est en &#xE9;bullition, comptez 4 minutes. </para> <para> Apr&#xE8;s les 4 minutes, r&#xE9;duire le feu &#xE0; moyen-doux et compter 10 minutes.</para> <para> Continuez &#xE0; mijoter plus doucement, en tournant la casserole au besoin. Disposer les crevettes et le homard canadien dans la po&#xEA;le. </para> <para> Cr&#xE9;er le socarrat:</para> <para> Augmenter le feu &#xE0; moyen-vif et, en faisant tourner la casserole, cuire environ 2 minutes, jusqu'&#xE0; ce que la couche de riz commence &#xE0; caram&#xE9;liser, cr&#xE9;ant le socarrat. Le riz cr&#xE9;pite, mais s'il commence &#xE0; sentir br&#xFB;l&#xE9;, retirez imm&#xE9;diatement la casserole du feu. </para> <para> Laissez la paella se reposer:</para> <para> Retirer la casserole du feu et laisser reposer la paella pendant 5 minutes. &#xE9;galiser la cuisson et laisser m&#xE9;langer les saveurs. </para> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <p> Faire un bouillon de fruits de mer:</p> <p> Pour le homard canadien - Dans une casserole moyenne, porter à ébullition 8 tasses d'eau. Ajouter une feuille de laurier, de l'oignon haché, du sel et du poivre selon le poids du homard canadien. Le temps d'ébullition sera basé sur le poids. </p> <p> Retirer le homard canadien cuit, filtrer le bouillon et laisser reposer.</p> <p> Pour les moules - Chauffer le beurre ou l'huile d'olive à feu moyen-vif dans le pot (si vous utilisez du beurre, laissez-le fondre complètement). Ajouter l'oignon émincé et l'ail. Faire sauter jusqu'à ce que les oignons soient translucides et que l'ail soit parfumé, 1 à 2 minutes. Verser l'eau (2 tasses) et la moitié de la tasse de vin blanc, porter à ébullition. Verser toutes les moules dans le pot. Retirez les moules ouvertes et jetez les moules qui ne se sont pas ouvertes. Passer le bouillon et laisser reposer. </p> <p> Mélanger les deux bouillons et les laisser reposer.</p> <p> Saute les fruits de mer:</p> <p> Dans une poêle à paella de 16 pouces, chauffer l'huile jusqu'à ce qu'elle soit chaude. Faire sauter les crevettes, les pétoncles et les calamars (si vous utilisez) jusqu'à ce qu'ils soient bien cuits, environ 2 minutes. </p> <p> Faites le sofrito:</p> <p> Réduire le feu à moyen et faire revenir l'ail. Ajouter la tomate et le pimenton et cuire, en remuant souvent, jusqu'à ce que le mélange noircisse et soit épais comme une compote, de 5 à 10 min. </p> <p> Ajouter le riz et cuire:</p> <p> Ramener le bouillon à ébullition. Ajouter le riz dans la poêle et cuire de 1 à 2 minutes. Augmenter le feu à moyen-élevé. Verser 7 tasses de bouillon de fruits de mer (réserver le reste) et mélanger ou agiter la poêle pour répartir uniformément le riz. À partir de ce moment, ne pas remuer le riz. Une fois que le bouillon est en ébullition, comptez 4 minutes. </p> <p> Après les 4 minutes, réduire le feu à moyen-doux et compter 10 minutes.</p> <p> Continuez à mijoter plus doucement, en tournant la casserole au besoin. Disposer les crevettes et le homard canadien dans la poêle. </p> <p> Créer le socarrat:</p> <p> Augmenter le feu à moyen-vif et, en faisant tourner la casserole, cuire environ 2 minutes, jusqu'à ce que la couche de riz commence à caraméliser, créant le socarrat. Le riz crépite, mais s'il commence à sentir brûlé, retirez immédiatement la casserole du feu. </p> <p> Laissez la paella se reposer:</p> <p> Retirer la casserole du feu et laisser reposer la paella pendant 5 minutes. égaliser la cuisson et laisser mélanger les saveurs. </p> </section> + + + + + 1073 + cooking_instructions + ger-DE + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <para> Machen Sie eine Meeresfr&#xFC;chte-Br&#xFC;he:</para> <para> F&#xFC;r den kanadischen Hummer - In einem mittleren Topf 8 Tassen Wasser zum Kochen bringen. F&#xFC;gen Sie ein Lorbeerblatt, gehackte Zwiebel und Salz und Pfeffer entsprechend dem Gewicht des kanadischen Hummers hinzu. Die Kochzeit basiert auf dem Gewicht. </para> <para> Den gekochten kanadischen Hummer entfernen, die Br&#xFC;he entkernen und abtropfen lassen.</para> <para> F&#xFC;r die Muscheln - Erw&#xE4;rmen Sie die Butter oder das Oliven&#xF6;l bei mittlerer Hitze im Topf (wenn Sie Butter verwenden, lassen Sie sie vollst&#xE4;ndig schmelzen). F&#xFC;gen Sie die gehackte Zwiebel und den Knoblauch hinzu. Die Zwiebeln glasig machen und 1 bis 2 Minuten d&#xFC;nsten. Gie&#xDF;en Sie das Wasser (2 Tassen) und eine halbe Tasse Wei&#xDF;wein, bringen Sie es zum Kochen. Gie&#xDF;en Sie alle Muscheln in den Topf. Entfernen Sie die ge&#xF6;ffneten Muscheln und entsorgen Sie Muscheln, die nicht ge&#xF6;ffnet haben. Die Br&#xFC;he abseihen und absetzen lassen. </para> <para> Mischen Sie beide Br&#xFC;hen und lassen Sie sie sitzen.</para> <para> Braten Sie die Meeresfr&#xFC;chte:</para> <para> In einer 16-Zoll-Paellapfanne das &#xD6;l erhitzen, bis es hei&#xDF; ist. Die Garnelen, die Jakobsmuscheln und die Calamari (falls verwendet) bissfest durchgaren, ca. 2 Minuten. </para> <para> Mach das sofrito:</para> <para> Reduziere die Hitze auf mittlere Stufe und sautiere den Knoblauch. F&#xFC;gen Sie die Tomate und das Piment hinzu und kochen Sie, h&#xE4;ufig r&#xFC;hrend, bis die Mischung zu einem tiefen Burgunder verdunkelt wird und dick wie ein Kompott ist, 5 bis 10 min. </para> <para> F&#xFC;gen Sie den Reis hinzu und kochen Sie:</para> <para> Bringen Sie die Br&#xFC;he wieder zum Kochen. Den Reis in die Pfanne geben und 1 bis 2 Minuten kochen lassen. Erh&#xF6;hen Sie die Hitze auf mittlere bis hohe. Pour 7 Tassen der Meeresfr&#xFC;chtesuppe (behalten Sie den Rest) und r&#xFC;hren Sie oder sch&#xFC;tteln Sie die Wanne, um den Reis gleichm&#xE4;&#xDF;ig zu verteilen. Von diesem Punkt an den Reis nicht umr&#xFC;hren. Sobald die Br&#xFC;he kocht, z&#xE4;hlen Sie 4 Minuten. </para> <para> Nach den 4 Minuten die Hitze auf mittel-niedrig reduzieren und 10 Minuten z&#xE4;hlen.</para> <para> Weiter vorsichtig k&#xF6;cheln lassen und die Pfanne nach Bedarf drehen. Ordnen Sie die Garnelen und den kanadischen Hummer in der Pfanne an. </para> <para> Erstelle den Socarrat:</para> <para> Erh&#xF6;hen Sie die Hitze auf mittlere bis hohe und drehen Sie die Pfanne, f&#xFC;r etwa 2 Minuten kochen, bis die untere Schicht Reis zu karamellisieren beginnt, so dass die Socarrat. Der Reis wird knistern, aber wenn er anf&#xE4;ngt zu brennen, entfernen Sie die Pfanne sofort von der Hitze. </para> <para> Lass die Paella ruhen:</para> <para> Die Pfanne von der Hitze nehmen und die Paella f&#xFC;r 5 Minuten ruhen lassen. um sogar das Kochen und die Aromen verschmelzen zu lassen. </para> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <p> Machen Sie eine Meeresfrüchte-Brühe:</p> <p> Für den kanadischen Hummer - In einem mittleren Topf 8 Tassen Wasser zum Kochen bringen. Fügen Sie ein Lorbeerblatt, gehackte Zwiebel und Salz und Pfeffer entsprechend dem Gewicht des kanadischen Hummers hinzu. Die Kochzeit basiert auf dem Gewicht. </p> <p> Den gekochten kanadischen Hummer entfernen, die Brühe entkernen und abtropfen lassen.</p> <p> Für die Muscheln - Erwärmen Sie die Butter oder das Olivenöl bei mittlerer Hitze im Topf (wenn Sie Butter verwenden, lassen Sie sie vollständig schmelzen). Fügen Sie die gehackte Zwiebel und den Knoblauch hinzu. Die Zwiebeln glasig machen und 1 bis 2 Minuten dünsten. Gießen Sie das Wasser (2 Tassen) und eine halbe Tasse Weißwein, bringen Sie es zum Kochen. Gießen Sie alle Muscheln in den Topf. Entfernen Sie die geöffneten Muscheln und entsorgen Sie Muscheln, die nicht geöffnet haben. Die Brühe abseihen und absetzen lassen. </p> <p> Mischen Sie beide Brühen und lassen Sie sie sitzen.</p> <p> Braten Sie die Meeresfrüchte:</p> <p> In einer 16-Zoll-Paellapfanne das Öl erhitzen, bis es heiß ist. Die Garnelen, die Jakobsmuscheln und die Calamari (falls verwendet) bissfest durchgaren, ca. 2 Minuten. </p> <p> Mach das sofrito:</p> <p> Reduziere die Hitze auf mittlere Stufe und sautiere den Knoblauch. Fügen Sie die Tomate und das Piment hinzu und kochen Sie, häufig rührend, bis die Mischung zu einem tiefen Burgunder verdunkelt wird und dick wie ein Kompott ist, 5 bis 10 min. </p> <p> Fügen Sie den Reis hinzu und kochen Sie:</p> <p> Bringen Sie die Brühe wieder zum Kochen. Den Reis in die Pfanne geben und 1 bis 2 Minuten kochen lassen. Erhöhen Sie die Hitze auf mittlere bis hohe. Pour 7 Tassen der Meeresfrüchtesuppe (behalten Sie den Rest) und rühren Sie oder schütteln Sie die Wanne, um den Reis gleichmäßig zu verteilen. Von diesem Punkt an den Reis nicht umrühren. Sobald die Brühe kocht, zählen Sie 4 Minuten. </p> <p> Nach den 4 Minuten die Hitze auf mittel-niedrig reduzieren und 10 Minuten zählen.</p> <p> Weiter vorsichtig köcheln lassen und die Pfanne nach Bedarf drehen. Ordnen Sie die Garnelen und den kanadischen Hummer in der Pfanne an. </p> <p> Erstelle den Socarrat:</p> <p> Erhöhen Sie die Hitze auf mittlere bis hohe und drehen Sie die Pfanne, für etwa 2 Minuten kochen, bis die untere Schicht Reis zu karamellisieren beginnt, so dass die Socarrat. Der Reis wird knistern, aber wenn er anfängt zu brennen, entfernen Sie die Pfanne sofort von der Hitze. </p> <p> Lass die Paella ruhen:</p> <p> Die Pfanne von der Hitze nehmen und die Paella für 5 Minuten ruhen lassen. um sogar das Kochen und die Aromen verschmelzen zu lassen. </p> </section> + + + + + 1088 + cooking_instructions + nor-NO + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <para> Lag en sj&#xF8;mat kj&#xF8;ttkraft:</para> <para> For den kanadiske hummeren - I en middels potte, ta med 8 kopper vann &#xE5; koke. Legg ett l&#xF8;vblad, hakket l&#xF8;k og salt og pepper i henhold til den kanadiske hummerens vekt. Koketiden vil v&#xE6;re basert p&#xE5; vekt. </para> <para> Fjern kokt kanadisk hummer, stamme kj&#xF8;ttkraft og la sitte av.</para> <para> For bl&#xE5;skjellene - Varm sm&#xF8;r eller olivenolje over middels h&#xF8;y varme i potten (hvis du bruker sm&#xF8;r, la den smelte helt). Tilsett hakket l&#xF8;k og hvitl&#xF8;k. Saut&#xE9;r til l&#xF8;kene er gjennomskinnelige og hvitl&#xF8;k er duftende, 1 til 2 minutter. Hell vannet (2 kopper) og en halv kopp hvitvin, bring den til &#xE5; koke. Hell alle muslingene i potten. Fjern de &#xE5;pnede bl&#xE5;skjellene og kast bort eventuelle bl&#xE5;skjell som ikke har &#xE5;pnet. Stam kj&#xF8;ttkraft og la sitte av. </para> <para> Bland begge kj&#xF8;ttkraftene og la dem sitte av.</para> <para> Saute sj&#xF8;mat:</para> <para> I en 16-tommers paella-panne, varme opp oljen til den er varm. Saute reker, kamskjell og calamari (hvis du bruker) til bare kokt gjennom, ca 2 minutter. </para> <para> Gj&#xF8;r sofrito:</para> <para> Reduser varmen til middels og saut&#xE9; hvitl&#xF8;k. Tilsett tomat og pimenton og kok, r&#xF8;r ofte, til blandingen er m&#xF8;rknet til en dyp bourgogne og tykk som en kompott, 5 til 10 minutter. </para> <para> Tilsett ris og kok:</para> <para> Ta med buljongen tilbake til en simmer. Legg risen p&#xE5; pannen, og lag den i 1 til 2 minutter. &#xD8;k varmen til middels h&#xF8;y. Hell i 7 kopper av sj&#xF8;mat kj&#xF8;ttkraft (reserve resten) og r&#xF8;r eller rist pannen for jevnt fordelt risen. Fra dette punktet m&#xE5; du ikke r&#xF8;re risen. N&#xE5;r kj&#xF8;ttkraft koker, teller 4 minutter. </para> <para> Etter 4 minutter, reduser varmen til middels lav og teller 10 minutter.</para> <para> Fortsett &#xE5; simre mer forsiktig, roter pannen etter behov. Ordne reker og kanadisk hummer i pannen.</para> <para> Opprett socarrat:</para> <para> &#xD8;k varmen til middels h&#xF8;y og roter pannen, kok i ca 2 minutter, til bunnlaget av ris begynner &#xE5; karamellisere, og skape socarraten. Risen vil knekke, men hvis det begynner &#xE5; lukte brent, ta av pannen fra varmen umiddelbart. </para> <para> La paellaen hvile:</para> <para> Ta av pannen fra varmen og la paella hvile i 5 min. til og med matlagingen og la smakene meldes.</para> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <p> Lag en sjømat kjøttkraft:</p> <p> For den kanadiske hummeren - I en middels potte, ta med 8 kopper vann å koke. Legg ett løvblad, hakket løk og salt og pepper i henhold til den kanadiske hummerens vekt. Koketiden vil være basert på vekt. </p> <p> Fjern kokt kanadisk hummer, stamme kjøttkraft og la sitte av.</p> <p> For blåskjellene - Varm smør eller olivenolje over middels høy varme i potten (hvis du bruker smør, la den smelte helt). Tilsett hakket løk og hvitløk. Sautér til løkene er gjennomskinnelige og hvitløk er duftende, 1 til 2 minutter. Hell vannet (2 kopper) og en halv kopp hvitvin, bring den til å koke. Hell alle muslingene i potten. Fjern de åpnede blåskjellene og kast bort eventuelle blåskjell som ikke har åpnet. Stam kjøttkraft og la sitte av. </p> <p> Bland begge kjøttkraftene og la dem sitte av.</p> <p> Saute sjømat:</p> <p> I en 16-tommers paella-panne, varme opp oljen til den er varm. Saute reker, kamskjell og calamari (hvis du bruker) til bare kokt gjennom, ca 2 minutter. </p> <p> Gjør sofrito:</p> <p> Reduser varmen til middels og sauté hvitløk. Tilsett tomat og pimenton og kok, rør ofte, til blandingen er mørknet til en dyp bourgogne og tykk som en kompott, 5 til 10 minutter. </p> <p> Tilsett ris og kok:</p> <p> Ta med buljongen tilbake til en simmer. Legg risen på pannen, og lag den i 1 til 2 minutter. Øk varmen til middels høy. Hell i 7 kopper av sjømat kjøttkraft (reserve resten) og rør eller rist pannen for jevnt fordelt risen. Fra dette punktet må du ikke røre risen. Når kjøttkraft koker, teller 4 minutter. </p> <p> Etter 4 minutter, reduser varmen til middels lav og teller 10 minutter.</p> <p> Fortsett å simre mer forsiktig, roter pannen etter behov. Ordne reker og kanadisk hummer i pannen.</p> <p> Opprett socarrat:</p> <p> Øk varmen til middels høy og roter pannen, kok i ca 2 minutter, til bunnlaget av ris begynner å karamellisere, og skape socarraten. Risen vil knekke, men hvis det begynner å lukte brent, ta av pannen fra varmen umiddelbart. </p> <p> La paellaen hvile:</p> <p> Ta av pannen fra varmen og la paella hvile i 5 min. til og med matlagingen og la smakene meldes.</p> </section> + + + + + 1044 + ingredients + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <itemizedlist> <listitem><para>6 oz. large shrimp</para></listitem> <listitem><para>1 Canadian lobster (around 1.7 pounds)</para></listitem> <listitem><para>1 pound of mussels, scrubbed</para></listitem> <listitem><para>Pinch saffron threads</para></listitem> <listitem><para>Kosher salt</para></listitem> <listitem><para>1/4 cup extra-virgin olive oil</para></listitem> <listitem><para>6 oz. dry scallops, patted dry</para></listitem> <listitem><para>4 oz. calamari, cut in rings (optional), patted dry</para></listitem> <listitem><para>6 garlic cloves, peeled</para></listitem> <listitem><para>1 tomato, halved and grated on a box grater (discard the skin)</para></listitem> <listitem><para>1/2 teaspoon pimenton</para></listitem> <listitem><para>3 1/2 cups of arborio rice</para></listitem> <listitem><para>7 cups seafood broth (from the Canadian lobster and mussels)</para></listitem> </itemizedlist> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <ul> <li>6 oz. large shrimp</li> <li>1 Canadian lobster (around 1.7 pounds)</li> <li>1 pound of mussels, scrubbed</li> <li>Pinch saffron threads</li> <li>Kosher salt</li> <li>1/4 cup extra-virgin olive oil</li> <li>6 oz. dry scallops, patted dry</li> <li>4 oz. calamari, cut in rings (optional), patted dry</li> <li>6 garlic cloves, peeled</li> <li>1 tomato, halved and grated on a box grater (discard the skin)</li> <li>1/2 teaspoon pimenton</li> <li>3 1/2 cups of arborio rice</li> <li>7 cups seafood broth (from the Canadian lobster and mussels)</li> </ul> </section> + + + + + 1059 + ingredients + fre-FR + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <itemizedlist> <listitem> <para> 6 onces. grosses crevettes</para> </listitem> <listitem> <para> 1 homard canadien (environ 1,7 lb)</para> </listitem> <listitem> <para> 1 livre de moules, nettoy&#xE9;e</para> </listitem> <listitem> <para> Pincer les fils de safran</para> </listitem> <listitem> <para> Sel kasher</para> </listitem> <listitem> <para> 1/4 tasse d'huile d'olive extra-vierge</para> </listitem> <listitem> <para> 6 onces. p&#xE9;toncles secs, patt&#xE9;s secs</para> </listitem> <listitem> <para> 125 grammes. calamars, coup&#xE9;s en rondelles (facultatif), ass&#xE9;ch&#xE9;s</para> </listitem> <listitem> <para> 6 gousses d'ail &#xE9;pluch&#xE9;es</para> </listitem> <listitem> <para> 1 tomate, coup&#xE9;e en deux et r&#xE2;p&#xE9;e sur une r&#xE2;pe &#xE0; bo&#xEE;te (jeter la peau)</para> </listitem> <listitem> <para> 1/2 cuill&#xE8;re &#xE0; caf&#xE9; de pimenton</para> </listitem> <listitem> <para> 3 1/2 tasses de riz arborio</para> </listitem> <listitem> <para> 7 tasses de bouillon de fruits de mer (du homard canadien et des moules)</para> </listitem> </itemizedlist> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <ul> <li> 6 onces. grosses crevettes</li> <li> 1 homard canadien (environ 1,7 lb)</li> <li> 1 livre de moules, nettoyée</li> <li> Pincer les fils de safran</li> <li> Sel kasher</li> <li> 1/4 tasse d'huile d'olive extra-vierge</li> <li> 6 onces. pétoncles secs, pattés secs</li> <li> 125 grammes. calamars, coupés en rondelles (facultatif), asséchés</li> <li> 6 gousses d'ail épluchées</li> <li> 1 tomate, coupée en deux et râpée sur une râpe à boîte (jeter la peau)</li> <li> 1/2 cuillère à café de pimenton</li> <li> 3 1/2 tasses de riz arborio</li> <li> 7 tasses de bouillon de fruits de mer (du homard canadien et des moules)</li> </ul> </section> + + + + + 1074 + ingredients + ger-DE + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <itemizedlist> <listitem> <para> 6 Unzen. gro&#xDF;e Garnelen</para> </listitem> <listitem> <para> 1 kanadischer Hummer (ca. 1,7 Pfund)</para> </listitem> <listitem> <para> 1 Pfund Muscheln, geschrubbt</para> </listitem> <listitem> <para> Safranf&#xE4;den falten</para> </listitem> <listitem> <para> Koscheres Salz</para> </listitem> <listitem> <para> 1/4 Tasse extra-natives Oliven&#xF6;l</para> </listitem> <listitem> <para> 6 Unzen. trockene Jakobsmuscheln, trocken getupft</para> </listitem> <listitem> <para> 4 Unzen. Calamari, in Ringe geschnitten (optional), trocken getupft</para> </listitem> <listitem> <para> 6 Knoblauchzehen, gesch&#xE4;lt</para> </listitem> <listitem> <para> 1 Tomate, halbiert und gerieben auf einer Kastenreibe (die Haut wegwerfen)</para> </listitem> <listitem> <para> 1/2 Teel&#xF6;ffel Pimenton</para> </listitem> <listitem> <para> 3 1/2 Tassen Arborio Reis</para> </listitem> <listitem> <para> 7 Tassen Meeresfr&#xFC;chte Br&#xFC;he (vom kanadischen Hummer und Muscheln)</para> </listitem> </itemizedlist> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <ul> <li> 6 Unzen. große Garnelen</li> <li> 1 kanadischer Hummer (ca. 1,7 Pfund)</li> <li> 1 Pfund Muscheln, geschrubbt</li> <li> Safranfäden falten</li> <li> Koscheres Salz</li> <li> 1/4 Tasse extra-natives Olivenöl</li> <li> 6 Unzen. trockene Jakobsmuscheln, trocken getupft</li> <li> 4 Unzen. Calamari, in Ringe geschnitten (optional), trocken getupft</li> <li> 6 Knoblauchzehen, geschält</li> <li> 1 Tomate, halbiert und gerieben auf einer Kastenreibe (die Haut wegwerfen)</li> <li> 1/2 Teelöffel Pimenton</li> <li> 3 1/2 Tassen Arborio Reis</li> <li> 7 Tassen Meeresfrüchte Brühe (vom kanadischen Hummer und Muscheln)</li> </ul> </section> + + + + + 1089 + ingredients + nor-NO + ezrichtext + + <?xml version="1.0"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> <itemizedlist> <listitem> <para> 6 oz. store reker</para> </listitem> <listitem> <para> 1 kanadisk hummer (rundt 1,7 pund)</para> </listitem> <listitem> <para> 1 pund bl&#xE5;skjell, skrubbet</para> </listitem> <listitem> <para> Klem saffron tr&#xE5;der</para> </listitem> <listitem> <para> Kosher salt</para> </listitem> <listitem> <para> 1/4 kopp ekstra jomfruolje</para> </listitem> <listitem> <para> 6 oz. t&#xF8;rr kamskjell, klappert&#xF8;rr</para> </listitem> <listitem> <para> 4 oz. calamari, kuttet i ringer (valgfritt), t&#xF8;rketatt</para> </listitem> <listitem> <para> 6 hvitl&#xF8;kskl&#xE6;r, skr&#xE6;llet</para> </listitem> <listitem> <para> 1 tomat, halvert og revet p&#xE5; en boksrist (kast huden)</para> </listitem> <listitem> <para> 1/2 ts pimenton</para> </listitem> <listitem> <para> 3 1/2 kopper arborio ris</para> </listitem> <listitem> <para> 7 kopper sj&#xF8;mat kj&#xF8;ttkraft (fra canadisk hummer og bl&#xE5;skjell)</para> </listitem> </itemizedlist> </section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"> <ul> <li> 6 oz. store reker</li> <li> 1 kanadisk hummer (rundt 1,7 pund)</li> <li> 1 pund blåskjell, skrubbet</li> <li> Klem saffron tråder</li> <li> Kosher salt</li> <li> 1/4 kopp ekstra jomfruolje</li> <li> 6 oz. tørr kamskjell, klappertørr</li> <li> 4 oz. calamari, kuttet i ringer (valgfritt), tørketatt</li> <li> 6 hvitløksklær, skrællet</li> <li> 1 tomat, halvert og revet på en boksrist (kast huden)</li> <li> 1/2 ts pimenton</li> <li> 3 1/2 kopper arborio ris</li> <li> 7 kopper sjømat kjøttkraft (fra canadisk hummer og blåskjell)</li> </ul> </section> + + + + + 1045 + image + eng-GB + ezimage + + 5/4/0/1/1045-1-eng-GB/cheesecake_figs.jpg + /5/4/0/1/1045-1-eng-GB/cheesecake_figs.jpg + + cheesecake_figs.jpg + 44846 + 67-1045-5 + /var/site/storage/images/5/4/0/1/1045-1-eng-GB/cheesecake_figs.jpg + + 686 + 686 + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/article_full_width + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/card_view + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/embed-header + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/gallery + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/large + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/medium + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/place + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/product_card + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/product_large + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/reference + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/search_result_item + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/search_result_item_large + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/small + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/social_network_image + + + /api/ezp/v2/content/binary/images/67-1045-5/variations/tiny + + + + + + 1060 + image + fre-FR + ezimage + + 0/6/0/1/1060-2-fre-FR/cheesecake_figs.jpg + /0/6/0/1/1060-2-fre-FR/cheesecake_figs.jpg + + cheesecake_figs.jpg + 44846 + 67-1060-5 + /var/site/storage/images/0/6/0/1/1060-2-fre-FR/cheesecake_figs.jpg + + 686 + 686 + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/article_full_width + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/card_view + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/embed-header + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/gallery + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/large + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/medium + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/place + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/product_card + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/product_large + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/reference + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/search_result_item + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/search_result_item_large + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/small + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/social_network_image + + + /api/ezp/v2/content/binary/images/67-1060-5/variations/tiny + + + + + + 1075 + image + ger-DE + ezimage + + 5/7/0/1/1075-3-ger-DE/cheesecake_figs.jpg + /5/7/0/1/1075-3-ger-DE/cheesecake_figs.jpg + + cheesecake_figs.jpg + 44846 + 67-1075-5 + /var/site/storage/images/5/7/0/1/1075-3-ger-DE/cheesecake_figs.jpg + + 686 + 686 + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/article_full_width + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/card_view + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/embed-header + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/gallery + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/large + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/medium + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/place + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/product_card + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/product_large + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/reference + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/search_result_item + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/search_result_item_large + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/small + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/social_network_image + + + /api/ezp/v2/content/binary/images/67-1075-5/variations/tiny + + + + + + 1090 + image + nor-NO + ezimage + + 0/9/0/1/1090-4-nor-NO/cheesecake_figs.jpg + /0/9/0/1/1090-4-nor-NO/cheesecake_figs.jpg + + cheesecake_figs.jpg + 44846 + 67-1090-5 + /var/site/storage/images/0/9/0/1/1090-4-nor-NO/cheesecake_figs.jpg + + 686 + 686 + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/article_full_width + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/card_view + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/embed-header + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/gallery + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/large + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/medium + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/place + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/product_card + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/product_large + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/reference + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/search_result_item + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/search_result_item_large + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/small + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/social_network_image + + + /api/ezp/v2/content/binary/images/67-1090-5/variations/tiny + + + + + + 1046 + price + eng-GB + ezfloat + 30.000000 + + + 1061 + price + fre-FR + ezfloat + 30.000000 + + + 1076 + price + ger-DE + ezfloat + 30.000000 + + + 1091 + price + nor-NO + ezfloat + 30.000000 + + + 1047 + serving + eng-GB + ezinteger + 8 + + + 1062 + serving + fre-FR + ezinteger + 8 + + + 1077 + serving + ger-DE + ezinteger + 8 + + + 1092 + serving + nor-NO + ezinteger + 8 + + + 1048 + spicy + eng-GB + ezselection + + 0 + + + + 1063 + spicy + fre-FR + ezselection + + 0 + + + + 1078 + spicy + ger-DE + ezselection + + 0 + + + + 1093 + spicy + nor-NO + ezselection + + 0 + + + + 1049 + dairy + eng-GB + ezboolean + false + + + 1064 + dairy + fre-FR + ezboolean + false + + + 1079 + dairy + ger-DE + ezboolean + false + + + 1094 + dairy + nor-NO + ezboolean + false + + + 1050 + egg + eng-GB + ezboolean + false + + + 1065 + egg + fre-FR + ezboolean + false + + + 1080 + egg + ger-DE + ezboolean + false + + + 1095 + egg + nor-NO + ezboolean + false + + + 1051 + nuts + eng-GB + ezboolean + false + + + 1066 + nuts + fre-FR + ezboolean + false + + + 1081 + nuts + ger-DE + ezboolean + false + + + 1096 + nuts + nor-NO + ezboolean + false + + + 1052 + seafood + eng-GB + ezboolean + true + + + 1067 + seafood + fre-FR + ezboolean + true + + + 1082 + seafood + ger-DE + ezboolean + true + + + 1097 + seafood + nor-NO + ezboolean + true + + + 1053 + gluten + eng-GB + ezboolean + true + + + 1068 + gluten + fre-FR + ezboolean + true + + + 1083 + gluten + ger-DE + ezboolean + true + + + 1098 + gluten + nor-NO + ezboolean + true + + + 1054 + tags + eng-GB + eztags + + + 394 + 360 + 0 + + Spanish + + 3 + /359/360/394/ + 1542478859 + 6e74965a20befeb124245bf9d3ec27dc + true + eng-GB + + eng-GB + + + + 413 + 402 + 0 + + Entrée + + 3 + /359/402/413/ + 1542478859 + f92af0861ca801f630f8d25e59d7b812 + true + eng-GB + + eng-GB + + + + 425 + 402 + 0 + + Rice + + 3 + /359/402/425/ + 1542478859 + 2f658a344299b3aea907d0bf1b83aee0 + true + eng-GB + + eng-GB + + + + 428 + 402 + 0 + + Seafood + + 3 + /359/402/428/ + 1542478859 + 3ee44c45d910bb3a1d258f52a693215c + true + eng-GB + + eng-GB + + + + 430 + 428 + 0 + + Shrimp + + 4 + /359/402/428/430/ + 1542478859 + 470a900e8d271699a2074f67859b0405 + true + eng-GB + + eng-GB + + + + + + 1069 + tags + fre-FR + eztags + + + 394 + 360 + 0 + + Spanish + + 3 + /359/360/394/ + 1542478859 + 6e74965a20befeb124245bf9d3ec27dc + true + eng-GB + + eng-GB + + + + 413 + 402 + 0 + + Entrée + + 3 + /359/402/413/ + 1542478859 + f92af0861ca801f630f8d25e59d7b812 + true + eng-GB + + eng-GB + + + + 425 + 402 + 0 + + Rice + + 3 + /359/402/425/ + 1542478859 + 2f658a344299b3aea907d0bf1b83aee0 + true + eng-GB + + eng-GB + + + + 428 + 402 + 0 + + Seafood + + 3 + /359/402/428/ + 1542478859 + 3ee44c45d910bb3a1d258f52a693215c + true + eng-GB + + eng-GB + + + + 430 + 428 + 0 + + Shrimp + + 4 + /359/402/428/430/ + 1542478859 + 470a900e8d271699a2074f67859b0405 + true + eng-GB + + eng-GB + + + + + + 1084 + tags + ger-DE + eztags + + + 394 + 360 + 0 + + Spanish + + 3 + /359/360/394/ + 1542478859 + 6e74965a20befeb124245bf9d3ec27dc + true + eng-GB + + eng-GB + + + + 413 + 402 + 0 + + Entrée + + 3 + /359/402/413/ + 1542478859 + f92af0861ca801f630f8d25e59d7b812 + true + eng-GB + + eng-GB + + + + 425 + 402 + 0 + + Rice + + 3 + /359/402/425/ + 1542478859 + 2f658a344299b3aea907d0bf1b83aee0 + true + eng-GB + + eng-GB + + + + 428 + 402 + 0 + + Seafood + + 3 + /359/402/428/ + 1542478859 + 3ee44c45d910bb3a1d258f52a693215c + true + eng-GB + + eng-GB + + + + 430 + 428 + 0 + + Shrimp + + 4 + /359/402/428/430/ + 1542478859 + 470a900e8d271699a2074f67859b0405 + true + eng-GB + + eng-GB + + + + + + 1099 + tags + nor-NO + eztags + + + 394 + 360 + 0 + + Spanish + + 3 + /359/360/394/ + 1542478859 + 6e74965a20befeb124245bf9d3ec27dc + true + eng-GB + + eng-GB + + + + 413 + 402 + 0 + + Entrée + + 3 + /359/402/413/ + 1542478859 + f92af0861ca801f630f8d25e59d7b812 + true + eng-GB + + eng-GB + + + + 425 + 402 + 0 + + Rice + + 3 + /359/402/425/ + 1542478859 + 2f658a344299b3aea907d0bf1b83aee0 + true + eng-GB + + eng-GB + + + + 428 + 402 + 0 + + Seafood + + 3 + /359/402/428/ + 1542478859 + 3ee44c45d910bb3a1d258f52a693215c + true + eng-GB + + eng-GB + + + + 430 + 428 + 0 + + Shrimp + + 4 + /359/402/428/430/ + 1542478859 + 470a900e8d271699a2074f67859b0405 + true + eng-GB + + eng-GB + + + + + + 1055 + metas + eng-GB + novaseometas + + + canonical + + + + description + + + + keywords + + + + og:description + + + + og:image + + + + og:title + + + + title + + + + twitter:description + + + + twitter:image + + + + twitter:title + + + + + + 1070 + metas + fre-FR + novaseometas + + + + 1085 + metas + ger-DE + novaseometas + + + + 1100 + metas + nor-NO + novaseometas + + + + + + + + EMBED + + + + + EMBED + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.json.example new file mode 100644 index 0000000000..411c868cbd --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.json.example @@ -0,0 +1,136 @@ +{ + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/54", + "_remoteId": "9e863fbb0fb835ce050032b4f00de61d", + "_id": 54, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "Forms", + "TranslatedName": "Forms", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/54/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/54/currentversion", + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/54/versions/1", + "VersionInfo": { + "id": 514, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2018-09-17T06:48:13+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2018-09-17T06:48:13+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Forms" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/54" + } + }, + "Fields": { + "field": [ + { + "id": 249, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Forms" + }, + { + "id": 250, + "fieldDefinitionIdentifier": "short_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 251, + "fieldDefinitionIdentifier": "short_description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 252, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/54/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#folder", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/6" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/55" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/54/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2018-09-17T06:48:13+00:00", + "publishedDate": "2018-09-17T06:48:13+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/54/objectstates" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.xml.example new file mode 100644 index 0000000000..d4bb2f1341 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/Content.xml.example @@ -0,0 +1,93 @@ + + + + Forms + Forms + + + + + 514 + 1 + PUBLISHED + 2018-09-17T06:48:13+00:00 + + 2018-09-17T06:48:13+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Forms + + + + + + 249 + name + eng-GB + ezstring + Forms + + + 250 + short_name + eng-GB + ezstring + + + + 251 + short_description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 252 + description + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + + + /bundles/ezplatformadminui/img/ez-icons.svg#folder + + + image/svg+xml + + + +
    + + + + 2018-09-17T06:48:13+00:00 + 2018-09-17T06:48:13+00:00 + eng-GB + 1 + false + false + PUBLISHED + + \ No newline at end of file diff --git a/material/readthedocs-dynamic-include.js b/docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/ContentInfo.xml.example similarity index 100% rename from material/readthedocs-dynamic-include.js rename to docs/api/rest_api_reference/input/examples/content/objects/content_id/GET/ContentInfo.xml.example diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentInfo.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentInfo.xml.example new file mode 100644 index 0000000000..5e5d57a080 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentInfo.xml.example @@ -0,0 +1,17 @@ + + + + article1 + + +
    + + + + 2019-02-19T15:00:34+01:00 + 2019-02-19T15:00:34+01:00 + eng-GB + 1 + false + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example new file mode 100644 index 0000000000..f88c514fc3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/PATCH/ContentUpdate.xml.example @@ -0,0 +1,9 @@ + + + eng-GB +
    + + + false + 69848aeb86164c35e5c98202eebe9e05 + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.json.example new file mode 100644 index 0000000000..36cc9aa7ab --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.json.example @@ -0,0 +1,194 @@ +{ + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3", + "VersionInfo": { + "id": 581, + "versionNo": 3, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:16:50+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:16:50+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + }, + "Fields": { + "field": [ + { + "id": 279, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Art1" + }, + { + "id": 280, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 281, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [ + { + "id": "1", + "name": "Administrator User", + "email": "admin@link.invalid" + } + ] + }, + { + "id": 282, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    aaaaa
    \n", + "xhtml5edit": "\n

    aaaaa

    \n" + } + }, + { + "id": 283, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 284, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 285, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimageasset", + "fieldValue": { + "destinationContentId": "62", + "alternativeText": null, + "source": null, + "variations": { + "article_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/article_full" + }, + "content_list": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/content_list" + }, + "dog_breed_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full" + }, + "ezplatform_admin_ui_profile_picture_user_menu": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu" + }, + "featured_article": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/featured_article" + }, + "fourth_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3" + }, + "fourth_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2" + }, + "full_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3" + }, + "full_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2" + }, + "gallery": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/gallery" + }, + "half_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3" + }, + "half_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2" + }, + "large": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/large" + }, + "medium": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/medium" + }, + "reference": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/reference" + }, + "small": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/small" + }, + "third_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3" + }, + "third_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2" + }, + "tiny": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/tiny" + } + } + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3/relations", + "Relation": [ + { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3/relations/17", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/62" + }, + "SourceFieldDefinitionIdentifier": "image", + "RelationType": "ASSET" + } + ] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.xml.example new file mode 100644 index 0000000000..73505db81a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/COPY/Version.xml.example @@ -0,0 +1,170 @@ + + + + 580 + 2 + DRAFT + 2021-07-26T08:10:02+00:00 + + 2021-07-26T08:10:02+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + 279 + title + eng-GB + ezstring + Art1 + + + 280 + short_title + eng-GB + ezstring + + + + 281 + author + eng-GB + ezauthor + + + 1 + Administrator User + admin@link.invalid + + + + + 282 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>aaaaa</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>aaaaa</p></section> + + + + + 283 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 284 + enable_comments + eng-GB + ezboolean + false + + + 285 + image + eng-GB + ezimageasset + + 62 + + + + + /api/ezp/v2/content/binary/images/62-288-1/variations/article_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/content_list + + + /api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/62-288-1/variations/featured_article + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/gallery + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/large + + + /api/ezp/v2/content/binary/images/62-288-1/variations/medium + + + /api/ezp/v2/content/binary/images/62-288-1/variations/reference + + + /api/ezp/v2/content/binary/images/62-288-1/variations/small + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/tiny + + + + + + + + + + image + ASSET + + + + /bundles/ezplatformadminui/img/ez-icons.svg#article + + + image/svg+xml + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.json.example new file mode 100644 index 0000000000..f6e4d78e38 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.json.example @@ -0,0 +1,194 @@ +{ + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/1", + "VersionInfo": { + "id": 521, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-28T11:33:49+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-06-28T11:33:31+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + }, + "Fields": { + "field": [ + { + "id": 279, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Art1" + }, + { + "id": 280, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 281, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [ + { + "id": "1", + "name": "Administrator User", + "email": "admin@link.invalid" + } + ] + }, + { + "id": 282, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    aaaaa
    \n", + "xhtml5edit": "\n

    aaaaa

    \n" + } + }, + { + "id": 283, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 284, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 285, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimageasset", + "fieldValue": { + "destinationContentId": "62", + "alternativeText": null, + "source": null, + "variations": { + "article_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/article_full" + }, + "content_list": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/content_list" + }, + "dog_breed_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full" + }, + "ezplatform_admin_ui_profile_picture_user_menu": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu" + }, + "featured_article": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/featured_article" + }, + "fourth_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3" + }, + "fourth_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2" + }, + "full_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3" + }, + "full_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2" + }, + "gallery": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/gallery" + }, + "half_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3" + }, + "half_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2" + }, + "large": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/large" + }, + "medium": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/medium" + }, + "reference": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/reference" + }, + "small": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/small" + }, + "third_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3" + }, + "third_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2" + }, + "tiny": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/tiny" + } + } + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/61/versions/1/relations", + "Relation": [ + { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/61/versions/1/relations/2", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/62" + }, + "SourceFieldDefinitionIdentifier": "image", + "RelationType": "ASSET" + } + ] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.xml.example new file mode 100644 index 0000000000..fa797c089a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/currentversion/GET/Version.xml.example @@ -0,0 +1,170 @@ + + + + 521 + 1 + PUBLISHED + 2021-06-28T11:33:49+00:00 + + 2021-06-28T11:33:31+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + 279 + title + eng-GB + ezstring + Art1 + + + 280 + short_title + eng-GB + ezstring + + + + 281 + author + eng-GB + ezauthor + + + 1 + Administrator User + admin@link.invalid + + + + + 282 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>aaaaa</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>aaaaa</p></section> + + + + + 283 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 284 + enable_comments + eng-GB + ezboolean + false + + + 285 + image + eng-GB + ezimageasset + + 62 + + + + + /api/ezp/v2/content/binary/images/62-288-1/variations/article_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/content_list + + + /api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/62-288-1/variations/featured_article + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/gallery + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/large + + + /api/ezp/v2/content/binary/images/62-288-1/variations/medium + + + /api/ezp/v2/content/binary/images/62-288-1/variations/reference + + + /api/ezp/v2/content/binary/images/62-288-1/variations/small + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/tiny + + + + + + + + + + image + ASSET + + + + /bundles/ezplatformadminui/img/ez-icons.svg#article + + + image/svg+xml + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.json.example new file mode 100644 index 0000000000..f2dbb8b5ec --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.json.example @@ -0,0 +1,12 @@ +{ + "LocationList": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/63/locations", + "Location": [ + { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/57/65" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.xml.example new file mode 100644 index 0000000000..794112df70 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/GET/LocationList.xml.example @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.json.example new file mode 100644 index 0000000000..3634de6f21 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.json.example @@ -0,0 +1,80 @@ +{ + "Location": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/114", + "id": 114, + "priority": 0, + "hidden": false, + "invisible": false, + "explicitlyHidden": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/59" + }, + "pathString": "/1/2/59/114/", + "depth": 3, + "childCount": 0, + "remoteId": "47a1e4ee082fb64e93a822dcfe3a5614", + "Children": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/114/children" + }, + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/59" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "UrlAliases": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/locations/1/2/59/114/urlaliases" + }, + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59", + "_remoteId": "0fe1b1543f886b0becd4d9b2c7c517d0", + "_id": 59, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2" + }, + "Name": "Art1", + "TranslatedName": "Art1", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/59/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/59/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/59/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-06-28T11:33:01+00:00", + "publishedDate": "2021-06-28T11:33:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "PUBLISHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/59/objectstates" + } + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.xml.example new file mode 100644 index 0000000000..c4ef53bac3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/Location.xml.example @@ -0,0 +1,38 @@ + + + 96 + 0 + false + true + false + + /1/2/42/96/ + 3 + 0 + 58133c8c75230e5debe362a28b92d27a + + + PATH + ASC + + + + + Art3 + Art3 + + +
    + + + 2021-06-28T11:34:17+00:00 + 2021-06-28T11:34:17+00:00 + eng-GB + 1 + false + false + PUBLISHED + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.json.example new file mode 100644 index 0000000000..5ecdafe920 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.json.example @@ -0,0 +1,12 @@ +{ + "LocationCreate": { + "ParentLocation": { + "_href": "/api/ezp/v2/content/locations/1/59" + }, + "priority": "0", + "hidden": false, + "sortField": "PATH", + "sortOrder": "ASC" + + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example new file mode 100644 index 0000000000..debbc52f81 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/locations/POST/LocationCreate.xml.example @@ -0,0 +1,8 @@ + + + + 0 + false + PATH + ASC + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example new file mode 100644 index 0000000000..6fe438af3f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example @@ -0,0 +1,23 @@ +{ + "ContentObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "ObjectState": [ + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/1" + }, + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/3/objectstates/3" + }, + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates/7" + }, + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/8/objectstates/8" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example new file mode 100644 index 0000000000..823cf9f8a2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.json.example new file mode 100644 index 0000000000..9b92d284f0 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.json.example @@ -0,0 +1,125 @@ +{ + "VersionList": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/61/versions", + "VersionItem": [ + { + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/1" + }, + "VersionInfo": { + "id": 521, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-28T11:33:49+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-06-28T11:33:31+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + } + }, + { + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/2" + }, + "VersionInfo": { + "id": 580, + "versionNo": 2, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:10:02+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:10:02+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + } + }, + { + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3" + }, + "VersionInfo": { + "id": 581, + "versionNo": 3, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:16:50+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:16:50+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.xml.example new file mode 100644 index 0000000000..8874e61655 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/GET/VersionList.xml.example @@ -0,0 +1,69 @@ + + + + + + 521 + 1 + PUBLISHED + 2021-06-28T11:33:49+00:00 + + 2021-06-28T11:33:31+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + + + 580 + 2 + DRAFT + 2021-07-26T08:10:02+00:00 + + 2021-07-26T08:10:02+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + + + 581 + 3 + DRAFT + 2021-07-26T08:16:50+00:00 + + 2021-07-26T08:16:50+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example new file mode 100644 index 0000000000..d785ba7c3d --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.json.example @@ -0,0 +1,194 @@ +{ + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/4", + "VersionInfo": { + "id": 583, + "versionNo": 4, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:55:27+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:55:27+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + }, + "Fields": { + "field": [ + { + "id": 279, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Art1" + }, + { + "id": 280, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 281, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [ + { + "id": "1", + "name": "Administrator User", + "email": "admin@link.invalid" + } + ] + }, + { + "id": 282, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    aaaaa
    \n", + "xhtml5edit": "\n

    aaaaa

    \n" + } + }, + { + "id": 283, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 284, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 285, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimageasset", + "fieldValue": { + "destinationContentId": "62", + "alternativeText": null, + "source": null, + "variations": { + "article_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/article_full" + }, + "content_list": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/content_list" + }, + "dog_breed_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full" + }, + "ezplatform_admin_ui_profile_picture_user_menu": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu" + }, + "featured_article": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/featured_article" + }, + "fourth_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3" + }, + "fourth_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2" + }, + "full_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3" + }, + "full_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2" + }, + "gallery": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/gallery" + }, + "half_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3" + }, + "half_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2" + }, + "large": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/large" + }, + "medium": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/medium" + }, + "reference": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/reference" + }, + "small": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/small" + }, + "third_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3" + }, + "third_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2" + }, + "tiny": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/tiny" + } + } + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/61/versions/4/relations", + "Relation": [ + { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/61/versions/4/relations/19", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/62" + }, + "SourceFieldDefinitionIdentifier": "image", + "RelationType": "ASSET" + } + ] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example new file mode 100644 index 0000000000..be415c324b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example @@ -0,0 +1,170 @@ + + + + 582 + 3 + DRAFT + 2021-07-26T08:54:47+00:00 + + 2021-07-26T08:54:47+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + 279 + title + eng-GB + ezstring + Art1 + + + 280 + short_title + eng-GB + ezstring + + + + 281 + author + eng-GB + ezauthor + + + 1 + Administrator User + admin@link.invalid + + + + + 282 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>aaaaa</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>aaaaa</p></section> + + + + + 283 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 284 + enable_comments + eng-GB + ezboolean + false + + + 285 + image + eng-GB + ezimageasset + + 62 + + + + + /api/ezp/v2/content/binary/images/62-288-1/variations/article_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/content_list + + + /api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/62-288-1/variations/featured_article + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/gallery + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/large + + + /api/ezp/v2/content/binary/images/62-288-1/variations/medium + + + /api/ezp/v2/content/binary/images/62-288-1/variations/reference + + + /api/ezp/v2/content/binary/images/62-288-1/variations/small + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/tiny + + + + + + + + + + image + ASSET + + + + /bundles/ezplatformadminui/img/ez-icons.svg#article + + + image/svg+xml + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.json.example new file mode 100644 index 0000000000..36cc9aa7ab --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.json.example @@ -0,0 +1,194 @@ +{ + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3", + "VersionInfo": { + "id": 581, + "versionNo": 3, + "status": "DRAFT", + "modificationDate": "2021-07-26T08:16:50+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-26T08:16:50+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + } + }, + "Fields": { + "field": [ + { + "id": 279, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Art1" + }, + { + "id": 280, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 281, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [ + { + "id": "1", + "name": "Administrator User", + "email": "admin@link.invalid" + } + ] + }, + { + "id": 282, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    aaaaa
    \n", + "xhtml5edit": "\n

    aaaaa

    \n" + } + }, + { + "id": 283, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 284, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 285, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimageasset", + "fieldValue": { + "destinationContentId": "62", + "alternativeText": null, + "source": null, + "variations": { + "article_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/article_full" + }, + "content_list": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/content_list" + }, + "dog_breed_full": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full" + }, + "ezplatform_admin_ui_profile_picture_user_menu": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu" + }, + "featured_article": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/featured_article" + }, + "fourth_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3" + }, + "fourth_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2" + }, + "full_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3" + }, + "full_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2" + }, + "gallery": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/gallery" + }, + "half_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3" + }, + "half_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2" + }, + "large": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/large" + }, + "medium": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/medium" + }, + "reference": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/reference" + }, + "small": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/small" + }, + "third_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3" + }, + "third_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2" + }, + "tiny": { + "href": "/api/ezp/v2/content/binary/images/62-288-1/variations/tiny" + } + } + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3/relations", + "Relation": [ + { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/61/versions/3/relations/17", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/61" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/62" + }, + "SourceFieldDefinitionIdentifier": "image", + "RelationType": "ASSET" + } + ] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example new file mode 100644 index 0000000000..f2af2e7b8e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/GET/Version.xml.example @@ -0,0 +1,170 @@ + + + + 581 + 3 + DRAFT + 2021-07-26T08:16:50+00:00 + + 2021-07-26T08:16:50+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + 279 + title + eng-GB + ezstring + Art1 + + + 280 + short_title + eng-GB + ezstring + + + + 281 + author + eng-GB + ezauthor + + + 1 + Administrator User + admin@link.invalid + + + + + 282 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>aaaaa</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>aaaaa</p></section> + + + + + 283 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 284 + enable_comments + eng-GB + ezboolean + false + + + 285 + image + eng-GB + ezimageasset + + 62 + + + + + /api/ezp/v2/content/binary/images/62-288-1/variations/article_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/content_list + + + /api/ezp/v2/content/binary/images/62-288-1/variations/dog_breed_full + + + /api/ezp/v2/content/binary/images/62-288-1/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/62-288-1/variations/featured_article + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/fourth_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/full_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/gallery + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/half_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/large + + + /api/ezp/v2/content/binary/images/62-288-1/variations/medium + + + /api/ezp/v2/content/binary/images/62-288-1/variations/reference + + + /api/ezp/v2/content/binary/images/62-288-1/variations/small + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_2_3 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/third_width_3_2 + + + /api/ezp/v2/content/binary/images/62-288-1/variations/tiny + + + + + + + + + + image + ASSET + + + + /bundles/ezplatformadminui/img/ez-icons.svg#article + + + image/svg+xml + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example new file mode 100644 index 0000000000..3c8c015d98 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example @@ -0,0 +1,48 @@ + + + + 45 + 4 + DRAFT + 2012-02-20T12:00:00 + + 22012-02-20T12:00:00 + ger-DE + + Neuer Titel + + + + + + 1234 + title + ger-DE + Neuer Titel + + + 1235 + summary + ger-DE + Dies ist eine neuse Zusammenfassungy + + + authors + ger-DE + + + + + + + + + + + + COMMON + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example new file mode 100644 index 0000000000..96943f5099 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example @@ -0,0 +1,21 @@ + + + 2012-02-20T12:00:00 + ger-DE + + + 1234 + title + ger-DE + Neuer Titel + + + 1235 + summary + ger-DE + Dies ist eine neuse Zusammenfassungy + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example new file mode 100644 index 0000000000..e524cdc6ec --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example @@ -0,0 +1,194 @@ +{ + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/59/versions/1", + "VersionInfo": { + "id": 519, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-28T11:33:01+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-06-28T11:32:39+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Art1" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59" + } + }, + "Fields": { + "field": [ + { + "id": 269, + "fieldDefinitionIdentifier": "title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Art1" + }, + { + "id": 270, + "fieldDefinitionIdentifier": "short_title", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + }, + { + "id": 271, + "fieldDefinitionIdentifier": "author", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezauthor", + "fieldValue": [ + { + "id": "1", + "name": "Administrator User", + "email": "admin@link.invalid" + } + ] + }, + { + "id": 272, + "fieldDefinitionIdentifier": "intro", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    aaaaa
    \n", + "xhtml5edit": "\n

    aaaaa

    \n" + } + }, + { + "id": 273, + "fieldDefinitionIdentifier": "body", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezrichtext", + "fieldValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + } + }, + { + "id": 274, + "fieldDefinitionIdentifier": "enable_comments", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezboolean", + "fieldValue": false + }, + { + "id": 275, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimageasset", + "fieldValue": { + "destinationContentId": "60", + "alternativeText": null, + "source": null, + "variations": { + "article_full": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/article_full" + }, + "content_list": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/content_list" + }, + "dog_breed_full": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/dog_breed_full" + }, + "ezplatform_admin_ui_profile_picture_user_menu": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/ezplatform_admin_ui_profile_picture_user_menu" + }, + "featured_article": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/featured_article" + }, + "fourth_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/fourth_width_2_3" + }, + "fourth_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/fourth_width_3_2" + }, + "full_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/full_width_2_3" + }, + "full_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/full_width_3_2" + }, + "gallery": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/gallery" + }, + "half_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/half_width_2_3" + }, + "half_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/half_width_3_2" + }, + "large": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/large" + }, + "medium": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/medium" + }, + "reference": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/reference" + }, + "small": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/small" + }, + "third_width_2_3": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/third_width_2_3" + }, + "third_width_3_2": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/third_width_3_2" + }, + "tiny": { + "href": "/api/ezp/v2/content/binary/images/60-278-1/variations/tiny" + } + } + } + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/59/versions/1/relations", + "Relation": [ + { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/59/versions/1/relations/1", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/60" + }, + "SourceFieldDefinitionIdentifier": "image", + "RelationType": "ASSET" + } + ] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#article", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example new file mode 100644 index 0000000000..948fe24029 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example @@ -0,0 +1,170 @@ + + + + 519 + 1 + PUBLISHED + 2021-06-28T11:33:01+00:00 + + 2021-06-28T11:32:39+00:00 + eng-GB + eng-GB + + + eng-GB + + + + Art1 + + + + + + 269 + title + eng-GB + ezstring + Art1 + + + 270 + short_title + eng-GB + ezstring + + + + 271 + author + eng-GB + ezauthor + + + 1 + Administrator User + admin@link.invalid + + + + + 272 + intro + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>aaaaa</para></section> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>aaaaa</p></section> + + + + + 273 + body + eng-GB + ezrichtext + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> + + <?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> + + + + + 274 + enable_comments + eng-GB + ezboolean + false + + + 275 + image + eng-GB + ezimageasset + + 60 + + + + + /api/ezp/v2/content/binary/images/60-278-1/variations/article_full + + + /api/ezp/v2/content/binary/images/60-278-1/variations/content_list + + + /api/ezp/v2/content/binary/images/60-278-1/variations/dog_breed_full + + + /api/ezp/v2/content/binary/images/60-278-1/variations/ezplatform_admin_ui_profile_picture_user_menu + + + /api/ezp/v2/content/binary/images/60-278-1/variations/featured_article + + + /api/ezp/v2/content/binary/images/60-278-1/variations/fourth_width_2_3 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/fourth_width_3_2 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/full_width_2_3 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/full_width_3_2 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/gallery + + + /api/ezp/v2/content/binary/images/60-278-1/variations/half_width_2_3 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/half_width_3_2 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/large + + + /api/ezp/v2/content/binary/images/60-278-1/variations/medium + + + /api/ezp/v2/content/binary/images/60-278-1/variations/reference + + + /api/ezp/v2/content/binary/images/60-278-1/variations/small + + + /api/ezp/v2/content/binary/images/60-278-1/variations/third_width_2_3 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/third_width_3_2 + + + /api/ezp/v2/content/binary/images/60-278-1/variations/tiny + + + + + + + + + + image + ASSET + + + + /bundles/ezplatformadminui/img/ez-icons.svg#article + + + image/svg+xml + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example new file mode 100644 index 0000000000..595fb5a34a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example @@ -0,0 +1,15 @@ +{ + "Relation": { + "_media-type": "application/vnd.ez.api.Relation+json", + "_href": "/api/ezp/v2/content/objects/59/versions/2/relations/38", + "SourceContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59" + }, + "DestinationContent": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/59" + }, + "RelationType": "COMMON" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example new file mode 100644 index 0000000000..c322110b20 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example @@ -0,0 +1,6 @@ + + + + + COMMON + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example new file mode 100644 index 0000000000..5bd593734a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example @@ -0,0 +1,7 @@ +{ + "RelationCreate": { + "Destination": { + "_href": "/api/ezp/v2/content/objects/59" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example new file mode 100644 index 0000000000..897eef2c3f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example @@ -0,0 +1,4 @@ + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example new file mode 100644 index 0000000000..bba0c23a09 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example @@ -0,0 +1,6 @@ + + + + + COMMON + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example new file mode 100644 index 0000000000..cde557c750 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.json.example @@ -0,0 +1,92 @@ +{ + "ObjectStateGroupList": { + "_media-type": "application/vnd.ez.api.ObjectStateGroupList+json", + "_href": "/api/ezp/v2/content/objectstategroups", + "ObjectStateGroup": [ + { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/2", + "id": 2, + "identifier": "ez_lock", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Lock" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/3", + "id": 3, + "identifier": "accessibility", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/3/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Accessibility" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/6", + "id": 6, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/6/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example new file mode 100644 index 0000000000..e03d3e3e76 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example @@ -0,0 +1,42 @@ + + + + 2 + ez_lock + eng-GB + eng-GB + + + Lock + + + + + + + 3 + accessibility + eng-GB + eng-GB + + + Accessibility + + + + + + + 6 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.json.example new file mode 100644 index 0000000000..1f8fca65d7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example new file mode 100644 index 0000000000..82f30ff940 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 5 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example new file mode 100644 index 0000000000..894442b874 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example @@ -0,0 +1,22 @@ +{ + "ObjectStateGroup": { + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example new file mode 100644 index 0000000000..6a68c5c282 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example @@ -0,0 +1,11 @@ + + + custom + eng-GB + + Custom State + + + Custom Object state + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example new file mode 100644 index 0000000000..1f8fca65d7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example new file mode 100644 index 0000000000..d723e924ed --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 7 + custom-states + eng-GB + eng-GB + + + Custom State + + + Custom Object state + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example new file mode 100644 index 0000000000..eb8be3f0e1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example @@ -0,0 +1,30 @@ +{ + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/7", + "id": 7, + "identifier": "custom-states", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates" + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Custom State name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Custom Object state" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example new file mode 100644 index 0000000000..79c0eeedd5 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example @@ -0,0 +1,14 @@ + + + 7 + custom-states + eng-GB + eng-GB + + + New Custom State name + + + Custom Object state + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example new file mode 100644 index 0000000000..8ba08e6644 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example @@ -0,0 +1,12 @@ +{ + "ObjectStateGroup": { + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Custom State name" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example new file mode 100644 index 0000000000..c0fa5e9e43 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example @@ -0,0 +1,6 @@ + + + + New Custom State name + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example new file mode 100644 index 0000000000..69cb89a52b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example @@ -0,0 +1,66 @@ +{ + "ObjectStateList": { + "_media-type": "application/vnd.ez.api.ObjectStateList+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates", + "ObjectState": [ + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/1", + "id": 1, + "identifier": "not_locked", + "priority": 0, + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Not locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/2", + "id": 2, + "identifier": "locked", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example new file mode 100644 index 0000000000..78d14a035a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example @@ -0,0 +1,31 @@ + + + + 1 + not_locked + 0 + + eng-GB + eng-GB + + Not locked + + + + + + + 2 + locked + 1 + + eng-GB + eng-GB + + Locked + + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example new file mode 100644 index 0000000000..373f39e078 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates/7", + "id": 7, + "identifier": "new-state", + "priority": 0, + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/7" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example new file mode 100644 index 0000000000..fe40774ec2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example @@ -0,0 +1,15 @@ + + + 5 + new-state + 0 + + eng-GB + eng-GB + + New State + + + New Object State + + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example new file mode 100644 index 0000000000..8405172599 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example @@ -0,0 +1,24 @@ +{ + "ObjectStateCreate": { + "identifier": "new-state", + "priority": "1", + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New State" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example new file mode 100644 index 0000000000..b021c96d1c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example @@ -0,0 +1,12 @@ + + + new-state + 1 + eng-GB + + New State + + + New Object State + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example new file mode 100644 index 0000000000..7ef50958a4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/2", + "id": 2, + "identifier": "locked", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/2" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Locked" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example new file mode 100644 index 0000000000..9eb927312f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example @@ -0,0 +1,15 @@ + + +2 +locked +1 + +eng-GB +eng-GB + + Locked + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example new file mode 100644 index 0000000000..d7d0291644 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example @@ -0,0 +1,31 @@ +{ + "ObjectState": { + "_media-type": "application/vnd.ez.api.ObjectState+json", + "_href": "/api/ezp/v2/content/objectstategroups/6/objectstates/9", + "id": 9, + "identifier": "closed", + "priority": 1, + "ObjectStateGroup": { + "_media-type": "application/vnd.ez.api.ObjectStateGroup+json", + "_href": "/api/ezp/v2/content/objectstategroups/6" + }, + "defaultLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example new file mode 100644 index 0000000000..18678b6835 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example @@ -0,0 +1,15 @@ + + + 2 + locked + 1 + + eng-GB + eng-GB + + New Object State name + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example new file mode 100644 index 0000000000..fbf9c69e38 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example @@ -0,0 +1,12 @@ +{ + "ObjectStateGroup": { + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Object State name" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example new file mode 100644 index 0000000000..579ee16e61 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example @@ -0,0 +1,8 @@ + + + 3 + eng-GB + + New Object State name + + diff --git a/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.json.example b/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.json.example new file mode 100644 index 0000000000..3080193759 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.json.example @@ -0,0 +1,50 @@ +{ + "SectionList": { + "_media-type": "application/vnd.ez.api.SectionList+json", + "_href": "/api/ezp/v2/content/sections", + "Section": [ + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1", + "sectionId": 1, + "identifier": "standard", + "name": "Standard" + }, + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2", + "sectionId": 2, + "identifier": "users", + "name": "Users" + }, + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/3", + "sectionId": 3, + "identifier": "media", + "name": "Media" + }, + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/6", + "sectionId": 6, + "identifier": "form", + "name": "Form" + }, + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/7", + "sectionId": 7, + "identifier": "template", + "name": "Template" + }, + { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/8", + "sectionId": 8, + "identifier": "restricted", + "name": "Restricted" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.xml.example new file mode 100644 index 0000000000..2edbc4af6f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/GET/SectionList.xml.example @@ -0,0 +1,33 @@ + + +
    + 1 + standard + Standard +
    +
    + 2 + users + Users +
    +
    + 3 + media + Media +
    +
    + 6 + form + Form +
    +
    + 7 + template + Template +
    +
    + 8 + restricted + Restricted +
    +
    diff --git a/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.json.example b/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.json.example new file mode 100644 index 0000000000..b27251b47d --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.json.example @@ -0,0 +1,6 @@ +{ + "SectionInput": { + "identifier": "restricted", + "name": "Restricted" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.xml.example new file mode 100644 index 0000000000..de01c181ae --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/POST/Section.xml.example @@ -0,0 +1,6 @@ + +
    + 7 + restricted + Restricted +
    diff --git a/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.json.example b/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.json.example new file mode 100644 index 0000000000..5c8b159c7a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/13", + "sectionId": 13, + "identifier": "restricted", + "name": "Restricted" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.xml.example new file mode 100644 index 0000000000..d09d790154 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/POST/SectionInput.xml.example @@ -0,0 +1,5 @@ + + + restricted + Restricted + \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.json.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.json.example new file mode 100644 index 0000000000..7226576642 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/10", + "sectionId": 10, + "identifier": "design", + "name": "Design" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.xml.example new file mode 100644 index 0000000000..9985f2de1c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/GET/Section.xml.example @@ -0,0 +1,6 @@ + +
    + 10 + design + Design +
    diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.json.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.json.example new file mode 100644 index 0000000000..fde2758b85 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.json.example @@ -0,0 +1,9 @@ +{ + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/7", + "sectionId": 7, + "identifier": "template", + "name": "Template" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.xml.example new file mode 100644 index 0000000000..7ee3afde19 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/Section.xml.example @@ -0,0 +1,6 @@ + +
    + 7 + template + Template +
    diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.json.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.json.example new file mode 100644 index 0000000000..6e93f42018 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.json.example @@ -0,0 +1,6 @@ +{ + "SectionInput": { + "identifier": "template", + "name": "Template" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.xml.example b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.xml.example new file mode 100644 index 0000000000..1160bc4167 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/sections/section_id/PATCH/SectionInput.xml.example @@ -0,0 +1,5 @@ + + + template + Template + diff --git a/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.json.example b/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.json.example new file mode 100644 index 0000000000..ed356935f2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.json.example @@ -0,0 +1,146 @@ +{ + "Trash": { + "_media-type": "application/vnd.ez.api.Trash+json", + "_href": "/api/ezp/v2/content/trash", + "TrashItem": [ + { + "_media-type": "application/vnd.ez.api.TrashItem+json", + "_href": "/api/ezp/v2/content/trash/87", + "id": 87, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/87/", + "depth": 3, + "childCount": 0, + "remoteId": "7cc6565354858f39a794bf64aa2c2761", + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/91" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/91", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/91", + "_remoteId": "de906bef76f08700662bfaf1579871f0", + "_id": 91, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2" + }, + "Name": "test_article", + "TranslatedName": "test_article", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/91/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/91/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/91/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/11" + }, + "lastModificationDate": "2021-07-19T08:31:01+00:00", + "publishedDate": "2021-07-19T08:31:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/91/objectstates" + } + } + } + }, + { + "_media-type": "application/vnd.ez.api.TrashItem+json", + "_href": "/api/ezp/v2/content/trash/89", + "id": 89, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2" + }, + "pathString": "/1/2/89/", + "depth": 2, + "childCount": 0, + "remoteId": "256c2e7d71e927bab32a901878827312", + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/96" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/96", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/96", + "_remoteId": "d5bad9eb55cfe8572adf04452a2b206e", + "_id": 96, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1" + }, + "Name": "All Articles test", + "TranslatedName": "All Articles test", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/96/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/96/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/96/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "lastModificationDate": "2021-07-19T12:43:02+00:00", + "publishedDate": "2021-07-19T12:42:47+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 2, + "alwaysAvailable": true, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/96/objectstates" + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.xml.example b/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.xml.example new file mode 100644 index 0000000000..a99cfab343 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/trash/GET/Trash.xml.example @@ -0,0 +1,34 @@ + + + + 58 + 0 + false + false + + /1/2/56/58/ + 3 + 0 + 59800915ad2eb8514de0bebe84f6ccba + + PATH + ASC + + + + Folder 1 + + +
    + + + 2019-02-06T09:03:09+01:00 + 2019-02-06T09:03:09+01:00 + eng-GB + 1 + true + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.json.example b/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.json.example new file mode 100644 index 0000000000..27f29b8676 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.json.example @@ -0,0 +1,71 @@ +{ + "TrashItem": { + "_media-type": "application/vnd.ez.api.TrashItem+json", + "_href": "/api/ezp/v2/content/trash/87", + "id": 87, + "priority": 0, + "hidden": false, + "invisible": false, + "ParentLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/2/57" + }, + "pathString": "/1/2/57/87/", + "depth": 3, + "childCount": 0, + "remoteId": "7cc6565354858f39a794bf64aa2c2761", + "Content": { + "_media-type": "application/vnd.ez.api.Content+json", + "_href": "/api/ezp/v2/content/objects/91" + }, + "sortField": "PATH", + "sortOrder": "ASC", + "ContentInfo": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/91", + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/91", + "_remoteId": "de906bef76f08700662bfaf1579871f0", + "_id": 91, + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2" + }, + "Name": "test_article", + "TranslatedName": "test_article", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/91/versions" + }, + "CurrentVersion": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/91/currentversion" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/1" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/91/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/11" + }, + "lastModificationDate": "2021-07-19T08:31:01+00:00", + "publishedDate": "2021-07-19T08:31:01+00:00", + "mainLanguageCode": "eng-GB", + "currentVersionNo": 1, + "alwaysAvailable": false, + "isHidden": false, + "status": "TRASHED", + "ObjectStates": { + "_media-type": "application/vnd.ez.api.ContentObjectStates+json", + "_href": "/api/ezp/v2/content/objects/91/objectstates" + } + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.xml.example b/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.xml.example new file mode 100644 index 0000000000..f32225606d --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/trash/trash_itemid/GET/TrashItem.xml.example @@ -0,0 +1,33 @@ + + + 81 + 0 + false + false + + /1/2/42/81/ + 3 + 0 + 135e8a84b61848a67be36e9552d2724d + + PATH + ASC + + + + Folder 1 + + +
    + + + 2019-04-25T12:45:58+02:00 + 2019-04-25T12:45:58+02:00 + eng-GB + 1 + true + TRASHED + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.json.example new file mode 100644 index 0000000000..ae35e9e20c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.json.example @@ -0,0 +1,68 @@ +{ + "ContentTypeGroupList": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupList+json", + "_href": "/api/ezp/v2/content/typegroups", + "ContentTypeGroup": [ + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/1", + "id": 1, + "identifier": "Content", + "created": "2002-09-05T09:08:48+00:00", + "modified": "2002-10-06T16:35:06+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json", + "_href": "/api/ezp/v2/content/typegroups/1/types" + } + }, + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/2", + "id": 2, + "identifier": "Users", + "created": "2002-09-05T09:09:01+00:00", + "modified": "2002-10-06T16:35:13+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json", + "_href": "/api/ezp/v2/content/typegroups/2/types" + } + }, + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/3", + "id": 3, + "identifier": "Media", + "created": "2002-09-14T13:22:23+00:00", + "modified": "2002-10-06T16:35:20+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json", + "_href": "/api/ezp/v2/content/typegroups/3/types" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.xml.example new file mode 100644 index 0000000000..a20da36053 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/GET/ContentTypeGroupList.xml.example @@ -0,0 +1,30 @@ + + + + 1 + Content + 2002-09-05T11:08:48+02:00 + 2002-10-06T18:35:06+02:00 + + + + + + 2 + Users + 2002-09-05T11:09:01+02:00 + 2002-10-06T18:35:13+02:00 + + + + + + 3 + Media + 2002-09-14T15:22:23+02:00 + 2002-10-06T18:35:20+02:00 + + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.json.example new file mode 100644 index 0000000000..a8f5aa8012 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.json.example @@ -0,0 +1,22 @@ +{ + "ContentTypeGroup": { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/6", + "id": 6, + "identifier": "newContentTypeGroup", + "created": "2021-08-11T09:44:18+00:00", + "modified": "2021-08-11T09:44:18+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json", + "_href": "/api/ezp/v2/content/typegroups/6/types" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.xml.example new file mode 100644 index 0000000000..2c14412623 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroup.xml.example @@ -0,0 +1,10 @@ + + + 7 + newContentTypeGroup + 2012-02-31T12:45:00 + 2012-02-31T12:45:00 + + + + diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.json.example new file mode 100644 index 0000000000..164105095b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.json.example @@ -0,0 +1,5 @@ +{ + "ContentTypeGroupInput": { + "identifier": "newContentTypeGroup" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example new file mode 100644 index 0000000000..f88b35a97b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/POST/ContentTypeGroupInput.xml.example @@ -0,0 +1,4 @@ + + + newContentTypeGroup + diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example new file mode 100644 index 0000000000..5c96428208 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example @@ -0,0 +1,1595 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ez.api.ContentTypeList+json", + "_href": "/api/ezp/v2/content/typegroups/1/types", + "ContentType": [ + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/50", + "id": 50, + "status": "DEFINED", + "identifier": "copy_of_article_50", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-14T10:30:00+00:00", + "modificationDate": "2021-07-14T10:30:00+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/50/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/50/draft" + }, + "remoteId": "fc9434492469d136ffe13377bfdcbb28", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/197", + "id": 197, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/198", + "id": 198, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/199", + "id": 199, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/200", + "id": 200, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/201", + "id": 201, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/202", + "id": 202, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/203", + "id": 203, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/44", + "id": 44, + "status": "DEFINED", + "identifier": "dog_breed", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Dog Breed" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:28:00+00:00", + "modificationDate": "2021-06-28T11:29:02+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/44/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/44/draft" + }, + "remoteId": "b348c95f7c573d4d502d6b59f80e618a", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/190", + "id": 190, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/191", + "id": 191, + "identifier": "photo", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Photo" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/192", + "id": 192, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Full Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/55", + "id": 55, + "status": "DEFINED", + "identifier": "folder_55", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-28T07:57:22+00:00", + "modificationDate": "2021-07-28T07:57:34+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/55/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/55/draft" + }, + "remoteId": "4eb7bc76f6d0bd1d5ded2d8936cc6afb", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/55/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/55/fieldDefinitions/210", + "id": 210, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/55/fieldDefinitions/211", + "id": 211, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/55/fieldDefinitions/212", + "id": 212, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/55/fieldDefinitions/213", + "id": 213, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "\n
    \n", + "xhtml5edit": "\n
    \n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/45", + "id": 45, + "status": "DEFINED", + "identifier": "tip", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Tip" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:29:19+00:00", + "modificationDate": "2021-07-26T09:33:06+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/45/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/45/draft" + }, + "remoteId": "e6490e8a785edacd48629f2022be3125", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/193", + "id": 193, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/194", + "id": 194, + "identifier": "body", + "fieldType": "eztext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/204", + "id": 204, + "identifier": "richtext", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "richtext" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example new file mode 100644 index 0000000000..299e70eba5 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example @@ -0,0 +1,1034 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ez.api.ContentTypeList+xml" href="/api/ezp/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2021-06-28T11:31:22+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/50"> + <id>50</id> + <status>DEFINED</status> + <identifier>copy_of_article_50</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-07-14T10:30:00+00:00</creationDate> + <modificationDate>2021-07-14T10:30:00+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/50/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/50/draft"/> + <remoteId>fc9434492469d136ffe13377bfdcbb28</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/197"> + <id>197</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/198"> + <id>198</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/199"> + <id>199</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/200"> + <id>200</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/201"> + <id>201</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/202"> + <id>202</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/50/fieldDefinitions/203"> + <id>203</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/44"> + <id>44</id> + <status>DEFINED</status> + <identifier>dog_breed</identifier> + <names> + <value languageCode="eng-GB">Dog Breed</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-06-28T11:28:00+00:00</creationDate> + <modificationDate>2021-06-28T11:29:02+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/44/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/44/draft"/> + <remoteId>b348c95f7c573d4d502d6b59f80e618a</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/44/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/44/fieldDefinitions/190"> + <id>190</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/44/fieldDefinitions/191"> + <id>191</id> + <identifier>photo</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Photo</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/44/fieldDefinitions/192"> + <id>192</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup>content</fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Full Description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2015-11-29T21:14:32+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/4"> + <id>4</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/155"> + <id>155</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/119"> + <id>119</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/156"> + <id>156</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/55"> + <id>55</id> + <status>DEFINED</status> + <identifier>folder_55</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-07-28T07:57:22+00:00</creationDate> + <modificationDate>2021-07-28T07:57:34+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/55/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/55/draft"/> + <remoteId>4eb7bc76f6d0bd1d5ded2d8936cc6afb</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/55/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/55/fieldDefinitions/210"> + <id>210</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/55/fieldDefinitions/211"> + <id>211</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/55/fieldDefinitions/212"> + <id>212</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/55/fieldDefinitions/213"> + <id>213</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/43"> + <id>43</id> + <status>DEFINED</status> + <identifier>form</identifier> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <creationDate>2018-09-17T06:46:13+00:00</creationDate> + <modificationDate>2018-09-17T06:47:14+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/43/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/43/draft"/> + <remoteId>6f7f21df775a33c1e4bbc76b48c38476</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions/189"> + <id>189</id> + <identifier>form</identifier> + <fieldType>ezform</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="fields"/> + <value key="content_id"/> + <value key="content_field_id"/> + <value key="language_code"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/45"> + <id>45</id> + <status>DEFINED</status> + <identifier>tip</identifier> + <names> + <value languageCode="eng-GB">Tip</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2021-06-28T11:29:19+00:00</creationDate> + <modificationDate>2021-07-26T09:33:06+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/45/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/45/draft"/> + <remoteId>e6490e8a785edacd48629f2022be3125</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/45/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/45/fieldDefinitions/193"> + <id>193</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/45/fieldDefinitions/194"> + <id>194</id> + <identifier>body</identifier> + <fieldType>eztext</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings> + <value key="textRows">10</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/45/fieldDefinitions/204"> + <id>204</id> + <identifier>richtext</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup>content</fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">richtext</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/42"> + <id>42</id> + <status>DEFINED</status> + <identifier>landing_page</identifier> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2015-07-03T12:00:26+00:00</creationDate> + <modificationDate>2015-07-03T12:00:26+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/42/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/42/draft"/> + <remoteId>60c03e9758465eb69d56b3afb6adf18e</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/185"> + <id>185</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">Title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/186"> + <id>186</id> + <identifier>description</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>20</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page description</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/> + </value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/187"> + <id>187</id> + <identifier>page</identifier> + <fieldType>ezlandingpage</fieldType> + <fieldGroup>content</fieldGroup> + <position>30</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="layout">default</value> + <value key="zones"> + <value> + <value key="id">default_id</value> + <value key="name">default</value> + <value key="blocks"/> + </value> + </value> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page</value> + </descriptions> + <fieldSettings> + <value key="availableBlocks"/> + <value key="availableLayouts"/> + <value key="editorMode">page_view_mode</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example new file mode 100644 index 0000000000..b05e1466a3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example @@ -0,0 +1,22 @@ +{ + "ContentTypeGroup": { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/4", + "id": 4, + "identifier": "updatedIdentifer", + "created": "2021-08-11T09:44:18+00:00", + "modified": "2021-08-11T10:10:04+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "ContentTypes": { + "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json", + "_href": "/api/ezp/v2/content/typegroups/6/types" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example new file mode 100644 index 0000000000..d77ad659b9 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroup media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1"> + <id>4</id> + <identifier>updatedIdentifer</identifier> + <created>2002-09-05T11:08:48+02:00</created> + <modified>2019-02-22T14:42:55+01:00</modified> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <ContentTypes media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/typegroups/1/types"/> +</ContentTypeGroup> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example new file mode 100644 index 0000000000..bf3fa9ebc7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example @@ -0,0 +1,5 @@ +{ + "ContentTypeGroupInput": { + "identifier": "updatedIdentifer" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example new file mode 100644 index 0000000000..2a79ff0f65 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupInput> + <identifier>updatedIdentifer</identifier> +</ContentTypeGroupInput> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example new file mode 100644 index 0000000000..3b95581a2e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example @@ -0,0 +1,2011 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ez.api.ContentTypeList+json", + "_href": "/api/ezp/v2/content/types", + "ContentType": [ + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/50", + "id": 50, + "status": "DEFINED", + "identifier": "copy_of_article_50", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-07-14T10:30:00+00:00", + "modificationDate": "2021-07-14T10:30:00+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/50/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/50/draft" + }, + "remoteId": "fc9434492469d136ffe13377bfdcbb28", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/197", + "id": 197, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/198", + "id": 198, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/199", + "id": 199, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/200", + "id": 200, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/201", + "id": 201, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/202", + "id": 202, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/50/fieldDefinitions/203", + "id": 203, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/44", + "id": 44, + "status": "DEFINED", + "identifier": "dog_breed", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Dog Breed" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:28:00+00:00", + "modificationDate": "2021-06-28T11:29:02+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/44/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/44/draft" + }, + "remoteId": "b348c95f7c573d4d502d6b59f80e618a", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/190", + "id": 190, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/191", + "id": 191, + "identifier": "photo", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Photo" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/44/fieldDefinitions/192", + "id": 192, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Full Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/45", + "id": 45, + "status": "DEFINED", + "identifier": "tip", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Tip" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2021-06-28T11:29:19+00:00", + "modificationDate": "2021-07-26T09:33:06+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/45/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/45/draft" + }, + "remoteId": "e6490e8a785edacd48629f2022be3125", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/193", + "id": 193, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/194", + "id": 194, + "identifier": "body", + "fieldType": "eztext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/45/fieldDefinitions/204", + "id": 204, + "identifier": "richtext", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "richtext" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4", + "id": 4, + "status": "DEFINED", + "identifier": "user", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2004-04-15T08:39:24+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/4/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4/draft" + }, + "remoteId": "40faa822edc579b02c25f6bb7beec3ad", + "urlAliasSchema": null, + "nameSchema": "<first_name> <last_name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions/8", + "id": 8, + "identifier": "first_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "First name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions/9", + "id": 9, + "identifier": "last_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Last name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions/12", + "id": 12, + "identifier": "user_account", + "fieldType": "ezuser", + "fieldGroup": "", + "position": 3, + "isTranslatable": false, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User account" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "PasswordTTL": 0, + "PasswordTTLWarning": 0, + "RequireUniqueEmail": false, + "UsernamePattern": "^[^@]+$" + }, + "validatorConfiguration": { + "PasswordValueValidator": { + "requireAtLeastOneUpperCaseCharacter": true, + "requireAtLeastOneLowerCaseCharacter": true, + "requireAtLeastOneNumericCharacter": true, + "requireAtLeastOneNonAlphanumericCharacter": false, + "requireNewPassword": false, + "minLength": 10 + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions/179", + "id": 179, + "identifier": "signature", + "fieldType": "eztext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Signature" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "textRows": 10 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/4/fieldDefinitions/180", + "id": 180, + "identifier": "image", + "fieldType": "ezimage", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": 10 + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/3", + "id": 3, + "status": "DEFINED", + "identifier": "user_group", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "User group" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2003-03-24T08:32:23+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/3/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/3/draft" + }, + "remoteId": "25b4268cdcd01921b808a0d854b877ef", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/3/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/3/fieldDefinitions/6", + "id": 6, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/3/fieldDefinitions/7", + "id": 7, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/12", + "id": 12, + "status": "DEFINED", + "identifier": "file", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "File" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2003-05-08T09:17:52+00:00", + "modificationDate": "2003-05-08T09:21:09+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/12/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/12/draft" + }, + "remoteId": "637d58bfddf164627bdfd265733280a0", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/12/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/12/fieldDefinitions/146", + "id": 146, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New file", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/12/fieldDefinitions/147", + "id": 147, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/12/fieldDefinitions/148", + "id": 148, + "identifier": "file", + "fieldType": "ezbinaryfile", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "File" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": null + } + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/5", + "id": 5, + "status": "DEFINED", + "identifier": "image", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-09-08T11:36:32+00:00", + "modificationDate": "2003-03-24T08:33:04+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/5/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/5/draft" + }, + "remoteId": "f6df12aa74e36230eb675f364fccd25a", + "urlAliasSchema": null, + "nameSchema": "<name>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/5/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/5/fieldDefinitions/116", + "id": 116, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 150, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/5/fieldDefinitions/117", + "id": 117, + "identifier": "caption", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Caption" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/5/fieldDefinitions/118", + "id": 118, + "identifier": "image", + "fieldType": "ezimage", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "FileSizeValidator": { + "maxFileSize": 10 + } + } + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example new file mode 100644 index 0000000000..5d864b86f3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfoList media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/typegroups/1/types"> + <ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + </ContentTypeInfo> + <ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2015-11-29T22:14:32+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + </ContentTypeInfo> +</ContentTypeInfoList> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example new file mode 100644 index 0000000000..7b62dbd957 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ez.api.ContentTypeList+xml" href="/api/ezp/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example new file mode 100644 index 0000000000..7989457814 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/20/draft"> + <id>20</id> + <status>DRAFT</status> + <identifier>newContentType</identifier> + <names> + <value languageCode="eng-GB">New Content Type</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is a description</value> + </descriptions> + <creationDate>2019-02-26T09:39:58+01:00</creationDate> + <modificationDate>2019-02-26T09:39:58+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/20/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/20/draft"/> + <remoteId>remoteId-qwert548</remoteId> + <urlAliasSchema><title></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/20/draft/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/20/draft/fieldDefinitions/223"> + <id>223</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New Title</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is the title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="minStringLength">0</value> + <value key="maxStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example new file mode 100644 index 0000000000..71ea8b6d68 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example @@ -0,0 +1,86 @@ +{ + "ContentTypeCreate": { + "identifier": "new_content_type", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "New Content Type" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is a description" + } + ] + }, + "remoteId": "remoteId-qwerty548", + "urlAliasSchema": "<title>", + "nameSchema": "<title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "FieldDefinition": [ + { + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New Title", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is the title" + } + ] + } + }, + { + "identifier": "summary", + "fieldType": "ezrichtext", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Summary" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is the summary" + } + ] + } + } + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example new file mode 100644 index 0000000000..2b11ae2953 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeCreate> + <identifier>newContentType</identifier> + <names> + <value languageCode="eng-US">New Content Type</value> + </names> + <descriptions> + <value languageCode="eng-US">This is a description</value> + </descriptions> + <remoteId>remoteId-qwert548</remoteId> + <urlAliasSchema><title></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-US</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions> + <FieldDefinition> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New Title</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-US">Title</value> + </names> + <descriptions> + <value languageCode="eng-US">This is the title</value> + </descriptions> + </FieldDefinition> + <FieldDefinition> + <identifier>summary</identifier> + <fieldType>ezxmltext</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="utf-8"?><section/></value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-US">Summary</value> + </names> + <descriptions> + <value languageCode="eng-US">This is the summary</value> + </descriptions> + </FieldDefinition> + </FieldDefinitions> +</ContentTypeCreate> diff --git a/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.json.example b/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.json.example new file mode 100644 index 0000000000..11fbcc45c3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.json.example @@ -0,0 +1,733 @@ +{ + "ContentTypeList": { + "_media-type": "application/vnd.ez.api.ContentTypeList+json", + "_href": "/api/ezp/v2/content/typegroups/1/types", + "ContentType": [ + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2004-04-20T09:56:29+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": null, + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/154", + "id": 154, + "identifier": "image", + "fieldType": "ezobjectrelation", + "fieldGroup": "", + "position": 7, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": { + "selectionMethod": "SELECTION_BROWSE", + "selectionRoot": "", + "selectionContentTypes": [] + }, + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1", + "id": 1, + "status": "DEFINED", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2015-11-29T21:14:32+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/4", + "id": 4, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "Folder", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/155", + "id": 155, + "identifier": "short_name", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short name" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 100, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/119", + "id": 119, + "identifier": "short_description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/1/fieldDefinitions/156", + "id": 156, + "identifier": "description", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43", + "id": 43, + "status": "DEFINED", + "identifier": "form", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2018-09-17T06:46:13+00:00", + "modificationDate": "2018-09-17T06:47:14+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/43/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/43/draft" + }, + "remoteId": "6f7f21df775a33c1e4bbc76b48c38476", + "urlAliasSchema": "", + "nameSchema": "<title>", + "isContainer": false, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/188", + "id": 188, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/43/fieldDefinitions/189", + "id": 189, + "identifier": "form", + "fieldType": "ezform", + "fieldGroup": "content", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "fields": [], + "content_id": null, + "content_field_id": null, + "language_code": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Form" + } + ] + }, + "descriptions": { + "value": [] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42", + "id": 42, + "status": "DEFINED", + "identifier": "landing_page", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "" + } + ] + }, + "creationDate": "2015-07-03T12:00:26+00:00", + "modificationDate": "2015-07-03T12:00:26+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/42/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/42/draft" + }, + "remoteId": "60c03e9758465eb69d56b3afb6adf18e", + "urlAliasSchema": "", + "nameSchema": "<name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PUBLISHED", + "defaultSortOrder": "DESC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/185", + "id": 185, + "identifier": "name", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 10, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/186", + "id": 186, + "identifier": "description", + "fieldType": "ezstring", + "fieldGroup": "content", + "position": 20, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Description" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page description" + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": null, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/42/fieldDefinitions/187", + "id": 187, + "identifier": "page", + "fieldType": "ezlandingpage", + "fieldGroup": "content", + "position": 30, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "layout": "default", + "zones": [ + { + "id": "default_id", + "name": "default", + "blocks": [] + } + ] + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Landing page" + } + ] + }, + "fieldSettings": { + "availableBlocks": null, + "availableLayouts": null, + "editorMode": "page_view_mode" + }, + "validatorConfiguration": [] + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.xml.example b/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.xml.example new file mode 100644 index 0000000000..27a1f1d83a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/GET/ContentTypeInfoList.xml.example @@ -0,0 +1,480 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeList media-type="application/vnd.ez.api.ContentTypeList+xml" href="/api/ezp/v2/content/typegroups/1/types"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2004-04-20T09:56:29+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1"> + <id>1</id> + <status>DEFINED</status> + <identifier>folder</identifier> + <names> + <value languageCode="eng-GB">Folder</value> + </names> + <descriptions/> + <creationDate>2002-06-18T09:21:38+00:00</creationDate> + <modificationDate>2015-11-29T21:14:32+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/1/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1/draft"/> + <remoteId>a3d405b81be900468eb153d774f4f0d2</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_name|name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/4"> + <id>4</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>Folder</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/155"> + <id>155</id> + <identifier>short_name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short name</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">100</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/119"> + <id>119</id> + <identifier>short_description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/1/fieldDefinitions/156"> + <id>156</id> + <identifier>description</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/43"> + <id>43</id> + <status>DEFINED</status> + <identifier>form</identifier> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <creationDate>2018-09-17T06:46:13+00:00</creationDate> + <modificationDate>2018-09-17T06:47:14+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/43/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/43/draft"/> + <remoteId>6f7f21df775a33c1e4bbc76b48c38476</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><title></nameSchema> + <isContainer>false</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/43/fieldDefinitions/189"> + <id>189</id> + <identifier>form</identifier> + <fieldType>ezform</fieldType> + <fieldGroup>content</fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="fields"/> + <value key="content_id"/> + <value key="content_field_id"/> + <value key="language_code"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Form</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/42"> + <id>42</id> + <status>DEFINED</status> + <identifier>landing_page</identifier> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <creationDate>2015-07-03T12:00:26+00:00</creationDate> + <modificationDate>2015-07-03T12:00:26+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/42/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/42/draft"/> + <remoteId>60c03e9758465eb69d56b3afb6adf18e</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PUBLISHED</defaultSortField> + <defaultSortOrder>DESC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/185"> + <id>185</id> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions> + <value languageCode="eng-GB">Title</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/186"> + <id>186</id> + <identifier>description</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup>content</fieldGroup> + <position>20</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Description</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page description</value> + </descriptions> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength"/> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/42/fieldDefinitions/187"> + <id>187</id> + <identifier>page</identifier> + <fieldType>ezlandingpage</fieldType> + <fieldGroup>content</fieldGroup> + <position>30</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="layout">default</value> + <value key="zones"> + <value> + <value key="id">default_id</value> + <value key="name">default</value> + <value key="blocks"/></value> + </value> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Landing page</value> + </names> + <descriptions> + <value languageCode="eng-GB">Landing page</value> + </descriptions> + <fieldSettings> + <value key="availableBlocks"/> + <value key="availableLayouts"/> + <value key="editorMode">page_view_mode</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> + </ContentType> +</ContentTypeList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.json.example new file mode 100644 index 0000000000..f2c7cad0fa --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.json.example @@ -0,0 +1,303 @@ +{ + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2", + "id": 2, + "status": "DEFINED", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Article" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-06-28T11:31:22+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": "", + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": false, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC", + "FieldDefinitions": { + "_media-type": "application/vnd.ez.api.FieldDefinitionList+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions", + "FieldDefinition": [ + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/1", + "id": 1, + "identifier": "title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 1, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": "New article", + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/152", + "id": 152, + "identifier": "short_title", + "fieldType": "ezstring", + "fieldGroup": "", + "position": 2, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": null, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Short title" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": { + "StringLengthValidator": { + "maxStringLength": 255, + "minStringLength": null + } + } + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/153", + "id": 153, + "identifier": "author", + "fieldType": "ezauthor", + "fieldGroup": "", + "position": 3, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": [], + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Author" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": { + "defaultAuthor": 1 + }, + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/120", + "id": 120, + "identifier": "intro", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 4, + "isTranslatable": true, + "isRequired": true, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Intro" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/121", + "id": 121, + "identifier": "body", + "fieldType": "ezrichtext", + "fieldGroup": "", + "position": 5, + "isTranslatable": true, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://docbook.org/ns/docbook\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"5.0-variant ezpublish-1.0\"/>\n", + "xhtml5edit": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"/>\n" + }, + "isSearchable": true, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Body" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/123", + "id": 123, + "identifier": "enable_comments", + "fieldType": "ezboolean", + "fieldGroup": "", + "position": 6, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": false, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Enable comments" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + }, + { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.xml.example new file mode 100644 index 0000000000..8b9f0edb19 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/GET/ContentType.xml.example @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"> + <id>2</id> + <status>DEFINED</status> + <identifier>article</identifier> + <names> + <value languageCode="eng-GB">Article</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2004-04-20T11:56:29+02:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/> + <remoteId>c15b600eb9198b1924063b5a68758232</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/1"> + <id>1</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/152"> + <id>152</id> + <identifier>short_title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>2</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Short title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/153"> + <id>153</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup></fieldGroup> + <position>3</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/120"> + <id>120</id> + <identifier>intro</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>4</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Intro</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/121"> + <id>121</id> + <identifier>body</identifier> + <fieldType>ezrichtext</fieldType> + <fieldGroup></fieldGroup> + <position>5</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="xml"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-variant ezpublish-1.0"/> +</value> + <value key="xhtml5edit"><?xml version="1.0" encoding="UTF-8"?> +<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"/> +</value> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Body</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/123"> + <id>123</id> + <identifier>enable_comments</identifier> + <fieldType>ezboolean</fieldType> + <fieldGroup></fieldGroup> + <position>6</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>false</defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Enable comments</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration/> + </FieldDefinition> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/154"> + <id>154</id> + <identifier>image</identifier> + <fieldType>ezobjectrelation</fieldType> + <fieldGroup></fieldGroup> + <position>7</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + </defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions/> + <fieldSettings> + <value key="selectionMethod">SELECTION_BROWSE</value> + <value key="selectionRoot"></value> + <value key="selectionContentTypes"/> + </fieldSettings> + <validatorConfiguration/> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example new file mode 100644 index 0000000000..e5bbf09fc1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.json.example @@ -0,0 +1,46 @@ +{ + "ContentTypeInfo": { + "_media-type": "application/vnd.ez.api.ContentTypeInfo+json", + "_href": "/api/ezp/v2/content/types/1/draft", + "id": 1, + "status": "DRAFT", + "identifier": "folder", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Folder" + } + ] + }, + "descriptions": { + "value": [] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-08-11T11:17:58+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/1/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/1/draft" + }, + "remoteId": "a3d405b81be900468eb153d774f4f0d2", + "urlAliasSchema": null, + "nameSchema": "<short_name|name>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example new file mode 100644 index 0000000000..895315bbae --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/3/draft"> + <id>3</id> + <status>DRAFT</status> + <identifier>user_group</identifier> + <names> + <value languageCode="eng-GB">User group</value> + </names> + <descriptions/> + <creationDate>2002-06-18T11:21:38+02:00</creationDate> + <modificationDate>2019-02-25T14:41:53+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/3/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3/draft"/> + <remoteId>25b4268cdcd01921b808a0d854b877ef</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><name></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> +</ContentTypeInfo> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example new file mode 100644 index 0000000000..78464994fc --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example @@ -0,0 +1,5 @@ +{ + "ContentTypeUpdate": { + "defaultAlwaysAvailable": "true" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example new file mode 100644 index 0000000000..9e9d31ba70 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeUpdate> + <defaultAlwaysAvailable>true</defaultAlwaysAvailable> +</ContentTypeUpdate> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example new file mode 100644 index 0000000000..cb592563cc --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example @@ -0,0 +1,51 @@ +{ + "ContentTypeInfo": { + "_media-type": "application/vnd.ez.api.ContentTypeInfo+json", + "_href": "/api/ezp/v2/content/types/2/draft", + "id": 2, + "status": "DRAFT", + "identifier": "article", + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Updated Content Type name" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "This is an updated Content Type description" + } + ] + }, + "creationDate": "2002-06-18T09:21:38+00:00", + "modificationDate": "2021-08-11T11:58:24+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Modifier": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/types/2/groups" + }, + "Draft": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/2/draft" + }, + "remoteId": "c15b600eb9198b1924063b5a68758232", + "urlAliasSchema": null, + "nameSchema": "<short_title|title>", + "isContainer": true, + "mainLanguageCode": "eng-GB", + "defaultAlwaysAvailable": true, + "defaultSortField": "PATH", + "defaultSortOrder": "ASC" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example new file mode 100644 index 0000000000..2c7ada2010 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/14/draft"> + <id>14</id> + <status>DRAFT</status> + <identifier>new_content_type</identifier> + <names> + <value languageCode="eng-GB">Updated Content Type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated Content Type description</value> + </descriptions> + <creationDate>2019-02-06T10:56:36+01:00</creationDate> + <modificationDate>2019-02-25T12:15:51+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/14/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14/draft"/> + <remoteId>d7ae816f22fe929e67a359a2ef6e7cde</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> +</ContentTypeInfo> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example new file mode 100644 index 0000000000..13a838c6b2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example @@ -0,0 +1,16 @@ +{ + "ContentTypeUpdate": { + "names": { + "value": [ { + "_languageCode": "eng-GB", + "#text": "Updated Content Type name" + } ] + }, + "descriptions": { + "value": [ { + "_languageCode": "eng-GB", + "#text": "This is an updated Content Type description" + } ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example new file mode 100644 index 0000000000..803142b186 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeUpdate> + <names> + <value languageCode="eng-GB">Updated Content Type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated Content Type description</value> + </descriptions> +</ContentTypeUpdate> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example new file mode 100644 index 0000000000..e5c2c379b7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14"> + <id>14</id> + <status>DEFINED</status> + <identifier>copy_of_article_14</identifier> + <names> + <value languageCode="eng-GB">Updated Content Type name</value> + </names> + <descriptions> + <value languageCode="eng-GB">This is an updated Content Type description</value> + </descriptions> + <creationDate>2019-02-06T10:56:36+01:00</creationDate> + <modificationDate>2019-02-25T12:15:51+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/14/groups"/> + <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14/draft"/> + <remoteId>d7ae816f22fe929e67a359a2ef6e7cde</remoteId> + <urlAliasSchema></urlAliasSchema> + <nameSchema><short_title|title></nameSchema> + <isContainer>true</isContainer> + <mainLanguageCode>eng-GB</mainLanguageCode> + <defaultAlwaysAvailable>false</defaultAlwaysAvailable> + <defaultSortField>PATH</defaultSortField> + <defaultSortOrder>ASC</defaultSortOrder> + <FieldDefinitions media-type="application/vnd.ez.api.FieldDefinitionList+xml" href="/api/ezp/v2/content/types/14/fieldDefinitions"> + <FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/14/fieldDefinitions/188"> + <id>188</id> + <identifier>title</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>1</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue>New article</defaultValue> + <isSearchable>true</isSearchable> + <names> + <value languageCode="eng-GB">Title</value> + </names> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">255</value> + <value key="minStringLength"/></value> + </validatorConfiguration> + </FieldDefinition> + </FieldDefinitions> +</ContentType> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example new file mode 100644 index 0000000000..8e8a21479a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/14/draft/fieldDefinitions/221"> + <id>221</id> + <identifier>name2</identifier> + <fieldType>ezstring</fieldType> + <fieldGroup></fieldGroup> + <position>0</position> + <isTranslatable>true</isTranslatable> + <isRequired>true</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>true</isSearchable> + <names/> + <descriptions/> + <fieldSettings/> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">32</value> + <value key="minStringLength">8</value> + </value> + </validatorConfiguration> +</FieldDefinition> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example new file mode 100644 index 0000000000..3282099a83 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinitionCreate> + <identifier>name</identifier> + <fieldType>ezstring</fieldType> + <isRequired>true</isRequired> + <validatorConfiguration> + <value key="StringLengthValidator"> + <value key="maxStringLength">32</value> + <value key="minStringLength">8</value> + </value> +</validatorConfiguration> +</FieldDefinitionCreate> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example new file mode 100644 index 0000000000..c1be5e0e01 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example @@ -0,0 +1,38 @@ +{ + "FieldDefinition": { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example new file mode 100644 index 0000000000..f04ca9c000 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> +</FieldDefinition> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example new file mode 100644 index 0000000000..0c20b43165 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/15/draft/fieldDefinitions/197"> + <id>197</id> + <identifier>author</identifier> + <fieldType>ezauthor</fieldType> + <fieldGroup>new_field_group</fieldGroup> + <position>10</position> + <isTranslatable>true</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue/> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Author</value> + </names> + <descriptions/> + <fieldSettings> + <value key="defaultAuthor">1</value> + </fieldSettings> + <validatorConfiguration/> +</FieldDefinition> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example new file mode 100644 index 0000000000..c1f333a86c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinitionUpdate> + <fieldGroup>new_field_group</fieldGroup> + <position>10</position> +</FieldDefinitionUpdate> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example new file mode 100644 index 0000000000..c1be5e0e01 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example @@ -0,0 +1,38 @@ +{ + "FieldDefinition": { + "_media-type": "application/vnd.ez.api.FieldDefinition+json", + "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195", + "id": 195, + "identifier": "image", + "fieldType": "ezimageasset", + "fieldGroup": "content", + "position": 7, + "isTranslatable": false, + "isRequired": false, + "isInfoCollector": false, + "defaultValue": { + "destinationContentId": null, + "alternativeText": null, + "source": null + }, + "isSearchable": false, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Image" + } + ] + }, + "descriptions": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": null + } + ] + }, + "fieldSettings": [], + "validatorConfiguration": [] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example new file mode 100644 index 0000000000..f04ca9c000 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/195"> + <id>195</id> + <identifier>image</identifier> + <fieldType>ezimageasset</fieldType> + <fieldGroup>content</fieldGroup> + <position>7</position> + <isTranslatable>false</isTranslatable> + <isRequired>false</isRequired> + <isInfoCollector>false</isInfoCollector> + <defaultValue> + <value key="destinationContentId"/> + <value key="alternativeText"/> + <value key="source"/> + </defaultValue> + <isSearchable>false</isSearchable> + <names> + <value languageCode="eng-GB">Image</value> + </names> + <descriptions> + <value languageCode="eng-GB"></value> + </descriptions> + <fieldSettings/> + <validatorConfiguration/> +</FieldDefinition> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example new file mode 100644 index 0000000000..5aa4bfac42 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "ContentTypeGroupRefList": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/typegroups/2/types", + "ContentTypeGroupRef": [ + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/1" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example new file mode 100644 index 0000000000..e2d1132822 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/typegroups/2/types"> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1"/> +</ContentTypeGroupRefList> diff --git a/material/search.html b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.json.example similarity index 100% rename from material/search.html rename to docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.json.example diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example new file mode 100644 index 0000000000..b9f32acf68 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/typegroups/15/types"> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1"> + <unlink href="/api/ezp/v2/content/types/15/groups/1" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/3"> + <unlink href="/api/ezp/v2/content/types/15/groups/3" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/2"> + <unlink href="/api/ezp/v2/content/types/15/groups/2" method="DELETE"/> + </ContentTypeGroupRef> +</ContentTypeGroupRefList> diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.json.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.json.example new file mode 100644 index 0000000000..faac6e6ee8 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.json.example @@ -0,0 +1,24 @@ +{ + "ContentTypeGroupRefList": { + "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json", + "_href": "/api/ezp/v2/content/typegroups/2/types", + "ContentTypeGroupRef": [ + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/1", + "unlink": { + "_href": "/api/ezp/v2/content/types/2/groups/1", + "_method": "DELETE" + } + }, + { + "_media-type": "application/vnd.ez.api.ContentTypeGroup+json", + "_href": "/api/ezp/v2/content/typegroups/2", + "unlink": { + "_href": "/api/ezp/v2/content/types/2/groups/2", + "_method": "DELETE" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.xml.example b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.xml.example new file mode 100644 index 0000000000..b533f749eb --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ContentTypeGroupRefList media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/typegroups/15/types"> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1"> + <unlink href="/api/ezp/v2/content/types/15/groups/1" method="DELETE"/> + </ContentTypeGroupRef> + <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/2"> + <unlink href="/api/ezp/v2/content/types/15/groups/2" method="DELETE"/> + </ContentTypeGroupRef> +</ContentTypeGroupRefList> diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.json.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.json.example new file mode 100644 index 0000000000..92be31a0a4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.json.example @@ -0,0 +1,12 @@ +{ + "UrlAliasRefList": { + "_media-type": "application/vnd.ez.api.UrlAliasRefList+json", + "_href": "/api/ezp/v2/content/urlaliases", + "UrlAlias": [ + { + "_media-type": "application/vnd.ez.api.UrlAlias+json", + "_href": "/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.xml.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.xml.example new file mode 100644 index 0000000000..8e7cee1235 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/GET/UrlAliasRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAliasRefList media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/urlaliases"> + <UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" /> +</UrlAliasRefList> diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.json.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.json.example new file mode 100644 index 0000000000..b8c4bc1df6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.json.example @@ -0,0 +1,15 @@ +{ + "UrlAlias": { + "_media-type": "application/vnd.ez.api.UrlAlias+json", + "_href": "/api/ezp/v2/content/urlaliases/0-f530173ad554787c1fe30dc929d98360", + "_id": "0-f530173ad554787c1fe30dc929d98360", + "_type": "RESOURCE", + "resource": "content/view/full", + "path": "/example-global", + "languageCodes": "eng-GB", + "alwaysAvailable": true, + "isHistory": false, + "forward": true, + "custom": true + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.xml.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.xml.example new file mode 100644 index 0000000000..8b504eeb9e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAlias.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-deca3dadca45c3dff7861274b8e67ed7" id="0-deca3dadca45c3dff7861274b8e67ed7" type="LOCATION"> + <location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/59"/> + <path>/example-urlalias</path> + <languageCodes>eng-GB</languageCodes> + <alwaysAvailable>true</alwaysAvailable> + <isHistory>false</isHistory> + <forward>true</forward> + <custom>true</custom> +</UrlAlias> diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.json.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.json.example new file mode 100644 index 0000000000..0eb5205140 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.json.example @@ -0,0 +1,10 @@ +{ + "UrlAliasCreate": { + "_type": "GLOBAL", + "resource": "module:content/view/full", + "path": "example-global", + "languageCode": "eng-GB", + "alwaysAvailable": "true", + "forward": "true" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.xml.example new file mode 100644 index 0000000000..5bfbbd326a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/POST/UrlAliasCreate.xml.example @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAliasCreate type="LOCATION"> + <location href='/api/ezp/v2/content/locations/1/2/59' /> + <path>example-global</path> + <languageCode>eng-GB</languageCode> + <alwaysAvailable>true</alwaysAvailable> + <forward>true</forward> +</UrlAliasCreate> diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example new file mode 100644 index 0000000000..5ceabfb13f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example @@ -0,0 +1,15 @@ +{ + "UrlAlias": { + "_media-type": "application/vnd.ez.api.UrlAlias+json", + "_href": "/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36", + "_id": "0-2cecdff5f90b8595d76b68c417b09f36", + "_type": "RESOURCE", + "resource": "content/view/full", + "path": "/example-global", + "languageCodes": "eng-GB", + "alwaysAvailable": true, + "isHistory": false, + "forward": true, + "custom": true + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example b/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example new file mode 100644 index 0000000000..2b800d76a3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" id="0-2cecdff5f90b8595d76b68c417b09f36" type="RESOURCE"> + <resource>content/view/full</resource> + <path>/example-global</path> + <languageCodes>eng-GB</languageCodes> + <alwaysAvailable>true</alwaysAvailable> + <isHistory>false</isHistory> + <forward>true</forward> + <custom>true</custom> +</UrlAlias> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.json.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.json.example new file mode 100644 index 0000000000..197e742bc7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.json.example @@ -0,0 +1,16 @@ +{ + "UrlWildcardList": { + "_media-type": "application/vnd.ez.api.UrlWildcardList+json", + "_href": "/api/ezp/v2/content/urlwildcards", + "UrlWildcard": [ + { + "_media-type": "application/vnd.ez.api.UrlWildcard+json", + "_href": "/api/ezp/v2/content/urlwildcards/1", + "_id": 1, + "sourceUrl": "/api/ezp/v2/content/location/2", + "destinationUrl": "/api/ezp/v2/content/location/59", + "forward": true + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.xml.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.xml.example new file mode 100644 index 0000000000..f6fe096f64 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/GET/UrlWildcardList.xml.example @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcardList media-type="application/vnd.ez.api.UrlWildcardList+xml" href="/api/ezp/v2/content/urlwildcards"> + <UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/1" id="1"> + <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl> + <forward>true</forward> + </UrlWildcard> +</UrlWildcardList> diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.json.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.json.example new file mode 100644 index 0000000000..b2e8e7960d --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.json.example @@ -0,0 +1,10 @@ +{ + "UrlWildcard": { + "_media-type": "application/vnd.ez.api.UrlWildcard+json", + "_href": "/api/ezp/v2/content/urlwildcards/6", + "_id": 6, + "sourceUrl": "/api/ezp/v2/content/location/2", + "destinationUrl": "/api/ezp/v2/content/location/59", + "forward": true + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.xml.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.xml.example new file mode 100644 index 0000000000..0cb622d56e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcard.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/1" id="1"> + <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcard> diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example new file mode 100644 index 0000000000..33566c5c41 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.json.example @@ -0,0 +1,7 @@ +{ + "UrlWildCardCreate": { + "sourceUrl": "/api/ezp/v2/content/location/2", + "destinationUrl": "/api/ezp/v2/content/location/59", + "forward": true + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example new file mode 100644 index 0000000000..f039197ea6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + <UrlWildcardCreate> + <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcardCreate> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example new file mode 100644 index 0000000000..d906853ee8 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example @@ -0,0 +1,10 @@ +{ + "UrlWildcard": { + "_media-type": "application/vnd.ez.api.UrlWildcard+json", + "_href": "/api/ezp/v2/content/urlwildcards/4", + "_id": 4, + "sourceUrl": "/api/ezp/v2/content/location/2", + "destinationUrl": "/api/ezp/v2/content/location/59", + "forward": true + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example b/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example new file mode 100644 index 0000000000..b83c42126a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/4" id="4"> + <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl> + <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl> + <forward>true</forward> +</UrlWildcard> diff --git a/docs/api/rest_api_reference/input/examples/services/countries/GET/CountriesList.xml.example b/docs/api/rest_api_reference/input/examples/services/countries/GET/CountriesList.xml.example new file mode 100644 index 0000000000..209972ea5c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/services/countries/GET/CountriesList.xml.example @@ -0,0 +1,1479 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CountryList media-type="application/vnd.ez.api.CountryList+xml"> + <Country media-type="application/vnd.ez.api.Country+xml" id="AF"> + <name>Afghanistan</name> + <Alpha2>AF</Alpha2> + <Alpha3>AFG</Alpha3> + <IDC>93</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AX"> + <name>Åland</name> + <Alpha2>AX</Alpha2> + <Alpha3>ALA</Alpha3> + <IDC>358</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AL"> + <name>Albania</name> + <Alpha2>AL</Alpha2> + <Alpha3>ALB</Alpha3> + <IDC>355</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DZ"> + <name>Algeria</name> + <Alpha2>DZ</Alpha2> + <Alpha3>DZA</Alpha3> + <IDC>213</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AS"> + <name>American Samoa</name> + <Alpha2>AS</Alpha2> + <Alpha3>ASM</Alpha3> + <IDC>1684</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AD"> + <name>Andorra</name> + <Alpha2>AD</Alpha2> + <Alpha3>AND</Alpha3> + <IDC>376</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AO"> + <name>Angola</name> + <Alpha2>AO</Alpha2> + <Alpha3>AGO</Alpha3> + <IDC>244</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AI"> + <name>Anguilla</name> + <Alpha2>AI</Alpha2> + <Alpha3>AIA</Alpha3> + <IDC>1264</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AQ"> + <name>Antarctica</name> + <Alpha2>AQ</Alpha2> + <Alpha3>ATA</Alpha3> + <IDC>672</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AG"> + <name>Antigua and Barbuda</name> + <Alpha2>AG</Alpha2> + <Alpha3>ATG</Alpha3> + <IDC>1268</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AR"> + <name>Argentina</name> + <Alpha2>AR</Alpha2> + <Alpha3>ARG</Alpha3> + <IDC>54</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AM"> + <name>Armenia</name> + <Alpha2>AM</Alpha2> + <Alpha3>ARM</Alpha3> + <IDC>374</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AW"> + <name>Aruba</name> + <Alpha2>AW</Alpha2> + <Alpha3>ABW</Alpha3> + <IDC>297</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AU"> + <name>Australia</name> + <Alpha2>AU</Alpha2> + <Alpha3>AUS</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AT"> + <name>Austria</name> + <Alpha2>AT</Alpha2> + <Alpha3>AUT</Alpha3> + <IDC>43</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AZ"> + <name>Azerbaijan</name> + <Alpha2>AZ</Alpha2> + <Alpha3>AZE</Alpha3> + <IDC>994</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BS"> + <name>Bahamas</name> + <Alpha2>BS</Alpha2> + <Alpha3>BHS</Alpha3> + <IDC>1242</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BH"> + <name>Bahrain</name> + <Alpha2>BH</Alpha2> + <Alpha3>BHR</Alpha3> + <IDC>973</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BD"> + <name>Bangladesh</name> + <Alpha2>BD</Alpha2> + <Alpha3>BGD</Alpha3> + <IDC>880</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BB"> + <name>Barbados</name> + <Alpha2>BB</Alpha2> + <Alpha3>BRB</Alpha3> + <IDC>1246</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BY"> + <name>Belarus</name> + <Alpha2>BY</Alpha2> + <Alpha3>BLR</Alpha3> + <IDC>375</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BE"> + <name>Belgium</name> + <Alpha2>BE</Alpha2> + <Alpha3>BEL</Alpha3> + <IDC>32</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BZ"> + <name>Belize</name> + <Alpha2>BZ</Alpha2> + <Alpha3>BLZ</Alpha3> + <IDC>501</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BJ"> + <name>Benin</name> + <Alpha2>BJ</Alpha2> + <Alpha3>BEN</Alpha3> + <IDC>229</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BM"> + <name>Bermuda</name> + <Alpha2>BM</Alpha2> + <Alpha3>BMU</Alpha3> + <IDC>1441</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BT"> + <name>Bhutan</name> + <Alpha2>BT</Alpha2> + <Alpha3>BTN</Alpha3> + <IDC>975</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BO"> + <name>Bolivia</name> + <Alpha2>BO</Alpha2> + <Alpha3>BOL</Alpha3> + <IDC>591</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BA"> + <name>Bosnia and Herzegovina</name> + <Alpha2>BA</Alpha2> + <Alpha3>BIH</Alpha3> + <IDC>387</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BW"> + <name>Botswana</name> + <Alpha2>BW</Alpha2> + <Alpha3>BWA</Alpha3> + <IDC>267</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BV"> + <name>Bouvet Island</name> + <Alpha2>BV</Alpha2> + <Alpha3>BVT</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BR"> + <name>Brazil</name> + <Alpha2>BR</Alpha2> + <Alpha3>BRA</Alpha3> + <IDC>55</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IO"> + <name>British Indian Ocean Territory</name> + <Alpha2>IO</Alpha2> + <Alpha3>IOT</Alpha3> + <IDC>246</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BN"> + <name>Brunei Darussalam</name> + <Alpha2>BN</Alpha2> + <Alpha3>BRN</Alpha3> + <IDC>673</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BG"> + <name>Bulgaria</name> + <Alpha2>BG</Alpha2> + <Alpha3>BGR</Alpha3> + <IDC>359</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BF"> + <name>Burkina Faso</name> + <Alpha2>BF</Alpha2> + <Alpha3>BFA</Alpha3> + <IDC>226</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BI"> + <name>Burundi</name> + <Alpha2>BI</Alpha2> + <Alpha3>BDI</Alpha3> + <IDC>257</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KH"> + <name>Cambodia</name> + <Alpha2>KH</Alpha2> + <Alpha3>KHM</Alpha3> + <IDC>855</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CM"> + <name>Cameroon</name> + <Alpha2>CM</Alpha2> + <Alpha3>CMR</Alpha3> + <IDC>237</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CA"> + <name>Canada</name> + <Alpha2>CA</Alpha2> + <Alpha3>CAN</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CV"> + <name>Cape Verde</name> + <Alpha2>CV</Alpha2> + <Alpha3>CPV</Alpha3> + <IDC>238</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KY"> + <name>Cayman Islands</name> + <Alpha2>KY</Alpha2> + <Alpha3>CYM</Alpha3> + <IDC>1345</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CF"> + <name>Central African Republic</name> + <Alpha2>CF</Alpha2> + <Alpha3>CAF</Alpha3> + <IDC>236</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TD"> + <name>Chad</name> + <Alpha2>TD</Alpha2> + <Alpha3>TCD</Alpha3> + <IDC>235</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CL"> + <name>Chile</name> + <Alpha2>CL</Alpha2> + <Alpha3>CHL</Alpha3> + <IDC>56</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CN"> + <name>China</name> + <Alpha2>CN</Alpha2> + <Alpha3>CHN</Alpha3> + <IDC>86</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CX"> + <name>Christmas Island</name> + <Alpha2>CX</Alpha2> + <Alpha3>CXR</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CC"> + <name>Cocos (Keeling) Islands</name> + <Alpha2>CC</Alpha2> + <Alpha3>CCK</Alpha3> + <IDC>61</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CO"> + <name>Colombia</name> + <Alpha2>CO</Alpha2> + <Alpha3>COL</Alpha3> + <IDC>57</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KM"> + <name>Comoros</name> + <Alpha2>KM</Alpha2> + <Alpha3>COM</Alpha3> + <IDC>269</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CG"> + <name>Congo</name> + <Alpha2>CG</Alpha2> + <Alpha3>COG</Alpha3> + <IDC>242</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CD"> + <name>Congo, The Democratic Republic Of The</name> + <Alpha2>CD</Alpha2> + <Alpha3>COD</Alpha3> + <IDC>243</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CK"> + <name>Cook Islands</name> + <Alpha2>CK</Alpha2> + <Alpha3>COK</Alpha3> + <IDC>682</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CR"> + <name>Costa Rica</name> + <Alpha2>CR</Alpha2> + <Alpha3>CRI</Alpha3> + <IDC>506</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CI"> + <name>Côte d'Ivoire</name> + <Alpha2>CI</Alpha2> + <Alpha3>CIV</Alpha3> + <IDC>225</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HR"> + <name>Croatia</name> + <Alpha2>HR</Alpha2> + <Alpha3>HRV</Alpha3> + <IDC>385</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CU"> + <name>Cuba</name> + <Alpha2>CU</Alpha2> + <Alpha3>CUB</Alpha3> + <IDC>53</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CY"> + <name>Cyprus</name> + <Alpha2>CY</Alpha2> + <Alpha3>CYP</Alpha3> + <IDC>357</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CZ"> + <name>Czech Republic</name> + <Alpha2>CZ</Alpha2> + <Alpha3>CZE</Alpha3> + <IDC>420</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DK"> + <name>Denmark</name> + <Alpha2>DK</Alpha2> + <Alpha3>DNK</Alpha3> + <IDC>45</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DJ"> + <name>Djibouti</name> + <Alpha2>DJ</Alpha2> + <Alpha3>DJI</Alpha3> + <IDC>253</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DM"> + <name>Dominica</name> + <Alpha2>DM</Alpha2> + <Alpha3>DMA</Alpha3> + <IDC>1767</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DO"> + <name>Dominican Republic</name> + <Alpha2>DO</Alpha2> + <Alpha3>DOM</Alpha3> + <IDC>1809</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="EC"> + <name>Ecuador</name> + <Alpha2>EC</Alpha2> + <Alpha3>ECU</Alpha3> + <IDC>593</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="EG"> + <name>Egypt</name> + <Alpha2>EG</Alpha2> + <Alpha3>EGY</Alpha3> + <IDC>20</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SV"> + <name>El Salvador</name> + <Alpha2>SV</Alpha2> + <Alpha3>SLV</Alpha3> + <IDC>503</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GQ"> + <name>Equatorial Guinea</name> + <Alpha2>GQ</Alpha2> + <Alpha3>GNQ</Alpha3> + <IDC>240</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ER"> + <name>Eritrea</name> + <Alpha2>ER</Alpha2> + <Alpha3>ERI</Alpha3> + <IDC>291</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="EE"> + <name>Estonia</name> + <Alpha2>EE</Alpha2> + <Alpha3>EST</Alpha3> + <IDC>372</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ET"> + <name>Ethiopia</name> + <Alpha2>ET</Alpha2> + <Alpha3>ETH</Alpha3> + <IDC>251</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FK"> + <name>Falkland Islands (Malvinas)</name> + <Alpha2>FK</Alpha2> + <Alpha3>FLK</Alpha3> + <IDC>500</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FO"> + <name>Faroe Islands</name> + <Alpha2>FO</Alpha2> + <Alpha3>FRO</Alpha3> + <IDC>298</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FJ"> + <name>Fiji</name> + <Alpha2>FJ</Alpha2> + <Alpha3>FJI</Alpha3> + <IDC>679</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FI"> + <name>Finland</name> + <Alpha2>FI</Alpha2> + <Alpha3>FIN</Alpha3> + <IDC>358</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FR"> + <name>France</name> + <Alpha2>FR</Alpha2> + <Alpha3>FRA</Alpha3> + <IDC>33</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GF"> + <name>French Guiana</name> + <Alpha2>GF</Alpha2> + <Alpha3>GUF</Alpha3> + <IDC>594</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PF"> + <name>French Polynesia</name> + <Alpha2>PF</Alpha2> + <Alpha3>PYF</Alpha3> + <IDC>689</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TF"> + <name>French Southern Territories</name> + <Alpha2>TF</Alpha2> + <Alpha3>ATF</Alpha3> + <IDC>0</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GA"> + <name>Gabon</name> + <Alpha2>GA</Alpha2> + <Alpha3>GAB</Alpha3> + <IDC>241</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GM"> + <name>Gambia</name> + <Alpha2>GM</Alpha2> + <Alpha3>GMB</Alpha3> + <IDC>220</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GE"> + <name>Georgia</name> + <Alpha2>GE</Alpha2> + <Alpha3>GEO</Alpha3> + <IDC>995</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="DE"> + <name>Germany</name> + <Alpha2>DE</Alpha2> + <Alpha3>DEU</Alpha3> + <IDC>49</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GH"> + <name>Ghana</name> + <Alpha2>GH</Alpha2> + <Alpha3>GHA</Alpha3> + <IDC>233</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GI"> + <name>Gibraltar</name> + <Alpha2>GI</Alpha2> + <Alpha3>GIB</Alpha3> + <IDC>350</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GR"> + <name>Greece</name> + <Alpha2>GR</Alpha2> + <Alpha3>GRC</Alpha3> + <IDC>30</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GL"> + <name>Greenland</name> + <Alpha2>GL</Alpha2> + <Alpha3>GRL</Alpha3> + <IDC>299</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GD"> + <name>Grenada</name> + <Alpha2>GD</Alpha2> + <Alpha3>GRD</Alpha3> + <IDC>1473</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GP"> + <name>Guadeloupe</name> + <Alpha2>GP</Alpha2> + <Alpha3>GLP</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GU"> + <name>Guam</name> + <Alpha2>GU</Alpha2> + <Alpha3>GUM</Alpha3> + <IDC>1671</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GT"> + <name>Guatemala</name> + <Alpha2>GT</Alpha2> + <Alpha3>GTM</Alpha3> + <IDC>502</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GG"> + <name>Guernsey</name> + <Alpha2>GG</Alpha2> + <Alpha3>GGY</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GN"> + <name>Guinea</name> + <Alpha2>GN</Alpha2> + <Alpha3>GIN</Alpha3> + <IDC>224</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GW"> + <name>Guinea-Bissau</name> + <Alpha2>GW</Alpha2> + <Alpha3>GNB</Alpha3> + <IDC>245</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GY"> + <name>Guyana</name> + <Alpha2>GY</Alpha2> + <Alpha3>GUY</Alpha3> + <IDC>592</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HT"> + <name>Haiti</name> + <Alpha2>HT</Alpha2> + <Alpha3>HTI</Alpha3> + <IDC>509</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HM"> + <name>Heard Island and McDonald Islands</name> + <Alpha2>HM</Alpha2> + <Alpha3>HMD</Alpha3> + <IDC>672</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HN"> + <name>Honduras</name> + <Alpha2>HN</Alpha2> + <Alpha3>HND</Alpha3> + <IDC>504</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HK"> + <name>Hong Kong</name> + <Alpha2>HK</Alpha2> + <Alpha3>HKG</Alpha3> + <IDC>852</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="HU"> + <name>Hungary</name> + <Alpha2>HU</Alpha2> + <Alpha3>HUN</Alpha3> + <IDC>36</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IS"> + <name>Iceland</name> + <Alpha2>IS</Alpha2> + <Alpha3>ISL</Alpha3> + <IDC>354</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IN"> + <name>India</name> + <Alpha2>IN</Alpha2> + <Alpha3>IND</Alpha3> + <IDC>91</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ID"> + <name>Indonesia</name> + <Alpha2>ID</Alpha2> + <Alpha3>IDN</Alpha3> + <IDC>62</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IR"> + <name>Iran, Islamic Republic of</name> + <Alpha2>IR</Alpha2> + <Alpha3>IRN</Alpha3> + <IDC>98</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IQ"> + <name>Iraq</name> + <Alpha2>IQ</Alpha2> + <Alpha3>IRQ</Alpha3> + <IDC>964</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IE"> + <name>Ireland</name> + <Alpha2>IE</Alpha2> + <Alpha3>IRL</Alpha3> + <IDC>353</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IM"> + <name>Isle of Man</name> + <Alpha2>IM</Alpha2> + <Alpha3>IMN</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IL"> + <name>Israel</name> + <Alpha2>IL</Alpha2> + <Alpha3>ISR</Alpha3> + <IDC>972</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="IT"> + <name>Italy</name> + <Alpha2>IT</Alpha2> + <Alpha3>ITA</Alpha3> + <IDC>39</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="JM"> + <name>Jamaica</name> + <Alpha2>JM</Alpha2> + <Alpha3>JAM</Alpha3> + <IDC>1876</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="JP"> + <name>Japan</name> + <Alpha2>JP</Alpha2> + <Alpha3>JPN</Alpha3> + <IDC>81</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="JE"> + <name>Jersey</name> + <Alpha2>JE</Alpha2> + <Alpha3>JEY</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="JO"> + <name>Jordan</name> + <Alpha2>JO</Alpha2> + <Alpha3>JOR</Alpha3> + <IDC>962</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KZ"> + <name>Kazakhstan</name> + <Alpha2>KZ</Alpha2> + <Alpha3>KAZ</Alpha3> + <IDC>7</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KE"> + <name>Kenya</name> + <Alpha2>KE</Alpha2> + <Alpha3>KEN</Alpha3> + <IDC>254</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KI"> + <name>Kiribati</name> + <Alpha2>KI</Alpha2> + <Alpha3>KIR</Alpha3> + <IDC>686</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KP"> + <name>Korea, Democratic People's Republic of</name> + <Alpha2>KP</Alpha2> + <Alpha3>PRK</Alpha3> + <IDC>850</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KR"> + <name>Korea, Republic of</name> + <Alpha2>KR</Alpha2> + <Alpha3>KOR</Alpha3> + <IDC>82</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KW"> + <name>Kuwait</name> + <Alpha2>KW</Alpha2> + <Alpha3>KWT</Alpha3> + <IDC>965</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KG"> + <name>Kyrgyzstan</name> + <Alpha2>KG</Alpha2> + <Alpha3>KGZ</Alpha3> + <IDC>996</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LA"> + <name>Lao People's Democratic Republic</name> + <Alpha2>LA</Alpha2> + <Alpha3>LAO</Alpha3> + <IDC>856</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LV"> + <name>Latvia</name> + <Alpha2>LV</Alpha2> + <Alpha3>LVA</Alpha3> + <IDC>371</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LB"> + <name>Lebanon</name> + <Alpha2>LB</Alpha2> + <Alpha3>LBN</Alpha3> + <IDC>961</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LS"> + <name>Lesotho</name> + <Alpha2>LS</Alpha2> + <Alpha3>LSO</Alpha3> + <IDC>266</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LR"> + <name>Liberia</name> + <Alpha2>LR</Alpha2> + <Alpha3>LBR</Alpha3> + <IDC>231</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LY"> + <name>Libyan Arab Jamahiriya</name> + <Alpha2>LY</Alpha2> + <Alpha3>LBY</Alpha3> + <IDC>218</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LI"> + <name>Liechtenstein</name> + <Alpha2>LI</Alpha2> + <Alpha3>LIE</Alpha3> + <IDC>423</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LT"> + <name>Lithuania</name> + <Alpha2>LT</Alpha2> + <Alpha3>LTU</Alpha3> + <IDC>370</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LU"> + <name>Luxembourg</name> + <Alpha2>LU</Alpha2> + <Alpha3>LUX</Alpha3> + <IDC>352</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MO"> + <name>Macau</name> + <Alpha2>MO</Alpha2> + <Alpha3>MAC</Alpha3> + <IDC>853</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MK"> + <name>Macedonia, The Former Yugoslav Republic of</name> + <Alpha2>MK</Alpha2> + <Alpha3>MKD</Alpha3> + <IDC>389</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MG"> + <name>Madagascar</name> + <Alpha2>MG</Alpha2> + <Alpha3>MDG</Alpha3> + <IDC>261</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MW"> + <name>Malawi</name> + <Alpha2>MW</Alpha2> + <Alpha3>MWI</Alpha3> + <IDC>265</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MY"> + <name>Malaysia</name> + <Alpha2>MY</Alpha2> + <Alpha3>MYS</Alpha3> + <IDC>60</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MV"> + <name>Maldives</name> + <Alpha2>MV</Alpha2> + <Alpha3>MDV</Alpha3> + <IDC>960</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ML"> + <name>Mali</name> + <Alpha2>ML</Alpha2> + <Alpha3>MLI</Alpha3> + <IDC>223</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MT"> + <name>Malta</name> + <Alpha2>MT</Alpha2> + <Alpha3>MLT</Alpha3> + <IDC>356</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MH"> + <name>Marshall Islands</name> + <Alpha2>MH</Alpha2> + <Alpha3>MHL</Alpha3> + <IDC>692</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MQ"> + <name>Martinique</name> + <Alpha2>MQ</Alpha2> + <Alpha3>MTQ</Alpha3> + <IDC>596</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MR"> + <name>Mauritania</name> + <Alpha2>MR</Alpha2> + <Alpha3>MRT</Alpha3> + <IDC>222</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MU"> + <name>Mauritius</name> + <Alpha2>MU</Alpha2> + <Alpha3>MUS</Alpha3> + <IDC>230</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="YT"> + <name>Mayotte</name> + <Alpha2>YT</Alpha2> + <Alpha3>MYT</Alpha3> + <IDC>262</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MX"> + <name>Mexico</name> + <Alpha2>MX</Alpha2> + <Alpha3>MEX</Alpha3> + <IDC>52</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="FM"> + <name>Micronesia, Federated States of</name> + <Alpha2>FM</Alpha2> + <Alpha3>FSM</Alpha3> + <IDC>691</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MD"> + <name>Moldova, Republic of</name> + <Alpha2>MD</Alpha2> + <Alpha3>MDA</Alpha3> + <IDC>373</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MC"> + <name>Monaco</name> + <Alpha2>MC</Alpha2> + <Alpha3>MCO</Alpha3> + <IDC>377</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MN"> + <name>Mongolia</name> + <Alpha2>MN</Alpha2> + <Alpha3>MNG</Alpha3> + <IDC>976</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ME"> + <name>Montenegro</name> + <Alpha2>ME</Alpha2> + <Alpha3>MNE</Alpha3> + <IDC>382</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MS"> + <name>Montserrat</name> + <Alpha2>MS</Alpha2> + <Alpha3>MSR</Alpha3> + <IDC>1664</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MA"> + <name>Morocco</name> + <Alpha2>MA</Alpha2> + <Alpha3>MAR</Alpha3> + <IDC>212</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MZ"> + <name>Mozambique</name> + <Alpha2>MZ</Alpha2> + <Alpha3>MOZ</Alpha3> + <IDC>258</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MM"> + <name>Myanmar</name> + <Alpha2>MM</Alpha2> + <Alpha3>MMR</Alpha3> + <IDC>95</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NA"> + <name>Namibia</name> + <Alpha2>NA</Alpha2> + <Alpha3>NAM</Alpha3> + <IDC>264</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NR"> + <name>Nauru</name> + <Alpha2>NR</Alpha2> + <Alpha3>NRU</Alpha3> + <IDC>674</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NP"> + <name>Nepal</name> + <Alpha2>NP</Alpha2> + <Alpha3>NPL</Alpha3> + <IDC>977</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NL"> + <name>Netherlands</name> + <Alpha2>NL</Alpha2> + <Alpha3>NLD</Alpha3> + <IDC>31</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AN"> + <name>Netherlands Antilles</name> + <Alpha2>AN</Alpha2> + <Alpha3>ANT</Alpha3> + <IDC>599</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NC"> + <name>New Caledonia</name> + <Alpha2>NC</Alpha2> + <Alpha3>NCL</Alpha3> + <IDC>687</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NZ"> + <name>New Zealand</name> + <Alpha2>NZ</Alpha2> + <Alpha3>NZL</Alpha3> + <IDC>64</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NI"> + <name>Nicaragua</name> + <Alpha2>NI</Alpha2> + <Alpha3>NIC</Alpha3> + <IDC>505</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NE"> + <name>Niger</name> + <Alpha2>NE</Alpha2> + <Alpha3>NER</Alpha3> + <IDC>227</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NG"> + <name>Nigeria</name> + <Alpha2>NG</Alpha2> + <Alpha3>NGA</Alpha3> + <IDC>234</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NU"> + <name>Niue</name> + <Alpha2>NU</Alpha2> + <Alpha3>NIU</Alpha3> + <IDC>683</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NF"> + <name>Norfolk Island</name> + <Alpha2>NF</Alpha2> + <Alpha3>NFK</Alpha3> + <IDC>6723</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MP"> + <name>Northern Mariana Islands</name> + <Alpha2>MP</Alpha2> + <Alpha3>MNP</Alpha3> + <IDC>1670</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="NO"> + <name>Norway</name> + <Alpha2>NO</Alpha2> + <Alpha3>NOR</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="OM"> + <name>Oman</name> + <Alpha2>OM</Alpha2> + <Alpha3>OMN</Alpha3> + <IDC>968</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PK"> + <name>Pakistan</name> + <Alpha2>PK</Alpha2> + <Alpha3>PAK</Alpha3> + <IDC>92</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PW"> + <name>Palau</name> + <Alpha2>PW</Alpha2> + <Alpha3>PLW</Alpha3> + <IDC>680</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PS"> + <name>Palestinian Territory, Occupied</name> + <Alpha2>PS</Alpha2> + <Alpha3>PSE</Alpha3> + <IDC>970</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PA"> + <name>Panama</name> + <Alpha2>PA</Alpha2> + <Alpha3>PAN</Alpha3> + <IDC>507</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PG"> + <name>Papua New Guinea</name> + <Alpha2>PG</Alpha2> + <Alpha3>PNG</Alpha3> + <IDC>675</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PY"> + <name>Paraguay</name> + <Alpha2>PY</Alpha2> + <Alpha3>PRY</Alpha3> + <IDC>595</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PE"> + <name>Peru</name> + <Alpha2>PE</Alpha2> + <Alpha3>PER</Alpha3> + <IDC>51</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PH"> + <name>Philippines</name> + <Alpha2>PH</Alpha2> + <Alpha3>PHL</Alpha3> + <IDC>63</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PN"> + <name>Pitcairn</name> + <Alpha2>PN</Alpha2> + <Alpha3>PCN</Alpha3> + <IDC>64</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PL"> + <name>Poland</name> + <Alpha2>PL</Alpha2> + <Alpha3>POL</Alpha3> + <IDC>48</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PT"> + <name>Portugal</name> + <Alpha2>PT</Alpha2> + <Alpha3>PRT</Alpha3> + <IDC>351</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PR"> + <name>Puerto Rico</name> + <Alpha2>PR</Alpha2> + <Alpha3>PRI</Alpha3> + <IDC>1787</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="QA"> + <name>Qatar</name> + <Alpha2>QA</Alpha2> + <Alpha3>QAT</Alpha3> + <IDC>974</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="RE"> + <name>Reunion</name> + <Alpha2>RE</Alpha2> + <Alpha3>REU</Alpha3> + <IDC>262</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="RO"> + <name>Romania</name> + <Alpha2>RO</Alpha2> + <Alpha3>ROU</Alpha3> + <IDC>40</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="RU"> + <name>Russian Federation</name> + <Alpha2>RU</Alpha2> + <Alpha3>RUS</Alpha3> + <IDC>7</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="RW"> + <name>Rwanda</name> + <Alpha2>RW</Alpha2> + <Alpha3>RWA</Alpha3> + <IDC>250</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="BL"> + <name>Saint Barthélemy</name> + <Alpha2>BL</Alpha2> + <Alpha3>BLM</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SH"> + <name>Saint Helena</name> + <Alpha2>SH</Alpha2> + <Alpha3>SHN</Alpha3> + <IDC>290</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="KN"> + <name>Saint Kitts and Nevis</name> + <Alpha2>KN</Alpha2> + <Alpha3>KNA</Alpha3> + <IDC>1869</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LC"> + <name>Saint Lucia</name> + <Alpha2>LC</Alpha2> + <Alpha3>LCA</Alpha3> + <IDC>1758</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="MF"> + <name>Saint Martin</name> + <Alpha2>MF</Alpha2> + <Alpha3>MAF</Alpha3> + <IDC>590</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="PM"> + <name>Saint Pierre and Miquelon</name> + <Alpha2>PM</Alpha2> + <Alpha3>SPM</Alpha3> + <IDC>508</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VC"> + <name>Saint Vincent and The Grenadines</name> + <Alpha2>VC</Alpha2> + <Alpha3>VCT</Alpha3> + <IDC>1784</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="WS"> + <name>Samoa</name> + <Alpha2>WS</Alpha2> + <Alpha3>WSM</Alpha3> + <IDC>685</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SM"> + <name>San Marino</name> + <Alpha2>SM</Alpha2> + <Alpha3>SMR</Alpha3> + <IDC>378</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ST"> + <name>Sao Tome and Principe</name> + <Alpha2>ST</Alpha2> + <Alpha3>STP</Alpha3> + <IDC>239</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SA"> + <name>Saudi Arabia</name> + <Alpha2>SA</Alpha2> + <Alpha3>SAU</Alpha3> + <IDC>966</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SN"> + <name>Senegal</name> + <Alpha2>SN</Alpha2> + <Alpha3>SEN</Alpha3> + <IDC>221</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="RS"> + <name>Serbia</name> + <Alpha2>RS</Alpha2> + <Alpha3>SRB</Alpha3> + <IDC>381</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SC"> + <name>Seychelles</name> + <Alpha2>SC</Alpha2> + <Alpha3>SYC</Alpha3> + <IDC>248</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SL"> + <name>Sierra Leone</name> + <Alpha2>SL</Alpha2> + <Alpha3>SLE</Alpha3> + <IDC>232</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SG"> + <name>Singapore</name> + <Alpha2>SG</Alpha2> + <Alpha3>SGP</Alpha3> + <IDC>65</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SK"> + <name>Slovakia</name> + <Alpha2>SK</Alpha2> + <Alpha3>SVK</Alpha3> + <IDC>421</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SI"> + <name>Slovenia</name> + <Alpha2>SI</Alpha2> + <Alpha3>SVN</Alpha3> + <IDC>386</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SB"> + <name>Solomon Islands</name> + <Alpha2>SB</Alpha2> + <Alpha3>SLB</Alpha3> + <IDC>677</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SO"> + <name>Somalia</name> + <Alpha2>SO</Alpha2> + <Alpha3>SOM</Alpha3> + <IDC>252</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ZA"> + <name>South Africa</name> + <Alpha2>ZA</Alpha2> + <Alpha3>ZAF</Alpha3> + <IDC>27</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GS"> + <name>South Georgia and The South Sandwich Islands</name> + <Alpha2>GS</Alpha2> + <Alpha3>SGS</Alpha3> + <IDC>500</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ES"> + <name>Spain</name> + <Alpha2>ES</Alpha2> + <Alpha3>ESP</Alpha3> + <IDC>34</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="LK"> + <name>Sri Lanka</name> + <Alpha2>LK</Alpha2> + <Alpha3>LKA</Alpha3> + <IDC>94</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SD"> + <name>Sudan</name> + <Alpha2>SD</Alpha2> + <Alpha3>SDN</Alpha3> + <IDC>249</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SR"> + <name>Suriname</name> + <Alpha2>SR</Alpha2> + <Alpha3>SUR</Alpha3> + <IDC>597</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SJ"> + <name>Svalbard and Jan Mayen</name> + <Alpha2>SJ</Alpha2> + <Alpha3>SJM</Alpha3> + <IDC>47</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SZ"> + <name>Swaziland</name> + <Alpha2>SZ</Alpha2> + <Alpha3>SWZ</Alpha3> + <IDC>268</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SE"> + <name>Sweden</name> + <Alpha2>SE</Alpha2> + <Alpha3>SWE</Alpha3> + <IDC>46</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="CH"> + <name>Switzerland</name> + <Alpha2>CH</Alpha2> + <Alpha3>CHE</Alpha3> + <IDC>41</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="SY"> + <name>Syrian Arab Republic</name> + <Alpha2>SY</Alpha2> + <Alpha3>SYR</Alpha3> + <IDC>963</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TW"> + <name>Taiwan</name> + <Alpha2>TW</Alpha2> + <Alpha3>TWN</Alpha3> + <IDC>886</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TJ"> + <name>Tajikistan</name> + <Alpha2>TJ</Alpha2> + <Alpha3>TJK</Alpha3> + <IDC>992</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TZ"> + <name>Tanzania, United Republic of</name> + <Alpha2>TZ</Alpha2> + <Alpha3>TZA</Alpha3> + <IDC>255</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TH"> + <name>Thailand</name> + <Alpha2>TH</Alpha2> + <Alpha3>THA</Alpha3> + <IDC>66</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TL"> + <name>Timor-Leste</name> + <Alpha2>TL</Alpha2> + <Alpha3>TLS</Alpha3> + <IDC>670</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TG"> + <name>Togo</name> + <Alpha2>TG</Alpha2> + <Alpha3>TGO</Alpha3> + <IDC>228</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TK"> + <name>Tokelau</name> + <Alpha2>TK</Alpha2> + <Alpha3>TKL</Alpha3> + <IDC>690</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TO"> + <name>Tonga</name> + <Alpha2>TO</Alpha2> + <Alpha3>TON</Alpha3> + <IDC>676</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TT"> + <name>Trinidad and Tobago</name> + <Alpha2>TT</Alpha2> + <Alpha3>TTO</Alpha3> + <IDC>1868</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TN"> + <name>Tunisia</name> + <Alpha2>TN</Alpha2> + <Alpha3>TUN</Alpha3> + <IDC>216</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TR"> + <name>Turkey</name> + <Alpha2>TR</Alpha2> + <Alpha3>TUR</Alpha3> + <IDC>90</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TM"> + <name>Turkmenistan</name> + <Alpha2>TM</Alpha2> + <Alpha3>TKM</Alpha3> + <IDC>993</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TC"> + <name>Turks and Caicos Islands</name> + <Alpha2>TC</Alpha2> + <Alpha3>TCA</Alpha3> + <IDC>1649</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="TV"> + <name>Tuvalu</name> + <Alpha2>TV</Alpha2> + <Alpha3>TUV</Alpha3> + <IDC>688</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="UG"> + <name>Uganda</name> + <Alpha2>UG</Alpha2> + <Alpha3>UGA</Alpha3> + <IDC>256</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="UA"> + <name>Ukraine</name> + <Alpha2>UA</Alpha2> + <Alpha3>UKR</Alpha3> + <IDC>380</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="AE"> + <name>United Arab Emirates</name> + <Alpha2>AE</Alpha2> + <Alpha3>ARE</Alpha3> + <IDC>971</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="GB"> + <name>United Kingdom</name> + <Alpha2>GB</Alpha2> + <Alpha3>GBR</Alpha3> + <IDC>44</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="UM"> + <name>United States Minor Outlying Islands</name> + <Alpha2>UM</Alpha2> + <Alpha3>UMI</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="US"> + <name>United States of America</name> + <Alpha2>US</Alpha2> + <Alpha3>USA</Alpha3> + <IDC>1</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="UY"> + <name>Uruguay</name> + <Alpha2>UY</Alpha2> + <Alpha3>URY</Alpha3> + <IDC>598</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="UZ"> + <name>Uzbekistan</name> + <Alpha2>UZ</Alpha2> + <Alpha3>UZB</Alpha3> + <IDC>998</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VU"> + <name>Vanuatu</name> + <Alpha2>VU</Alpha2> + <Alpha3>VUT</Alpha3> + <IDC>678</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VA"> + <name>Holy See (Vatican City State)</name> + <Alpha2>VA</Alpha2> + <Alpha3>VAT</Alpha3> + <IDC>3906</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VE"> + <name>Venezuela</name> + <Alpha2>VE</Alpha2> + <Alpha3>VEN</Alpha3> + <IDC>58</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VN"> + <name>Viet Nam</name> + <Alpha2>VN</Alpha2> + <Alpha3>VNM</Alpha3> + <IDC>84</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VG"> + <name>Virgin Islands, British</name> + <Alpha2>VG</Alpha2> + <Alpha3>VGB</Alpha3> + <IDC>1284</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="VI"> + <name>Virgin Islands, U.S.</name> + <Alpha2>VI</Alpha2> + <Alpha3>VIR</Alpha3> + <IDC>1340</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="WF"> + <name>Wallis and Futuna</name> + <Alpha2>WF</Alpha2> + <Alpha3>WLF</Alpha3> + <IDC>681</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="EH"> + <name>Western Sahara</name> + <Alpha2>EH</Alpha2> + <Alpha3>ESH</Alpha3> + <IDC>212</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="YE"> + <name>Yemen</name> + <Alpha2>YE</Alpha2> + <Alpha3>YEM</Alpha3> + <IDC>967</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ZM"> + <name>Zambia</name> + <Alpha2>ZM</Alpha2> + <Alpha3>ZMB</Alpha3> + <IDC>260</IDC> + </Country> + <Country media-type="application/vnd.ez.api.Country+xml" id="ZW"> + <name>Zimbabwe</name> + <Alpha2>ZW</Alpha2> + <Alpha3>ZWE</Alpha3> + <IDC>263</IDC> + </Country> +</CountryList> diff --git a/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.json.example new file mode 100644 index 0000000000..7acbe8c34b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.json.example @@ -0,0 +1,156 @@ +{ + "UserGroupList": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/15", + "_id": 14, + "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4" + }, + "name": "Administrator User", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/14/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/13/15" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/14/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:13:50+00:00", + "lastModificationDate": "2011-03-25T14:07:04+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/14/versions/3", + "VersionInfo": { + "id": 499, + "versionNo": 3, + "status": "PUBLISHED", + "modificationDate": "2011-03-25T14:07:04+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2011-03-25T14:03:03+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator User" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/14" + } + }, + "Fields": { + "field": [ + { + "id": 28, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator" + }, + { + "id": 29, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "User" + }, + { + "id": 30, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 14, + "login": "admin", + "email": "admin@link.invalid", + "passwordUpdatedAt": null, + "enabled": true, + "maxLogin": 10, + "plainPassword": null + } + }, + { + "id": 178, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 180, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/14/versions/3/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13" + }, + "Subgroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/15/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/15/users" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/15/roles" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.xml.example new file mode 100644 index 0000000000..6a18fa7c13 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/GET/UserGroupList.xml.example @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupList media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups"> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13/15" id="14" remoteId="1bb4fe25487f05527efa8bfd394cecc7"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/> + <name>Administrator User</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/14/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13/15"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/14/locations"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <publishDate>2002-10-06T16:13:50+00:00</publishDate> + <lastModificationDate>2011-03-25T14:07:04+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/14/versions/3"> + <VersionInfo> + <id>499</id> + <versionNo>3</versionNo> + <status>PUBLISHED</status> + <modificationDate>2011-03-25T14:07:04+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2011-03-25T14:03:03+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator User</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/14"/> + </VersionInfo> + <Fields> + <field> + <id>28</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator</fieldValue> + </field> + <field> + <id>29</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>User</fieldValue> + </field> + <field> + <id>30</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">14</value> + <value key="login">admin</value> + <value key="email">admin@link.invalid</value> + <value key="passwordUpdatedAt"/> + <value key="enabled">true</value> + <value key="maxLogin">10</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>178</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>180</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/14/versions/3/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13"/> + <Subgroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups/1/5/13/15/subgroups"/> + <Users media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/groups/1/5/13/15/users"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/13/15/roles"/> + </UserGroup> +</UserGroupList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example new file mode 100644 index 0000000000..9ab2b3be5a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/groups/13/subgroups", + "UserGroup": [ + { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/112" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example new file mode 100644 index 0000000000..ac358d4dd3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/groups/13/subgroups"> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13/112"/> +</UserGroupRefList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.json.example new file mode 100644 index 0000000000..8385e7fa0e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.json.example @@ -0,0 +1,16 @@ +{ + "UserRefList": { + "_media-type": "application/vnd.ez.api.UserRefList+json", + "_href": "/api/ezp/v2/user/groups/13/users", + "User": [ + { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/113" + }, + { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.xml.example new file mode 100644 index 0000000000..27dcac082e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/id/users/GET/UserRefList.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserRefList media-type="application/vnd.ez.api.UserRefList+xml" href="/api/ezp/v2/user/groups/13/users"> + <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/113"/> + <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> +</UserRefList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.json.example new file mode 100644 index 0000000000..9d2fcb0464 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13", + "_id": 12, + "_remoteId": "9b47a45624b023b1a76c73b74d704acf", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/3" + }, + "name": "Administrator users", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/12/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/13" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/12/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:12:55+00:00", + "lastModificationDate": "2002-10-06T16:12:55+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/12/versions/1", + "VersionInfo": { + "id": 440, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2002-10-06T16:12:55+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2002-10-06T16:12:40+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator users" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/12" + } + }, + "Fields": { + "field": [ + { + "id": 24, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator users" + }, + { + "id": 25, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/12/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/users" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/roles" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.xml.example new file mode 100644 index 0000000000..0f6da0d6ae --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/GET/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/> + <name>Administrator users</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/12/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/12/locations"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <publishDate>2002-10-06T16:12:55+00:00</publishDate> + <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/12/versions/1"> + <VersionInfo> + <id>440</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2002-10-06T16:12:55+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2002-10-06T16:12:40+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator users</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/12"/> + </VersionInfo> + <Fields> + <field> + <id>24</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator users</fieldValue> + </field> + <field> + <id>25</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/12/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups/1/5/13/subgroups"/> + <Users media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/groups/1/5/13/users"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/13/roles"/> +</UserGroup> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.json.example new file mode 100644 index 0000000000..9d2fcb0464 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13", + "_id": 12, + "_remoteId": "9b47a45624b023b1a76c73b74d704acf", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/3" + }, + "name": "Administrator users", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/12/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/13" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/12/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:12:55+00:00", + "lastModificationDate": "2002-10-06T16:12:55+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/12/versions/1", + "VersionInfo": { + "id": 440, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2002-10-06T16:12:55+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2002-10-06T16:12:40+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator users" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/12" + } + }, + "Fields": { + "field": [ + { + "id": 24, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator users" + }, + { + "id": 25, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/12/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/users" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/5/13/roles" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.xml.example new file mode 100644 index 0000000000..0f6da0d6ae --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/> + <name>Administrator users</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/12/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/12/locations"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <publishDate>2002-10-06T16:12:55+00:00</publishDate> + <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/12/versions/1"> + <VersionInfo> + <id>440</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2002-10-06T16:12:55+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2002-10-06T16:12:40+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator users</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/12"/> + </VersionInfo> + <Fields> + <field> + <id>24</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator users</fieldValue> + </field> + <field> + <id>25</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/12/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups/1/5/13/subgroups"/> + <Users media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/groups/1/5/13/users"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/13/roles"/> +</UserGroup> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.json.example new file mode 100644 index 0000000000..69212fb271 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.json.example @@ -0,0 +1,7 @@ +{ + "UserGroupUpdate":{ + "Section": { + "_href": "/api/ezp/v2/content/sections/2" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example new file mode 100644 index 0000000000..cb33310509 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/PATCH/UserGroupUpdate.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupUpdate> + <Section href="/api/ezp/v2/content/sections/2" /> +</UserGroupUpdate> diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example new file mode 100644 index 0000000000..f1daa63e77 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssigmentList.json.example @@ -0,0 +1,10 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/groups/1/42/44/roles/1", + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/1" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..2a67c8eaad --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/42/44/roles/1"> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1"/> +</RoleAssignment> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.json.example new file mode 100644 index 0000000000..845be47e83 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.json.example @@ -0,0 +1,26 @@ +{ + "RoleAssignInput": { + "Role": { + "_href": "/api/ezp/v2/user/roles/2", + "media-type": "application/vnd.ez.api.RoleAssignInput+json", + "self-closing": "true" + }, + "Limitation": { + "identifier": "Section", + "values": { + "ref": [ + { + "_href": "/api/ezp/v2/content/sections/1", + "media-type": "application/vnd.ez.api.Section+json", + "self-closing": "true" + }, + { + "_href": "/api/ezp/v2/content/sections/4", + "media-type": "application/vnd.ez.api.Section+json", + "self-closing": "true" + } + ] + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example new file mode 100644 index 0000000000..c6020bef2b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignInput.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignInput> + <Role href="/api/ezp/v2/user/roles/10" media-type="application/vnd.ez.api.RoleAssignInput+xml"/> + <limitation identifier="Section"> + <values> + <ref href="/api/ezp/v2/content/sections/1" media-type="application/vnd.ez.api.Section+xml" /> + <ref href="/api/ezp/v2/content/sections/4" media-type="application/vnd.ez.api.Section+xml" /> + </values> + </limitation> +</RoleAssignInput> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example new file mode 100644 index 0000000000..69c20b1252 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.json.example @@ -0,0 +1,16 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/12/2/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/groups/1/12/2/roles/2", + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..48d46d7bfd --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/65/roles"> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/> +</RoleAssignment> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/10"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/10"/> +</RoleAssignment> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/10"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="4"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/10"/> +</RoleAssignment> +</RoleAssignmentList> diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example new file mode 100644 index 0000000000..3203ca84e9 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example @@ -0,0 +1,27 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/57/58/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/groups/1/57/58/roles/3", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/3" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..12d121e6eb --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/65/roles"> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/> +</RoleAssignment> +</RoleAssignmentList> diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example new file mode 100644 index 0000000000..a64d1d760a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example @@ -0,0 +1,10 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/groups/1/11/12/roles/1", + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/1" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example new file mode 100644 index 0000000000..3766bbe0a9 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/11/12/roles/1"> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1"/> +</RoleAssignment> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.json.example new file mode 100644 index 0000000000..e060453f92 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.json.example @@ -0,0 +1,120 @@ +{ + "UserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/81", + "_id": 87, + "_remoteId": "remoteId-qwert098", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/3" + }, + "name": "UserGroup2", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/87/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/81" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/87/locations" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2021-08-09T09:06:49+00:00", + "lastModificationDate": "2021-08-09T09:06:49+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/87/versions/1", + "VersionInfo": { + "id": 578, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-08-09T09:06:49+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-08-09T09:06:49+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "UserGroup2" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/87" + } + }, + "Fields": { + "field": [ + { + "id": 384, + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "UserGroup2" + }, + { + "id": 385, + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "This is the description of the user group" + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/87/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user_group", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "ParentUserGroup": { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5" + }, + "Subgroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/groups/1/5/81/subgroups" + }, + "Users": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/groups/1/5/81/users" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/groups/1/5/81/roles" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.xml.example new file mode 100644 index 0000000000..2f44f3d63c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroup.xml.example @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/79" id="85" remoteId="remoteId-qwert098"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/> + <name>UserGroup2</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/85/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/79"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/85/locations"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <publishDate>2021-08-09T09:03:35+00:00</publishDate> + <lastModificationDate>2021-08-09T09:03:35+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/85/versions/1"> + <VersionInfo> + <id>576</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-08-09T09:03:35+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2021-08-09T09:03:35+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">UserGroup2</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/85"/> + </VersionInfo> + <Fields> + <field> + <id>380</id> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>UserGroup2</fieldValue> + </field> + <field> + <id>381</id> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>This is the description of the user group</fieldValue> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/85/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user_group</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <ParentUserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5"/> + <Subgroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups/1/5/79/subgroups"/> + <Users media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/groups/1/5/79/users"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/79/roles"/> +</UserGroup> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example new file mode 100644 index 0000000000..876e65bdf2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example @@ -0,0 +1,20 @@ +{ + "UserGroupCreate": { + "mainLanguageCode": "eng-GB", + "remoteId": "remoteId-qwert098", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "name", + "languageCode": "eng-GB", + "fieldValue": "UserGroup2" + }, + { + "fieldDefinitionIdentifier": "description", + "languageCode": "eng-GB", + "fieldValue": "This is the description of the user group" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example new file mode 100644 index 0000000000..2ae0b4aaab --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupCreate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <remoteId>remoteId-qwert098</remoteId> + <fields> + <field> + <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>UserGroup</fieldValue> + </field> + <field> + <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>This is the description of the user group</fieldValue> + </field> + </fields> +</UserGroupCreate> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.json.example new file mode 100644 index 0000000000..f0e650c3d3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.json.example @@ -0,0 +1,149 @@ +{ + "User": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/57", + "_id": 57, + "_remoteId": "remoteId-qwert426", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4" + }, + "name": "Yura Rajzer", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/57/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/13/58" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/57/locations" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/57/groups" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2021-08-11T13:52:13+00:00", + "lastModificationDate": "2021-08-11T13:52:13+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/57/versions/1", + "VersionInfo": { + "id": 517, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-08-11T13:52:13+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-08-11T13:52:13+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Yura Rajzer" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/57" + } + }, + "Fields": { + "field": [ + { + "id": 262, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Yura" + }, + { + "id": 263, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Rajzer" + }, + { + "id": 264, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 57, + "login": "yura", + "email": "yurarajzer@example.net", + "passwordUpdatedAt": 1628689933, + "enabled": true, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 265, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 266, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/57/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "yura", + "email": "yurarajzer@example.net", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/users/57/groups" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/57/roles" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.xml.example new file mode 100644 index 0000000000..ee6d50e2d9 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/User.xml.example @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/59" id="59" remoteId="remoteId-qwert426"> +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/> +<name>Yura Rajzer</name> +<Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/59/versions"/> +<Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> +<MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/65/67"/> +<Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/59/locations"/> +<Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/59/groups"/> +<Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> +<publishDate>2019-02-27T11:23:42+01:00</publishDate> +<lastModificationDate>2019-02-27T11:23:42+01:00</lastModificationDate> +<mainLanguageCode>eng-GB</mainLanguageCode> +<alwaysAvailable>true</alwaysAvailable> +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/59/versions/1"> + <VersionInfo> + <id>515</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2019-02-27T11:23:42+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2019-02-27T11:23:42+01:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Yura Rajzer</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/59"/> + </VersionInfo> + <Fields> + <field> + <id>207</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Yura</fieldValue> + </field> + <field> + <id>208</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Rajzer</fieldValue> + </field> + <field> + <id>209</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">59</value> + <value key="login">yura</value> + <value key="email">yurarajzer@example.net</value> + <value key="enabled">true</value> + <value key="maxLogin">0</value> + </fieldValue> + </field> + <field> + <id>210</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>211</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/59/versions/1/relations"/> +</Version> +<login>yura</login> +<email>yurarajzer@example.net</email> +<enabled>true</enabled> +<UserGroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/users/59/groups"/> +<Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/59/roles"/> +</User> diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.json.example b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.json.example new file mode 100644 index 0000000000..861822416e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.json.example @@ -0,0 +1,23 @@ +{ + "UserCreate": { + "mainLanguageCode": "eng-GB", + "remoteId": "remoteId-qwert426", + "login": "yura", + "email": "yurarajzer@example.net", + "password": "Password1!", + "fields": { + "field": [ + { + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldValue": "Yura" + }, + { + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldValue": "Rajzer" + } + ] + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.xml.example b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.xml.example new file mode 100644 index 0000000000..533ba7dbd6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/groups/path/users/POST/UserCreate.xml.example @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserCreate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <remoteId>remoteId-qwert426</remoteId> + <login>yura</login> + <email>yurarajzer@example.net</email> + <password>Secrepassword5!</password> + <fields> + <field> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>Yura</fieldValue> + </field> + <field> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldValue>Rajzer</fieldValue> + </field> + </fields> +</UserCreate> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.json.example b/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.json.example new file mode 100644 index 0000000000..c1665d52be --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.json.example @@ -0,0 +1,104 @@ +{ + "PolicyList": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/policies", + "Policy": [ + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/349", + "id": 349, + "module": "content", + "function": "read", + "limitations": { + "limitation": [ + { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "3" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "6" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/350", + "id": 350, + "module": "user", + "function": "login", + "limitations": { + "limitation": [ + { + "_identifier": "SiteAccess", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "2282622326" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1766001124" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/351", + "id": 351, + "module": "content", + "function": "view_embed", + "limitations": { + "limitation": [ + { + "_identifier": "Class", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "5" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "12" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/353", + "id": 353, + "module": "user", + "function": "password" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.xml.example b/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.xml.example new file mode 100644 index 0000000000..086d75d813 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/policies/GET/PolicyList.xml.example @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyList media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/policies"> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/349"> + <id>349</id> + <module>content</module> + <function>read</function> + <limitations> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="3"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="6"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/350"> + <id>350</id> + <module>user</module> + <function>login</function> + <limitations> + <limitation identifier="SiteAccess"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="2282622326"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="1766001124"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/351"> + <id>351</id> + <module>content</module> + <function>view_embed</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="5"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="12"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/352"> + <id>352</id> + <module>user</module> + <function>register</function> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/353"> + <id>353</id> + <module>user</module> + <function>password</function> + </Policy> +</PolicyList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.json.example b/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.json.example new file mode 100644 index 0000000000..6b10257fc2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.json.example @@ -0,0 +1,71 @@ +{ + "RoleList": { + "_media-type": "application/vnd.ez.api.RoleList+json", + "_href": "/api/ezp/v2/user/roles", + "Role": [ + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/1", + "identifier": "Anonymous", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/1/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2", + "identifier": "Administrator", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/2/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/3", + "identifier": "Editor", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/3/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/4", + "identifier": "Member", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/4/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/10", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/10/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/14", + "identifier": "NewRole5", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/14/policies" + } + }, + { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/15", + "identifier": "NewRole7", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/15/policies" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.xml.example new file mode 100644 index 0000000000..8acc449a52 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/GET/RoleList.xml.example @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleList media-type="application/vnd.ez.api.RoleList+xml" href="/api/ezp/v2/user/roles"> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1"> + <identifier>Anonymous</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/1/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"> + <identifier>Administrator</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/2/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"> + <identifier>Editor</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/3/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/4"> + <identifier>Member</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/4/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/10"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/10/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/14"> + <identifier>NewRole5</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/14/policies"/> + </Role> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/15"> + <identifier>NewRole7</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/15/policies"/> + </Role> +</RoleList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.json.example b/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.json.example new file mode 100644 index 0000000000..68dc077429 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/21", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/21/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.xml.example new file mode 100644 index 0000000000..1f17f0ae36 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/POST/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/19"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/19/policies"/> +</Role> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.json.example b/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.json.example new file mode 100644 index 0000000000..c2cfef57ef --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "NewRole" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.xml.example new file mode 100644 index 0000000000..febdc5635e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/POST/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>NewRole</identifier> +</RoleInput> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.json.example new file mode 100644 index 0000000000..68dc077429 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/21", + "identifier": "NewRole", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/21/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.xml.example new file mode 100644 index 0000000000..cb8e8a213a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/GET/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/21"> + <identifier>NewRole</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/21/policies"/> +</Role> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.json.example new file mode 100644 index 0000000000..2cae7081b1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/21", + "identifier": "NewIdentifier", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/21/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.xml.example new file mode 100644 index 0000000000..5edef6cd7e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/5"> + <identifier>NewIdentifier</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/5/policies"/> +</Role> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.json.example new file mode 100644 index 0000000000..d318744692 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "NewIdentifier" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.xml.example new file mode 100644 index 0000000000..ffe9e53bb2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/PATCH/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>NewIdentifier</identifier> +</RoleInput> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.json.example new file mode 100644 index 0000000000..0858c5cb5c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/6", + "identifier": "Editor", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/6/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.xml.example new file mode 100644 index 0000000000..b163e1d305 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/POST/RoleDraft.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role href="/user/roles/11" media-type="application/vnd.ez.api.RoleDraft+xml"> + <identifier>MyRole</identifier> + <Policies href="/user/roles/11/policies" media-type="application/vnd.ez.api.PolicyList+xml"/> +</Role> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.json.example new file mode 100644 index 0000000000..a5f7fe4782 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/27", + "identifier": "Anonymous", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/27/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.xml.example new file mode 100644 index 0000000000..a8313e3abf --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/GET/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/27"> + <identifier>Anonymous</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/27/policies"/> +</Role> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.json.example new file mode 100644 index 0000000000..aef476fde1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.json.example @@ -0,0 +1,11 @@ +{ + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/6", + "identifier": "UpdatedIdentifier", + "Policies": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/6/policies" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.xml.example new file mode 100644 index 0000000000..65dc71837c --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/Role.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/9"> + <identifier>UpdatedIdentifier</identifier> + <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/9/policies"/> +</Role> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.json.example new file mode 100644 index 0000000000..23bed0ebf3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.json.example @@ -0,0 +1,5 @@ +{ + "RoleInput": { + "identifier": "UpdatedIdentifier" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.xml.example new file mode 100644 index 0000000000..1796642eaf --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/draft/PATCH/RoleInput.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleInput> + <identifier>UpdatedIdentifier</identifier> +</RoleInput> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.json.example new file mode 100644 index 0000000000..838c90155d --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.json.example @@ -0,0 +1,104 @@ +{ + "PolicyList": { + "_media-type": "application/vnd.ez.api.PolicyList+json", + "_href": "/api/ezp/v2/user/roles/1/policies", + "Policy": [ + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/349", + "id": 349, + "module": "content", + "function": "read", + "limitations": { + "limitation": [ + { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "3" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "6" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/350", + "id": 350, + "module": "user", + "function": "login", + "limitations": { + "limitation": [ + { + "_identifier": "SiteAccess", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "2282622326" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1766001124" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/351", + "id": 351, + "module": "content", + "function": "view_embed", + "limitations": { + "limitation": [ + { + "_identifier": "Class", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "5" + }, + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "12" + } + ] + } + } + ] + } + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + }, + { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/353", + "id": 353, + "module": "user", + "function": "password" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.xml.example new file mode 100644 index 0000000000..467ad610b6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/GET/PolicyList.xml.example @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyList media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/1/policies"> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/349"> + <id>349</id> + <module>content</module> + <function>read</function> + <limitations> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="3"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="6"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/350"> + <id>350</id> + <module>user</module> + <function>login</function> + <limitations> + <limitation identifier="SiteAccess"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="2282622326"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="1766001124"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/351"> + <id>351</id> + <module>content</module> + <function>view_embed</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="5"/> + <ref media-type="application/vnd.ez.api.ref+xml" href="12"/> + </values> + </limitation> + </limitations> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/352"> + <id>352</id> + <module>user</module> + <function>register</function> + </Policy> + <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/353"> + <id>353</id> + <module>user</module> + <function>password</function> + </Policy> +</PolicyList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/Policy.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/Policy.xml.example new file mode 100644 index 0000000000..71a3e3886f --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/Policy.xml.example @@ -0,0 +1,17 @@ +<Policy href="/user/roles/11/policies/55" media-type="application/vnd.ez.api.Policy+xml"> + <id>55</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/13"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/12"/> + </values> + </limitation> + </limitations> + </Policy> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/PolicyCreate.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/PolicyCreate.xml.example new file mode 100644 index 0000000000..9f1b59ad66 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/POST/PolicyCreate.xml.example @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyCreate> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/api/ezp/v2/content/types/13"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/api/ezp/v2/content/types/12"/> + </values> + </limitation> + </limitations> +</PolicyCreate> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.json.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.json.example new file mode 100644 index 0000000000..5943fa8269 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.json.example @@ -0,0 +1,9 @@ +{ + "Policy": { + "_media-type": "application/vnd.ez.api.Policy+json", + "_href": "/api/ezp/v2/user/roles/1/policies/352", + "id": 352, + "module": "user", + "function": "register" + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.xml.example new file mode 100644 index 0000000000..a99e96c986 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/GET/Policy.xml.example @@ -0,0 +1,19 @@ +<Policy href="/user/roles/11/policies/45" media-type="application/vnd.ez.api.Policy+xml"> + <id>45</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/10" media-type="application/vnd.ez.api.ContentType+xml" /> + <ref href="/content/types/11" media-type="application/vnd.ez.api.ContentType+xml" /> + <ref href="/content/types/12" media-type="application/vnd.ez.api.ContentType+xml" /> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/4" media-type="application/vnd.ez.api.ContentType+xml" /> + </values> + </limitation> + </limitations> +</Policy> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/Policy.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/Policy.xml.example new file mode 100644 index 0000000000..7a2c61aab4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/Policy.xml.example @@ -0,0 +1,17 @@ +<Policy href="/user/roles/11/policies/55" media-type="application/vnd.ez.api.Policy+xml"> + <id>55</id> + <module>content</module> + <function>create</function> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/14"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/10"/> + </values> + </limitation> + </limitations> + </Policy> diff --git a/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example new file mode 100644 index 0000000000..d77f2df5fc --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<PolicyUpdate> + <limitations> + <limitation identifier="Class"> + <values> + <ref href="/content/types/14"/> + </values> + </limitation> + <limitation identifier="ParentClass"> + <values> + <ref href="/content/types/10"/> + </values> + </limitation> + </limitations> +</PolicyUpdate> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.json.example b/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.json.example new file mode 100644 index 0000000000..e08f02f1d3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.json.example @@ -0,0 +1,11 @@ +{ + "Session": { + "name": "eZSSID", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lkneri34ijajedfw39orj3j93", + "User": { + "_href": "/user/users/14", + "_media-type": "application/vnd.ez.api.User+json" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.xml.example b/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.xml.example new file mode 100644 index 0000000000..a0b6f0e102 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/POST/Session.xml.example @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Session href="/user/sessions/sessionID" media-type="application/vnd.ez.api.Session+xml"> + <name>eZSSID</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/> +</Session> diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.json.example b/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.json.example new file mode 100644 index 0000000000..f86713dde0 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.json.example @@ -0,0 +1,6 @@ +{ + "SessionInput": { + "login": "admin", + "password": "secret" + } +} diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.xml.example b/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.xml.example new file mode 100644 index 0000000000..ae0f88db02 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/POST/SessionInput.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SessionInput> + <login>admin</login> + <password>secret</password> +</SessionInput> diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.json.example b/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.json.example new file mode 100644 index 0000000000..e08f02f1d3 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.json.example @@ -0,0 +1,11 @@ +{ + "Session": { + "name": "eZSSID", + "identifier": "go327ij2cirpo59pb6rrv2a4el2", + "csrfToken": "23lkneri34ijajedfw39orj3j93", + "User": { + "_href": "/user/users/14", + "_media-type": "application/vnd.ez.api.User+json" + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.xml.example b/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.xml.example new file mode 100644 index 0000000000..2d28e631e4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/sessions/session_id/refresh/POST/Session.xml.example @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Session href="user/sessions/go327ij2cirpo59pb6rrv2a4el2/refresh" media-type="application/vnd.ez.api.Session+xml"> + <name>eZSSID</name> + <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier> + <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken> + <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/> +</Session> diff --git a/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.json.example b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.json.example new file mode 100644 index 0000000000..69e2cc99a1 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.json.example @@ -0,0 +1,6 @@ +{ + "JWT": { + "_media-type": "application/vnd.ez.api.JWT+json", + "_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3ODAsImV4cCI6MTYwMjg0NDM4MCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.0LHa799HwSwwfDBZd2V0q2xHwGt86PpyZamKnXHQyYI" + } +} diff --git a/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.xml.example b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.xml.example new file mode 100644 index 0000000000..a5e5f5fc78 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWT.xml.example @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<JWT media-type="application/vnd.ez.api.JWT+xml" token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3NjEsImV4cCI6MTYwMjg0NDM2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.LsmdVjad7wMwVQUo4vSftT0zHbJyArOMd23b417E2jI"/> diff --git a/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.json.example b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.json.example new file mode 100644 index 0000000000..fd5e55c7fd --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.json.example @@ -0,0 +1,6 @@ +{ + "JWTInput": { + "username": "admin", + "password": "publish" + } +} diff --git a/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.xml.example b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.xml.example new file mode 100644 index 0000000000..b7a0534789 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/token/jwt/POST/JWTInput.xml.example @@ -0,0 +1,4 @@ +<JWTInput> + <password>publish</password> + <username>admin</username> +</JWTInput> diff --git a/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.json.example b/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.json.example new file mode 100644 index 0000000000..70092ebbc2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.json.example @@ -0,0 +1,155 @@ +{ + "UserList": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/users", + "User": [ + { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14", + "_id": 14, + "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4" + }, + "name": "Administrator User", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/14/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/13/15" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/14/locations" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/14/groups" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "publishDate": "2002-10-06T16:13:50+00:00", + "lastModificationDate": "2011-03-25T14:07:04+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/14/versions/3", + "VersionInfo": { + "id": 499, + "versionNo": 3, + "status": "PUBLISHED", + "modificationDate": "2011-03-25T14:07:04+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2011-03-25T14:03:03+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Administrator User" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/14" + } + }, + "Fields": { + "field": [ + { + "id": 28, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Administrator" + }, + { + "id": 29, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "User" + }, + { + "id": 30, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 14, + "login": "admin", + "email": "admin@link.invalid", + "passwordUpdatedAt": null, + "enabled": true, + "maxLogin": 10, + "plainPassword": null + } + }, + { + "id": 178, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 180, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/14/versions/3/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "admin", + "email": "admin@link.invalid", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/users/14/groups" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/14/roles" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.xml.example new file mode 100644 index 0000000000..ee59b86a0e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/GET/UserList.xml.example @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserList media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/users"> + <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14" id="14" remoteId="1bb4fe25487f05527efa8bfd394cecc7"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/> + <name>Administrator User</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/14/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13/15"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/14/locations"/> + <Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/14/groups"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <publishDate>2002-10-06T18:13:50+02:00</publishDate> + <lastModificationDate>2011-03-25T15:07:04+01:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/14/versions/3"> + <VersionInfo> + <id>499</id> + <versionNo>3</versionNo> + <status>PUBLISHED</status> + <modificationDate>2011-03-25T15:07:04+01:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2011-03-25T15:03:03+01:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Administrator User</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/14"/> + </VersionInfo> + <Fields> + <field> + <id>28</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Administrator</fieldValue> + </field> + <field> + <id>29</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>User</fieldValue> + </field> + <field> + <id>30</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">14</value> + <value key="login">admin</value> + <value key="email">nospam@ez.no</value> + <value key="enabled">true</value> + <value key="maxLogin">10</value> + </fieldValue> + </field> + <field> + <id>178</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>180</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/14/versions/3/relations"/> + </Version> + <login>admin</login> + <email>nospam@ez.no</email> + <enabled>true</enabled> + <UserGroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/users/14/groups"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/14/roles"/> + </User> +</UserList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/GET/UserRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/GET/UserRefList.xml.example new file mode 100644 index 0000000000..fc35f6e338 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/GET/UserRefList.xml.example @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserRefList media-type="application/vnd.ez.api.UserRefList+xml" href="/api/ezp/v2/user/users"> + <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> +</UserRefList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.json.example new file mode 100644 index 0000000000..4cccefd3c7 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.json.example @@ -0,0 +1,155 @@ +{ + "UserList": { + "_media-type": "application/vnd.ez.api.UserList+json", + "_href": "/api/ezp/v2/user/users", + "User": [ + { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/79", + "_id": 79, + "_remoteId": "bcf0764b417f05af21852a1f03fb1f13", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4" + }, + "name": "Jose Vargas", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/79/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/12/79" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/79/locations" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/79/groups" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/10" + }, + "publishDate": "2021-06-29T08:23:42+00:00", + "lastModificationDate": "2021-06-29T08:23:42+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/79/versions/1", + "VersionInfo": { + "id": 547, + "versionNo": 1, + "status": "PUBLISHED", + "modificationDate": "2021-06-29T08:23:42+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/10" + }, + "creationDate": "2021-06-29T08:23:42+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Jose Vargas" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/79" + } + }, + "Fields": { + "field": [ + { + "id": 342, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Jose" + }, + { + "id": 343, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Vargas" + }, + { + "id": 344, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 79, + "login": "josevargas", + "email": "aa@bb.cc", + "passwordUpdatedAt": 1624955022, + "enabled": true, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 345, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 346, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/79/versions/1/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "josevargas", + "email": "aa@bb.cc", + "enabled": true, + "UserGroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/users/79/groups" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/79/roles" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.xml.example new file mode 100644 index 0000000000..2749d54bb4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/GET/User.xml.example @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserList media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/users"> + <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/> + <name>Jose Vargas</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/79/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/12/79"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/79/locations"/> + <Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/79/groups"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/10"/> + <publishDate>2021-06-29T08:23:42+00:00</publishDate> + <lastModificationDate>2021-06-29T08:23:42+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/79/versions/1"> + <VersionInfo> + <id>547</id> + <versionNo>1</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-06-29T08:23:42+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/10"/> + <creationDate>2021-06-29T08:23:42+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Jose Vargas</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/79"/> + </VersionInfo> + <Fields> + <field> + <id>342</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Jose</fieldValue> + </field> + <field> + <id>343</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Vargas</fieldValue> + </field> + <field> + <id>344</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">79</value> + <value key="login">josevargas</value> + <value key="email">aa@bb.cc</value> + <value key="passwordUpdatedAt">1624955022</value> + <value key="enabled">true</value> + <value key="maxLogin">0</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>345</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>346</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/79/versions/1/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <login>josevargas</login> + <email>aa@bb.cc</email> + <enabled>true</enabled> + <UserGroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/users/79/groups"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/79/roles"/> + </User> +</UserList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.json.example new file mode 100644 index 0000000000..5a0dd5e86b --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.json.example @@ -0,0 +1,149 @@ +{ + "User": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/79", + "_id": 79, + "_remoteId": "bcf0764b417f05af21852a1f03fb1f13", + "ContentType": { + "_media-type": "application/vnd.ez.api.ContentType+json", + "_href": "/api/ezp/v2/content/types/4" + }, + "name": "Jose Vargas", + "Versions": { + "_media-type": "application/vnd.ez.api.VersionList+json", + "_href": "/api/ezp/v2/content/objects/79/versions" + }, + "Section": { + "_media-type": "application/vnd.ez.api.Section+json", + "_href": "/api/ezp/v2/content/sections/2" + }, + "MainLocation": { + "_media-type": "application/vnd.ez.api.Location+json", + "_href": "/api/ezp/v2/content/locations/1/5/12/79" + }, + "Locations": { + "_media-type": "application/vnd.ez.api.LocationList+json", + "_href": "/api/ezp/v2/content/objects/79/locations" + }, + "Groups": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/79/groups" + }, + "Owner": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/10" + }, + "publishDate": "2021-06-29T08:23:42+00:00", + "lastModificationDate": "2021-07-29T12:00:24+00:00", + "mainLanguageCode": "eng-GB", + "alwaysAvailable": true, + "Version": { + "_media-type": "application/vnd.ez.api.Version+json", + "_href": "/api/ezp/v2/content/objects/79/versions/11", + "VersionInfo": { + "id": 614, + "versionNo": 11, + "status": "PUBLISHED", + "modificationDate": "2021-07-29T12:00:24+00:00", + "Creator": { + "_media-type": "application/vnd.ez.api.User+json", + "_href": "/api/ezp/v2/user/users/14" + }, + "creationDate": "2021-07-29T12:00:24+00:00", + "initialLanguageCode": "eng-GB", + "languageCodes": "eng-GB", + "VersionTranslationInfo": { + "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json", + "Language": [ + { + "languageCode": "eng-GB" + } + ] + }, + "names": { + "value": [ + { + "_languageCode": "eng-GB", + "#text": "Jose Vargas" + } + ] + }, + "Content": { + "_media-type": "application/vnd.ez.api.ContentInfo+json", + "_href": "/api/ezp/v2/content/objects/79" + } + }, + "Fields": { + "field": [ + { + "id": 342, + "fieldDefinitionIdentifier": "first_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Jose" + }, + { + "id": 343, + "fieldDefinitionIdentifier": "last_name", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezstring", + "fieldValue": "Vargas" + }, + { + "id": 344, + "fieldDefinitionIdentifier": "user_account", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezuser", + "fieldValue": { + "hasStoredLogin": true, + "contentId": 79, + "login": "josevargas", + "email": "aa@bb.cc", + "passwordUpdatedAt": null, + "enabled": false, + "maxLogin": 0, + "plainPassword": null + } + }, + { + "id": 345, + "fieldDefinitionIdentifier": "signature", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "eztext", + "fieldValue": null + }, + { + "id": 346, + "fieldDefinitionIdentifier": "image", + "languageCode": "eng-GB", + "fieldTypeIdentifier": "ezimage", + "fieldValue": null + } + ] + }, + "Relations": { + "_media-type": "application/vnd.ez.api.RelationList+json", + "_href": "/api/ezp/v2/content/objects/79/versions/11/relations", + "Relation": [] + }, + "Thumbnail": { + "_media-type": "application/vnd.ez.api.Thumbnail+json", + "resource": "/bundles/ezplatformadminui/img/ez-icons.svg#user", + "width": null, + "height": null, + "mimeType": "image/svg+xml" + } + }, + "login": "josevargas", + "email": "aa@bb.cc", + "enabled": false, + "UserGroups": { + "_media-type": "application/vnd.ez.api.UserGroupList+json", + "_href": "/api/ezp/v2/user/users/79/groups" + }, + "Roles": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/79/roles" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.xml.example new file mode 100644 index 0000000000..21e8150728 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/User.xml.example @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13"> + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/> + <name>Jose Vargas</name> + <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/79/versions"/> + <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/> + <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/12/79"/> + <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/79/locations"/> + <Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/79/groups"/> + <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/10"/> + <publishDate>2021-06-29T08:23:42+00:00</publishDate> + <lastModificationDate>2021-07-29T11:45:07+00:00</lastModificationDate> + <mainLanguageCode>eng-GB</mainLanguageCode> + <alwaysAvailable>true</alwaysAvailable> + <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/79/versions/6"> + <VersionInfo> + <id>609</id> + <versionNo>6</versionNo> + <status>PUBLISHED</status> + <modificationDate>2021-07-29T11:45:07+00:00</modificationDate> + <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/> + <creationDate>2021-07-29T11:45:07+00:00</creationDate> + <initialLanguageCode>eng-GB</initialLanguageCode> + <languageCodes>eng-GB</languageCodes> + <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml"> + <Language> + <languageCode>eng-GB</languageCode> + </Language> + </VersionTranslationInfo> + <names> + <value languageCode="eng-GB">Jose Vargas</value> + </names> + <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/79"/> + </VersionInfo> + <Fields> + <field> + <id>342</id> + <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Jose</fieldValue> + </field> + <field> + <id>343</id> + <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezstring</fieldTypeIdentifier> + <fieldValue>Vargas</fieldValue> + </field> + <field> + <id>344</id> + <fieldDefinitionIdentifier>user_account</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezuser</fieldTypeIdentifier> + <fieldValue> + <value key="hasStoredLogin">true</value> + <value key="contentId">79</value> + <value key="login">josevargas</value> + <value key="email">aa@bb.cc</value> + <value key="passwordUpdatedAt"/> + <value key="enabled">false</value> + <value key="maxLogin">0</value> + <value key="plainPassword"/> + </fieldValue> + </field> + <field> + <id>345</id> + <fieldDefinitionIdentifier>signature</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>eztext</fieldTypeIdentifier> + <fieldValue/> + </field> + <field> + <id>346</id> + <fieldDefinitionIdentifier>image</fieldDefinitionIdentifier> + <languageCode>eng-GB</languageCode> + <fieldTypeIdentifier>ezimage</fieldTypeIdentifier> + <fieldValue/> + </field> + </Fields> + <Relations media-type="application/vnd.ez.api.RelationList+xml" href="/api/ezp/v2/content/objects/79/versions/6/relations"/> + <Thumbnail media-type="application/vnd.ez.api.Thumbnail+xml"> + <resource>/bundles/ezplatformadminui/img/ez-icons.svg#user</resource> + <width></width> + <height></height> + <mimeType>image/svg+xml</mimeType> + </Thumbnail> + </Version> + <login>josevargas</login> + <email>aa@bb.cc</email> + <enabled>false</enabled> + <UserGroups media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/users/79/groups"/> + <Roles media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/79/roles"/> +</User> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.json.example new file mode 100644 index 0000000000..fd43e1d7f2 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.json.example @@ -0,0 +1,6 @@ +{ + "UserUpdate": { + "login": "josevargas", + "enabled": false + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.xml.example new file mode 100644 index 0000000000..fe09ab444e --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/PATCH/UserUpdate.xml.example @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserUpdate> + <login>josevargas</login> + <enabled>false</enabled> +</UserUpdate> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example new file mode 100644 index 0000000000..80ec565966 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/55/groups"> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/12"> + <unassign href="/api/ezp/v2/user/users/55/groups/12" method="DELETE"/> + </UserGroup> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/14"> + <unassign href="/api/ezp/v2/user/users/55/groups/14" method="DELETE"/> + </UserGroup> +</UserGroupRefList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example new file mode 100644 index 0000000000..7701d48621 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.json.example @@ -0,0 +1,24 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/115/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/12", + "unassign": { + "_href": "/api/ezp/v2/user/users/115/groups/12", + "_method": "DELETE" + } + }, + { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13", + "unassign": { + "_href": "/api/ezp/v2/user/users/115/groups/13", + "_method": "DELETE" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example new file mode 100644 index 0000000000..3cb30957d0 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/115/groups"> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/12"> + <unassign href="/api/ezp/v2/user/users/79/groups/12" method="DELETE"/> + </UserGroup> + <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13"> + <unassign href="/api/ezp/v2/user/users/115/groups/13" method="DELETE"/> + </UserGroup> +</UserGroupRefList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example new file mode 100644 index 0000000000..81bf551bfb --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<UserGroupRefList href="/user/users/45/groups" + media-type="application/vnd.ez.api.UserGroupRefList"> + <UserGroup href="/user/groups/1/5/34" media-type="application/vnd.ez.api.UserGroup"> + <unassign href="/user/users/45/groups/34" method="DELETE" /> + </UserGroup> + <UserGroup href="/user/groups/1/5/88" media-type="application/vnd.ez.api.UserGroup"> + <unassign href="/user/users/45/groups/88" method="DELETE" /> + </UserGroup> +</UserGroupRefList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example new file mode 100644 index 0000000000..9e41abbf01 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/groups/group_id/UserGroupRefList.json.example @@ -0,0 +1,12 @@ +{ + "UserGroupRefList": { + "_media-type": "application/vnd.ez.api.UserGroupRefList+json", + "_href": "/api/ezp/v2/user/users/57/groups", + "UserGroup": [ + { + "_media-type": "application/vnd.ez.api.UserGroup+json", + "_href": "/api/ezp/v2/user/groups/1/5/13" + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example new file mode 100644 index 0000000000..d44787dba4 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example @@ -0,0 +1,27 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/115/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..fa92cc9311 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/115/roles"> + <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/> + </RoleAssignment> +</RoleAssignmentList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example new file mode 100644 index 0000000000..4042bb7362 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.json.example @@ -0,0 +1,26 @@ +{ + "RoleAssignInput": { + "Role": { + "_href": "/api/ezp/v2/user/roles/2", + "_media-type": "application/vnd.ez.api.RoleAssignInput+xml", + "self-closing": "true" + }, + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_href": "/api/ezp/v2/content/sections/1", + "media-type": "application/vnd.ez.api.Section+xml", + "self-closing": "true" + }, + { + "_href": "/api/ezp/v2/content/sections/2", + "_media-type": "application/vnd.ez.api.Section+xml", + "self-closing": "true" + } + ] + } + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example new file mode 100644 index 0000000000..c70ab931c6 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignInput> + <Role href="/api/ezp/v2/user/roles/2" media-type="application/vnd.ez.api.RoleAssignInput+xml"/> + <limitation identifier="Section"> + <values> + <ref href="/api/ezp/v2/content/sections/1" media-type="application/vnd.ez.api.Section+xml" /> + <ref href="/api/ezp/v2/content/sections/2" media-type="application/vnd.ez.api.Section+xml" /> + </values> + </limitation> +</RoleAssignInput> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example new file mode 100644 index 0000000000..6bfc4518e9 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example @@ -0,0 +1,46 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/115/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "2" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2" + } + }, + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/115/roles/2", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "1" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..8c7bc1fd43 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/115/roles"> + <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="1"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/> + </RoleAssignment> + <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="2"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/> + </RoleAssignment> +</RoleAssignmentList> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example new file mode 100644 index 0000000000..cfcd77fc44 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example @@ -0,0 +1,24 @@ +{ + "RoleAssignmentList": { + "_media-type": "application/vnd.ez.api.RoleAssignmentList+json", + "_href": "/api/ezp/v2/user/users/57/roles", + "RoleAssignment": [ + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/57/roles/1", + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/1" + } + }, + { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/57/roles/2", + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/2" + } + } + ] + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example new file mode 100644 index 0000000000..8c5b829b3a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignmentList href="/user/groups/1/5/65/roles" media-type="application/vnd.ez.api.RoleAssignmentList+xml"> + <RoleAssignment href="/user/groups/1/5/65/roles/5" media-type="application/vnd.ez.api.RoleAssignment+xml"> + <Role href="/user/roles/5" media-type="application/vnd.ez.api.Role+xml"/> + </RoleAssignment> + <RoleAssignment href="/user/groups/1/5/65/roles/11" media-type="application/vnd.ez.api.RoleAssignment+xml"> + <limitation identifier="Section"> + <values> + <ref href="/content/sections/1" media-type="application/vnd.ez.api.Section+xml" /> + <ref href="/content/sections/4" media-type="application/vnd.ez.api.Section+xml" /> + </values> + </limitation> + <Role href="/user/roles/11" media-type="application/vnd.ez.api.Role+xml"/> + </RoleAssignment> +</RoleAssignmentList> diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example new file mode 100644 index 0000000000..31b132402a --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example @@ -0,0 +1,21 @@ +{ + "RoleAssignment": { + "_media-type": "application/vnd.ez.api.RoleAssignment+json", + "_href": "/api/ezp/v2/user/users/113/roles/3", + "limitation": { + "_identifier": "Section", + "values": { + "ref": [ + { + "_media-type": "application/vnd.ez.api.ref+json", + "_href": "6" + } + ] + } + }, + "Role": { + "_media-type": "application/vnd.ez.api.Role+json", + "_href": "/api/ezp/v2/user/roles/3" + } + } +} \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example new file mode 100644 index 0000000000..440701ab73 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/113/roles/3"> + <limitation identifier="Section"> + <values> + <ref media-type="application/vnd.ez.api.ref+xml" href="6"/> + </values> + </limitation> + <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/> +</RoleAssignment> \ No newline at end of file diff --git a/docs/api/rest_api_reference/input/examples/views/POST/View.xml.v11.example b/docs/api/rest_api_reference/input/examples/views/POST/View.xml.v11.example new file mode 100644 index 0000000000..8b6a592e68 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/views/POST/View.xml.v11.example @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<View href="/views/TitleView" media-type="application/vnd.ez.api.View+xml; version=1.1"> + <identifier>TitleView</identifier> + <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/> + <public>false</public> + <LocationQuery> + <Filter> + <ParentLocationIdCriterion>2</ParentLocationIdCriterion> + </Filter> + <limit>10</limit> + <offset>0</offset> + <SortClauses> + <ContentName>ascending</ContentName> + </SortClauses> + <FacetBuilders> + <contentTypeFacetBuilder/> + </FacetBuilders> + </LocationQuery> + <Result href="/content/views/view1234/results" + media-type="application/vnd.ez.api.ViewResult+xml" count="34" time="31" maxScore="1.0"> + <searchHits> + <searchHit score="1.0" index="installid1234567890"> + <hightlight/> + <value> + <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2"> + <id>2</id> + <priority>0</priority> + <hidden>false</hidden> + <invisible>false</invisible> + <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1"/> + <pathString>/1/2/</pathString> + <depth>1</depth> + <childCount>8</childCount> + <remoteId>f3e90596361e31d496d4026eb624c983</remoteId> + <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/children"/> + <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/57"/> + <sortField>PRIORITY</sortField> + <sortOrder>ASC</sortOrder> + <UrlAliases media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/locations/1/2/urlaliases"/> + </Location> + + </value> + </searchHit> + <!-- ... --> + </searchHits> + <facets> + <contentTypeFacet> + <contentTypeFacetEntry> + <contentType href="/content/types/1" media-type="application/vnd.ez.api.ContentType+xml"/> + <count>3</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/7" media-type="application/vnd.ez.api.ContentType+xml"/> + <count>9</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/11" media-type="application/vnd.ez.api.ContentType+xml"/> + <count>1</count> + </contentTypeFacetEntry> + <contentTypeFacetEntry> + <contentType href="/content/types/15" media-type="application/vnd.ez.api.ContentType+xml"/> + <count>8</count> + </contentTypeFacetEntry> + </contentTypeFacet> + </facets> + </Result> +</View> diff --git a/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.json.example b/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.json.example new file mode 100644 index 0000000000..eb4514cc91 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.json.example @@ -0,0 +1,28 @@ +{ + "ViewInput": { + "identifier": "TitleView", + "Query": { + "Filter": { + "ContentTypeIdentifierCriterion": "image", + "SectionIdentifierCriterion": "media", + "DateMetadataCriterion": { + "Target": "modified", + "Value": 1675681020, + "Operator": "gte" + } + }, + "limit": 10, + "offset": 0, + "SortClauses": { + "ContentName": "ascending" + }, + "Aggregations": [ + { + "ContentTypeTermAggregation": { + "name": "some name" + } + } + ] + } + } +} diff --git a/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.xml.example b/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.xml.example new file mode 100644 index 0000000000..45cc1f4897 --- /dev/null +++ b/docs/api/rest_api_reference/input/examples/views/POST/ViewInput.xml.example @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ViewInput> + <identifier>TitleView</identifier> + <Query> + <Filter> + <ContentTypeIdentifierCriterion>image</ContentTypeIdentifierCriterion> + <SectionIdentifierCriterion>media</SectionIdentifierCriterion> + <DateMetadataCriterion> + <Target>modified</Target> + <Value>1675681020</Value> + <Operator>gte</Operator> + </DateMetadataCriterion> + </Filter> + <limit>10</limit> + <offset>0</offset> + <SortClauses> + <ContentName>ascending</ContentName> + </SortClauses> + <Aggregations> + <Aggregation> + <ContentTypeTermAggregation> + <name>some name</name> + </ContentTypeTermAggregation> + </Aggregation> + </Aggregations> + </Query> +</ViewInput> diff --git a/docs/api/rest_api_reference/input/ez-bookmark.raml b/docs/api/rest_api_reference/input/ez-bookmark.raml new file mode 100644 index 0000000000..96931d6a1a --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-bookmark.raml @@ -0,0 +1,63 @@ +displayName: Managing bookmarks +get: + displayName: List of bookmarks + description: Lists bookmarked Locations for the current user. + queryParameters: + offset: + description: The offset of the result set. + type: integer + default: 0 + limit: + description: The number of returned bookmarks. + type: integer + default: 25 + headers: + Accept: + description: If set, the list is returned in XML or JSON format. + example: | + application/vnd.ez.api.BookmarkList+xml + application/vnd.ez.api.BookmarkList+json + responses: + 200: + body: + application/vnd.ez.api.BookmarkList+xml: + type: BookmarkList + example: !include examples/bookmark/GET/BookmarkList.xml.example + application/vnd.ez.api.BookmarkList+json: + type: BookmarkList + example: !include examples/bookmark/GET/BookmarkList.json.example + 401: + description: Error - the user is not authorized to list bookmarks. +/{locationId}: + post: + displayName: Create bookmark + description: Add given Location to bookmarks of the current user. + responses: + 201: + description: Created. + 401: + description: Error - the user is not authorized to given Location. + 404: + description: Error - the given Location does not exist. + 409: + description: Error - Location is already bookmarked. + head: + displayName: Check if Location is bookmarked + description: Checks if the given Location is bookmarked by the current user. + responses: + 200: + description: OK - bookmarked. + 401: + description: Error - the user is not authorized for the given Location. + 404: + description: Error - the given Location does not exist / is not bookmarked. + delete: + displayName: Delete bookmark + description: Deletes the given Location from bookmarks of the current user. + responses: + 204: + description: Deleted - no content. + 401: + description: Error - the user is not authorized for the given Location. + 404: + description: Error - the given Location does not exist / is not bookmarked. diff --git a/docs/api/rest_api_reference/input/ez-calendar.raml b/docs/api/rest_api_reference/input/ez-calendar.raml new file mode 100644 index 0000000000..2ead969047 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-calendar.raml @@ -0,0 +1,93 @@ +displayName: Calendar +/event: + get: + displayName: Calendar list + description: Calendar event list. + queryParameters: + start: + description: Query start date. + type: datetime + end: + description: Query end date. + type: datetime + types: + description: The types of events that are displayed. + type: string + languages: + description: Language code. Restricts the output of translatable Fields to the given languages. + type: string + count: + description: Number of parameters that are returned in the list. + type: string + cursor: + description: Starting point of calendar event list. It should be taken from the URL. + type: string + headers: + Accept: + description: If set, the calendar event list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + responses: + 200: + body: + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/calendar/event/GET/ContentInfo.xml.example + application/vnd.ez.api.ContentInfo+json: + type: ContentInfo + example: !include examples/calendar/event/GET/ContentInfo.json.example + /grouped-by-day: + get: + displayName: Calendar list grouped by day + description: Calendar event list grouped by day. + queryParameters: + start: + description: Query start date. + type: integer + end: + description: Query end date. + type: integer + types: + description: The types of events that are displayed. + type: string + languages: + description: Language code. Restricts the output of event list to the given languages. + type: string + count: + description: Number of parameters that are returned in the list. + type: integer + cursor: + description: Starting point of calendar event list. It should be taken from the URL. + type: string + headers: + Accept: + description: If set, the calendar event list grouped by day is returned in XML or JSON format. + example: | + application/vnd.ez.api.ScheduledVersion+xml + application/vnd.ez.api.ScheduledVersion+json + responses: + 200: + body: + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/calendar/event/grouped-by-day/GET/ContentInfo.xml.example + application/vnd.ez.api.ContentInfo+json: + type: ContentInfo + example: !include examples/calendar/event/grouped-by-day/GET/ContentInfo.json.example + /{eventType}: + post: + displayName: Calendar action + description: A calendar action that e.g. reschedules or unschedules calendar events. The event type should always be copied from an event. + headers: + Content-Type: + description: + example: | + application/vnd.ez.api.calendar.future_publication.UnscheduleAction+json + body: + application/vnd.ez.api.UnscheduleActionInput+json: + type: RoleAssignInput + example: !include examples/calendar/event/eventType/POST/UnscheduleActionInput.json.example + responses: + 204: + description: No Content - the action has been unscheduled. diff --git a/docs/api/rest_api_reference/input/ez-commerce.raml b/docs/api/rest_api_reference/input/ez-commerce.raml new file mode 100644 index 0000000000..2c3dcffabc --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-commerce.raml @@ -0,0 +1,323 @@ +displayName: eCommerce API + +/basket: + get: + displayName: Read list of baskets + description: | + Returns baskets for the current user. Only "storedBasket" and "wishList" types are handled. Others will return validation error. + queryParameters: + type: + description: Either "storedBasket" or "wishList". "storedBasket" is assumed if not passed. + responses: + 200: + body: + application/vnd.ez.api.BasketListResponse+json: + type: BasketListResponse + example: !include examples/commerce/basket/headers/GET/BasketListResponse.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse: + type: ValidationResponse + + /current: + get: + displayName: Get current basket + description: | + Returns a Basket instance for the current session. If there isn't one, a new one will be created. + headers: + Accept: + example: application/vnd.ez.api.Basket+json + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + + /{basketId}: + copy: + displayName: Copy basket to user session + description: |- + Adds basket lines into current basket in user session, if any, using basket stored in permanent + storage. + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + example: !include examples/commerce/basket/current/copyfrom/_basket_id_/POST/ValidationResponse.json.example + + /referencenumber: + patch: + displayName: Save the external reference number in basket + + /remark: + patch: + displayName: Save the remark in basket + + /party/{partyType}: + patch: + displayName: Update party information in the basket + description: Updates party (buyer, delivery, invoice) information in the basket. + body: + application/vnd.ez.api.PartyInvoice+json: + type: PartyTypeInput + example: !include examples/commerce/basket/current/party/invoice/PATCH/PartyInvoice.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + + /shippingmethod: + patch: + displayName: Update shipping information in current basket + description: Updates shipping information in the current basket. + body: + application/vnd.ez.api.ShippingMethodData+json: + type: ShippingMethodData + example: !include examples/commerce/basket/current/shippingmethod/PATCH/ShippingMethodData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + + /paymentmethod: + patch: + displayName: Update payment information in current basket + description: Updates payment information in the current basket. + body: + application/vnd.ez.api.PaymentMethodData.json: + type: PaymentMethodData + example: !include examples/commerce/basket/current/paymentmethod/PATCH/PaymentMethodData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + + /voucher: + patch: + displayName: Update and check voucher in current basket + description: Updates and checks voucher in current basket. + body: + application/vnd.ez.api.VoucherData.json: + type: VoucherData + example: !include examples/commerce/basket/current/voucher/PATCH/VoucherData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + + /line: + post: + displayName: Add products to current basket + description: Adds products to the current basket. + body: + application/vnd.ez.api.BasketLineData+json: + type: BasketLineData + example: !include examples/commerce/basket/current/lines/POST/BasketLineData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + example: !include examples/commerce/basket/current/lines/POST/ValidationResponse.json.example + + /{basketId}: + get: + displayName: Get Basket by ID + description: Gets basket by ID. + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/_id_/GET/Basket.json.example + delete: + displayName: Delete Basket by ID (removed) + description: Deletes basket by ID. Removed as of Ibexa DXP 4.4, use DELETE /cart/{identifier} instead. + responses: + 200: + description: OK - The basket has been deleted + 400: + description: Error - The basket does not exist + 403: + description: Error - Access denied + + /name: + patch: + displayName: Update the name of the stored basket + description: | + Updates the name of the stored basket. + body: + application/vnd.ez.api.BasketHeaderData+json: + type: BasketHeaderData + example: !include examples/commerce/basket/_id_/_mode_/POST/BasketHeaderData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + + /note: + patch: + displayName: Update the note of the stored basket + description: | + Updates the note of the stored basket. + body: + application/vnd.ez.api.BasketHeaderData+json: + type: BasketHeaderData + example: !include examples/commerce/basket/_id_/_mode_/POST/BasketHeaderData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + + /line: + post: + displayName: Add products to stored basket + description: Adds products to the stored basket. + body: + application/vnd.ez.api.BasketLineData+json: + type: BasketLineData + example: !include examples/commerce/basket/current/lines/POST/BasketLineData.json.example + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + example: !include examples/commerce/basket/current/lines/POST/ValidationResponse.json.example + + /line/{lineId}: + delete: + displayName: Delete a line from a stored basket + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + example: !include examples/commerce/basket/_id_/line/_line_id_/DELETE/ValidationResponse.json.example + + /{basketName}: + post: + displayName: Create a new persisted basket + description: Creates a new persisted basket. + responses: + 200: + body: + application/vnd.ez.api.Basket+json: + type: Basket + example: !include examples/commerce/basket/Basket.json.example + 400: + body: + application/vnd.ez.api.ValidationResponse+json: + type: ValidationResponse + example: !include examples/commerce/basket/POST/ValidationResponse.json.example + +/checkout: + /paymentmethods: + get: + displayName: Get list of payment methods + responses: + 200: + body: + application/vnd.ez.api.PaymentMethodDataResponse+json: + type: PaymentMethodDataResponse + example: !include examples/commerce/checkout/payment-methods/GET/PaymentMethodDataResponse.json.example + + /shippingmethods: + get: + displayName: Get list of shipping methods + responses: + 200: + body: + application/vnd.ez.api.ShippingMethodDataResponse+json: + type: ShippingMethodDataResponse + example: !include examples/commerce/checkout/shipping-methods/GET/ShippingMethodDataResponse.json.example + +/customer/addresses/shipping: + get: + displayName: Get list of shipping addresses + responses: + 200: + body: + application/json: + type: ShippingAddressesResponse + example: !include examples/commerce/customer/addresses/shipping/GET/ShippingAddressesResponse.json.example + +/customerprice: + post: + displayName: Get customer prices for products + description: Gets customer prices for the requested products. + body: + application/json: + type: CustomerPriceData + example: !include examples/commerce/customerprice/POST/CustomerPriceData.json.example + responses: + 200: + body: + application/json: + type: PriceResponse + example: !include examples/commerce/customerprice/POST/PriceResponse.json.example + + +/common/check_sku_file/{mode}: + post: + displayName: Add to basket from CSV or Excel + description: Adds to basket a line from a CSV or Excel file. + +/country: + get: + displayName: Gets list of countries. + responses: + 200: + body: + application/json: + type: CountrySelectionResponse + example: !include examples/commerce/country-selection/GET/CountrySelectionResponse.json.example diff --git a/docs/api/rest_api_reference/input/ez-content-assets.raml b/docs/api/rest_api_reference/input/ez-content-assets.raml new file mode 100644 index 0000000000..5da71ec636 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-assets.raml @@ -0,0 +1,20 @@ +displayName: content/assets/images +/images/{assetId}/{assetSource}: + get: + displayName: Fetch image asset value + description: Fetches the image asset value object. Depending on the provider, the asset metadata object can contain different parameters. + headers: + Accept: + description: If set, the value XML object with image asset metadata is returned. + example: | + application/vnd.ez.api.Asset+xml + responses: + 200: + body: + application/vnd.ez.api.Asset+xml: + type: Asset + example: !include examples/content/assets/images/assetId/assetSource/GET/Asset.xml.example + 401: + description: Error - the user is not authorized to read this image asset. + 404: + description: Error - asset Id/asset source pair does not match any image asset. diff --git a/docs/api/rest_api_reference/input/ez-content-binary.raml b/docs/api/rest_api_reference/input/ez-content-binary.raml new file mode 100644 index 0000000000..a123359d77 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-binary.raml @@ -0,0 +1,21 @@ +displayName: content/binary/name +/binary/images/{imageId}/variations/{variationIdentifier}: + get: + displayName: Load an image variation + description: Loads an image variation. + headers: + Accept: + description: If set, the image is returned in XML or JSON format. + example: | + application/vnd.ez.api.ImageVariation+xml + application/vnd.ez.api.ImageVariation+json + responses: + 200: + body: + application/vnd.ez.api.ImageVariation+xml: + type: ContentImageVariation + example: !include examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - image ID doesn't match any image or variationIdentifier doesn't match any known variation. diff --git a/docs/api/rest_api_reference/input/ez-content-locations.raml b/docs/api/rest_api_reference/input/ez-content-locations.raml new file mode 100644 index 0000000000..817e8131fe --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-locations.raml @@ -0,0 +1,187 @@ +get: + displayName: Load Locations by id/remoteId/urlAlias + description: Loads the Location for a given ID (x), remote ID or URL alias. + queryParameters: + id: + description: The ID of the Location. If present, the Location with the given ID is returned. + remoteId: + description: The remote ID of the Location. If present, the Location with the given remote ID is returned. + urlAlias: + description: One of the URL aliases of the Location. If present, the Location with given URL Alias is returned. + responses: + 307: + description: Temporary redirect to the main resource URL. + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: Location + example: !include examples/content/locations/GET/LocationList.xml.example + application/vnd.ez.api.LocationList+json: + type: Location + example: !include examples/content/locations/GET/LocationList.json.example + 404: + description: Error - the Location with the given ID (remote ID or URL Alias) does not exist. +/{path}: + get: + displayName: Load Location + description: Loads the Location for the given path e.g. '/content/locations/1/2/61'. + headers: + Accept: + description: If set, the new Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/locations/path/GET/Location.xml.example + application/vnd.ez.api.Location+json: + type: Location + example: !include examples/content/locations/path/GET/Location.json.example + 401: + description: Error - the user is not authorized to read this Location. + 404: + description: Error - the Location with the given path does not exist. + move: + displayName: Move subtree + description: Moves Location to a different parent. The destination can also be '/content/trash' where the Location is put into the trash. (NOTE - Be aware that the user might lose access to the item after it has been moved, for example when read access is limited by a subtree). MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 201: + description: Created. If destination is '/api/ezp/v2/content/trash' and content only has one Location (NOTE - Like on normal subtree moves, be aware that the user might lose access to the item after it has been moved to Trash.) + 204: + description: No Content. If destination is '/api/ezp/v2/content/trash' and content still has other Locations (no trash item is created). + 401: + description: Error - the user is not authorized to move this Location. + 404: + description: Error - the Location with the given ID does not exist. + copy: + displayName: Copy subtree + description: Copies the subtree to a different parent. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 201: + description: Created. Copied the subtree to a different parent. + 401: + description: Error - the user is not authorized to move this Location. + 404: + description: Error - the Location with the given ID does not exist. + delete: + displayName: Delete subtree + description: Deletes the complete subtree for the given path. Every Content item which does not have any other Location is deleted. Otherwise the deleted Location is removed from the Content item. The children are recursively deleted. + responses: + 204: + description: No Content - deleted. + 401: + description: Error - the user is not authorized to delete this subtree. + 404: + description: Error - the Location with the given ID does not exist. + patch: + displayName: Update Location + description: Updates the Location. This method can also be used to hide/reveal a Location via the hidden field in the LocationUpdate. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + Content-Type: + description: The LocationUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.LocationUpdate+xml + application/vnd.ez.api.LocationUpdate+json + If-Match: + description: ETag + body: + application/vnd.ez.api.LocationUpdate+xml: + type: LocationUpdateStruct + example: !include examples/content/locations/location_id/PATCH/LocationUpdate.xml.example + application/vnd.ez.api.LocationUpdate+json: + type: LocationUpdateStruct + example: !include examples/content/locations/location_id/PATCH/LocationUpdate.json.example + responses: + 200: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/locations/location_id/PATCH/Location.xml.example + application/vnd.ez.api.Location+json: + type: Location + example: !include examples/content/locations/location_id/PATCH/Location.json.example + 401: + description: Error - the user is not authorized to update this Location. + 404: + description: Error - the Location with the given ID does not exist. + swap: + displayName: Swap Location + description: Swaps the Location of a Content item with the given Location of another Content item. SWAP or POST with header X-HTTP-Method-Override SWAP. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 204: + description: No Content. Swapped the Location of a Content item with the given Location of another Content item. + 401: + description: Error - the user is not authorized to swap this Location. + 404: + description: Error - the Location with the given ID does not exist. + /children: + get: + displayName: Get child Locations. + description: Loads all child Locations for the given parent Location. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of Locations returned. + type: integer + headers: + Accept: + description: If set, the new Location list is returned in XML or JSON format. + example: | + application/vnd.ez.api.LocationList+xml + application/vnd.ez.api.LocationList+json + responses: + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: LocationList + example: !include examples/content/locations/path/children/GET/LocationList.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID does not exist. + /urlaliases: + get: + displayName: List URL aliases for Location + description: Returns the list of URL aliases for a Location. + queryParameters: + custom: + description: Indicates whether autogenerated (false) or manual URL aliases (true) should be returned (default true). + type: boolean + headers: + Accept: + description: If set, the URL alias list contains only references and is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasRefList+xml + application/vnd.ez.api.UrlAliasRefList+json + responses: + 200: + description: OK - returns the list of URL aliases. + body: + application/vnd.ez.api.UrlAliasRefList+xml: + type: UrlAliasRefList + exammple: examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example + 400: + description: Error - The user has no permission to read URL aliases. + 401: + description: Error - The Location was not found. diff --git a/docs/api/rest_api_reference/input/ez-content-objects.raml b/docs/api/rest_api_reference/input/ez-content-objects.raml new file mode 100644 index 0000000000..e554ede7df --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-objects.raml @@ -0,0 +1,616 @@ +post: + displayName: Create Content item + description: Creates a draft assigned to the authenticated user. If a different user ID is given in the input, the draft is assigned to the given user but this action requires special permissions for the authenticated user (this is useful for content staging where the transfer process does not have to authenticate with the user who created the Content item in the source server). The user needs to publish the Content item if it should be visible. + headers: + Accept: + description: Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.Content+xml + application/vnd.ez.api.Content+json + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + Content-Type: + description: The ContentCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentCreate+xml + application/vnd.ez.api.ContentCreate+json + body: + application/vnd.ez.api.ContentCreate+xml: + type: ContentCreate + example: !include examples/content/objects/POST/ContentCreate.xml.example + application/vnd.ez.api.ContentCreate+json: + type: ContentCreate + example: !include examples/content/objects/POST/ContentCreate.json.example + responses: + 201: + body: + application/vnd.ez.api.Content+xml: + type: Content + example: !include examples/content/objects/POST/Content.xml.example + application/vnd.ez.api.Content+json: + type: Content + example: !include examples/content/objects/POST/Content.json.example + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/POST/ContentInfo.xml.example + 400: + description: Error - the input does not match the input schema definition or the validation on a field fails. + 401: + description: Error - the user is not authorized to create this Object in this Location. + 404: + description: Error - the parent Location specified in the request body does not exist. +get: + displayName: Load content by remote ID + description: Loads Content item for a given remote ID. + queryParameters: + remoteId: + description: The remote ID of the Content item. If present, the Content item with the given remote ID is returned. + responses: + 307: + description: Temporary redirect. + 404: + description: Error - the content with the given remote ID does not exist. +/{contentId}: + get: + displayName: Load content + description: Loads the Content item for the given ID. Depending on the Accept header the current version is embedded (i.e. the current published version or if it does not exist, the draft of the authenticated user). + queryParameters: + languages: + description: Restricts the output of translatable fields to the given languages. Comma separated list. + type: string + headers: + Accept: + description: Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.Content+xml + application/vnd.ez.api.Content+json + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + If-None-Match: + description: If the provided ETag matches the current ETag then a "304 Not Modified" is returned. The ETag changes if the meta data has changed, this happens also if there is a new published version. + example: ETag + responses: + 200: + body: + application/vnd.ez.api.Content+xml: + type: Content + example: !include examples/content/objects/content_id/GET/Content.xml.example + application/vnd.ez.api.Content+json: + type: Content + example: !include examples/content/objects/content_id/GET/Content.json.example + + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/GET/ContentInfo.xml.example + 401: + description: Error - the user is not authorized to read this object. This could also happen if there is no published version yet and another user owns a draft of this Content item. + 404: + description: Error - the ID is not found. + patch: + displayName: Update content + description: This method updates the content metadata which is independent from a version. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + If-match: + description: Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + Content-Type: + description: The ContentUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentUpdate+xml + application/vnd.ez.api.ContentUpdate+json + body: + application/vnd.ez.api.ContentUpdate+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/PATCH/ContentUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/PATCH/ContentInfo.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update this object. + 404: + description: Error - the content ID does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + 415: + description: Error - the media-type is not one of those specified in headers. + delete: + displayName: Delete Content + description: Deletes Content item. If Content item has multiple Locations, all of them will be deleted via delete a subtree. + responses: + 204: + description: The Content item is deleted. + 404: + description: Error - Content item was not found. + 401: + description: Error - the user is not authorized to delete this Content item. + copy: + displayName: Copy content + description: Creates new Content item as a copy, under the given parent Location given in the destination header. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + destination: + description: A Location resource to which the Content item should be copied. + responses: + 201: + description: Copy created. + 401: + description: Error - the user is not authorized to copy this Content item to the given Location. + 404: + description: Error - the source or destination resource does not exist. + /translations/{languageCode}: + delete: + displayName: Delete translation (permanently) + description: Permanently deletes a translation from all versions of a Content item. + responses: + 204: + description: No Content + 401: + description: Error - the user is not authorized to delete Content item (content/remove policy). + 404: + description: Error - the Content item was not found. + 406: + description: Error - the given translation does not exist for the Content item. + 409: + description: Error - the specified translation is the only one any version has or is the main translation. + /currentversion: + get: + displayName: Get current version + description: Redirects to the current version of the Content item. + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/currentversion/GET/Version.xml.example + application/vnd.ez.api.Version+json: + type: Version + example: !include examples/content/objects/content_id/currentversion/GET/Version.json.example + 307: + description: Temporary redirect. + 404: + description: Error - the resource does not exist. + copy: + displayName: Create a draft from current version + description: The system creates a new draft as a copy of the current version. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 201: + description: Created + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/currentversion/COPY/Version.xml.example + application/vnd.ez.api.Version+json: + type: Version + example: !include examples/content/objects/content_id/currentversion/COPY/Version.json.example + 401: + description: Error - the user is not authorized to update this Content item. + 403: + description: Error - the current version is already a draft. + 404: + description: Error - the Content item was not found. + /versions: + get: + displayName: List versions + description: Returns a list of all versions of the Content item. This method does not include fields and relations in the version elements of the response. + headers: + Accept: + description: If set, the version list is returned in XML or JSON format. + example: | + application/vnd.ez.api.VersionList+xml + application/vnd.ez.api.VersionList+json + responses: + 200: + body: + application/vnd.ez.api.VersionList+xml: + type: VersionList + example: !include examples/content/objects/content_id/versions/GET/VersionList.xml.example + application/vnd.ez.api.VersionList+json: + type: VersionList + example: !include examples/content/objects/content_id/versions/GET/VersionList.json.example + 401: + description: Error - the user has no permission to read the versions. + /{versionNo}: + get: + displayName: Load version + description: Loads a specific version of a Content item. This method returns Fields and relations. + queryParameters: + fields: + description: Fields which should be returned in the response. Comma separated list. + type: string + responseGroups: + description: Alternative comma separated lists of predefined Field groups. + type: string + languages: + description: Restricts the output of translatable Fields to the given languages. Comma separated list. + type: string + headers: + If-None-Match: + description: Only return the version if the given ETag is the not current one, otherwise a 304 is returned. + example: ETag + Accept: + description: If set, the version list is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/GET/Version.xml.example + application/vnd.ez.api.Version+json: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/GET/Version.json.example + 304: + description: Error - the ETag does not match the current one. + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the ID or version is not found. + patch: + displayName: Update version + description: A specific draft is updated. PATCH or POST with header X-HTTP-Method-Override PATCH. + queryParameters: + languages: + description: Restricts the output of translatable Fields to the given languages. Comma separated list. + type: string + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + If-match: + description: Performs the patch only if the specified ETag is the current one. + Content-Type: + description: The VersionUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.VersionUpdate+xml + application/vnd.ez.api.VersionUpdate+json + body: + application/vnd.ez.api.VersionUpdate+xml: + type: VersionUpdate + example: !include examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update this version. + 403: + description: Error - the version is not allowed to change - i.e. version is not a DRAFT. + 404: + description: Error - the content ID or version ID does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + copy: + displayName: Create a draft from a version + description: The system creates a new draft as a copy of the given version. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 201: + description: Created. + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example + application/vnd.ez.api.Version+json: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/COPY/Version.json.example + 401: + description: Error - the user is not authorized to update this Content item. + 404: + description: Error - the Content item was not found. + delete: + displayName: Delete content version + description: Deletes the content version. + responses: + 204: + description: No Content - the version is deleted. + 404: + description: Error - the Content item or version were not found. + 401: + description: Error - the user is not authorized to delete this version. + 403: + description: Error - the version is in published state. + publish: + displayName: Publish a content version + description: Publishes the content version. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH + responses: + 204: + description: No Content - the content version is published. + 401: + description: Error - the user is not authorized to publish this version. + 403: + description: Error - the version is not a draft. + 404: + description: Error - the Content item or version were not found. + /translations/{languageCode}: + delete: + displayName: Delete translation from version draft + description: Removes a translation from a version draft. + responses: + 204: + description: No Content - removes a translation from a version draft. + 401: + description: Error - the user is not authorized to delete this translation. + 403: + description: Error - the version is not in draft state. + 404: + description: Error - the Content item or version number were not found. + 406: + description: Error - the given translation does not exist for the version. + 409: + description: Error - the specified translation is the only one the version has or is the main translation. + /relations: + get: + displayName: Load Relations of Content item version + description: Loads the Relations of the given version. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of bookmarks returned. + type: integer + headers: + Accept: + description: If set, the Relation is returned in XML or JSON format. + example: | + application/vnd.ez.api.RelationList+xml + application/vnd.ez.api.RelationList+json + responses: + 200: + body: + application/vnd.ez.api.RelationList+xml: + type: RelationList + example: !include examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example + application/vnd.ez.api.RelationList+json: + type: RelationList + example: !include examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.json.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item was not found. + post: + displayName: Create new Relation + description: Creates a new Relation of type COMMON for the given draft. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Relation+xml + application/vnd.ez.api.Relation+json + Content-Type: + description: The RelationCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RelationCreate+xml + application/vnd.ez.api.RelationCreate+json + body: + application/vnd.ez.api.RelationCreate+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example + application/vnd.ez.api.RelationCreate+json: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.json.example + responses: + 201: + body: + application/vnd.ez.api.Relation+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example + application/vnd.ez.api.Relation+json: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/Relation.json.example + + /{relationId}: + get: + displayName: Load Relation + description: Loads a Relation for the given Content item. + headers: + Accept: + description: If set, the Relation is returned in XML or JSON format. + example: | + application/vnd.ez.api.Relation+xml + application/vnd.ez.api.Relation+json + responses: + 200: + description: OK - loads a Relation for the given Content item. + body: + application/vnd.ez.api.Relation+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example + application/vnd.ez.api.Relation+json: + type: Relation + example: examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.json.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID or the Relation does not exist. + delete: + displayName: Delete Relation + description: Deletes a Relation of the given draft. + responses: + 204: + description: No Content - deleted a Relation of the given draft. + 401: + description: Error - the user is not authorized to delete this Relation. + 403: + description: Error - the Relation is not of type COMMON or the given version is not a draft. + 404: + description: Error - Content item or the Relation were not found in the given version. + /relations: + get: + displayName: Load Relations of Content item + description: Redirects to the Relations of the current version. + responses: + 307: + description: Temporary redirect. + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item was not found. + /locations: + post: + displayName: Create new Location for Content item + description: Creates a new Location for the given Content item. + headers: + Accept: + description: If set, the new Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + Content-Type: + description: The LocationCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.LocationCreate+json + application/vnd.ez.api.LocationCreate+xml + body: + application/vnd.ez.api.LocationCreate+xml: + type: LocationCreate + example: !include examples/content/objects/content_id/locations/POST/LocationCreate.xml.example + application/vnd.ez.api.LocationCreate+json: + type: LocationCreate + example: !include examples/content/objects/content_id/locations/POST/LocationCreate.json.example + responses: + 201: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/objects/content_id/locations/POST/Location.xml.example + application/vnd.ez.api.Location+json: + type: Location + example: !include examples/content/objects/content_id/locations/POST/Location.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this Location. + 403: + description: Error - a Location under the given parent ID already exists. + get: + displayName: Get Locations for Content item + description: Loads all Locations for the given Content item. + headers: + Accept: + description: If set, the Location list is returned in XML or JSON format. + example: | + application/vnd.ez.api.LocationList+xml + application/vnd.ez.api.LocationList+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: LocationList + example: !include examples/content/objects/content_id/locations/GET/LocationList.xml.example + application/vnd.ez.api.LocationList+json: + type: LocationList + example: !include examples/content/objects/content_id/locations/GET/LocationList.json.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID does not exist. + /objectstates: + get: + displayName: Get Object states of Content item + description: Returns the Object states of a Content item + headers: + Accept: + description: If set, the Object states are returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentObjectStates+xml + application/vnd.ez.api.ContentObjectStates+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state. + body: + application/vnd.ez.api.ContentObjectStates+xml: + type: ContentObjectStates + example: !include examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example + application/vnd.ez.api.ContentObjectStates+json: + type: ContentObjectStates + example: !include examples/content/objects/content_id/objectstates/GET/ContentObjectStates.json.example + 404: + description: Error - The Content item does not exist. + patch: + displayName: Set Object states of Content item + description: Updates Object states of a Content item. An Object state in the input overrides the state of the Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Content item Object states input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateUpdate+xml + application/vnd.ez.api.ObjectStateUpdate+json + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectStateUpdate + responses: + 204: + description: OK - Object state updated. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to set an Object state. + 403: + description: Error - The input contains multiple Object states of the same Object state group. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + /hide: + post: + displayName: Hide Content item + description: Makes or keep the Content item invisible + responses: + 204: + description: OK - Object item is hidden. + 401: + description: Error - The user has no permission to change Object item visibility. + 404: + description: Error - The Content item was not found. + /reveal: + post: + displayName: Reveal Content item + description: Makes or keep the Content item visible + responses: + 204: + description: OK - Object item is revealed. + 401: + description: Error - The user has no permission to change Object item visibility. + 404: + description: Error - The Content item was not found. diff --git a/docs/api/rest_api_reference/input/ez-content-sections.raml b/docs/api/rest_api_reference/input/ez-content-sections.raml new file mode 100644 index 0000000000..486f1d30d3 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-sections.raml @@ -0,0 +1,138 @@ +post: + displayName: Create new Section + description: Creates a new Section. + headers: + Accept: + description: If set, the new Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + Content-Type: + description: The Section input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.SectionInput+json + application/vnd.ez.api.SectionInput+xml + body: + application/vnd.ez.api.SectionInput+xml: + type: SectionInput + example: !include examples/content/sections/POST/SectionInput.xml.example + application/vnd.ez.api.SectionInput+json: + type: SectionInput + example: !include examples/content/sections/POST/SectionInput.json.example + responses: + 201: + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/POST/Section.xml.example + application/vnd.ez.api.Section+json: + type: Section + example: !include examples/content/sections/POST/Section.json.example +get: + displayName: Get Sections + description: Returns a list of all Sections. + queryParameters: + identifer: + description: Only the Section with the given identifier is returned. + headers: + Accept: + description: If set, the Section list is returned in XML or JSON format. + example: | + application/vnd.ez.api.SectionList+xml + application/vnd.ez.api.SectionList+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.SectionList+xml: + type: SectionList + example: !include examples/content/sections/GET/SectionList.xml.example + application/vnd.ez.api.SectionList+json: + type: SectionList + example: !include examples/content/sections/GET/SectionList.json.example + 401: + description: Error - The user has no permission to read the Section. +/{sectionId}: + get: + displayName: Get Section + description: Returns the Section by given Section ID. + headers: + Accept: + description: If set, the Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + If-None-match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/section_id/GET/Section.xml.example + application/vnd.ez.api.Section+json: + type: Section + example: !include examples/content/sections/section_id/GET/Section.json.example + 401: + description: Error - The user is not authorized to read this Section. + 404: + description: Error - The Section does not exist. + patch: + displayName: Update a Section + description: Updates a Section. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + Content-Type: + description: The Section input schema encoded in XML or JSON. + example: | + application/vnd.ez.api.SectionInput+xml + application/vnd.ez.api.SectionInput+json + If-Match: + description: ETag + body: + application/vnd.ez.api.SectionInput+xml: + type: SectionInput + example: !include examples/content/sections/section_id/PATCH/SectionInput.xml.example + application/vnd.ez.api.SectionInput+json: + type: SectionInput + example: !include examples/content/sections/section_id/PATCH/SectionInput.json.example + responses: + 200: + description: OK - Section updated. + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/section_id/PATCH/Section.xml.example + application/vnd.ez.api.Section+json: + type: Section + example: !include examples/content/sections/section_id/PATCH/Section.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this Section. + 403: + description: Error - a Section with the given identifier already exists. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + delete: + displayName: Delete Section + description: The given Section is deleted. + #headers: TODO + # a little obvious? + #Accept: + #description: If set, in the case of an error the error message is returned in XML or JSON format + #example: | + #application/vnd.ez.api.ErrorMessage+xml + #application/vnd.ez.api.ErrorMessage+json + responses: + 204: + description: No Content - given Section is deleted. + 401: + description: Error - the user is not authorized to delete this Section. + 404: + description: Error - the Section does not exist. diff --git a/docs/api/rest_api_reference/input/ez-content-trash.raml b/docs/api/rest_api_reference/input/ez-content-trash.raml new file mode 100644 index 0000000000..be03d85e49 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-trash.raml @@ -0,0 +1,82 @@ +get: + displayName: List Trash items + description: Returns a list of all items in the Trash. + queryParameters: + limit: + description: Only limit. Items will be returned, starting with the offset. + offset: + description: Offset of the result set. + headers: + Accept: + description: If set, the Trash item list is returned in XML or JSON format. + example: | + application/vnd.ez.api.Trash+xml + application/vnd.ez.api.Trash+json + responses: + 200: + description: OK - returns the list of items in the Trash. + body: + application/vnd.ez.api.Trash+xml: + type: Trash + example: !include examples/content/trash/GET/Trash.xml.example + application/vnd.ez.api.Trash+json: + type: Trash + example: !include examples/content/trash/GET/Trash.json.example + 401: + description: Error - The user has no permission to read the Trash. +delete: + displayName: Empty Trash + description: Empties the Trash. + responses: + 204: + description: No Content - Trash emptied. + 401: + description: Error - The user is not authorized to empty all items from Trash. +/{trashItemId}: + get: + displayName: Get Trash item + description: Returns the item in Trash with the provided ID. + headers: + Accept: + description: If set, the item in Trash is returned in XML or JSON format. + example: | + application/vnd.ez.api.TrashItem+xml + application/vnd.ez.api.TrashItem+json + responses: + 200: + body: + application/vnd.ez.api.TrashItem+xml: + type: TrashItem + example: !include examples/content/trash/trash_itemid/GET/TrashItem.xml.example + application/vnd.ez.api.TrashItem+json: + type: TrashItem + example: !include examples/content/trash/trash_itemid/GET/TrashItem.json.example + 401: + description: Error - The user has no permission to read the item in Trash. + 404: + description: Error - An item in Trash with the provided ID does not exist. + move: + displayName: Untrash Content item + description: Restores an item from Trash. MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: If the destination Location URI is provided, the item from Trash is restored under this Location, otherwise it is restored under its original parent Location. + responses: + 201: + description: Item restored. + 401: + description: Error - The user is not authorized to restore this item from Trash. + 403: + description: Error - The provided parent Location does not exist. + 404: + description: Error - The provided item does not exist in Trash. + delete: + displayName: Delete Trash item + description: Deletes the provided item from Trash. + responses: + 204: + description: No Content - item deleted. + 401: + description: Error - The user is not authorized to delete the provided item. + 404: + description: Error - The provided item does not exist in Trash. diff --git a/docs/api/rest_api_reference/input/ez-content-typegroups.raml b/docs/api/rest_api_reference/input/ez-content-typegroups.raml new file mode 100644 index 0000000000..c15e794b73 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-typegroups.raml @@ -0,0 +1,208 @@ +displayName: Managing Content Type groups +get: + displayName: Get Content Type groups + description: Returns a list of all Content Type groups. If an identifier is provided, loads the Content Type group for this identifier. + queryParameters: + identifier: + description: The identifier of the Content Type group. If present, the Content Type group with this identifier is returned. + required: false + headers: + Accept: + description: If set, the Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupList+xml + application/vnd.ez.api.ContentTypeGroupList+json + responses: + 200: + description: OK - returns a list of Content Type groups. + body: + application/vnd.ez.api.ContentTypeGroupList+xml: + type: ContentTypeGroupList + example: !include examples/content/typegroups/GET/ContentTypeGroupList.xml.example + application/vnd.ez.api.ContentTypeGroupList+json: + type: ContentTypeGroupList + example: !include examples/content/typegroups/GET/ContentTypeGroupList.json.example + 307: + description: Temporary redirect. + 401: + description: Error - The user has no permission to read Content Types. + 404: + description: Error - The Content Type group with the given identifier does not exist. +post: + displayName: Create Content Type group + description: Creates a new Content Type group. + headers: + Accept: + description: If set, the new Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + Content-Type: + description: The Content Type group input schema encoded in XML or JSON. + example: | + application/vnd.ez.api.ContentTypeGroupInput+xml + application/vnd.ez.api.ContentTypeGroupInput+json + body: + application/vnd.ez.api.ContentTypeGroupInput+xml: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/POST/ContentTypeGroupInput.xml.example + application/vnd.ez.api.ContentTypeGroupInput+json: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/POST/ContentTypeGroupInput.json.example + responses: + 201: + description: Content Type group created. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/POST/ContentTypeGroup.xml.example + application/vnd.ez.api.ContentTypeGroup+json: + type: ContentTypeGroup + example: !include examples/content/typegroups/POST/ContentTypeGroup.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create this Content Type group. + 403: + description: Error - A Content Type group with the same identifier already exists. +/{contentTypeGroupId}: + get: + displayName: Get Content Type group + description: Returns the Content Type group with provided ID. + headers: + Accept: + description: If set, the Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Content Type group. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example + application/vnd.ez.api.ContentTypeGroup+json: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.json.example + 401: + description: Error - The user is not authorized to read this Content Type group. + 404: + description: Error - The Content Type group does not exist. + patch: + displayName: Update Content Type group + description: Updates a Content Type group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + Content-Type: + description: The Content Type group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupInput+xml + application/vnd.ez.api.ContentTypeGroupInput+json + If-Match: + description: ETag causes patching only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.ContentTypeGroupInput+xml: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example + application/vnd.ez.api.ContentTypeGroupInput+json: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.json.example + responses: + 200: + description: Content Type group updated. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example + application/vnd.ez.api.ContentTypeGroup+json: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create this Content Type group. + 403: + description: Error - A Content Type group with the given identifier already exists. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Content Type group + description: Deletes the provided Content Type group. + responses: + 204: + description: No Content - Content Type group deleted. + 401: + description: Error - The user is not authorized to delete this Content Type group. + 403: + description: Error - The Content Type group is not empty. + 404: + description: Error - The Content Type group does not exist. + /types: + get: + displayName: List Content Types for group + description: Returns a list of Content Types in the provided group. + headers: + Accept: + description: If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfoList+xml + application/vnd.ez.api.ContentTypeInfoList+json + application/vnd.ez.api.ContentTypeList+xml + application/vnd.ez.api.ContentTypeList+json + responses: + 200: + description: OK - returns a list on Content Types. + body: + application/vnd.ez.api.ContentTypeInfoList+xml: + type: ContentTypeInfoList + example: !include examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example + application/vnd.ez.api.ContentTypeInfoList+json: + type: ContentTypeInfoList + example: !include examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.json.example + 401: + description: Error - The user has no permission to read the Content Types. + post: + displayName: Create Content Type + description: Creates a new Content Type draft in the given Content Type group. + queryParameters: + publish: + description: If true, the Content Type is published after creating (default false). + type: boolean + headers: + Accept: + description: If set, the new Content Type or draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentType+xml + application/vnd.ez.api.ContentType+json + Content-Type: + description: The Content Type Create schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeCreate+xml + application/vnd.ez.api.ContentTypeCreate+json + body: + application/vnd.ez.api.ContentTypeCreate+xml: + type: ContentTypeCreate + example: !include examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example + application/vnd.ez.api.ContentTypeCreate+json: + type: ContentTypeCreate + example: !include examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.json.example + responses: + 201: + description: Content Type created. + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example + 400: + description: Error - The input does not match the input schema definition. Validation on a Field definition fails. Validation of the Content Type fails, e.g. multiple Fields of a same singular Field Type are provided. Publish is set to true and the input is not complete e.g. no Field definitions are provided. + 401: + description: Error - The user is not authorized to create this Content Type. + 403: + description: Error - A Content Type with same identifier already exists. diff --git a/docs/api/rest_api_reference/input/ez-content-types.raml b/docs/api/rest_api_reference/input/ez-content-types.raml new file mode 100644 index 0000000000..b17f5837fe --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-types.raml @@ -0,0 +1,414 @@ +displayName: Managing Content Types +get: + displayName: List Content Types + description: Returns a list of Content Types. + queryParameters: + identifier: + description: Retrieves the Content Type for the given identifer. + remoteId: + description: Retrieves the Content Type for the given remote ID. + limit: + description: Only 'limit' items will be returned, starting with the offset. + offset: + description: Offset of the result set. + orderby: + description: One of (name|lastmodified). + sort: + description: One of (asc|desc). + headers: + Accept: + description: If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfoList+xml + application/vnd.ez.api.ContentTypeInfoList+json + application/vnd.ez.api.ContentTypeList+xml + application/vnd.ez.api.ContentTypeList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeInfoList+xml: + type: ContentTypeInfoList + example: !include examples/content/types/GET/ContentTypeInfoList.xml.example + application/vnd.ez.api.ContentTypeInfoList+json: + type: ContentTypeInfoList + example: !include examples/content/types/GET/ContentTypeInfoList.json.example + description: OK - returns a list of Content Types. + 401: + description: Error - The user has no permission to read the Content Types. +/{contentTypeId}: + get: + displayName: Get Content Type + description: Returns the Content Type with the provided ID. + headers: + Accept: + description: If set, the Content Type is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentType+xml + application/vnd.ez.api.ContentType+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/types/content_type_id/GET/ContentType.xml.example + application/vnd.ez.api.ContentType+json: + type: ContentType + example: !include examples/content/types/content_type_id/GET/ContentType.json.example + description: OK - returns the Content Type. + 401: + description: Error - The user is not authorized to read this Content Type. + 404: + description: Error - The Content Type does not exist. + copy: + displayName: Copy Content Type + description: Copies a Content Type. A new remote ID is generated, and the identifier of the copy is set to 'copy_of_originalBaseIdentifier_newTypeId' (or another random string). COPY or POST with header X-HTTP-Method-Override COPY. + responses: + 201: + description: Copy of the Content Type created. + 401: + description: Error - The user is not authorized to copy this Content Type. + post: + displayName: Create Draft + description: Creates a draft and updates it with the given data. + headers: + Accept: + description: If set, the new Content Type draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfo+xml + application/vnd.ez.api.ContentTypeInfo+json + Content-Type: + description: The Content Type Update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeUpdate+xml + application/vnd.ez.api.ContentTypeUpdate+json + body: + application/vnd.ez.api.ContentTypeUpdate+xml: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example + application/vnd.ez.api.ContentTypeUpdate+json: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/POST/ContentTypeUpdate.json.example + responses: + 201: + description: Draft created. + body: + application/vnd.ez.api.ContentTypeInfo+xml: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example + application/vnd.ez.api.ContentTypeInfo+json: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/POST/ContentTypeInfo.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create the draft. + 403: + description: Error - A Content Type with the given new identifier already exists. A draft already exists. + delete: + displayName: Delete Content Type + description: Deletes the provided Content Type. + responses: + 204: + description: No Content - Content Type deleted. + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - There are object instances of this Content Type. + 404: + description: Error - The Content Type does not exist. + /fieldDefinitions: + /{fieldDefinitionId}: + get: + displayName: Get Field definition + description: Returns the Field definition by the given ID. + headers: + Accept: + description: If set, the Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + responses: + 200: + description: OK - returns the Field definition. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example + application/vnd.ez.api.FieldDefinition+json: + type: FieldDefinition + example: !include examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.json.example + 401: + description: Error - The user is not authorized to read the Content Type. + 404: + description: Error - The Content Type does not exist. + /draft: + get: + displayName: Get Content Type draft + description: Returns the draft of the Content Type with the provided ID. + headers: + Accept: + description: If set, the Content Type is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentType+xml + application/vnd.ez.api.ContentType+json + responses: + 200: + body: + application/vnd.ibexa.api.ContentType+xml: + type: ContentType + application/vnd.ibexa.api.ContentType+json: + type: ContentType + description: OK - returns the Content Type. + 401: + description: Error - The user is not authorized to read this Content Type. + 404: + description: Error - The Content Type does not exist or does not have a draft. + patch: + displayName: Update Content Type draft + description: Updates metadata of a draft. This method does not handle Field definitions. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the new Content Type draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfo+xml + application/vnd.ez.api.ContentTypeInfo+json + Content-Type: + description: The Content Type update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeUpdate+xml + application/vnd.ez.api.ContentTypeUpdate+json + body: + application/vnd.ez.api.ContentTypeUpdate+xml: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example + application/vnd.ez.api.ContentTypeUpdate+json: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.json.example + responses: + 200: + description: Draft metadata updated. + body: + application/vnd.ez.api.ContentTypeInfo+xml: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example + application/vnd.ez.api.ContentTypeInfo+json: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.json.example + + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the draft. + 403: + description: Error - A Content Type with the given new identifier already exists. + 404: + description: Error - There is no draft for this Content Type. + publish: + displayName: Publish Content Type draft + description: Publishes a Content Type draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH. + responses: + 200: + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example + description: Content Type draft published. + 401: + description: Error - The user is not authorized to publish this Content Type draft. + 403: + description: Error - The Content Type draft is not complete, e.g. there is no Field definition provided. + 404: + description: Error - If there is no draft or Content Type with the given ID. + delete: + displayName: Delete Content Type draft + description: Deletes the provided Content Type draft. + responses: + 204: + description: No Content - Content Type draft deleted. + 401: + description: Error - The user is not authorized to delete this Content Type draft. + 404: + description: Error - The Content Type draft does not exist. + /fieldDefinitions: + get: + displayName: Get Draft Field definition list + description: Returns all Field definitions of the provided Content Type Draft. + responses: + 200: + description: OK - return a list of Field definitions. + 404: + description: Error - The Content Type draft does not exist. + post: + displayName: Add Content Type Draft Field definition + description: Creates a new Field definition for the given Content Type. + headers: + Accept: + description: If set, the new Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + Content-Type: + description: The Field Definition Create schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinitionCreate+xml + application/vnd.ez.api.FieldDefinitionCreate+json + body: + application/vnd.ez.api.FieldDefinitionCreate+xml: + type: FieldDefinitionCreate + example: !include examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example + responses: + 201: + description: Field definition created. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example + 400: + description: Error - The input does not match the input schema definition or validation on the Field definition fails. + 401: + description: Error - The user is not authorized to add a Field definition. + 403: + description: Error - A Field definition with the same identifier already exists in the given Content Type. The Field definition is of singular type, already existing in the given Content Type. The Field definition you want to add is of a type that can't be added to a Content Type that already has content instances. + /{fieldDefinitionId}: + get: + displayName: Get Content Type Draft Field definition + description: Returns the Field definition by the given ID. + headers: + Accept: + description: If set, the Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + responses: + 200: + description: OK - returns the Field definition. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example + application/vnd.ez.api.FieldDefinition+json: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.json.example + 401: + description: Error - The user is not authorized to read the Content Type draft. + 404: + description: Error - The Content Type or draft does not exist. + patch: + displayName: Update Content Type Draft Field definition + description: Updates the attributes of a Field definition. + headers: + Accept: + description: If set, the updated Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + Content-Type: + description: The Field definition update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinitionUpdate+xml + application/vnd.ez.api.FieldDefinitionUpdate+json + body: + application/vnd.ez.api.FieldDefinitionUpdate+xml: + type: FieldDefinitionUpdate + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example + responses: + 200: + description: OK - attributes updated. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the Field definition. + 403: + description: Error - A Field definition with the given identifier already exists in the given Content Type. + delete: + displayName: Delete Content Type Draft Field definition + description: Deletes the provided Field definition. + responses: + 204: + description: No Content - Field definition deleted. + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - There is no draft of the Content Type assigned to the authenticated user. + /groups: + get: + displayName: Get groups of Content Type + description: Returns the Content Type group to which Content Type belongs to. + headers: + Accept: + description: If set, the Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroupRefList+xml: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example + application/vnd.ez.api.ContentTypeGroupRefList+json: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.json.example + 401: + description: Error - The user is not authorized to read this Content Type. + 404: + description: Error - The Content Type does not exist. + post: + displayName: Link group to Content Type + description: Links a Content Type group to the Content Type and returns the updated group list. + queryParameters: + group: + description: The URI of the group to which the Content Type should be linked to. + type: string + headers: + Accept: + description: If set, the updated Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroupRefList+xml: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example + application/vnd.ez.api.ContentTypeGroupRefList+json: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to add a group. + 403: + description: Error - The Content Type is already assigned to the group. + /{id}: + delete: + displayName: Unlink group from Content Type + description: Removes the given group from the Content Type and returns the updated group list. + headers: + Accept: + description: If set, the updated Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.xml.example + application/vnd.ez.api.ContentTypeGroup+json: + type: ContentTypeGroup + example: !include examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.json.example + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - Content Type cannot be unlinked from the only remaining group. + 404: + description: Error - The resource does not exist. diff --git a/docs/api/rest_api_reference/input/ez-content-urlaliases.raml b/docs/api/rest_api_reference/input/ez-content-urlaliases.raml new file mode 100644 index 0000000000..3a866241ca --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-urlaliases.raml @@ -0,0 +1,92 @@ +get: + displayName: List global URL aliases + description: Returns the list of global URL aliases. + headers: + Accept: + description: If set, the URL alias list contains only references and is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasRefList+xml + application/vnd.ez.api.UrlAliasRefList+json + responses: + 200: + description: OK - returns the list of URL aliases. + body: + application/vnd.ez.api.UrlAliasRefList+xml: + type: UrlAliasRefList + example: !include examples/content/urlaliases/GET/UrlAliasRefList.xml.example + application/vnd.ez.api.UrlAliasRefList+json: + type: UrlAliasRefList + example: !include examples/content/urlaliases/GET/UrlAliasRefList.json.example + 401: + description: Error - The user has no permission to read URL aliases. +post: + displayName: Create URL alias + description: Creates a URL alias. + headers: + Accept: + description: If set, the created URL alias is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAlias+xml + application/vnd.ez.api.UrlAlias+json + Content-Type: + description: The URL alias input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasCreate+xml + application/vnd.ez.api.UrlAliasCreate+json + body: + application/vnd.ez.api.UrlAliasCreate+xml: + type: UrlAliasCreate + example: !include examples/content/urlaliases/POST/UrlAliasCreate.xml.example + application/vnd.ez.api.UrlAliasCreate+json: + type: UrlAliasCreate + example: !include examples/content/urlaliases/POST/UrlAliasCreate.json.example + responses: + 201: + description: URL alias created. + body: + application/vnd.ez.api.UrlAlias+xml: + type: UrlAlias + example: !include examples/content/urlaliases/POST/UrlAlias.xml.example + application/vnd.ez.api.UrlAlias+json: + type: UrlAlias + example: !include examples/content/urlaliases/POST/UrlAlias.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create a URL alias. + 403: + description: Error - A URL alias with the same identifier already exists. +/{urlAliasId}: + get: + displayName: Get URL alias + description: Returns the URL alias with the given ID. + headers: + Accept: + description: If set, the URL alias is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAlias+xml + application/vnd.ez.api.UrlAlias+json + responses: + 200: + description: OK - returns the URL alias. + body: + application/vnd.ez.api.UrlAlias+xml: + type: UrlAlias + example: !include examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example + application/vnd.ez.api.UrlAlias+json: + type: UrlAlias + example: !include examples/content/urlaliases/url_alias_id/GET/UrlAlias.json.example + 401: + description: Error - The user is not authorized to read URL aliases. + 404: + description: Error - The URL alias does not exist. + delete: + displayName: Delete URL alias + description: Deletes the provided URL alias. + responses: + 204: + description: No Content - URL alias deleted. + 401: + description: Error - The user is not authorized to delete a URL alias. + 404: + description: Error - The URL alias does not exist. diff --git a/docs/api/rest_api_reference/input/ez-content-urlwildcards.raml b/docs/api/rest_api_reference/input/ez-content-urlwildcards.raml new file mode 100644 index 0000000000..be5a995c23 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-urlwildcards.raml @@ -0,0 +1,92 @@ +get: + displayName: List URL wildcards + description: Returns a list of URL wildcards. + headers: + Accept: + description: If set, the URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcardList+xml + application/vnd.ez.api.UrlWildcardList+json + responses: + 200: + description: OK - returns a list of URL wildcards. + body: + application/vnd.ez.api.UrlWildcardList+xml: + type: UrlWildcardList + example: !include examples/content/urlwildcards/GET/UrlWildcardList.xml.example + application/vnd.ez.api.UrlWildcardList+json: + type: UrlWildcardList + example: !include examples/content/urlwildcards/GET/UrlWildcardList.json.example + 401: + description: Error - The user has no permission to read URL wildcards. +post: + displayName: Create URL wildcard + description: Creates a new URL wildcard. + headers: + Accept: + description: If set, the new URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcard+xml + application/vnd.ez.api.UrlWildcard+json + Content-Type: + description: The URL Wildcard input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcardCreate+xml + application/vnd.ez.api.UrlWildcardCreate+json + body: + application/vnd.ez.api.UrlWildcardCreate.xml: + type: UrlWildcardCreate + example: !include examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example + application/vnd.ez.api.UrlWildcardCreate.json: + type: UrlWildcardCreate + example: !include examples/content/urlwildcards/POST/UrlWildcardCreate.json.example + responses: + 201: + description: URL wildcard created. + body: + application/vnd.ez.api.UrlWildcard+xml: + type: UrlWildcard + example: !include examples/content/urlwildcards/POST/UrlWildcard.xml.example + application/vnd.ez.api.UrlWildcard+json: + type: UrlWildcard + example: !include examples/content/urlwildcards/POST/UrlWildcard.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create a URL wildcard. + 403: + description: Error - A URL wildcard with the same identifier already exists. +/{wildcardId}: + get: + displayName: Get URL wildcard + description: Returns the URL wildcard with the given ID. + headers: + Accept: + description: If set, the URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcard+xml + application/vnd.ez.api.UrlWildcard+json + responses: + 200: + description: OK - returns the URL wildcard. + body: + application/vnd.ez.api.UrlWildcard+xml: + type: UrlWildcard + example: !include examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example + application/vnd.ez.api.UrlWildcard+json: + type: UrlWildcard + example: !include examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.json.example + 401: + description: Error - The user is not authorized to read URL wildcards. + 404: + description: Error - The URL wildcard does not exist. + delete: + displayName: Delete URL wildcard + description: Deletes the given URL wildcard. + responses: + 204: + description: No Content - URL wildcard deleted. + 401: + description: Error - The user is not authorized to delete a URL wildcard. + 404: + description: Error - The URL wildcard does not exist. diff --git a/docs/api/rest_api_reference/input/ez-content-views.raml b/docs/api/rest_api_reference/input/ez-content-views.raml new file mode 100644 index 0000000000..6b849a1134 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-content-views.raml @@ -0,0 +1,23 @@ +post: + displayName: Create View + description: Executes a query and returns View including the results. The View input reflects the criteria model of the public API. Will respond with a 301, as the resource has been moved to /views (Platform 1.0) - DEPRECATED. + headers: + Accept: + description: The View in XML or JSON format. + example: | + application/vnd.ez.api.View+xml + application/vnd.ez.api.View+json + application/vnd.ez.api.View+xml; version=1.1 + application/vnd.ez.api.View+json; version=1.1 + Content-Type: + description: The View input in XML or JSON format. + example: | + application/vnd.ez.api.ViewInput+xml + application/vnd.ez.api.ViewInput+json + application/vnd.ez.api.ViewInput+xml; version=1.1 + application/vnd.ez.api.ViewInput+json; version=1.1 + responses: + 301: + description: Moved permanently. + 400: + description: Error - the input does not match the input schema definition. diff --git a/docs/api/rest_api_reference/input/ez-objectstategroups.raml b/docs/api/rest_api_reference/input/ez-objectstategroups.raml new file mode 100644 index 0000000000..e90e068dcb --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-objectstategroups.raml @@ -0,0 +1,276 @@ +displayName: Managing Content and Object state groups +get: + displayName: List Object state groups + description: Returns a list of all Object state groups. + headers: + Accept: + description: If set, the Object state group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupList+xml + application/vnd.ez.api.ObjectStateGroupList+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns a list of Object state groups. + body: + application/vnd.ez.api.ObjectStateGroupList+xml: + type: ObjectStateGroupList + example: !include examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example + application/vnd.ez.api.ObjectStateGroupList+json: + type: ObjectStateGroupList + example: !include examples/content/objectstategroups/GET/ObjectStateGroupList.json.example + 401: + description: Error - The user has no permission to read Object state groups. +post: + displayName: Create Object state group + description: Creates a new Object state group. + headers: + Accept: + description: If set, the new Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + Content-Type: + description: The Object state group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupCreate+json + application/vnd.ez.api.ObjectStateGroupCreate+xml + body: + application/vnd.ez.api.ObjectStateGroupCreate+xml: + type: ObjectStateGroupCreate + example: !include examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example + application/vnd.ez.api.ObjectStateGroupCreate+json: + type: ObjectStateGroupCreate + example: !include examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example + responses: + 201: + description: Object state group created. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/POST/ObjectStateGroup.xml.example + application/vnd.ez.api.ObjectStateGroup+json: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/POST/ObjectStateGroup.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create an Object state group. + 403: + description: Error - An Object state group with the same identifier already exists. +/{objectStateGroupId}: + get: + displayName: Get Object state group + description: Returns the Object state group with the provided ID. + headers: + Accept: + description: If set, the Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state group. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example + application/vnd.ez.api.ObjectStateGroup+json: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.json.example + 401: + description: Error - The user is not authorized to read this Object state group. + 404: + description: Error - The Object state group does not exist. + patch: + displayName: Update Object state group + description: Updates an Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + Content-Type: + description: The Object state group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupUpdate+json + application/vnd.ez.api.ObjectStateGroupUpdate+xml + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectStateGroupUpdate+xml: + type: ObjectStateGroupUpdate + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example + application/vnd.ez.api.ObjectStateGroupUpdate+json: + type: ObjectStateGroupUpdate + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.json.example + responses: + 200: + description: OK - Object stated group updated. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example + application/vnd.ez.api.ObjectStateGroup+json: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update an Object state group. + 403: + description: Error - An Object state group with the provided identifier already exists. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Object state group + description: Deletes the given Object state group including Object states. + responses: + 204: + description: No Content - Object state group deleted. + 401: + description: Error - The user is not authorized to delete an Object state group. + 404: + description: Error - The Object state group does not exist. + /objectstates: + get: + displayName: List Object states + description: Returns a list of all Object states of the given group. + headers: + Accept: + description: If set, the Object state list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateList+xml + application/vnd.ez.api.ObjectStateList+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns a list of Object states. + body: + application/vnd.ez.api.ObjectStateList+xml: + type: ObjectStateList + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example + application/vnd.ez.api.ObjectStateList+json: + type: ObjectStateList + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.json.example + 401: + description: Error - The user has no permission to read Object states. + post: + displayName: Create Object state + description: Creates a new Object state. + headers: + Accept: + description: If set, the new Object state is returned in XML or JSON format. + example: + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Object state input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateCreate+json + application/vnd.ez.api.ObjectStateCreate+xml + body: + application/vnd.ez.api.ObjectStateCreate+xml: + type: ObjectStateCreate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example + application/vnd.ez.api.ObjectStateCreate+json: + type: ObjectStateCreate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.json.example + responses: + 201: + description: Object state created. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example + application/vnd.ez.api.ObjectState+json: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create an Object state. + 403: + description: Error - An Object state with the same identifier already exists in the given group. + /{objectStateId}: + get: + displayName: Get Object state + description: Returns the Object state. + headers: + Accept: + description: If set, the Object State is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example + application/vnd.ez.api.ObjectState+json: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.json.example + 401: + description: Error - The user is not authorized to read this Object state. + 404: + description: Error - The Object state does not exist. + patch: + displayName: Update Object state + description: Updates an Object state. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Object state input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateUpdate+json + application/vnd.ez.api.ObjectStateUpdate+xml + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectStateUpdate+xml: + type: ObjectStateUpdate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example + application/vnd.ez.api.ObjectStateUpdate+json: + type: ObjectStateUpdate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.json.example + responses: + 200: + description: OK - Object State updated + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example + application/vnd.ez.api.ObjectState+json: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.json.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the Object state. + 403: + description: Error - An Object state with the provided identifier already exists in this group. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Object state + description: Deletes provided Object state. + responses: + 204: + description: No Content - Object state deleted. + 401: + description: Error - The user is not authorized to delete an Object state. + 404: + description: Error - The Object state does not exist. diff --git a/docs/api/rest_api_reference/input/ez-root.raml b/docs/api/rest_api_reference/input/ez-root.raml new file mode 100644 index 0000000000..594417306f --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-root.raml @@ -0,0 +1,20 @@ +displayName: Root resources +get: + displayName: List of root resources + description: Lists the root resources of the Ibexa Platform installation. + headers: + Accept: + description: If set, the list is return in XML or JSON format. + type: string + example: | + application/vnd.ez.api.Root+xml + application/vnd.ez.api.Root+json + responses: + 200: + body: + application/vnd.ez.api.Root+xml: + type: Root + example: !include examples/GET/Root.xml.example + application/vnd.ez.api.Root+json: + type: Root + example: !include examples/GET/Root.json.example diff --git a/docs/api/rest_api_reference/input/ez-services.raml b/docs/api/rest_api_reference/input/ez-services.raml new file mode 100644 index 0000000000..bff857c7e4 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-services.raml @@ -0,0 +1,17 @@ +displayName: Services +/countries: + get: + displayName: Countries list + description: Gives access to an ISO-3166 formatted list of world countries. It is useful when presenting a country options list from any application. + headers: + Accept: + description: If set, the country list is returned in XML or JSON format. + example: | + application/vnd.ez.api.CountriesLis+xml + application/vnd.ez.api.CountriesLis+json + responses: + 200: + body: + application/vnd.ez.api.CountriesList+xml: + type: CountryList + example: !include examples/services/countries/GET/CountriesList.xml.example diff --git a/docs/api/rest_api_reference/input/ez-types.raml b/docs/api/rest_api_reference/input/ez-types.raml new file mode 100644 index 0000000000..1839b1e45b --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-types.raml @@ -0,0 +1,2263 @@ +Asset: + type: object + description: 'Asset value object' + properties: + uri: + type: string + description: Asset's URI address. + assetId: + type: string + description: Asset's identifier. + assetSource: + type: string + description: Asset provider's identifier. + assetMetadata: + type: assetMetadata + description: 'Asset metadata object. Depending on the provider, it can contain different parameters.' + +assetMetadata: + type: object + description: 'Asset metadata object. Depending on the provider, it can contain different parameters.' + properties: + alternativeText: + type: datetime + description: 'Alternative text for the image asset' + width: + type: integer + description: 'Image width' + height: + type: integer + description: 'Image height' + created_at: + type: datetime + description: Image asset's creation date + updated_at: + type: datetime + description: Image asset's modification date + author: + type: string + description: 'A name of the author of the image asset' + author_link_html: + type: string + description: 'A URL address of the author of the image asset' + +BaseObject: + type: object + properties: + _media-type: + type: string + _href: + type: string + +BookmarkList: + description: 'List of bookmarked Locations.' + type: object + properties: + totalCount: + description: 'The total number of bookmarks.' + type: integer + items: + description: 'List of bookmarked Locations.' + type: Location[] + +Content: + description: 'Content ID matcher class.' + type: BaseObject + properties: + _remoteId: + description: 'Remote ID of the Content Type.' + type: string + _id: + description: 'Unique ID of the Content Type.' + type: integer + ContentType: + description: 'Content Type.' + type: BaseObject + Name: + description: 'Name of the domain object in a given language.' + type: string + Versions: + description: 'Returns the VersionInfo for this version.' + type: BaseObject + CurrentVersion: + description: 'Current version.' + type: BaseObject + Section: + description: 'The Section to which the Content item is assigned to.' + type: BaseObject + Locations: + description: 'Location of the Content item.' + type: BaseObject + Owner: + description: 'The owner of the Content item.' + type: BaseObject + lastModificationDate: + description: 'Content item modification date.' + type: datetime + publishedDate: + description: 'Content item publication date.' + type: datetime + mainLanguageCode: + description: 'The main language code of the Content item.' + type: string + currentVersionNo: + description: 'Current version number is the version number of the published version or the version number of a newly created draft (which is 1).' + type: integer + alwaysAvailable: + description: "Indicates if the Content item is shown in the main language if it's not present in an other requested language." + type: boolean + status: + description: 'Status of the content. Possible values: const STATUS_DRAFT = 0;const STATUS_PUBLISHED = 1; const STATUS_TRASHED = 2.' + #enum: ['DRAFT', 'PUBLISHED', 'TRASHED'] + ObjectStates: + description: 'Object states.' + type: BaseObject + +ContentCreate: + description: 'This class is used for creating a new Content item.' + type: object + +ContentCreateStruct: + description: 'This class is used for creating a new Content item.' + type: object + properties: + contentType: + descritpion: 'The Content Type for which the new Content item is created.' + required: true + type: ContentType + sectionId: + description: 'The Section the Content item is assigned to. If not set the Section of the parent is used or a default Section.' + type: integer + ownerId: + description: 'The owner of the content. If not given the current authenticated User is set as owner.' + type: integer + alwaysAvailable: + description: "Indicates if the Content item is shown in the main language if it's not present in an other requested language." + type: boolean + remoteId: + description: 'Remote identifier used as a custom identifier for the Content item. Needs to be a unique Content->remoteId string value.' + type: string + mainLanguageCode: + description: 'The main language code for the content. This language will also be used for as initial language for the first created version. It is also used as default language for added fields.' + required: true + type: string + modificationDate: + descritpion: 'Modification date. If not given the current integer is used.' + type: datetime +ContentImageVariation: + description: 'This class represents an image.' + type: BaseObject + properties: + uri: + description: 'This class represents a URI.' + type: string + contentType: + description: 'Type of an image.' + type: string + width: + description: 'Width of an image.' + type: integer + height: + description: 'Height of an image.' + type: integer + fileSize: + description: 'Size of an image file.' + type: integer +ContentInfo: + description: 'This class provides all version independent information of the Content item.' + type: BaseObject + properties: + Content: + description: 'Content ID matcher class.' + type: Content +# id: +# description: 'The unique ID of the Content item.' +# type: integer +# contentTypeId: +# description: 'The Content Type ID of the Content item.' +# type: integer +# name: +# description: 'The computed name (via name schema) in the main language of the Content item.' +# type: string +# sectionId: +# description: 'The Section to which the Content item is assigned.' +# type: integer +# currentVersionNo: +# description: 'Current Version number is the version number of the published version or the version number of a newly created draft (which is 1).' +# type: integer +# published: +# description: 'True if there exists a published version, false otherwise.' +# type: boolean +# ownerId: +# description: 'The owner of the Content item.' +# type: integer +# modificationDate: +# description: 'Content modification date.' +# type: datetime +# publishedDate: +# description: 'Content publication date.' +# type: datetime +# alwaysAvailable: +# description: "Indicates if the Content item is shown in the main language if it's not present in an other requested language." +# type: boolean +# remoteId: +# description: 'Remote identifier used as a custom identifier for the object.' +# type: string +# mainLanguageCode: +# description: 'The main language code of the Content item.' +# type: string +# mainLocationId: +# description: 'Identifier of the main Location. If the Content item has multiple Locations, mainLocationId will point to the main one.' +# type: integer +# status: +# description: 'Status of the content. Possible values: const STATUS_DRAFT = 0;const STATUS_PUBLISHED = 1; const STATUS_TRASHED = 2.' +# type: integer +ContentMetadataUpdateStruct: + description: 'With this class data can be provided to update version independent Fields of the Content item. It is used in content update methods. At least one property in this class must be set.' + type: object + properties: + ownerId: + description: 'If set this value changes the owner ID of the Content item.' + type: integer + publishedDate: + description: 'If set this value overrides the publication date of the content. (Used in staging scenarios).' + type: datetime + modificationDate: + description: 'If set this value overrides the modification date. (Used for staging scenarios).' + type: datetime + mainLanguageCode: + description: 'If set the main language of the Content item is changed.' + type: string + alwaysAvailable: + description: 'If set this value changes the always available flag.' + type: boolean + remoteId: + description: 'If set this value changes the remoteId. Needs to be a unique Content->remoteId string value.' + type: string + mainLocationId: + description: 'If set main Location is changed to this value. If the Content item has multiple Locations, $mainLocationId will point to the main one.' + type: integer + name: + description: "If set, will change the content's 'always-available' name." + type: string +ContentObjectStates: + description: 'Base struct for content create/update structs.' + type: BaseObject + properties: + ObjectState: + description: 'Object state value.' + type: BaseObject +ContentStruct: + description: 'Base struct for content create/update structs.' + type: object + properties: + setField: + description: 'Adds a field to the field collection. This method could also be implemented by a magic setter so that $fields[$fieldDefIdentifier][$language] = $value or without language $fields[$fieldDefIdentifier] = $value is an equivalent call.' + type: string +ContentType: + descritpion: 'This class represents a Content Type.' + type: BaseObject + properties: + id: + description: 'Content Type ID.' + type: integer + status: + description: 'The status of the Content Type. Possible values: const STATUS_DEFINED = 0; Status constant for defined (aka published) Type, const STATUS_DRAFT = 1; Status constant for draft (aka temporary) Type; const STATUS_MODIFIED = 2; Status constant for modified (aka deferred for publishing) Type.' + enum: [] + identifier: + description: 'String identifier of a Content Type.' + type: string + names: + description: 'Name of a Content Type.' + type: Value[] + descriptions: + description: 'Description of a Content Type.' + type: Value[] + creationDate: + description: 'Creation date of the Content Type.' + type: datetime + modificationDate: + description: 'Modification date of the Content Type.' + type: datetime + Creator: + descritpion: 'Creator User of the Content Type.' + type: BaseObject + Modifier: + description: 'Modifier User of the Content Type.' + type: BaseObject + Groups: + description: 'Group User of the Content Type.' + type: BaseObject + Draft: + description: 'Draft of the Content Type.' + type: BaseObject + remoteId: + description: 'Unique remote ID of the Content Type.' + type: string + urlAliasSchema: + description: 'URL alias schema. If nothing is provided, $nameSchema will be used instead.' + type: string + nameSchema: + description: 'Name schema. Can be composed of FieldDefinition identifier place holders.' + type: string + isContainer: + description: 'A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected.' + type: boolean + defaultAlwaysAvailable: + description: 'If an instance of a Content Type is created the always available flag is set by default to this value.' + type: boolean + defaultSortField: + descritpion: 'Specifies which property the child Locations should be sorted on by default when created. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: const SORT_FIELD_PATH = 1; const SORT_FIELD_PUBLISHED = 2; const SORT_FIELD_MODIFIED = 3; const SORT_FIELD_SECTION = 4; const SORT_FIELD_DEPTH = 5; const SORT_FIELD_PRIORITY = 8; const SORT_FIELD_NAME = 9; const SORT_FIELD_NODE_ID = 11; const SORT_FIELD_CONTENTOBJECT_ID = 12.' + enum: [] + defaultSortOrder: + description: 'Specifies whether the sort order should be ascending or descending by default when created. Map for Location sort order to their respective Query SORT constants. Possible values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1.' + enum: [] + FieldDefinitions: + description: 'This method returns the Content Type Field definitions from this type.' + type: BaseObject + properties: + FieldDefinitions: + type: FieldDefinition[] + +ContentTypeCreateStruct: + description: 'This class is used for creating Content Types.' + type: object + properties: + identifier: + description: 'String unique identifier of a type.' + required: true + type: string + mainLanguageCode: + description: 'Main language Code.' + required: true + type: string + remoteId: + description: 'The remote ID.' + type: string + urlAliasSchema: + description: 'URL alias schema.' + type: string + nameSchema: + description: 'Name schema.' + type: string + isContainer: + description: 'Determines if the type is a container.' + type: boolean + defaultSortField: + description: 'Specifies which property the child Locations should be sorted on by default when created.' + type: integer + defaultSortOrder: + description: 'Specifies whether the sort order should be ascending or descending by default when created.' + type: integer + defaultAlwaysAvailable: + description: 'If an instance of a Content Type is created the always available flag is set by default this this value.' + type: boolean + names: + description: 'An array of names with languageCode keys. At least one name in the main language is required.' + required: true + type: string[] + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: string[] + addFieldDefinition: + description: 'Adds a new Field definition.' + type: FieldDefinitionCreateStruct + creatorId: + description: 'If set this value overrides the current User as creator.' + type: integer + creationDate: + description: 'f set this value overrides the current time for creation.' + type: datetime + +ContentTypeDraft: + description: 'This class represents a draft of a Content Type.' + type: object +ContentTypeGroup: + description: 'This class represents a Content Type group value.' + type: BaseObject + properties: + id: + description: 'Primary key.' + type: integer + identifier: + description: 'Readable string identifier of a group.' + type: string + created: + description: 'Created date (integer).' + type: datetime + modified: + description: 'Modified date (integer).' + type: datetime + Creator: + description: 'Creator User ID.' + type: BaseObject + Modifier: + description: 'Modifier User ID.' + type: BaseObject + ContentTypes: + description: 'Content Types.' + type: BaseObject + +ContentTypeGroupInput: + type: BaseObject + +ContentTypeGroupCreateStruct: + description: 'This class is used for creating a Content Type group.' + type: object + properties: + creatorId: + description: 'If set this value overrides the current User as creator.' + type: integer + creationDate: + description: 'If set this value overrides the current time for creation.' + type: datetime +ContentTypeGroupList: + description: 'List of Content Type groups.' + type: BaseObject + properties: + ContentTypeGroup: + type: ContentTypeGroup[] +ContentTypeGroupRef: + description: 'Content Type group reference.' + type: BaseObject + properties: + Unlink: + type: Unlink +ContentTypeGroupRefList: + description: 'List of Content Type groups references.' + type: BaseObject + properties: + ContentTypeGroupRef: + type: ContentTypeGroupRef +ContentTypeGroupStruct: + description: 'This class is used for updating a Content Type group.' + type: object + properties: + identifier: + description: 'Readable and unique string identifier of a group.' + type: string +ContentTypeGroupUpdateStruct: + description: 'This class is used for updating a Content Type group.' + type: object + properties: + modifierId: + description: 'If set this value overrides the current User as modifier.' + type: integer + modificationDate: + description: 'If set this value overrides the current time for modified.' + type: datetime +ContentTypeInfo: + description: 'This class stores Content Type information.' + type: BaseObject + properties: + id: + description: 'Content Type ID.' + type: integer + status: + description: 'The status of the Content Type. Possible values: const STATUS_DEFINED = 0; Status constant for defined (aka published) Type, const STATUS_DRAFT = 1; Status constant for draft (aka temporary) Type; const STATUS_MODIFIED = 2; Status constant for modified (aka deferred for publishing) Type.' + enum: [] + identifier: + description: 'String identifier of a Content Type.' + type: string + names: + description: 'Name of a Content Type.' + type: Value[] + descriptions: + description: 'Description of a Content Type.' + type: Value[] + creationDate: + description: 'Creation date of the Content Type.' + type: datetime + modificationDate: + description: 'Modification date of the Content Type.' + type: datetime + Creator: + descritpion: 'Creator User of the Content Type.' + type: BaseObject + Modifier: + description: 'Modifier User of the Content Type.' + type: BaseObject + Groups: + description: 'Group User of the Content Type.' + type: BaseObject + Draft: + description: 'Draft of the Content Type.' + type: BaseObject + remoteId: + description: 'Unique remote ID of the Content Type.' + type: string + urlAliasSchema: + description: 'URL alias schema. If nothing is provided, $nameSchema will be used instead.' + type: string + nameSchema: + description: 'Name schema. Can be composed of FieldDefinition identifier place holders.' + type: string + isContainer: + description: 'A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected.' + type: boolean + mainLanguageCode: + description: 'Main language code.' + type: string + defaultAlwaysAvailable: + description: 'If an instance of a Content Type is created the always available flag is set by default to this value.' + type: boolean + defaultSortField: + descritpion: 'Specifies which property the child Locations should be sorted on by default when created. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: const SORT_FIELD_PATH = 1; const SORT_FIELD_PUBLISHED = 2; const SORT_FIELD_MODIFIED = 3; const SORT_FIELD_SECTION = 4; const SORT_FIELD_DEPTH = 5; const SORT_FIELD_PRIORITY = 8; const SORT_FIELD_NAME = 9; const SORT_FIELD_NODE_ID = 11; const SORT_FIELD_CONTENTOBJECT_ID = 12.' + enum: [] + defaultSortOrder: + description: 'Specifies whether the sort order should be ascending or descending by default when created. Map for Location sort order to their respective Query SORT constants. Possible values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1.' + enum: [] +ContentTypeInfoList: + description: 'List of Content Type information.' + type: BaseObject + properties: + ContentType: + type: ContentTypeInfo[] +ContentTypeList: + description: 'List of Content Types.' + type: BaseObject + properties: + ContentType: + type: ContentType[] +ContentTypeUpdateStruct: + description: 'This class is used for updating a Content Type.' + type: object + properties: + identifier: + description: 'If set the unique identifier of a type is changed to this value.' + type: string + remoteId: + description: 'If set the remote ID is changed to this value.' + type: string + urlAliasSchema: + description: 'If set the URL alias schema is changed to this value.' + type: string + nameSchema: + description: 'f set the name schema is changed to this value.' + type: string + isContainer: + description: 'If set the container flag is set to this value.' + type: boolean + mainLanguageCode: + description: 'If set the main language is changed to this value.' + type: string + defaultSortField: + description: 'If set the default sort field is changed to this value.' + type: integer + defaultSortOrder: + description: 'If set the default sort order is set to this value.' + type: integer + defaultAlwaysAvailable: + description: 'If set the default always available flag is set to this value.' + type: boolean + modifierId: + description: 'If set this value overrides the current User as creator.' + type: integer + modificationDate: + description: 'If set this value overrides the current time for creation.' + type: datetime + names: + description: 'If set this array of names with languageCode keys replace the complete name collection.' + type: array + descriptions: + description: 'If set this array of descriptions with languageCode keys replace the complete description collection.' + type: array +ContentUpdateStruct: + description: 'This class is used for updating the fields of a Content item draft.' + type: object + properties: + initialLanguageCode: + description: 'The language code of the version. This code is used as the language code of the translation (which is shown in the admin interface). It is also used as default language for added fields.' + type: string + creatorId: + description: 'Creator User ID. Creator of the version, in the search API this is referred to as the modifier of the published content.' + type: integer +Country: + description: 'This class is representing a country.' + type: BaseObject + properties: + _id: + description: 'ID that represents a country name.' + type: string + name: + description: 'Name of the country.' + type: string + Alpha2: + description: 'Two-letter code that represents a country name.' + type: string + Alpha3: + description: 'Three-letter code that represents a country name.' + type: string + IDC: + description: 'IDC' + type: integer +CountryList: + description: 'This class is representing an ISO-3166 formatted list of world countries.' + type: BaseObject + properties: + Country: + type: Country[] +CreateStruct: + description: 'This class is used for creating a notification.' + type: object + properties: + ownerId: + description: 'Owner ID.' + type: integer + type: + description: 'This class represents type.' + type: string + isPending: + description: 'If the notification is pending.' + type: boolean + data: + description: 'Notification data.' + type: array +Criterion: + description: 'Criterion implementations.' + type: object + properties: + operator: + description: 'The operator used by the Criterion.' + type: string + value: + description: 'The value(s) matched by the Criteria.' + type: array + target: + description: 'The target used by the Criteria (field, metadata...).' + type: string + valueData: + description: 'Additional value data, required by some Criteria, MapLocationDistance for instance.' + type: Value + Specifications: + description: "Criterion description function. Returns the combination of the Criterion's supported operator/value, as an array of objects." + type: Specifications[] + ValueTypeCheckCallback: + description: 'Returns a callback that checks the values types depending on the operator specifications.' + type: integer +DateRange: + description: 'Representation of date range.' + type: object +datetime: + description: 'Representation of date and time.' + type: object +Event: + description: 'Calendar event.' + type: BaseObject + properties: + id: + description: 'The unique ID.' + type: string + datetime: + type: datetime + type: + description: 'Event type identifier or name.' + type: string + name: + description: 'Name of the calendar event.' + type: string + attributes: + description: 'List of event attributes.' + type: EventAttribute[] +EventAttribute: + type: object + properties: + label: + description: 'Label of the attribute.' + type: string + value: + description: 'Value of the attribute.' + type: string +EventGroup: + description: 'Event group.' + type: BaseObject + properties: + currentPage: + description: 'Current page representation.' + type: string + nextPage: + description: 'Next page representation.' + type: string + groupKey: + type: DateRange + totalCount: + description: 'Total count of events.' + type: integer + events: + type: Event[] +EventGroupList: + description: 'Grouped calendar event list.' + type: EventList + properties: + groups: + type: EventGroup[] +EventList: + description: 'List of calendar events.' + type: BaseObject + properties: + currentPage: + description: 'Current page representation.' + type: string + nextPage: + description: 'Next page representation.' + type: string + totalCount: + description: 'Total count of events.' + type: integer + events: + type: Event[] +FacetBuilder: + description: 'This class is the base class for facet builders.' + type: object + properties: + name: + description: 'The name of the facet.' + type: string + global: + description: 'If true the facet runs in a global mode not restricted by the query.' + type: boolean + filter: + description: 'An additional facet filter that will further filter the documents the facet will be executed on.' + type: Criterion + limit: + description: 'Number of facets (terms) returned.' + type: integer + minCount: + description: 'Specifies the minimum count. Only facet groups with more or equal results are returned.' + type: integer +Field: + description: 'This class represents a field of a Content item.' + type: object + properties: + id: + description: 'The field ID.' + type: integer + fieldDefinitionIdentifier: + description: 'The Field definition identifier.' + type: string + languageCode: + description: 'The language code.' + type: string + fieldTypeIdentifier: + description: 'Field Type identifier.' + type: string + fieldValue: + description: 'A Field Type value or a value type which can be converted by the corresponding field type.' + type: any +FieldDefinition: + description: 'This class represents a Field definition.' + type: BaseObject + properties: + id: + description: 'The unique ID of this Field definition.' + type: integer + identifier: + description: 'Readable string identifier of a Field definition.' + type: string + fieldType: + description: 'String identifier of the field type.' + type: string + fieldGroup: + description: 'Field group name.' + type: string + position: + description: 'The position of the Field definition in the content type.' + type: integer + isTranslatable: + description: 'If the field is translatable.' + type: boolean + isRequired: + description: 'Is the field required.' + type: boolean + isInfoCollector: + description: 'The flag if this field is used for information collection.' + type: boolean + defaultValue: + description: 'Default value of the field.' + type: any + isSearchable: + description: 'Indicates if th the content is searchable by this attribute.' + type: boolean + names: + description: 'Names of Content Types.' + type: Value[] + descriptions: + description: 'Descriptions of Content Types.' + type: Value[] + fieldSettings: + description: 'Settings for the Field definition supported by the field type.' + type: any + validatorConfiguration: + description: 'Validator configuration of this Field definition supported by the field type.' + type: StringLengthValidator + +FieldDefinitionCreate: + type: BaseObject + +FieldDefinitionUpdate: + type: BaseObject + +FieldDefinitionCreateStruct: + description: 'This class is used to create a Field definition.' + type: object + properties: + fieldTypeIdentifier: + description: 'String identifier of the field type.' + required: true + type: string + identifier: + description: 'Readable string identifier of a Field definition. Needs to be unique within the context of the Content Type this is added to.' + required: true + type: string + names: + description: 'An array of names with languageCode keys.' + type: array + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: array + fieldGroup: + description: 'Field group name.' + type: string + position: + description: 'The position of the Field definition in the Content Type if not set the field is added at the end.' + type: integer + isTranslatable: + description: 'Indicates if the field is translatable.' + type: boolean + isRequired: + description: 'Indicates if the field is required.' + type: boolean + isInfoCollector: + description: 'Indicates if this attribute is used for information collection.' + type: boolean + validatorConfiguration: + description: 'The validator configuration supported by the field type.' + type: any + fieldSettings: + description: 'The settings supported by the field type.' + type: any + defaultValue: + description: 'Default value of the field.' + type: any + isSearchable: + description: 'Indicates if th the content is searchable by this attribute.' + type: boolean +FieldDefinitionUpdateStruct: + description: 'This class is used to update a Field definition.' + type: object + properties: + identifier: + description: 'If set the identifier of a Field definition is changed to this value. Needs to be unique within the context of the Content Type this is added to.' + type: string + names: + description: 'If set this array of names with languageCode keys replace the complete name collection.' + type: array + descriptions: + description: 'If set this array of descriptions with languageCode keys replace the complete description collection.' + type: array + fieldGroup: + description: 'If set the field group is changed to this name.' + type: string + position: + description: 'If set the position of the field in the Content Type.' + type: integer + isTranslatable: + description: 'If set translatable flag is set to this value.' + type: boolean + isRequired: + description: 'If set the required flag is set to this value.' + type: boolean + isInfoCollector: + description: 'If set the information collector flag is set to this value.' + type: boolean + validatorConfiguration: + description: 'If set this validator configuration supported by the Field Type replaces the existing one.' + type: any + fieldSettings: + description: 'If set this settings supported by the Field Type replaces the existing ones.' + type: any + defaultValue: + description: 'If set the default value for this field is changed to the given value.' + type: any + isSearchable: + description: 'If set the the searchable flag is set to this value.' + type: boolean +JWT: + description: 'This class represents the JWT authentication token' + type: object + properties: + token: + description: 'JWT authentication token' + type: string +JWTInput: + description: 'This class represents the input for a JWT authentication token' + type: object + properties: + user: + description: 'User name' + type: string + password: + description: 'User password' + type: string +Language: + description: 'This class represents a language in the Repository.' + type: object + properties: + ALL: + description: "Constant for use in API's to specify that you want to load all languages." + type: array + id: + description: 'The language ID (auto generated).' + type: integer + languageCode: + description: 'The languageCode code.' + type: string + name: + description: 'Human readable name of the language.' + type: string + enabled: + description: 'Indicates if the language is enabled or not.' + type: boolean +LanguageCode: + description: 'Language code.' + type: object + properties: + languageCode: + type: string +LanguageCreateStruct: + description: 'This class represents a value for creating a language.' + type: object + properties: + languageCode: + description: 'The languageCode code. Needs to be a unique.' + type: string + name: + description: 'Human readable name of the language.' + type: string + enabled: + description: 'Indicates if the language is enabled or not.' + type: boolean +Limitation: + description: 'This class represents a Limitation applied to a policy.' + type: BaseObject + properties: + _identifier: + description: 'Returns the limitation identifier (one of the defined constants) or a custom limitation. Constants: CONTENTTYPE = Class; LANGUAGE = Language; LOCATION = Node; OWNER = Owner; PARENTOWNER = ParentOwner; PARENTCONTENTTYPE = ParentClass; PARENTDEPTH = ParentDepth; SECTION = Section; NEWSECTION = NewSection; SITEACCESS = SiteAccess; STATE = State; NEWSTATE = NewState; SUBTREE = Subtree; USERGROUP = Group; PARENTUSERGROUP = ParentGroup; STATUS = Status.' + enum: ['CONTENTTYPE', 'LANGUAGE', 'LOCATION', 'OWNER', 'PARENTOWNER', 'PARENTCONTENTTYPE', 'PARENTDEPTH', 'SECTION', 'NEWSECTION','SITEACCESS', 'STATE', 'NEWSTATE', 'SUBTREE', 'USERGROUP', 'PARENTUSERGROUP', 'STATUS'] + Values: + description: 'A read-only list of IDs or identifiers for which the limitation should be applied. The value of this property must conform to a hash, which means that it may only consist of array and scalar values, but must not contain objects or resources.' + type: Ref[] +Location: + description: 'This class represents a Location in the Repository.' + type: BaseObject + properties: + id: + description: 'The ID of the Location.' + type: integer + priority: + description: 'Location priority. Position of the Location among its siblings when sorted using priority sort order.' + type: integer + hidden: + description: 'Indicates that the Location entity has been explicitly marked as hidden.' + type: boolean + invisible: + description: 'Indicates that the Location is implicitly marked as hidden by a parent Location.' + type: boolean +# explicitlyHidden: +# description: 'Indicates that the Location entity has been explicitly marked as hidden.' +# type: boolean + remoteId: + description: 'Remote ID, universally unique identifier.' + type: string + ContentInfo: + description: 'This class provides all version independent information of the Content item.' + type: ContentInfo + ParentLocation: + description: 'Parent Location.' + type: Ref + Children: + description: 'Children Location.' + type: Ref + pathString: + description: 'The materialized path of the Location entry e.g. /1/2/.' + type: string + depth: + description: 'Depth Location has in the Location tree.' + type: integer + childCount: + description: 'Depth Location has in the Location tree.' + type: integer + sortField: + description: 'Specifies which property the child Locations should be sorted on. Map for Location sort fields to their respective SortClauses - class name/identifier and modified subnode. One of the fallowing values: PATH.' + enum: ['PATH', 'PUBLISHED', 'MODIFIED', 'SECTION', 'DEPTH', 'CLASS_IDENTIFIER', 'CLASS_NAME', 'PRIORITY', 'NAME', 'MODIFIED_SUBNODE', 'NODE_ID', 'CONTENTOBJECT_ID'] + sortOrder: + description: 'Specifies whether the sort order should be ascending or descending. Map for Location sort order to their respective Query SORT constants. One of the fallowing values: const SORT_ORDER_DESC = 0; const SORT_ORDER_ASC = 1.' + enum: ['ASC', 'DESC'] + Content: + description: 'Represents a Content item in a specific version.' + type: Ref + UrlAliases: + description: 'This class represents URL aliases.' + type: Ref + +LocationCreate: + type: BaseObject + +LocationCreateStruct: + description: 'This class is used to create a new Location for a Content item.' + type: object + properties: + priority: + description: 'Location priority. Position of the Location among its siblings when sorted using priority sort order.' + type: integer + hidden: + description: 'Indicates that the Location entity has been explicitly marked as hidden.' + type: boolean + remoteId: + description: 'An universally unique string identifier. Needs to be a unique Location->remoteId string value.' + type: integer + sortField: + description: 'Specifies which property the child locations should be sorted on. Valid values are found at {@link Location::SORT_FIELD_*}.' + type: integer + sortOrder: + description: 'Specifies whether the sort order should be ascending or descending. Valid values are {@link Location::SORT_ORDER_*}.' + type: integer + parentLocationId: + description: 'The ID of the parent Location under which the new Location should be created.' + required: true + type: integer +LocationList: + description: 'This class represents a queried Location list holding a totalCount and a partial list of Locations (by offset/limit parameters and permission filters).' + type: object + properties: + totalCount: + description: 'The total count of found Locations (filtered by permissions).' + type: integer + Locations: + description: 'The partial list of Locations controlled by offset/limit.' + type: Location[] +LocationQuery: + description: 'This class is used to perform a Location query.' + type: object +LocationUpdateStruct: + description: 'This class is used for updating Location meta data.' + type: object + properties: + priority: + description: 'If set the Location priority is changed to the new value.' + type: integer + remoteId: + description: 'If set the Location gets a new remoteId. Needs to be a unique Location->remoteId string value.' + type: integer + sortField: + description: 'If set the sortField is changed. The sort field specifies which property the child Locations should be sorted on. Valid values are found at {@link Location::SORT_FIELD_*}.' + type: integer + sortOrder: + description: 'If set the sortOrder is changed. The sort order specifies whether the sort order should be ascending or descending. Valid values are {@link Location::SORT_ORDER_*}.' + type: integer +Message: + description: 'Class for translatable messages, which only occur in singular form. The message might include replacements, in the form %[A-Za-z]%. Those are replaced by the values provided. A raw % can be escaped like %%.' + type: object + properties: + message: + description: 'Message string. Might use replacements like %foo%, which are replaced by the values specified in the values array.' + type: string + values: + description: 'Translation value objects. May not contain any numbers, which might result in requiring plural forms. Use Plural for that.' + type: array +Notification: + description: 'This class represents a notification value.' + type: object + properties: + id: + description: 'This class represents ID.' + type: integer + ownerId: + description: 'Owner ID.' + type: integer + isPending: + description: 'If the notification is pending.' + type: boolean + type: + description: 'This class represents type.' + type: string + created: + description: 'Creation date.' + type: datetime + data: + description: 'This class represents data.' + type: array +NotificationList: + description: 'This class represents a notifications list.' + type: object + properties: + totalCount: + description: 'Number of notifications.' + type: integer + items: + description: 'List of notifications.' + type: Notification[] +ObjectState: + description: 'This class represents a Object state value.' + type: BaseObject + properties: + id: + description: 'Primary key.' + type: integer + identifier: + description: 'Readable string identifier of the Object state.' + type: string + priority: + description: 'Priority for ordering.' + type: integer + ObjectStateGroup: + description: 'The Object state group this Object state belongs to.' + type: BaseObject + languageCodes: + description: 'The available language codes for names an descriptions.' + type: string[] + names: + description: 'Names.' + type: object + properties: + Value: + type: Value + descriptions: + description: 'Descriptions.' + type: object + properties: + Value: + type: Value +ObjectStateCreate: + type: BaseObject + +ObjectStateCreateStruct: + description: 'This class represents a value for creating Object states.' + type: object + properties: + identifier: + description: 'Readable unique string identifier of a group.' + required: true + type: string + priority: + description: 'Priority for ordering. If not set the Object state is created as the last one.' + type: integer + defaultLanguageCode: + description: 'The default language code.' + required: true + type: string + names: + description: 'An array of names with languageCode keys. At least one name in the main language is required.' + required: true + type: string[] + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: string[] +ObjectStateGroup: + description: 'This class represents an Object state group value.' + type: BaseObject + properties: + id: + description: 'Primary key.' + type: integer + identifier: + description: 'Readable string identifier of a group.' + type: string + defaultLanguageCode: + description: 'The default language code.' + type: string + languageCodes: + description: 'The available language codes for names an descriptions.' + type: string[] + ObjectStates: + description: 'Object States.' + type: BaseObject + Names: + description: 'List of names.' + type: object + properties: + Value: + type: Value + Descriptions: + description: 'List of descriptions.' + type: object + properties: + Value: + type: Value + +ObjectStateGroupCreate: + type: BaseObject + +ObjectStateGroupCreateStruct: + description: 'This class represents a value for creating Object state groups.' + type: object + properties: + identifier: + description: 'Readable unique string identifier of a group.' + required: true + type: string + defaultLanguageCode: + description: 'The default language code.' + required: true + type: string + names: + description: 'An array of names with languageCode keys. At least one name in the main language is required.' + required: true + type: string[] + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: string[] +ObjectStateGroupList: + description: 'List of Object state groups.' + type: BaseObject + properties: + ObjectStateGroup: + description: 'This class represents an Object state group value.' + type: ObjectStateGroup + +ObjectStateGroupUpdate: + type: BaseObject + +ObjectStateGroupUpdateStruct: + description: 'This class represents a value for updating Object state groups.' + type: object + properties: + identifier: + description: 'Readable unique string identifier of a group.' + type: string + defaultLanguageCode: + description: 'The default language code.' + type: string + names: + description: 'An array of names with languageCode keys.' + type: string[] + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: string[] +ObjectStateList: + description: 'List of Object states.' + type: BaseObject + properties: + ObjectState: + type: ObjectState[] + +ObjectStateUpdate: + type: BaseObject + +ObjectStateUpdateStruct: + description: 'This class represents a value for updating Object states.' + type: object + properties: + identifier: + description: 'Readable unique string identifier of a group.' + type: string + defaultLanguageCode: + description: 'The default language code.' + type: string + names: + description: 'An array of names with languageCode keys.' + type: string[] + descriptions: + description: 'An array of descriptions with languageCode keys.' + type: string[] +PasswordValidationContext: + description: 'Context of the password validation.' + type: object + properties: + contentType: + description: 'Content Type of the password owner.' + type: ContentType + user: + description: 'Owner of the password.' + type: User +Plural: + description: 'Class for translatable messages, which may contain plural forms. The message might include replacements, in the form %[A-Za-z]%. Those are replaced by the values provided. A raw % can be escaped like %%.' + type: object + properties: + singular: + description: 'Singular string. Might use replacements like %foo%, which are replaced by the values specified in the values array.' + type: string + plural: + description: 'Message string. Might use replacements like %foo%, which are replaced by the values specified in the values array.' + type: string + values: + description: 'Translation value objects. May not contain any numbers, which might result in requiring plural forms. Use MessagePlural for that.' + type: array +Policy: + description: 'This class represents a policy value.' + type: BaseObject + properties: + id: + description: 'ID of the policy.' + type: integer + module: + description: 'Name of module, associated with the Policy e.g. content.' + type: string + function: + description: "Name of the module function Or all functions with '*' e.g. read." + type: string + Limitations: + description: 'Limitations.' + type: Limitation[] +PolicyCreateStruct: + description: 'This class is used to create a policy.' + type: object + properties: + module: + description: 'Name of module, associated with the Policy e.g. content.' + type: string + function: + description: 'Name of the module function Or all functions with "*" e.g. read.' + type: string +PolicyDraft: + description: 'Original policy ID the policy was created from.' + type: object + properties: + originalId: + description: 'Original policy ID the policy was created from. Used when role status is Role::STATUS_DRAFT.' + type: integer +PolicyList: + description: 'List of policies.' + type: BaseObject + properties: + Policy: + type: Policy[] +PolicyStruct: + description: 'This class is used to edit a policy.' + type: object + properties: + Limitations: + description: 'Returns list of limitations added to policy.' + type: Limitation[] + Limitation: + description: 'Adds a limitation with the given identifier and list of values.' + type: Limitation +PolicyUpdateStruct: + description: 'This class is used for updating a policy. The limitations of the policy are replaced with those which are added in instances of this class.' + type: object +Query: + description: 'This class is used to perform a Content query.' + type: object + properties: + filter: + description: 'The Query filter. Can contain multiple criterion, as items of a logical one (by default AND).' + type: Criterion + query: + description: 'The Query query. Can contain multiple criterion, as items of a logical one (by default AND).' + type: Criterion + sortClauses: + description: 'Query sorting clauses.' + type: SortClause[] + facetBuilders: + description: "An array of facet builders. Search engines may ignore any, or given facet builders they don't support and will just return search result facets supported by the engine. API consumer should dynamically iterate over returned facets for further use." + type: FacetBuilder[] + offset: + description: 'Query offset. Sets the offset for search hits, used for paging the results.' + type: integer + limit: + description: 'Query limit. Limit for number of search hits to return. If value is `0`, search query will not return any search hits, useful for doing a count.' + type: integer + spellcheck: + description: 'If true spellcheck suggestions are returned.' + type: boolean + performCount: + description: 'If true, search engine should perform count even if that means extra lookup.' + type: boolean +Ref: + type: object + properties: + _media-type: + type: string + _href: + type: string +Relation: + description: 'Class representing a relation between content.' + type: BaseObject + properties: + id: + description: 'ID of the relation.' + type: integer + sourceFieldDefinitionIdentifier: + description: 'Source Content Type Field Definition ID. For relation not of type RelationType::COMMON this field denotes the Field definition ID of the attribute where the relation is anchored.' + type: string + SourceContentInfo: + description: 'The content of the source content of the relation.' + type: ContentInfo + DestinationContentInfo: + description: 'The content of the destination content of the relation.' + type: ContentInfo + type: + description: 'The relation type bitmask. Relations: Relation::COMMON = 1, Relation::EMBED = 2, Relation::LINK = 4, Relation::FIELD = 8, Relation::ASSET = 16' + type: integer +RelationList: + description: 'Class representing a list of relations between content.' + type: BaseObject + properties: + Relation: + type: Relation[] +Role: + description: 'This class represents a role.' + type: BaseObject + peoperties: + identifier: + description: 'Readable string identifier of a role.' + type: string + Policies: + description: 'Returns the list of policies of this role.' + type: BaseObject +RoleAssignment: + description: 'This value object represents an assignment of a User or User group to a role including a limitation.' + type: BaseObject + properties: + Limitation: + description: 'Returns the limitation of the role assignment.' + type: RoleLimitation + Role: + description: 'Returns the role to which the User or User group is assigned to.' + type: Ref +RoleAssignmentList: + description: 'This value object represents a list of assignments of a User or User group to a role including a limitation.' + type: BaseObject + properties: + RoleAssignment: + type: RoleAssignment[] +RoleCreateStruct: + description: 'This class is used to create a new role.' + type: object + properties: + identifier: + description: 'Readable string identifier of a role.' + type: string + Policies: + description: 'Returns policies associated with the role.' + type: PolicyCreateStruct[] + Policy: + description: 'Adds a policy to this role.' + type: PolicyCreateStruct +RoleDraft: + description: 'This class represents a draft of a role, extends Role.' + type: Role +RoleLimitation: + description: 'Role limitation extends Limitation.' + type: Limitation + +RoleUpdateStruct: + description: 'This class is used to update a role.' + type: object + properties: + identifier: + description: 'Readable string identifier of a role.' + type: string +RoleList: + description: 'This class represents a list roles.' + type: BaseObject + properties: + Role: + type: Role[] + +Root: + description: 'This class represents a root.' + type: BaseObject + properties: + content: + description: 'Content.' + type: BaseObject + contentByRemoteId: + description: 'Content by the given remote ID.' + type: BaseObject + contentTypes: + description: 'Content Types.' + type: BaseObject + contentTypeByIdentifier: + description: 'Content Type by the given identifier.' + type: BaseObject + contentTypeGroups: + description: 'Content Type Groups.' + type: BaseObject + contentTypeGroupByIdentifier: + description: 'Content Type Groups by the given identifier.' + type: BaseObject + users: + description: 'Users.' + type: BaseObject + usersByRoleId: + description: 'Users by Role ID.' + type: BaseObject + usersByRemoteId: + description: 'Users by remote ID.' + type: BaseObject + usersByEmail: + description: 'Users by e-mail.' + type: BaseObject + usersByLogin: + description: 'Users by login.' + type: BaseObject + roles: + description: 'Roles.' + type: BaseObject + rootLocation: + description: 'Root Location.' + type: BaseObject + rootUserGroup: + description: 'Root User Group.' + type: BaseObject + rootMediaFolder: + description: 'Root media folder.' + type: BaseObject + locationByRemoteId: + description: 'Location by remote ID.' + type: BaseObject + locationByPath: + description: 'Location by path.' + type: BaseObject + trash: + description: 'Trash.' + type: BaseObject + sections: + description: 'Sections.' + type: BaseObject + views: + description: 'Views.' + type: BaseObject + objectStateGroups: + description: 'Object state groups.' + type: BaseObject + objectStates: + description: 'Object states.' + type: BaseObject + globalUrlAliases: + description: 'Global URL aliases.' + type: BaseObject + urlWildcards: + description: 'URL Wildcards.' + type: BaseObject + createSession: + description: 'Creates a new session based on the credentials provided as POST parameters.' + type: BaseObject + refreshSession: + description: 'Refresh given session.' + type: BaseObject +SearchResult: + description: 'The total number of URLs.' + type: object + properties: + totalCount: + description: 'The total number of URLs.' + type: integer + items: + description: 'The value objects found for the query.' + type: URL[] +Section: + description: 'This class represents a Section.' + type: BaseObject + properties: + sectionId: + description: 'ID of the Section.' + type: integer + identifier: + description: 'Unique identifier of the Section.' + type: string + name: + description: 'Name of the Section.' + type: string +SectionInput: + type: BaseObject + +SectionCreateStruct: + description: 'This class represents a Section.' + type: object +SectionLimitation: + type: RoleLimitation +SectionList: + description: 'This class represents a Section list.' + type: BaseObject + properties: + Section: + type: Section[] +SectionStruct: + description: 'This class is used for updating Section meta data.' + type: object + properties: + identifier: + description: 'If set the Unique identifier of the Section is changes. Needs to be a unique Section->identifier string value.' + type: string + name: + description: 'If set the name of the Section is changed.' + type: string +SectionUpdateStruct: + description: 'This class is used to provide data for updating a Section. At least one property has to be set.' + type: object +Session: + description: 'Value for session.' + type: BaseObject + properties: + name: + description: 'Name.' + type: string + identifier: + description: 'Identifier.' + type: string + csrfToken: + description: 'csrfToken.' + type: string + User: + description: 'User.' + type: BaseObject +SortClause: + description: 'This class is the base for SortClause classes, used to set sorting of content queries.' + type: object + properties: + direction: + description: 'Sort direction. One of Query::SORT_ASC or Query::SORT_DESC.' + type: string + target: + description: 'Sort target, high level: section_identifier, attribute_value, etc.' + type: string + targetData: + description: 'Extra target data, required by some sort clauses, field for instance.' + type: Target +Specifications: + description: 'This class is used by Criteria to describe which operators they support. Instances of this class are returned in an array by the {@see Criterion::getSpecifications()} method.' + type: object + properties: + operator: + description: 'Specified operator, as one of the Operator::* constants.' + type: string + valueFormat: + description: 'Format supported for the Criterion value, either {@see self::FORMAT_SINGLE} for single or {@see self::FORMAT_ARRAY} for multiple.' + type: string + valueTypes: + description: 'Accepted values types, specifying what type of variables are accepted as a value. Criterion input value type description constants: const TYPE_INTEGER = 1; const TYPE_STRING = 2; const TYPE_BOOLEAN = 4.' + type: integer + valueCount: + description: 'Limitation on the number of items as the value. Only usable if {@see $valueFormat} is {@see self::FORMAT_ARRAY}.' + type: integer +StringLengthValidator: + description: 'Validator for checking min. and max. length of strings.' + type: object + properties: + maxStringLength: + description: 'Maximum length of strings.' + type: integer + minStringLength: + description: 'Minimum length of strings.' + type: integer +SubtreeLimitation: + type: RoleLimitation +Target: + description: 'Struct that stores extra target information for a SortClause object.' + type: object +Translation: + description: 'Base class for translation messages extends ValueObject.' + type: object +Trash: + type: BaseObject + properties: + TrashItem: + type: TrashItem[] +TrashItem: + description: 'This class represents a trash item, which is actually a trashed Location.' + type: Location +# TODO: Powinno byc w odpowiedzi: +# properties: +# trashed: +# description: 'Trashed integer.' +# type: datetime +Unlink: + description: 'Unlink a Content Type group from a Content Type.' + type: object + properties: + _href: + type: string + _method: + enum: [] +URL: + description: 'This class represents a URL in the Repository.' + type: object + properties: + id: + description: 'The unique ID of the URL.' + type: integer + url: + description: 'URL itself e.g. "http://ez.no".' + type: string + isValid: + description: 'Is URL valid?' + type: boolean + lastChecked: + description: 'Date of last check.' + type: datetime + created: + description: 'Creation date.' + type: datetime + modified: + description: 'Modified date.' + type: datetime + +UrlAliasCreate: + type: BaseObject + +UrlAlias: + description: 'This class represents a URL alias in the Repository.' + type: BaseObject + properties: + _id: + description: 'A unique identifier for the alias.' + type: integer + _type: + description: 'The type of the URL Alias i.e. one of URLAlias::LOCATION=0, URLAlias::RESOURCE=1, URLAlias::VIRTUAL=2.' + enum: [] + resource: + description: 'If type = URLAlias::LOCATION it is a Location ID otherwise a string (e.g. /content/search).' + type: any + path: + description: 'The full path of the alias.' + type: + languageCodes: + description: 'The languageCodes for which this path is valid.' + type: string[] + alwaysAvailable: + description: 'Fallback indicator for other languages.' + type: boolean + isHistory: + description: 'Indicates that this alias was autogenerated for an in the meanwhile archived version of the content.' + type: boolean + forward: + description: 'Indicates if the URL should be redirected.' + type: boolean + custom: + description: 'If false this alias was autogenerated otherwise manually created.' + type: boolean +UrlAliasRefList: + description: 'List of URL alias in the Repository.' + type: BaseObject + properties: + URLAlias: + type: URLAlias[] +UrlQuery: + description: 'This class is used to perform a URL query.' + type: object + properties: + filter: + description: 'The Query filter.' + type: Criterion + sortClauses: + description: 'Query sorting clauses.' + type: SortClause[] + offset: + description: 'Query offset. Sets the offset for search hits, used for paging the results.' + type: integer + limit: + description: 'Query limit. Limit for number of search hits to return. If value is `0`, search query will not return any search hits, useful for doing a count.' + type: integer +UsageSearchResult: + description: 'This class is used to find usage search results.' + type: object + properties: + totalCount: + description: 'The total number of content objects using URL.' + type: integer + items: + description: 'The value objects found for the query.' + type: ContentInfo[] +URLUpdateStruct: + description: 'Structure used to update URL data.' + type: object + properties: + url: + description: 'URL itself e.g. "http://ez.no".' + type: string + isValid: + description: 'Is URL valid?' + type: boolean + lastChecked: + description: 'Modified date.' + type: datetime +UrlWildcard: + description: 'This class represents a URL alias in the Repository.' + type: BaseObject + properties: + id: + description: 'The unique ID.' + type: integer + sourceUrl: + description: 'The source URL.' + type: string + destinationUrl: + description: 'The destination URL containing placeholders e.g. /destination/{1}.' + type: string + forward: + description: 'Indicates if the URL is redirected or not.' + type: boolean + +UrlWildcardCreate: + type: BaseObject + +UrlWildcardList: + description: 'List of URL alias in the Repository.' + type: BaseObject + properties: + UrlWildcard: + type: UrlWildcard[] +URLWildcardTranslationResult: + description: 'This class represents a result of a translated URL wildcard which is not an URLAlias.' + type: object + properties: + uri: + description: 'The found resource URI.' + type: string + forward: + description: 'Indicates if the URL is redirected or not.' + type: boolean +User: + description: 'This class represents a User value.' + type: BaseObject + properties: + _id: + description: 'Unique ID of the Content Type.' + type: integer + _remoteId: + description: 'Remote ID of the Content Type.' + type: string + ContentType: + description: 'This class represents a Content Type.' + type: BaseObject + name: + description: 'Name of the domain object in a given language.' + type: string + Versions: + description: 'Returns the VersionInfo for this version.' + type: BaseObject + Section: + description: 'The Section to which the Content item is assigned.' + type: BaseObject + MainLocation: + description: 'Main Location of the object.' + type: BaseObject + Locations: + description: 'Locations of the object.' + type: BaseObject + Groups: + description: 'Group User of the Content Type.' + type: BaseObject + Owner: + description: 'The owner of the Content item.' + type: BaseObject + publishDate: + description: 'Content publication date.' + type: datetime + lastModificationDate: + description: 'Content modification date.' + type: datetime + mainLanguageCode: + description: 'The main language code of the Content item.' + type: string + alwaysAvailable: + description: "Indicates if the Content item is shown in the main language if it's not present in an other requested language." + type: boolean + Version: + description: 'Returns the VersionInfo for this version.' + type: Version + login: + description: 'User login.' + type: string + email: + description: 'User email address.' + type: string + enabled: + description: 'Flag to Signal if User is enabled or not. User can not login if false.' + type: boolean + UserGroups: + description: 'User groups.' + type: BaseObject + Roles: + description: 'Roles.' + type: BaseObject +# maxLogin: +# description: 'Max number of time User is allowed to login.' +# type: integer +UserCreateStruct: + description: 'This class is used to create a new User in the Repository.' + type: object + properties: + login: + description: 'User login.' + required: true + type: string + email: + description: 'User email address.' + required: true + type: string + password: + description: 'The plain password.' + required: true + type: string + enabled: + description: 'Indicates if the User is enabled after creation.' + type: boolean +UserGroup: + description: 'This class represents a User group.' + type: BaseObject + properties: + Unassign: + type: Unlink +UserGroupCreateStruct: + description: 'This class is used to create a new User group in the Repository, extends ContentCreateStruct.' + type: object + +UserGroupRefList: + description: 'Returns a list of the sub groups.' + type: BaseObject + properties: + UserGroup: + description: 'This class represents a User group.' + type: UserGroup +UserGroupRoleAssignment: + description: 'This class represents a User group to role assignment.' + type: object + properties: + UserGroup: + description: 'Returns the User group to which the role is assigned to.' + type: UserGroup + +UserGroupUpdate: + type: BaseObject + +UserGroupUpdateStruct: + description: 'This class is used to update a User group in the Repository.' + type: object + properties: + contentUpdateStruct: + description: 'The update structure for the profile content.' + type: ContentUpdateStruct + contentMetadataUpdateStruct: + description: 'The update structure for the profile meta data.' + type: ContentMetadataUpdateStruct +UserGroupList: + type: BaseObject + +UserList: + description: 'This class represents a list of users.' + type: BaseObject + properties: + User: + type: User[] +UserPreference: + description: 'This class represents a User preference value.' + type: object + properties: + name: + description: 'Name of User preference e.g. timezone.' + type: string + value: + description: 'Value of User preference e.g. America/New_York.' + type: string +UserPreferenceList: + description: 'List of User preferences.' + type: object + properties: + totalCount: + description: 'The total number of User preferences.' + type: integer + items: + description: 'List of User preferences.' + type: UserPreference[] +UserPreferenceSetStruct: + description: 'This class creates User preferences.' + type: object + properties: + name: + description: 'Name of User preference e.g. timezone.' + type: string + value: + description: 'Value of User preference e.g. America/New_York.' + type: string +UserRefList: + description: 'Returns a list of the users.' + type: BaseObject + properties: + User: + description: 'This class represents a User.' + type: BaseObject[] +UserRoleAssignment: + description: 'This class represents a User to role assignment.' + type: object + properties: + User: + description: 'Returns the User to which the role is assigned to.' + type: User +UserTokenUpdateStruct: + description: 'This class is used to update a User token in the Repository.' + type: object + properties: + hashKey: + description: 'Hash key date for User account.' + type: string + time: + description: 'Time to which the token is valid.' + type: datetime +UserUpdateStruct: + description: 'This class is used to update a User in the Repository.' + type: object + properties: + email: + description: 'If set the email address is updated with this value.' + type: string + password: + description: 'If set the password is updated with this plain password.' + type: string + enabled: + description: 'Flag to Signal if User is enabled or not. If set the enabled status is changed to this value.' + type: boolean + maxLogin: + description: 'Max number of time User is allowed to login. If set the maximum number of login attempts is changed to this value.' + type: integer + contentUpdateStruct: + description: 'The update structure for the profile content.' + type: ContentUpdateStruct + contentMetadataUpdateStruct: + description: 'The update structure for the profile meta data.' + type: ContentMetadataUpdateStruct +Value: + description: 'Struct that stores extra value information for a Criterion object.' + type: object + properties: + _languageCode: + description: 'Language code.' + type: string + '#text': + description: 'Content Type description.' + type: string +ValueObject: + description: 'The base class for all value objects and structs. Supports readonly properties by marking them as protected.' + type: object + properties: + Properties: + description: 'Function where list of properties are returned.' + type: array +Version: + description: 'Returns the VersionInfo for this version.' + type: BaseObject + properties: + VersionInfo: + description: 'VersionInfo for this version.' + type: VersionInfo + Fields: + description: 'Fields.' + type: Field + Relations: + description: 'Relations of the user.' + type: BaseObject + properties: + Relation: + type: Relation[] +VersionInfo: + description: 'This class holds version information data. It also contains the corresponding {@link Content} to which the version belongs to.' + type: object + properties: + id: + description: 'Version ID.' + type: integer + versionNo: + description: 'Version number. In contrast to {@link $id}, this is the version number, which only increments in scope of a single Content item.' + type: integer + status: + description: 'One of: VersionInfo::STATUS_DRAFT=0, VersionInfo::STATUS_PUBLISHED=1, VersionInfo::STATUS_ARCHIVED=3.' + enum: [] + modificationDate: + description: 'The last modified date of this version.' + type: datetime + Creator: + description: 'Creator of the version, in the search API this is referred to as the modifier of the published content.' + type: BaseObject + creationDate: + description: 'Content creation date.' + type: datetime + initialLanguageCode: + description: 'The language code which is used for labeling a translation.' + type: string + languageCodes: + description: 'List of languages in this version. Reflects which languages fields exists in for this version.' + type: string[] + VersionTranslationInfo: + description: 'Translation information.' + type: VersionTranslationInfo + names: + description: 'Names.' + type: Value + Content: + description: 'Represents a Content item in a specific version.' + type: BaseObject +VersionItem: + description: 'Version of content.' + type: BaseObject + properties: + Version: + description: 'Returns the VersionInfo for this version.' + type: BaseObject + VersionInfo: + type: VersionInfo +VersionList: + description: 'List of all versions of the content.' + type: BaseObject + properties: + VersionItem: + type: VersionItem[] +VersionTranslationInfo: + description: 'Translation information.' + type: object + properties: + _media-type: + type: string # Always "application/vnd.ez.api.VersionTranslationInfo+json" + Language: + type: LanguageCode[] + +VersionUpdate: + type: BaseObject + +View: #TODO outdated example + description: 'View.' + type: BaseObject + properties: + identifier: + description: 'Content identifier.' + type: string + +ContentTypeCreate: + type: BaseObject + +ContentTypeUpdate: + type: BaseObject + +ViewInput: + type: BaseObject + +UserCreate: + type: BaseObject + +UserGroupCreate: + type: BaseObject + +RoleAssignInput: + type: BaseObject + +UserUpdate: + type: BaseObject + +RoleInput: + type: BaseObject + +PolicyCreate: + type: BaseObject + +PolicyUpdate: + type: BaseObject + +SessionInput: + type: BaseObject + +BaseCommerceResponse: + type: object + properties: + _media-type: + type: string + +Basket: + type: BaseCommerceResponse + +BasketLineData: + type: object + properties: + quantity: + type: string + required: true + isVariant: + type: string + variantCode: + type: string + sku: + type: string + required: true + dataMap: + type: array + required: false + +PartyTypeInput: + type: object + +Party: + type: object + properties: + PartyIdentification: array + PartyName: array + PostalAddress: object + Contact: object + Person: object + SesExtension: object + +ShippingMethodDataResponse: + type: BaseCommerceResponse + properties: + shippingMethods: object + defaultMethod: string + shippingMethodsSorted: array + +PaymentMethodDataResponse: + type: BaseCommerceResponse + +ShippingAddressesResponse: + type: BaseCommerceResponse + +BasketListResponse: + type: BaseCommerceResponse + properties: + Parties: Party[] + +ValidationResponse: + type: BaseCommerceResponse + properties: + _media_type: string + messages: array + +CountrySelectionResponse: + type: BaseCommerceResponse + properties: + _media_type: string + countryOptions: object + +ShippingMethodData: + type: object + properties: + shippingMethod: + type: string + required: true + +PaymentMethodData: + type: object + +VoucherData: + type: object + +CustomerPriceData: + type: object + properties: + Meta: + type: object + required: false + Items: array + +BasketHeaderData: + type: object + properties: + value: string + +BasketLinesData: + type: object + properties: + sku: string + quantity: string + basketLineId: integer + lineNumber: integer + +PriceResponse: + type: BaseCommerceResponse diff --git a/docs/api/rest_api_reference/input/ez-user-groups.raml b/docs/api/rest_api_reference/input/ez-user-groups.raml new file mode 100644 index 0000000000..a2ace465e9 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-groups.raml @@ -0,0 +1,360 @@ +get: + displayName: Load User Groups + description: Loads User Groups for either an an ID or a remote ID or a Role. + queryParameters: + roleId: + description: Lists User Groups assigned to the given Role (e.g. GET /user/groups?roleId=1). + id: + description: Retrieves the User Groups for the given ID. + remoteID: + description: Retrieves the User Groups for the given remote ID. + headers: + Accept: + description: UserGroupList - If set, the User Group List is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupList+xml + application/vnd.ez.api.UserGroupList+json + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupList+xml: + type: UserGroupList + example: !include examples/user/groups/GET/UserGroupList.xml.example + application/vnd.ez.api.UserGroupList+json: + type: UserGroupList + example: !include examples/user/groups/GET/UserGroupList.json.example + 401: + description: Error - the user has no permission to read User Groups. +/root: + get: + # Currently does not work + displayName: Get root User Group + description: Redirects to the root User Group. + responses: + 301: + description: Moved permanently. +/{path}: + get: + displayName: Load User Group + description: Loads User Groups for the given {path}. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - loads User Groups. + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/GET/UserGroup.xml.example + application/vnd.ez.api.UserGroup+json: + type: UserGroup + example: !include examples/user/groups/path/GET/UserGroup.json.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + patch: + displayName: Update User Group + description: Updates a User Group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + Content-Type: + description: The UserGroupUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupUpdate+json + application/vnd.ez.api.UserGroupUpdate+xml + If-Match: + description: Performs the PATCH only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + body: + application/vnd.ez.api.UserGroupUpdate+xml: + type: UserGroupUpdate + example: !include examples/user/groups/path/PATCH/UserGroupUpdate.xml.example + application/vnd.ez.api.UserGroupUpdate+json: + type: UserGroupUpdate + example: !include examples/user/groups/path/PATCH/UserGroupUpdate.json.example + responses: + 200: + description: OK - updated User Group. + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/PATCH/UserGroup.xml.example + application/vnd.ez.api.UserGroup+json: + type: UserGroup + example: !include examples/user/groups/path/PATCH/UserGroup.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the User Group. + 412: + description: Error - if the current ETag does not match with the one provided in the If-Match header. + delete: + displayName: Delete User Group + description: The given User Group is deleted. + responses: + 204: + description: No content - the given User Group is deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + 403: + description: Error - the User Group is not empty. + move: + displayName: Move User Group + description: Moves the User Group to another parent. MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: A parent group resource to which the Location is moved. + responses: + 201: + description: Created - moves the User Group to another parent. + 401: + description: Error - the user is not authorized to update the User Group. + 403: + description: Error - the new parent does not exist. + 404: + description: Error - the User Group does not exist. + /users: + get: + displayName: Load Users of Group + description: Loads the Users of the Group with the given ID. + queryParameters: + limit: + description: Only 'limit' items will be returned started by offset. + offset: + description: Offset of the result set. + headers: + Accept: + description: UserList - If set, the User list returned in XML or JSON format. UserRefList - If set, the link list of Users returned in XML or JSON format. + example: | + application/vnd.ez.api.UserList+xml + application/vnd.ez.api.UserList+json + application/vnd.ez.api.UserRefList+xml + application/vnd.ez.api.UserRefList+json + responses: + 200: + description: OK - the Users of the Group with the given ID. + body: + application/vnd.ez.api.UserRefList+xml: + type: UserRefList + example: !include examples/user/groups/id/users/GET/UserRefList.xml.example + application/vnd.ez.api.UserRefList+json: + type: UserRefList + example: !include examples/user/groups/id/users/GET/UserRefList.json.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + post: + displayName: Create User + description: Creates a new User in the given Group. + headers: + Accept: + description: If set, the new User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + Content-Type: + description: The UserCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserCreate+json + application/vnd.ez.api.UserCreate+xml + body: + application/vnd.ez.api.UserCreate+xml: + type: UserCreate + example: !include examples/user/groups/path/users/POST/UserCreate.xml.example + application/vnd.ez.api.UserCreate+json: + type: UserCreate + example: !include examples/user/groups/path/users/POST/UserCreate.json.example + responses: + 201: + body: + application/vnd.ez.api.User+xml: + type: User + example: !include examples/user/groups/path/users/POST/User.xml.example + application/vnd.ez.api.User+json: + type: User + example: !include examples/user/groups/path/users/POST/User.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this User. + 403: + description: Error - a User with the same login already exists. + 404: + description: Error - the Group with the given ID does not exist. + /subgroups: + get: + displayName: Load subgroups + description: Returns a list of the subgroups. + queryParameters: + limit: + description: The number of Locations returned. + offset: + description: The offset of the result set. + headers: + Accept: + description: UserGroupList - If set, the User Group list is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupList+xml + application/vnd.ez.api.UserGroupList+json + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + description: OK - list of the subgroups. + body: + application/vnd.ez.api.UserGroupRefList+xml: + type: UserGroupRefList + example: !include examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example + application/vnd.ez.api.UserGroupRefList+json: + type: UserGroupRefList + example: !include examples/user/groups/id/subgroups/GET/UserGroupRefList.json.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + post: + displayName: Create User Group + description: Creates a new User Group under the given parent. To create a top level group use '/user/groups/subgroups'. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + Content-Type: + description: The UserGroupCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupCreate+json + application/vnd.ez.api.UserGroupCreate+xml + body: + application/vnd.ez.api.UserGroupCreate+xml: + type: UserGroupCreate + example: !include examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example + application/vnd.ez.api.UserGroupCreate+json: + type: UserGroupCreate + example: !include examples/user/groups/path/subgroups/POST/UserGroupCreate.json.example + responses: + 201: + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/subgroups/POST/UserGroup.xml.example + application/vnd.ez.api.UserGroup+json: + type: UserGroup + example: !include examples/user/groups/path/subgroups/POST/UserGroup.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this User Group. + /roles: + get: + displayName: Load Roles for User Group + description: Returns a list of all Roles assigned to the given User Group. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example + #application/vnd.ez.api.RoleAssignmentList+json: + #type: RoleAssignmentList + #example: !include examples/user/groups/path/roles/GET/RoleAssignmentList.json.example + 400: + description: Error - the user has no permission to read Roles. + post: + displayName: Assign Role to User Group + description: Assigns a Role to a User Group. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + Content-Type: + description: The RoleAssignInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignInput+json + application/vnd.ez.api.RoleAssignInput+xml + body: + application/vnd.ez.api.RoleAssignInput+xml: + type: RoleAssignInput + example: !include examples/user/groups/path/roles/POST/RoleAssignInput.xml.example + application/vnd.ez.api.RoleAssignInput+json: + type: RoleAssignInput + example: !include examples/user/groups/path/roles/POST/RoleAssignInput.json.example + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example + application/vnd.ez.api.RoleAssignmentList+json: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/POST/RoleAssignmentList.json.example + 400: + description: Error - validation of limitation in RoleAssignInput fails. + 401: + description: Error - the user is not authorized to assign this Role. + /{roleId}: + get: + displayName: Load User Group Role Assignment + description: Returns a Role assignment of the given User Group. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignment+xml + application/vnd.ez.api.RoleAssignment+json + responses: + 200: + description: OK - returns a Role assignment of the given User Group. + body: + application/vnd.ez.api.RoleAssignment+xml: + type: RoleAssignment + example: !include examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example + application/vnd.ez.api.RoleAssignment+json: + type: RoleAssignment + example: !include examples/user/groups/path/roles/role_id/GET/RoleAssignment.json.example + + 401: + description: Error - the user has no permission to read Roles. + delete: + displayName: Unassign Role from User Group + description: The given Role is removed from the User or User Group. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example + application/vnd.ez.api.RoleAssignmentList+json: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.json.example + 401: + description: Error - the user is not authorized to delete this Role assignment. diff --git a/docs/api/rest_api_reference/input/ez-user-policies.raml b/docs/api/rest_api_reference/input/ez-user-policies.raml new file mode 100644 index 0000000000..148223c86a --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-policies.raml @@ -0,0 +1,24 @@ +get: + displayName: List Policies for User + description: Search all Policies which are applied to a given User. + queryParameters: + userId: + description: The User ID. + headers: + Accept: + description: If set, the Policy list is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyList+xml + application/vnd.ez.api.PolicyList+json + responses: + 200: + description: OK - Policies which are applied to a given User. + body: + application/vnd.ez.api.PolicyList+xml: + type: PolicyList + example: !include examples/user/policies/GET/PolicyList.xml.example + application/vnd.ez.api.PolicyList+json: + type: PolicyList + example: !include examples/user/policies/GET/PolicyList.json.example + 401: + description: Error - the user has no permission to read Roles. diff --git a/docs/api/rest_api_reference/input/ez-user-roles.raml b/docs/api/rest_api_reference/input/ez-user-roles.raml new file mode 100644 index 0000000000..e6bb7be6bf --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-roles.raml @@ -0,0 +1,389 @@ +get: + displayName: Load Roles + description: Returns a list of all Roles. + queryParameters: + identifier: + description: Restricts the result to a list containing the Role with the given identifier. If the Role is not found an empty list is returned. + offset: + description: The offset of the result set. + type: integer + limit: + description: Only limit items will be returned started by offset. + type: integer + headers: + Accept: + description: If set, the user list returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleList+xml + application/vnd.ez.api.RoleList+json + responses: + 200: + description: OK - list of all Roles. + body: + application/vnd.ez.api.RoleList+xml: + type: RoleList + example: !include examples/user/roles/GET/RoleList.xml.example + application/vnd.ez.api.RoleList+json: + type: RoleList + example: !include examples/user/roles/GET/RoleList.json.example + 401: + description: Error - the user has no permission to read Roles. +post: + displayName: Create Role or Role draft + description: Creates a new Role or Role draft. + queryParameters: + publish: + type: boolean + description: If true the Role is published after creation. + default: true + headers: + Accept: + description: If set, the new user is returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + application/vnd.ez.api.RoleDraft+xml + application/vnd.ez.api.RoleDraft+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON. + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: !include examples/user/roles/POST/RoleInput.xml.example + application/vnd.ez.api.RoleInput+json: + type: RoleInput + example: !include examples/user/roles/POST/RoleInput.json.example + responses: + 201: + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/POST/Role.xml.example + application/vnd.ez.api.Role+json: + type: Role + example: !include examples/user/roles/POST/Role.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create a Role or a Role draft. +/{id}: + get: + displayName: Load Role + description: Loads a Role for the given ID. + headers: + Accept: + description: If set, the user list returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - Role for the given ID. + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/GET/Role.xml.example + application/vnd.ez.api.Role+json: + type: Role + example: !include examples/user/roles/id/GET/Role.json.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - the Role does not exist. + post: + displayName: Create Role Draft + description: Creates a new Role draft from an existing Role. + headers: + Accept: + description: If set, the new user is returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON. + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + responses: + 201: + body: + application/vnd.ez.api.RoleDraft+xml: + type: RoleDraft + example: !include examples/user/roles/id/POST/RoleDraft.xml.example + application/vnd.ez.api.RoleDraft+json: + type: RoleDraft + example: !include examples/user/roles/id/POST/RoleDraft.json.example + 401: + description: Error - the user is not authorized to create a Role or a Role draft + patch: + displayName: Update Role + description: Updates a Role. PATCH or POST with header X-HTTP-Method-Override PATCH + headers: + Accept: + description: If set, the new user is returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + If-Match: + description: ETag Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: !include examples/user/roles/id/PATCH/RoleInput.xml.example + application/vnd.ez.api.RoleInput+json: + type: RoleInput + example: !include examples/user/roles/id/PATCH/RoleInput.json.example + responses: + 200: + description: OK - Role updated + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/PATCH/Role.xml.example + application/vnd.ez.api.Role+json: + type: Role + example: !include examples/user/roles/id/PATCH/Role.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the Role. + 412: + description: Error - the current ETag does not match with the provided one in the If-Match header. + delete: + displayName: Delete Role + description: The given Role and all assignments to Users or User Groups are deleted. + responses: + 204: + description: No Content. + 401: + description: Error - the User is not authorized to delete this Role. + /draft: + get: + displayName: Load Role draft + description: Loads a Role draft by original Role ID. + headers: + Accept: + description: If set, the User list returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - Role draft by original Role ID. + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/draft/GET/Role.xml.example + application/vnd.ez.api.Role+json: + type: Role + example: !include examples/user/roles/id/draft/GET/Role.json.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - there is no draft or Role with the given ID. + patch: + displayName: Update Role draft + description: Updates a Role draft. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Role is returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + If-Match: + description: Performs a PATCH only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: !include examples/user/roles/id/draft/PATCH/RoleInput.xml.example + application/vnd.ez.api.RoleInput+json: + type: RoleInput + example: !include examples/user/roles/id/draft/PATCH/RoleInput.json.example + responses: + 200: + description: OK - Role draft updated. + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/draft/PATCH/Role.xml.example + application/vnd.ez.api.Role+json: + type: Role + example: !include examples/user/roles/id/draft/PATCH/Role.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the Role. + 404: + description: Error - there is no draft or Role with the given ID. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + publish: + displayName: Publish Role draft + description: Publishes a Role draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to publish this Content Type draft. + 403: + description: Error - the Content Type draft is not complete e.g. there is no Field definition provided. + 404: + description: Error - there is no draft or Role with the given ID. + delete: + displayName: Delete Role draft + description: The given Role draft is deleted. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to delete this Role. + /policies: + get: + displayName: Load Policies + description: Loads Policies for the given Role. + headers: + Accept: + description: If set, the Policy list is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyList+xml + application/vnd.ez.api.PolicyList+json + responses: + 200: + body: + application/vnd.ez.api.PolicyList+xml: + type: PolicyList + example: !include examples/user/roles/id/policies/GET/PolicyList.xml.example + application/vnd.ez.api.PolicyList+json: + type: PolicyList + example: !include examples/user/roles/id/policies/GET/PolicyList.json.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - the Role does not exist. + delete: + displayName: Delete Policies + description: All Policies of the given Role are deleted. + responses: + 204: + description: No Content - all Policies of the given Role are deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + post: + # Currently does not work + displayName: Create Policy + description: Creates a Policy + headers: + Accept: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + Content-Type: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyCreate+xml + application/vnd.ez.api.PolicyCreate+json + body: + application/vnd.ez.api.PolicyCreate+xml: + type: PolicyCreate + example: !include examples/user/roles/id/policies/POST/PolicyCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/POST/Policy.xml.example + 400: + description: Error - the input does not match the input schema definition or validation of limitation in PolicyCreate fails. + 401: + description: Error - the user is not authorized to create the Policy. + 404: + description: Error - the Role does not exist. + /{id}: + patch: + # Currently does not work + displayName: Update Policy + description: Updates a Policy. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + Content-Type: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyUpdate+xml + application/vnd.ez.api.PolicyUpdate+json + If-Match: + description: Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + body: + application/vnd.ez.api.PolicyUpdate+xml: + type: PolicyUpdate + example: !include examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/id/PATCH/Policy.xml.example + 400: + description: Error - the input does not match the input schema definition or validation of limitation in PolicyUpdate fails. + 401: + description: Error - the user is not authorized to update the Policy. + 404: + description: Error - the Role does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + get: + displayName: Load Policy + description: Loads a Policy for the given module and function. + headers: + Accept: + description: If set, the Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/id/GET/Policy.xml.example + application/vnd.ez.api.Policy+json: + type: Policy + example: !include examples/user/roles/id/policies/id/GET/Policy.json.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - the Role or Policy does not exist. + delete: + displayName: Delete Policy + description: Deletes given Policy. + responses: + 204: + description: No Content - the given Policy is deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + 404: + description: Error - the Role or Policy does not exist. diff --git a/docs/api/rest_api_reference/input/ez-user-sessions.raml b/docs/api/rest_api_reference/input/ez-user-sessions.raml new file mode 100644 index 0000000000..da910cc4f3 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-sessions.raml @@ -0,0 +1,84 @@ +post: + displayName: Create session (login a User) + description: Performs a login for the user or checks if session exists and returns the session and session cookie. The client will need to remember both session name/ID and CSRF token as this is for security reasons not exposed via GET. + headers: + Accept: + description: If set, the session is returned in XML or JSON format. + example: | + application/vnd.ez.api.Session+xml + application/vnd.ez.api.Session+json + Content-Type: + description: The SessionInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.SessionInput+xml + application/vnd.ez.api.SessionInput+json + Cookie: + description: Only needed for session's checking {sessionName}={sessionID}. + X-CSRF-Token: + description: Only needed for session's checking. The {csrfToken} needed on all unsafe http methods with session. + body: + application/vnd.ez.api.SessionInput+xml: + type: SessionInput + example: !include examples/user/sessions/POST/SessionInput.xml.example + application/vnd.ez.api.SessionInput+json: + type: SessionInput + example: !include examples/user/sessions/POST/SessionInput.json.example + responses: + 200: + description: Session already exists. + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/POST/Session.json.example + 201: + description: Session is created. + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/POST/Session.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the authorization failed. + 409: + description: Error - header contained a session cookie but different user was authorized. +/{sessionId}: + delete: + displayName: Delete session (logout a User) + description: The user session is removed i.e. the user is logged out. + headers: + Cookie: + description: "{sessionName}={sessionID}" + X-CSRF-Token: + description: The {csrfToken} needed on all unsafe http methods with session. + responses: + 204: + description: OK - session deleted. + 404: + description: Error - the session does not exist. + /refresh: + post: + displayName: Refresh session + description: Get the session's User information. + headers: + Cookie: + description: "{sessionName}={sessionID}" + X-CSRF-Token: + description: The {csrfToken} needed on all unsafe http methods with session. + responses: + 200: + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/session_id/refresh/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/session_id/refresh/POST/Session.json.example + 404: + description: Error - the session does not exist. diff --git a/docs/api/rest_api_reference/input/ez-user-token.raml b/docs/api/rest_api_reference/input/ez-user-token.raml new file mode 100644 index 0000000000..044e33a5b7 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-token.raml @@ -0,0 +1,35 @@ +/jwt: + post: + displayName: Create JWT token + description: Creates JWT authentication token. + headers: + Accept: + description: If set, the token is returned in XML or JSON format. + example: | + application/vnd.ez.api.JWT+xml + application/vnd.ez.api.JWT+json + Content-Type: + description: The SessionInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.JWTInput+xml + application/vnd.ez.api.JWTInput+json + body: + application/vnd.ez.api.JWTInput+xml: + type: JWTInput + example: !include examples/user/token/jwt/POST/JWTInput.xml.example + application/vnd.ez.api.JWTInput+json: + type: JWTInput + example: !include examples/user/token/jwt/POST/JWTInput.json.example + responses: + 200: + body: + application/vnd.ez.api.JWT+xml: + type: JWT + example: !include examples/user/token/jwt/POST/JWT.xml.example + application/vnd.ez.api.JWT+json: + type: JWT + example: !include examples/user/token/jwt/POST/JWT.json.example + 401: + description: Error - Unauthorized + + diff --git a/docs/api/rest_api_reference/input/ez-user-users.raml b/docs/api/rest_api_reference/input/ez-user-users.raml new file mode 100644 index 0000000000..1b648f6ae2 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-user-users.raml @@ -0,0 +1,305 @@ +get: + displayName: List Users + description: Load Users either for a given remote ID or Role. + queryParameters: + roleId: + description: Lists Users assigned to the given Role (e.g. GET /user/users?roleId=/user/roles/1). + remoteId: + description: Retrieves the User for the given remote ID (e.g. GET /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). + login: + description: Retrieves the User for the given login (e.g. GET /user/users?login=editor). + email: + description: Lists Users with the given email (e.g. GET /user/users?email=editor@example.com). + headers: + Accept: + description: UserList - If set, the User list is returned in XML or JSON format. UserRefList - If set, the link list of Users is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserList+xml + application/vnd.ez.api.UserList+json + application/vnd.ez.api.UserRefList+xml + application/vnd.ez.api.UserRefList+json + responses: + 200: + description: OK - Loads Users either for a given remote ID or Role. + body: + application/vnd.ez.api.UserRefList+xml: + type: UserRefList + example: !include examples/user/users/GET/UserRefList.xml.example + application/vnd.ez.api.UserList+json: + type: UserList + example: !include examples/user/users/GET/UserList.json.example + 404: + description: If there are no visible Users matching the filter. +head: + displayName: Verify Users + description: Verifies if there are Users matching given filter. + queryParameters: + roleId: + description: Checks if there are Users directly assigned to the given Role (e.g. HEAD /user/users?roleId=/user/roles/1). + remoteId: + description: Checks if there is a User for the given remote ID (e.g. HEAD /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). + login: + description: Checks if there is a User for the given login (e.g. HEAD /user/users?login=editor). + email: + description: Checks if there is a User with the given email (e.g. HEAD /user/users?email=editor@example.com). + responses: + 200: + description: OK - verifies if there are Users matching the given filter. + 404: + description: Error - there are no visible Users matching the filter. +/{userId}: + get: + displayName: Load User + description: Loads User with the given ID. + headers: + Accept: + description: If set, the User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - the User with the given ID. + body: + application/vnd.ez.api.User+xml: + type: UserList + example: !include examples/user/users/user_id/GET/User.xml.example + application/vnd.ez.api.User+json: + type: UserList + example: !include examples/user/users/user_id/GET/User.json.example + 401: + description: Error - the user has no permission to read Users. + 404: + description: Error - the User does not exist. + patch: + displayName: Update User + description: Updates a User. + headers: + Accept: + description: If set, the updated User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + Content-Type: + description: The UserUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserUpdate+json + application/vnd.ez.api.UserUpdate+xml + If-Match: + description: Performs a PATCH only if the specified ETag is the current one. + example: ETag + body: + application/vnd.ez.api.UserUpdate+xml: + type: UserUpdate + example: !include examples/user/users/user_id/PATCH/UserUpdate.xml.example + application/vnd.ez.api.UserUpdate+json: + type: UserUpdate + example: !include examples/user/users/user_id/PATCH/UserUpdate.json.example + responses: + 200: + description: OK - User updated. + body: + application/vnd.ez.api.User+xml: + type: User + example: !include examples/user/users/user_id/PATCH/User.xml.example + application/vnd.ez.api.User+json: + type: User + example: !include examples/user/users/user_id/PATCH/User.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the User. + 404: + description: Error - the User does not exist. + 412: + description: Error - the current ETag does not match with the provided one in the If-Match header. + delete: + displayName: Delete User + description: Deletes the given User. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to delete this User. + 403: + description: Error - the user is the same as the authenticated User. + 404: + description: Error - the User does not exist. + /groups: + get: + displayName: Load Groups of User + description: Returns a list of User Groups the User belongs to. The returned list includes the resources for unassigning a User Group if the User is in multiple groups. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of Locations returned. + type: integer + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList+xml: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example + #application/vnd.ez.api.UserGroupRefList+json: + #type: UserGroupRefList + #example: !include examples/user/users/user_id/groups/GET/UserGroupRefList.json.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the user does not exist. + post: + displayName: Assign User Group + description: Assigns the User to a User Group. + queryParameters: + group: + description: The new parent group resource of the User. + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList+xml: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example + application/vnd.ez.api.UserGroupRefList+json: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/POST/UserGroupRefList.json.example + 401: + description: Error - the user is not authorized to assign User Groups. + 403: + description: Error - the new User Group does not exist or the User is already in this group. + 404: + description: Error - the User does not exist. + /{groupId}: + delete: + displayName: Unassign User Group + description: Unassigns the User from a User Group. + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList+xml: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example + 401: + description: Error - the user is not authorized to unassign User Groups. + 403: + description: Error - the User is not in the given group. + 404: + description: Error - the User does not exist. + /roles: + get: + displayName: Load Roles for User + description: Returns a list of all Roles assigned to the given User. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example + application/vnd.ez.api.RoleAssignmentList+json: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/GET/RoleAssignmentList.json.example + 400: + description: Error - the user has no permission to read Roles. + post: + displayName: Assign Role to User + description: Assigns a Role to a user. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + Content-Type: + description: The RoleAssignInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignInput+json + application/vnd.ez.api.RoleAssignInput+xml + body: + application/vnd.ez.api.RoleAssignInput+xml: + type: RoleAssignInput + example: !include examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example + application/vnd.ez.api.RoleAssignInput+json: + type: RoleAssignInput + example: !include examples/user/users/user_id/roles/POST/RoleAssignInput.json.example + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example + application/vnd.ez.api.RoleAssignmentList+json: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/POST/RoleAssignmentList.json.example + 400: + description: Error - validation of limitation in RoleAssignInput fails. + 401: + description: Error - the user is not authorized to assign this Role. + /{roleId}: + get: + displayName: Load User Role Assignment + description: Returns a Role assignment to the given User. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignment+xml + application/vnd.ez.api.RoleAssignment+json + responses: + 200: + description: OK - Role assignment to the given User Group. + body: + application/vnd.ez.api.RoleAssignment+xml: + type: RoleAssignment + example: !include examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example + application/vnd.ez.api.RoleAssignment+json: + type: RoleAssignment + example: !include examples/user/users/user_id/roles/role_id/GET/RoleAssignment.json.example + 401: + description: Error - the user has no permission to read Roles. + delete: + displayName: Unassign Role from User + description: The given Role is removed from the user. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example + application/vnd.ez.api.RoleAssignmentList+json: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.json.example + 401: + description: Error - the user is not authorized to delete this Content Type. diff --git a/docs/api/rest_api_reference/input/ez-views.raml b/docs/api/rest_api_reference/input/ez-views.raml new file mode 100644 index 0000000000..93eff24835 --- /dev/null +++ b/docs/api/rest_api_reference/input/ez-views.raml @@ -0,0 +1,39 @@ +displayName: Views +post: + displayName: Create View + description: | + Executes a query and returns a View including the results. + + View input reflects the criteria model of the public API. + + Refer to [Search Criteria Reference](/en/latest/search/criteria_reference/search_criteria_reference/) + headers: + Accept: + description: The view in XML or JSON format. + example: | + application/vnd.ez.api.View+xml + application/vnd.ez.api.View+json + application/vnd.ez.api.View+xml; version=1.1 + application/vnd.ez.api.View+json; version=1.1 + Content-Type: + description: The view input in XML or JSON format. + example: | + application/vnd.ez.api.ViewInput+xml + application/vnd.ez.api.ViewInput+json + application/vnd.ez.api.ViewInput+xml; version=1.1 + application/vnd.ez.api.ViewInput+json; version=1.1 + body: + application/vnd.ez.api.ViewInput+xml: + type: ViewInput + example: !include examples/views/POST/ViewInput.xml.example + application/vnd.ibexa.api.ViewInput+json: + type: ViewInput + example: !include examples/views/POST/ViewInput.json.example + responses: + 200: + body: + application/vnd.ez.api.View+xml; version=1.1: + type: View + example: !include examples/views/POST/View.xml.v11.example + 400: + description: Error - the input does not match the input schema definition. diff --git a/docs/api/rest_api_reference/input/ez.raml b/docs/api/rest_api_reference/input/ez.raml new file mode 100644 index 0000000000..f17d69c74b --- /dev/null +++ b/docs/api/rest_api_reference/input/ez.raml @@ -0,0 +1,42 @@ +#%RAML 1.0 +--- +title: Ibexa REST API +baseUri: https://ezsystems.github.io/ezplatform-rest-reference +version: v3.3.2 + +/: !include ez-root.raml + +/bookmark: !include ez-bookmark.raml + +/content: + displayName: Managing content + /objects: !include ez-content-objects.raml + /objectstategroups: !include ez-objectstategroups.raml + /locations: !include ez-content-locations.raml + /views: !include ez-content-views.raml + /sections: !include ez-content-sections.raml + /trash: !include ez-content-trash.raml + /urlaliases: !include ez-content-urlaliases.raml + /urlwildcards: !include ez-content-urlwildcards.raml + /typegroups: !include ez-content-typegroups.raml + /types: !include ez-content-types.raml + /assets: !include ez-content-assets.raml + +/calendar: !include ez-calendar.raml + +/views: !include ez-views.raml + +/user: + displayName: Managing users + /groups: !include ez-user-groups.raml + /users: !include ez-user-users.raml + /roles: !include ez-user-roles.raml + /policies: !include ez-user-policies.raml + /sessions: !include ez-user-sessions.raml + /token: !include ez-user-token.raml + +/services: !include ez-services.raml + +/commerce: !include ez-commerce.raml + +types: !include ez-types.raml diff --git a/docs/api/rest_api_reference/input/ez_original.raml b/docs/api/rest_api_reference/input/ez_original.raml new file mode 100644 index 0000000000..2f0378526c --- /dev/null +++ b/docs/api/rest_api_reference/input/ez_original.raml @@ -0,0 +1,3171 @@ +#%RAML 1.0 +--- +title: Ibexa Platform +baseUri: http://localhost/ +version: 2.5 + +/: + displayName: Root resources + get: + displayName: List of root resources + description: Lists the root resources of the Ibexa Platform installation. + headers: + Accept: + description: If set, the list is return in XML or JSON format. + type: string + example: + - application/vnd.ez.api.Root+xml + - application/vnd.ez.api.Root+json + responses: + 200: + body: + application/vnd.ez.api.Root+xml: + type: Root + example: !include examples/GET/Root.xml.example + application/vnd.ez.api.Root+json: + type: Root + example: !include examples/GET/Root.json.example + +/bookmark: + displayName: Managing bookmarks + get: + displayName: List of bookmarks + description: Lists bookmarked Locations for the current user. + queryParameters: + offset: + description: The offset of the result set. + type: integer + default: 0 + limit: + description: The number of returned bookmarks. + type: integer + default: 25 + headers: + Accept: + description: If set, the list is returned in XML or JSON format. + example: + - application/vnd.ez.api.BookmarkList+xml + - application/vnd.ez.api.BookmarkList+json + responses: + 200: + body: + application/vnd.ez.api.BookmarkList+xml: + type: BookmarkList + example: !include examples/bookmark/GET/BookmarkList.xml.example + application/vnd.ez.api.BookmarkList+json: + type: BookmarkList + example: !include examples/bookmark/GET/BookmarkList.json.example + 401: + description: Error - the user is not authorized to list bookmarks. + /{locationId}: + post: + displayName: Create bookmark + description: Add given Location to bookmarks of the current user. + responses: + 201: + description: Created. + 401: + description: Error - the user is not authorized to given Location. + 404: + description: Error - the given Location does not exist. + 409: + description: Error - Location is already bookmarked. + head: + displayName: Check if Location is bookmarked + description: Checks if the given Location is bookmarked by the current user. + responses: + 200: + description: OK - bookmarked. + 401: + description: Error - the user is not authorized for the given Location. + 404: + description: Error - the given Location does not exist / is not bookmarked. + delete: + displayName: Delete bookmark + description: Deletes the given Location from bookmarks of the current user. + responses: + 204: + description: Deleted - no content. + 401: + description: Error - the user is not authorized for the given Location. + 404: + description: Error - the given Location does not exist / is not bookmarked. + +/content: + displayName: Managing content + /objects: + post: + displayName: Create Content item + description: Creates a draft assigned to the authenticated user. If a different user ID is given in the input, the draft is assigned to the given user but this action requires special permissions for the authenticated user (this is useful for content staging where the transfer process does not have to authenticate with the user who created the Content item in the source server). The user needs to publish the Content item if it should be visible. + headers: + Accept: + description: Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.Content+xml + application/vnd.ez.api.Content+json + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + Content-Type: + description: The ContentCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentCreate+xml + application/vnd.ez.api.ContentCreate+json + body: + application/vnd.ez.api.ContentCreate+xml: + type: ContentCreate + example: !include examples/content/objects/POST/ContentCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.Content+xml: + type: Content + example: !include examples/content/objects/POST/Content.xml.example + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/POST/ContentInfo.xml.example + 400: + description: Error - the input does not match the input schema definition or the validation on a field fails. + 401: + description: Error - the user is not authorized to create this Object in this Location. + 404: + description: Error - the parent Location specified in the request body does not exist. + get: + displayName: Load content by remote ID + description: Loads Content item for a given remote ID. + queryParameters: + remoteId: + description: The remote ID of the Content item. If present, the Content item with the given remote ID is returned. + responses: + 307: + description: Temporary redirect. + 404: + description: Error - the content with the given remote ID does not exist. + /{contentId}: + get: + displayName: Load content + description: Loads the Content item for the given ID. Depending on the Accept header the current version is embedded (i.e. the current published version or if it does not exist, the draft of the authenticated user). + queryParameters: + languages: + description: Restricts the output of translatable fields to the given languages. Comma separated list. + type: string + headers: + Accept: + description: Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.Content+xml + application/vnd.ez.api.Content+json + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + If-None-Match: + description: If the provided ETag matches the current ETag then a "304 Not Modified" is returned. The ETag changes if the meta data has changed, this happens also if there is a new published version. + example: ETag + responses: + 200: + body: + application/vnd.ez.api.Content+xml: + type: Content + example: !include examples/content/objects/content_id/GET/Content.xml.example + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/GET/ContentInfo.xml.example + 401: + description: Error - the user is not authorized to read this object. This could also happen if there is no published version yet and another user owns a draft of this Content item. + 404: + description: Error - the ID is not found. + patch: + displayName: Update content + description: This method updates the content metadata which is independent from a version. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, all information for the Content item (excluding the current version) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentInfo+xml + application/vnd.ez.api.ContentInfo+json + If-match: + description: Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + Content-Type: + description: The ContentUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentUpdate+xml + application/vnd.ez.api.ContentUpdate+json + body: + application/vnd.ez.api.ContentUpdate+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/PATCH/ContentUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.ContentInfo+xml: + type: ContentInfo + example: !include examples/content/objects/content_id/PATCH/ContentInfo.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update this object. + 404: + description: Error - the content ID does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + 415: + description: Error - the media-type is not one of those specified in headers. + delete: + displayName: Delete Content + description: Deletes Content item. If Content item has multiple Locations, all of them will be deleted via delete a subtree. + responses: + 204: + description: The Content item is deleted. + 404: + description: Error - Content item was not found. + 401: + description: Error - the user is not authorized to delete this Content item. + copy: + displayName: Copy content + description: Creates new Content item as a copy, under the given parent Location given in the destination header. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + destination: + description: A Location resource to which the Content item should be copied. + responses: + 201: + description: Copy created. + 401: + description: Error - the user is not authorized to copy this Content item to the given Location. + 404: + description: Error - the source or destination resource does not exist. + /translations/{languageCode}: + delete: + displayName: Delete translation (permanently) + description: Permanently deletes a translation from all versions of a Content item. + responses: + 204: + description: No Content + 401: + description: Error - the user is not authorized to delete Content item (content/remove policy). + 404: + description: Error - the Content item was not found. + 406: + description: Error - the given translation does not exist for the Content item. + 409: + description: Error - the specified translation is the only one any version has or is the main translation. + /currentversion: + get: + displayName: Get current version + description: Redirects to the current version of the Content item. + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/currentversion/GET/Version.xml.example + 307: + description: Temporary redirect. + 404: + description: Error - the resource does not exist. + copy: + displayName: Create a draft from current version + description: The system creates a new draft as a copy of the current version. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 201: + description: Created + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/currentversion/COPY/Version.xml.example + 401: + description: Error - the user is not authorized to update this Content item. + 403: + description: Error - the current version is already a draft. + 404: + description: Error - the Content item was not found. + /versions: + get: + displayName: List versions + description: Returns a list of all versions of the Content item. This method does not include fields and relations in the version elements of the response. + headers: + Accept: + description: If set, the version list is returned in XML or JSON format. + example: | + application/vnd.ez.api.VersionList+xml + application/vnd.ez.api.VersionList+json + responses: + 200: + body: + application/vnd.ez.api.VersionList+xml: + type: VersionList + example: !include examples/content/objects/content_id/versions/GET/VersionList.xml.example + 401: + description: Error - the user has no permission to read the versions. + /{versionNo}: + get: + displayName: Load version + description: Loads a specific version of a Content item. This method returns Fields and relations. + queryParameters: + fields: + description: Fields which should be returned in the response. Comma separated list. + type: string + responseGroups: + description: Alternative comma separated lists of predefined Field groups. + type: string + languages: + description: Restricts the output of translatable Fields to the given languages. Comma separated list. + type: string + headers: + If-None-Match: + description: Only return the version if the given ETag is the not current one, otherwise a 304 is returned. + example: ETag + Accept: + description: If set, the version list is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/GET/Version.xml.example + 304: + description: Error - the ETag does not match the current one. + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the ID or version is not found. + patch: + displayName: Update version + description: A specific draft is updated. PATCH or POST with header X-HTTP-Method-Override PATCH. + queryParameters: + languages: + description: Restricts the output of translatable Fields to the given languages. Comma separated list. + type: string + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + If-match: + description: Performs the patch only if the specified ETag is the current one. + Content-Type: + description: The VersionUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.VersionUpdate+xml + application/vnd.ez.api.VersionUpdate+json + body: + application/vnd.ez.api.VersionUpdate+xml: + type: VersionUpdate + example: !include examples/content/objects/content_id/versions/version_no/PATCH/VersionUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/PATCH/Version.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update this version. + 403: + description: Error - the version is not allowed to change - i.e. version is not a DRAFT. + 404: + description: Error - the content ID or version ID does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + copy: + displayName: Create a draft from a version + description: The system creates a new draft as a copy of the given version. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Version+xml + application/vnd.ez.api.Version+json + responses: + 201: + description: Created. + body: + application/vnd.ez.api.Version+xml: + type: Version + example: !include examples/content/objects/content_id/versions/version_no/COPY/Version.xml.example + 401: + description: Error - the user is not authorized to update this Content item. + 404: + description: Error - the Content item was not found. + delete: + displayName: Delete content version + description: Deletes the content version. + responses: + 204: + description: No Content - the version is deleted. + 404: + description: Error - the Content item or version were not found. + 401: + description: Error - the user is not authorized to delete this version. + 403: + description: Error - the version is in published state. + publish: + displayName: Publish a content version + description: Publishes the content version. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH + responses: + 204: + description: No Content - the content version is published. + 401: + description: Error - the user is not authorized to publish this version. + 403: + description: Error - the version is not a draft. + 404: + description: Error - the Content item or version were not found. + /translations/{languageCode}: + delete: + displayName: Delete translation from version draft + description: Removes a translation from a version draft. + responses: + 204: + description: No Content - removes a translation from a version draft. + 401: + description: Error - the user is not authorized to delete this translation. + 403: + description: Error - the version is not in draft state. + 404: + description: Error - the Content item or version number were not found. + 406: + description: Error - the given translation does not exist for the version. + 409: + description: Error - the specified translation is the only one the version has or is the main translation. + /relations: + get: + displayName: Load Relations of Content item version + description: Loads the Relations of the given version. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of bookmarks returned. + type: integer + headers: + Accept: + description: If set, the Relation is returned in XML or JSON format. + example: | + application/vnd.ez.api.RelationList+xml + application/vnd.ez.api.RelationList+json + responses: + 200: + body: + application/vnd.ez.api.RelationList+xml: + type: RelationList + example: !include examples/content/objects/content_id/versions/version_no/relations/GET/RelationList.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item was not found. + post: + displayName: Create new Relation + description: Creates a new Relation of type COMMON for the given draft. + headers: + Accept: + description: If set, the updated version is returned in XML or JSON format. + example: | + application/vnd.ez.api.Relation+xml + application/vnd.ez.api.Relation+json + Content-Type: + description: The RelationCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RelationCreate+xml + application/vnd.ez.api.RelationCreate+json + body: + application/vnd.ez.api.RelationCreate+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/RelationCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.Relation+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/POST/Relation.xml.example + /{relationId}: + get: + displayName: Load Relation + description: Loads a Relation for the given Content item. + headers: + Accept: + description: If set, the Relation is returned in XML or JSON format. + example: | + application/vnd.ez.api.Relation+xml + application/vnd.ez.api.Relation+json + responses: + 200: + description: OK - loads a Relation for the given Content item. + body: + application/vnd.ez.api.Relation+xml: + type: Relation + example: !include examples/content/objects/content_id/versions/version_no/relations/relation_id/GET/Relation.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID or the Relation does not exist. + delete: + displayName: Delete Relation + description: Deletes a Relation of the given draft. + responses: + 204: + description: No Content - deleted a Relation of the given draft. + 401: + description: Error - the user is not authorized to delete this Relation. + 403: + description: Error - the Relation is not of type COMMON or the given version is not a draft. + 404: + description: Error - Content item or the Relation were not found in the given version. + /relations: + get: + displayName: Load Relations of Content item + description: Redirects to the Relations of the current version. + responses: + 307: + description: Temporary redirect. + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item was not found. + /locations: + post: + displayName: Create new Location for Content item + description: Creates a new Location for the given Content item. + headers: + Accept: + description: If set, the new Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + Content-Type: + description: The LocationCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.LocationCreate+json + application/vnd.ez.api.LocationCreate+xml + body: + application/vnd.ez.api.LocationCreate+xml: + type: LocationCreate + example: !include examples/content/objects/content_id/locations/POST/LocationCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/objects/content_id/locations/POST/Location.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this Location. + 403: + description: Error - a Location under the given parent ID already exists. + get: + displayName: Get Locations for Content item + description: Loads all Locations for the given Content item. + headers: + Accept: + description: If set, the Location list is returned in XML or JSON format. + example: | + application/vnd.ez.api.LocationList+xml + application/vnd.ez.api.LocationList+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: LocationList + example: !include examples/content/objects/content_id/locations/GET/LocationList.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID does not exist. + /objectstates: + get: + displayName: Get Object states of Content item + description: Returns the Object states of a Content item + headers: + Accept: + description: If set, the Object states are returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentObjectStates+xml + application/vnd.ez.api.ContentObjectStates+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state. + body: + application/vnd.ez.api.ContentObjectStates+xml: + type: ContentObjectStates + example: !include examples/content/objects/content_id/objectstates/GET/ContentObjectStates.xml.example + 404: + description: Error - The Content item does not exist. + patch: + displayName: Set Object states of Content item + description: Updates Object states of a Content item. An Object state in the input overrides the state of the Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Content item Object states input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateUpdate+xml + application/vnd.ez.api.ObjectStateUpdate+json + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectStateUpdate + responses: + 204: + description: OK - Object state updated. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to set an Object state. + 403: + description: Error - The input contains multiple Object states of the same Object state group. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + /objectstategroups: + get: + displayName: List Object state groups + description: Returns a list of all Object state groups. + headers: + Accept: + description: If set, the Object state group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupList+xml + application/vnd.ez.api.ObjectStateGroupList+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns a list of Object state groups. + body: + application/vnd.ez.api.ObjectStateGroupList+xml: + type: ObjectStateGroupList + example: !include examples/content/objectstategroups/GET/ObjectStateGroupList.xml.example + application/vnd.ez.api.ObjectStateGroupList+json: + type: ObjectStateGroupList + # example: !include examples/content/objectstategroups/GET/ObjectStateGroupList.json.example + 401: + description: Error - The user has no permission to read Object state groups. + post: + displayName: Create Object state group + description: Creates a new Object state group. + headers: + Accept: + description: If set, the new Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + Content-Type: + description: The Object state group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupCreate+json + application/vnd.ez.api.ObjectStateGroupCreate+xml + body: + application/vnd.ez.api.ObjectStateGroupCreate+xml: + type: ObjectStateGroupCreate + #example: !include examples/content/objectstategroups/POST/ObjectStateGroupCreate.xml.example + application/vnd.ez.api.ObjectStateGroupCreate+json: + type: ObjectStateGroupCreate + #example: !include examples/content/objectstategroups/POST/ObjectStateGroupCreate.json.example + responses: + 201: + description: Object state group created. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/POST/ObjectStateGroup.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create an Object state group. + 403: + description: Error - An Object state group with the same identifier already exists. + /{objectStateGroupId}: + get: + displayName: Get Object state group + description: Returns the Object state group with the provided ID. + headers: + Accept: + description: If set, the Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state group. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example + application/vnd.ez.api.ObjectStateGroup+json: + type: ObjectStateGroup + # example: !include examples/content/objectstategroups/object_state_group_id/GET/ObjectStateGroup.xml.example + 401: + description: Error - The user is not authorized to read this Object state group. + 404: + description: Error - The Object state group does not exist. + patch: + displayName: Update Object state group + description: Updates an Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroup+xml + application/vnd.ez.api.ObjectStateGroup+json + Content-Type: + description: The Object state group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateGroupUpdate+json + application/vnd.ez.api.ObjectStateGroupUpdate+xml + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectStateGroupUpdate+xml: + type: ObjectStateGroupUpdate + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroupUpdate.xml.example + responses: + 200: + description: OK - Object stated group updated. + body: + application/vnd.ez.api.ObjectStateGroup+xml: + type: ObjectStateGroup + example: !include examples/content/objectstategroups/object_state_group_id/PATCH/ObjectStateGroup.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update an Object state group. + 403: + description: Error - An Object state group with the provided identifier already exists. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Object state group + description: Deletes the given Object state group including Object states. + responses: + 204: + description: No Content - Object state group deleted. + 401: + description: Error - The user is not authorized to delete an Object state group. + 404: + description: Error - The Object state group does not exist. + /objectstates: + get: + displayName: List Object states + description: Returns a list of all Object states of the given group. + headers: + Accept: + description: If set, the Object state list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateList+xml + application/vnd.ez.api.ObjectStateList+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns a list of Object states. + body: + application/vnd.ez.api.ObjectStateList+xml: + type: ObjectStateList + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/GET/ObjectStateList.xml.example + 401: + description: Error - The user has no permission to read Object states. + post: + displayName: Create Object state + description: Creates a new Object state. + headers: + Accept: + description: If set, the new Object state is returned in XML or JSON format. + example: + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Object state input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateCreate+json + application/vnd.ez.api.ObjectStateCreate+xml + body: + application/vnd.ez.api.ObjectStateCreate+xml: + type: ObjectStateCreate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectStateCreate.xml.example + responses: + 201: + description: Object state created. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/POST/ObjectState.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create an Object state. + 403: + description: Error - An Object state with the same identifier already exists in the given group. + /{objectStateId}: + get: + displayName: Get Object state + description: Returns the Object state. + headers: + Accept: + description: If set, the Object State is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Object state. + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/GET/ObjectState.xml.example + 401: + description: Error - The user is not authorized to read this Object state. + 404: + description: Error - The Object state does not exist. + patch: + displayName: Update Object state + description: Updates an Object state. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Object state is returned in XML or JSON format. + example: | + application/vnd.ez.api.ObjectState+xml + application/vnd.ez.api.ObjectState+json + Content-Type: + description: The Object state input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ObjectStateUpdate+json + application/vnd.ez.api.ObjectStateUpdate+xml + If-Match: + description: ETag + body: + application/vnd.ez.api.ObjectStateUpdate+xml: + type: ObjectStateUpdate + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectStateUpdate.xml.example + responses: + 200: + description: OK - Object State updated + body: + application/vnd.ez.api.ObjectState+xml: + type: ObjectState + example: !include examples/content/objectstategroups/object_state_group_id/objectstates/object_state_id/PATCH/ObjectState.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the Object state. + 403: + description: Error - An Object state with the provided identifier already exists in this group. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Object state + description: Deletes provided Object state. + responses: + 204: + description: No Content - Object state deleted. + 401: + description: Error - The user is not authorized to delete an Object state. + 404: + description: Error - The Object state does not exist. + /binary/images/{imageId}/variations/{variationIdentifier}: + get: + displayName: Load an image variation + description: Loads an image variation. + headers: + Accept: + description: If set, the image is returned in XML or JSON format. + example: | + application/vnd.ez.api.ImageVariation+xml + application/vnd.ez.api.ImageVariation+json + responses: + 200: + body: + application/vnd.ez.api.ImageVariation+xml: + type: ContentImageVariation + example: !include examples/content/binary/images/image_id/variations/variation_identifier/GET/ImageVariation.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - image ID doesn't match any image or variationIdentifier doesn't match any known variation. + /locations: + get: + displayName: Load Locations by id/remoteId/urlAlias + description: Loads the Location for a given ID (x), remote ID or URL alias. + queryParameters: + id: + description: The ID of the Location. If present, the Location with the given ID is returned. + remoteId: + description: The remote ID of the Location. If present, the Location with the given remote ID is returned. + urlAlias: + description: One of the URL aliases of the Location. If present, the Location with given URL Alias is returned. + responses: + 307: + description: Temporary redirect to the main resource URL. + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: Location + example: !include examples/content/locations/GET/LocationList.xml.example + 404: + description: Error - the Location with the given ID (remote ID or URL Alias) does not exist. + /{path}: + get: + displayName: Load Location + description: Loads the Location for the given path e.g. '/content/locations/1/2/61'. + headers: + Accept: + description: If set, the new Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/locations/path/GET/Location.xml.example + 401: + description: Error - the user is not authorized to read this Location. + 404: + description: Error - the Location with the given path does not exist. + move: + displayName: Move subtree + description: Moves Location to a different parent. The destination can also be '/content/trash' where the Location is put into the trash. (NOTE - Be aware that the user might lose access to the item after it has been moved, for example when read access is limited by a subtree). MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 201: + description: Created. If destination is '/api/ezp/v2/content/trash' and content only has one Location (NOTE - Like on normal subtree moves, be aware that the user might lose access to the item after it has been moved to Trash.) + 204: + description: No Content. If destination is '/api/ezp/v2/content/trash' and content still has other Locations (no trash item is created). + 401: + description: Error - the user is not authorized to move this Location. + 404: + description: Error - the Location with the given ID does not exist. + copy: + displayName: Copy subtree + description: Copies the subtree to a different parent. COPY or POST with header X-HTTP-Method-Override COPY. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 201: + description: Created. Copied the subtree to a different parent. + 401: + description: Error - the user is not authorized to move this Location. + 404: + description: Error - the Location with the given ID does not exist. + delete: + displayName: Delete subtree + description: Deletes the complete subtree for the given path. Every Content item which does not have any other Location is deleted. Otherwise the deleted Location is removed from the Content item. The children are recursively deleted. + responses: + 204: + description: No Content - deleted. + 401: + description: Error - the user is not authorized to delete this subtree. + 404: + description: Error - the Location with the given ID does not exist. + /children: + get: + displayName: Get child Locations. + description: Loads all child Locations for the given parent Location. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of Locations returned. + type: integer + headers: + Accept: + description: If set, the new Location list is returned in XML or JSON format. + example: | + application/vnd.ez.api.LocationList+xml + application/vnd.ez.api.LocationList+json + responses: + 200: + body: + application/vnd.ez.api.LocationList+xml: + type: LocationList + example: !include examples/content/locations/path/children/GET/LocationList.xml.example + 401: + description: Error - the user is not authorized to read this Content item. + 404: + description: Error - the Content item with the given ID does not exist. + /urlaliases: + get: + displayName: List URL aliases for Location + description: Returns the list of URL aliases for a Location. + queryParameters: + custom: + description: Indicates whether autogenerated (false) or manual URL aliases (true) should be returned (default true). + type: boolean + headers: + Accept: + description: If set, the URL alias list contains only references and is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasRefList+xml + application/vnd.ez.api.UrlAliasRefList+json + responses: + 200: + description: OK - returns the list of URL aliases. + body: + application/vnd.ez.api.UrlAliasRefList+xml: + type: UrlAliasRefList + exammple: examples/content/locations/path/urlaliases/GET/UrlAliasRefList.xml.example + 400: + description: Error - The user has no permission to read URL aliases. + 401: + description: Error - The Location was not found. + /{locationId}: + patch: + displayName: Update Location + description: Updates the Location. This method can also be used to hide/reveal a Location via the hidden field in the LocationUpdate. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the Location is returned in XML or JSON format. + example: | + application/vnd.ez.api.Location+xml + application/vnd.ez.api.Location+json + Content-Type: + description: The LocationUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.LocationUpdate+xml + application/vnd.ez.api.LocationUpdate+json + If-Match: + description: ETag + body: + application/vnd.ez.api.LocationUpdate+xml: + type: LocationUpdateStruct + example: !include examples/content/locations/location_id/PATCH/LocationUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.Location+xml: + type: Location + example: !include examples/content/locations/location_id/PATCH/Location.xml.example + 401: + description: Error - the user is not authorized to update this Location. + 404: + description: Error - the Location with the given ID does not exist. + swap: + displayName: Swap Location + description: Swaps the Location of a Content item with the given Location of another Content item. SWAP or POST with header X-HTTP-Method-Override SWAP. + headers: + Destination: + description: A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'. + responses: + 204: + description: No Content. Swapped the Location of a Content item with the given Location of another Content item. + 401: + description: Error - the user is not authorized to swap this Location. + 404: + description: Error - the Location with the given ID does not exist. + /views: + post: + displayName: Create View + description: Executes a query and returns View including the results. The View input reflects the criteria model of the public API. Will respond with a 301, as the resource has been moved to /views (Platform 1.0) - DEPRECATED. + headers: + Accept: + description: The View in XML or JSON format. + example: | + application/vnd.ez.api.View+xml + application/vnd.ez.api.View+json + application/vnd.ez.api.View+xml; version=1.1 + application/vnd.ez.api.View+json; version=1.1 + Content-Type: + description: The View input in XML or JSON format. + example: | + application/vnd.ez.api.ViewInput+xml + application/vnd.ez.api.ViewInput+json + application/vnd.ez.api.ViewInput+xml; version=1.1 + application/vnd.ez.api.ViewInput+json; version=1.1 + responses: + 301: + description: Moved permanently. + 400: + description: Error - the input does not match the input schema definition. + get: + displayName: List Views + description: Returns a list of View URIs. The list includes a public View and a private View of the authenticated user. - DEPRECATED + headers: + Accept: + description: The view link list in XML or JSON format. + example: | + application/vnd.ez.api.RefList+xml + application/vnd.ez.api.RefList+json + responses: + 200: + description: OK - list of View URIs. + /{identifier}: + get: + displayName: Get View + description: Returns the View - DEPRECATED. + headers: + Accept: + description: The View results in XML or JSON format. + example: | + application/vnd.ez.api.View+xml + application/vnd.ez.api.View+json + responses: + 200: + description: OK - returns the View. + 401: + description: Error - the View is not public and from another user. + delete: + displayName: Delete View + description: The given View is deleted - DEPRECATED. + responses: + 204: + description: No content - the given View is deleted. + 401: + description: Error - the user is not authorized to delete this View. + 404: + description: Error - the View does not exist. + /results: + get: + displayName: Get results of existing View + description: Returns result of the View - DEPRECATED. + headers: + Accept: + description: The View excluding results in XML or JSON format. + example: | + application/vnd.ez.api.ViewResult+xml + application/vnd.ez.api.ViewResult+json + responses: + 200: + description: OK - result of the View. + 401: + description: Error - the View is not public and from another user. + /sections: + post: + displayName: Create new Section + description: Creates a new Section. + headers: + Accept: + description: If set, the new Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + Content-Type: + description: The Section input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.SectionInput+json + application/vnd.ez.api.SectionInput+xml + body: + application/vnd.ez.api.SectionInput+xml: + type: SectionInput + example: !include examples/content/sections/POST/SectionInput.xml.example + responses: + 201: + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/POST/Section.xml.example + get: + displayName: Get Sections + description: Returns a list of all Sections. + queryParameters: + identifer: + description: Only the Section with the given identifier is returned. + headers: + Accept: + description: If set, the Section list is returned in XML or JSON format. + example: | + application/vnd.ez.api.SectionList+xml + application/vnd.ez.api.SectionList+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.SectionList+xml: + type: SectionList + example: !include examples/content/sections/GET/SectionList.xml.example + 401: + description: Error - The user has no permission to read the Section. + /{sectionId}: + get: + displayName: Get Section + description: Returns the Section by given Section ID. + headers: + Accept: + description: If set, the Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + If-None-match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/section_id/GET/Section.xml.example + 401: + description: Error - The user is not authorized to read this Section. + 404: + description: Error - The Section does not exist. + patch: + displayName: Update a Section + description: Updates a Section. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Section is returned in XML or JSON format. + example: | + application/vnd.ez.api.Section+xml + application/vnd.ez.api.Section+json + Content-Type: + description: The Section input schema encoded in XML or JSON. + example: | + application/vnd.ez.api.SectionInput+xml + application/vnd.ez.api.SectionInput+json + If-Match: + description: ETag + body: + application/vnd.ez.api.SectionInput+xml: + type: SectionInput + example: !include examples/content/sections/section_id/PATCH/SectionInput.xml.example + responses: + 200: + description: OK - Section updated. + body: + application/vnd.ez.api.Section+xml: + type: Section + example: !include examples/content/sections/section_id/PATCH/Section.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this Section. + 403: + description: Error - a Section with the given identifier already exists. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + delete: + displayName: Delete Section + description: The given Section is deleted. + #headers: TODO + # a little obvious? + #Accept: + #description: If set, in the case of an error the error message is returned in XML or JSON format + #example: | + #application/vnd.ez.api.ErrorMessage+xml + #application/vnd.ez.api.ErrorMessage+json + responses: + 204: + description: No Content - given Section is deleted. + 401: + description: Error - the user is not authorized to delete this Section. + 404: + description: Error - the Section does not exist. + /trash: + get: + displayName: List Trash items + description: Returns a list of all items in the Trash. + queryParameters: + limit: + description: Only limit. Items will be returned, starting with the offset. + offset: + description: Offset of the result set. + headers: + Accept: + description: If set, the Trash item list is returned in XML or JSON format. + example: | + application/vnd.ez.api.Trash+xml + application/vnd.ez.api.Trash+json + responses: + 200: + description: OK - returns the list of items in the Trash. + body: + application/vnd.ez.api.Trash+xml: + type: Trash + example: !include examples/content/trash/GET/Trash.xml.example + 401: + description: Error - The user has no permission to read the Trash. + delete: + displayName: Empty Trash + description: Empties the Trash. + responses: + 204: + description: No Content - Trash emptied. + 401: + description: Error - The user is not authorized to empty all items from Trash. + /{trashItemid}: + get: + displayName: Get Trash item + description: Returns the item in Trash with the provided ID. + headers: + Accept: + description: If set, the item in Trash is returned in XML or JSON format. + example: | + application/vnd.ez.api.TrashItem+xml + application/vnd.ez.api.TrashItem+json + responses: + 200: + body: + application/vnd.ez.api.TrashItem+xml: + type: TrashItem + example: examples/content/trash/trash_itemid/GET/TrashItem.xml.example + 401: + description: Error - The user has no permission to read the item in Trash. + 404: + description: Error - An item in Trash with the provided ID does not exist. + move: + displayName: Untrash Content item + description: Restores an item from Trash. MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: If the destination Location URI is provided, the item from Trash is restored under this Location, otherwise it is restored under its original parent Location. + responses: + 201: + description: Item restored. + 401: + description: Error - The user is not authorized to restore this item from Trash. + 403: + description: Error - The provided parent Location does not exist. + 404: + description: Error - The provided item does not exist in Trash. + delete: + displayName: Delete Trash item + description: Deletes the provided item from Trash. + responses: + 204: + description: No Content - item deleted. + 401: + description: Error - The user is not authorized to delete the provided item. + 404: + description: Error - The provided item does not exist in Trash. + /urlaliases: + get: + displayName: List global URL aliases + description: Returns the list of global URL aliases. + headers: + Accept: + description: If set, the URL alias list contains only references and is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasRefList+xml + application/vnd.ez.api.UrlAliasRefList+json + responses: + 200: + description: OK - returns the list of URL aliases. + body: + application/vnd.ez.api.UrlAliasRefList+xml: + type: UrlAliasRefList + example: !include examples/content/urlaliases/GET/UrlAliasRefList.xml.example + 401: + description: Error - The user has no permission to read URL aliases. + post: + displayName: Create URL alias + description: Creates a URL alias. + headers: + Accept: + description: If set, the created URL alias is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAlias+xml + application/vnd.ez.api.UrlAlias+json + Content-Type: + description: The URL alias input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UrlAliasCreate+xml + application/vnd.ez.api.UrlAliasCreate+json + body: + application/vnd.ez.api.UrlAliasCreate+xml: + type: UrlAliasCreate + example: !include examples/content/urlaliases/POST/UrlAliasCreate.xml.example + responses: + 201: + description: URL alias created. + body: + application/vnd.ez.api.UrlAlias+xml: + type: UrlAlias + example: !include examples/content/urlaliases/POST/UrlAlias.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create a URL alias. + 403: + description: Error - A URL alias with the same identifier already exists. + /{urlAliasId}: + get: + displayName: Get URL alias + description: Returns the URL alias with the given ID. + headers: + Accept: + description: If set, the URL alias is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlAlias+xml + application/vnd.ez.api.UrlAlias+json + responses: + 200: + description: OK - returns the URL alias. + body: + application/vnd.ez.api.UrlAlias+xml: + type: UrlAlias + example: !include examples/content/urlaliases/url_alias_id/GET/UrlAlias.xml.example + 401: + description: Error - The user is not authorized to read URL aliases. + 404: + description: Error - The URL alias does not exist. + delete: + displayName: Delete URL alias + description: Deletes the provided URL alias. + responses: + 204: + description: No Content - URL alias deleted. + 401: + description: Error - The user is not authorized to delete a URL alias. + 404: + description: Error - The URL alias does not exist. + /urlwildcards: + get: + displayName: List URL wildcards + description: Returns a list of URL wildcards. + headers: + Accept: + description: If set, the URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcardList+xml + application/vnd.ez.api.UrlWildcardList+json + responses: + 200: + description: OK - returns a list of URL wildcards. + body: + application/vnd.ez.api.UrlWildcardList+xml: + type: UrlWildcardList + example: !include examples/content/urlwildcards/GET/UrlWildcardList.xml.example + 401: + description: Error - The user has no permission to read URL wildcards. + post: + displayName: Create URL wildcard + description: Creates a new URL wildcard. + headers: + Accept: + description: If set, the new URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcard+xml + application/vnd.ez.api.UrlWildcard+json + Content-Type: + description: The URL Wildcard input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcardCreate+xml + application/vnd.ez.api.UrlWildcardCreate+json + body: + application/vnd.ez.api.UrlWildcardCreate.xml: + type: UrlWildcardCreate + # example: !include examples/content/urlwildcards/POST/UrlWildcardCreate.xml.example + responses: + 201: + description: URL wildcard created. + body: + application/vnd.ez.api.UrlWildcard+xml: + type: UrlWildcard + example: !include examples/content/urlwildcards/POST/UrlWildcard.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create a URL wildcard. + 403: + description: Error - A URL wildcard with the same identifier already exists. + /{wildcardId}: + get: + displayName: Get URL wildcard + description: Returns the URL wildcard with the given ID. + headers: + Accept: + description: If set, the URL wildcard is returned in XML or JSON format. + example: | + application/vnd.ez.api.UrlWildcard+xml + application/vnd.ez.api.UrlWildcard+json + responses: + 200: + description: OK - returns the URL wildcard. + body: + application/vnd.ez.api.UrlWildcard+xml: + type: UrlWildcard + example: !include examples/content/urlwildcards/wildcard_id/GET/UrlWildcard.xml.example + 401: + description: Error - The user is not authorized to read URL wildcards. + 404: + description: Error - The URL wildcard does not exist. + delete: + displayName: Delete URL wildcard + description: Deletes the given URL wildcard. + responses: + 204: + description: No Content - URL wildcard deleted. + 401: + description: Error - The user is not authorized to delete a URL wildcard. + 404: + description: Error - The URL wildcard does not exist. + /typegroups: + displayName: Managing Content Type groups + get: + displayName: Get Content Type groups + description: Returns a list of all Content Type groups. If an identifier is provided, loads the Content Type group for this identifier. + queryParameters: + identifier: + description: The identifier of the Content Type group. If present, the Content Type group with this identifier is returned. + required: false + headers: + Accept: + description: If set, the Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupList+xml + application/vnd.ez.api.ContentTypeGroupList+json + responses: + 200: + description: OK - returns a list of Content Type groups. + body: + application/vnd.ez.api.ContentTypeGroupList+xml: + type: ContentTypeGroupList + example: !include examples/content/typegroups/GET/ContentTypeGroupList.xml.example + 307: + description: Temporary redirect. + 401: + description: Error - The user has no permission to read Content Types. + 404: + description: Error - The Content Type group with the given identifier does not exist. + post: + displayName: Create Content Type group + description: Creates a new Content Type group. + headers: + Accept: + description: If set, the new Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + Content-Type: + description: The Content Type group input schema encoded in XML or JSON. + example: | + application/vnd.ez.api.ContentTypeGroupInput+xml + application/vnd.ez.api.ContentTypeGroupInput+json + body: + application/vnd.ez.api.ContentTypeGroupInput+xml: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/POST/ContentTypeGroupInput.xml.example + responses: + 201: + description: Content Type group created. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/POST/ContentTypeGroup.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create this Content Type group. + 403: + description: Error - A Content Type group with the same identifier already exists. + /{contentTypeGroupId}: + get: + displayName: Get Content Type group + description: Returns the Content Type group with provided ID. + headers: + Accept: + description: If set, the Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - returns the Content Type group. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/GET/ContentTypeGroup.xml.example + 401: + description: Error - The user is not authorized to read this Content Type group. + 404: + description: Error - The Content Type group does not exist. + patch: + displayName: Update Content Type group + description: Updates a Content Type group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Content Type group is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroup+xml + application/vnd.ez.api.ContentTypeGroup+json + Content-Type: + description: The Content Type group input schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupInput+xml + application/vnd.ez.api.ContentTypeGroupInput+json + If-Match: + description: ETag causes patching only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.ContentTypeGroupInput+xml: + type: ContentTypeGroupInput + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroupInput.xml.example + responses: + 200: + description: Content Type group updated. + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/typegroups/content_type_group_id/PATCH/ContentTypeGroup.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create this Content Type group. + 403: + description: Error - A Content Type group with the given identifier already exists. + 412: + description: Error - The current ETag does not match the one provided in the If-Match header. + delete: + displayName: Delete Content Type group + description: Deletes the provided Content Type group. + responses: + 204: + description: No Content - Content Type group deleted. + 401: + description: Error - The user is not authorized to delete this Content Type group. + 403: + description: Error - The Content Type group is not empty. + 404: + description: Error - The Content Type group does not exist. + /types: + get: + displayName: List Content Types for group + description: Returns a list of Content Types in the provided group. + headers: + Accept: + description: If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfoList+xml + application/vnd.ez.api.ContentTypeInfoList+json + application/vnd.ez.api.ContentTypeList+xml + application/vnd.ez.api.ContentTypeList+json + responses: + 200: + description: OK - returns a list on Content Types. + body: + application/vnd.ez.api.ContentTypeInfoList+xml: + type: ContentTypeInfoList + example: !include examples/content/typegroups/content_type_group_id/types/GET/ContentTypeInfoList.xml.example + application/vnd.ez.api.ContentTypeList+xml: + type: ContentTypeList + example: !include examples/content/typegroups/content_type_group_id/types/GET/ContentTypeList.xml.example + 401: + description: Error - The user has no permission to read the Content Types. + post: + displayName: Create Content Type + description: Creates a new Content Type draft in the given Content Type group. + queryParameters: + publish: + description: If true, the Content Type is published after creating (default false). + type: boolean + headers: + Accept: + description: If set, the new Content Type or draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentType+xml + application/vnd.ez.api.ContentType+json + Content-Type: + description: The Content Type Create schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeCreate+xml + application/vnd.ez.api.ContentTypeCreate+json + body: + application/vnd.ez.api.ContentTypeCreate.xml: + type: ContentTypeCreate + example: !include examples/content/typegroups/content_type_group_id/types/POST/ContentTypeCreate.xml.example + responses: + 201: + description: Content Type created. + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/typegroups/content_type_group_id/types/POST/ContentType.xml.example + 400: + description: Error - The input does not match the input schema definition. Validation on a Field definition fails. Validation of the Content Type fails, e.g. multiple Fields of a same singular Field Type are provided. Publish is set to true and the input is not complete e.g. no Field definitions are provided. + 401: + description: Error - The user is not authorized to create this Content Type. + 403: + description: Error - A Content Type with same identifier already exists. + /types: + displayName: Managing Content Types + get: + displayName: List Content Types + description: Returns a list of Content Types. + queryParameters: + identifier: + description: Retrieves the Content Type for the given identifer. + remoteId: + description: Retrieves the Content Type for the given remote ID. + limit: + description: Only 'limit' items will be returned, starting with the offset. + offset: + description: Offset of the result set. + orderby: + description: One of (name|lastmodified). + sort: + description: One of (asc|desc). + headers: + Accept: + description: If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfoList+xml + application/vnd.ez.api.ContentTypeInfoList+json + application/vnd.ez.api.ContentTypeList+xml + application/vnd.ez.api.ContentTypeList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeInfoList+xml: + type: ContentTypeInfoList + example: !include examples/content/types/GET/ContentTypeInfoList.xml.example + description: OK - returns a list of Content Types. + 401: + description: Error - The user has no permission to read the Content Types. + /{contentTypeId}: + get: + displayName: Get Content Type + description: Returns the Content Type with the provided ID. + headers: + Accept: + description: If set, the Content Type is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentType+xml + application/vnd.ez.api.ContentType+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/types/content_type_id/GET/ContentType.xml.example + description: OK - returns the Content Type. + 401: + description: Error - The user is not authorized to read this Content Type. + 404: + description: Error - The Content Type does not exist. + copy: + displayName: Copy Content Type + description: Copies a Content Type. A new remote ID is generated, and the identifier of the copy is set to 'copy_of_originalBaseIdentifier_newTypeId' (or another random string). COPY or POST with header X-HTTP-Method-Override COPY. + responses: + 201: + description: Copy of the Content Type created. + 401: + description: Error - The user is not authorized to copy this Content Type. + post: + displayName: Create Draft + description: Creates a draft and updates it with the given data. + headers: + Accept: + description: If set, the new Content Type draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfo+xml + application/vnd.ez.api.ContentTypeInfo+json + Content-Type: + description: The Content Type Update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeUpdate+xml + application/vnd.ez.api.ContentTypeUpdate+json + body: + application/vnd.ez.api.ContentTypeUpdate+xml: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/POST/ContentTypeUpdate.xml.example + responses: + 201: + description: Draft created. + body: + application/vnd.ez.api.ContentTypeInfo+xml: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/POST/ContentTypeInfo.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to create the draft. + 403: + description: Error - A Content Type with the given new identifier already exists. A draft already exists. + delete: + displayName: Delete Content Type + description: Deletes the provided Content Type. + responses: + 204: + description: No Content - Content Type deleted. + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - There are object instances of this Content Type. + 404: + description: Error - The Content Type does not exist. + /{fieldDefinitionId}: + get: + displayName: Get Field definition + description: Returns the Field definition by the given ID. + headers: + Accept: + description: If set, the Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + responses: + 200: + description: OK - returns the Field definition. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/field_definition_id/GET/FieldDefinition.xml.example + 401: + description: Error - The user is not authorized to read the Content Type. + 404: + description: Error - The Content Type does not exist. + /draft: + patch: + displayName: Update Content Type draft + description: Updates metadata of a draft. This method does not handle Field definitions. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the new Content Type draft is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeInfo+xml + application/vnd.ez.api.ContentTypeInfo+json + Content-Type: + description: The Content Type update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeUpdate+xml + application/vnd.ez.api.ContentTypeUpdate+json + body: + application/vnd.ez.api.ContentTypeUpdate+xml: + type: ContentTypeUpdate + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeUpdate.xml.example + responses: + 200: + description: Draft metadata updated. + body: + application/vnd.ez.api.ContentTypeInfo+xml: + type: ContentTypeInfo + example: !include examples/content/types/content_type_id/draft/PATCH/ContentTypeInfo.xml.example + + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the draft. + 403: + description: Error - A Content Type with the given new identifier already exists. + 404: + description: Error - There is no draft for this Content Type. + publish: + displayName: Publish Content Type draft + description: Publishes a Content Type draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH. + responses: + 200: + body: + application/vnd.ez.api.ContentType+xml: + type: ContentType + example: !include examples/content/types/content_type_id/draft/PUBLISH/ContentType.xml.example + description: Content Type draft published. + 401: + description: Error - The user is not authorized to publish this Content Type draft. + 403: + description: Error - The Content Type draft is not complete, e.g. there is no Field definition provided. + 404: + description: Error - If there is no draft or Content Type with the given ID. + delete: + displayName: Delete Content Type draft + description: Deletes the provided Content Type draft. + responses: + 204: + description: No Content - Content Type draft deleted. + 401: + description: Error - The user is not authorized to delete this Content Type draft. + 404: + description: Error - The Content Type draft does not exist. + /fieldDefinitions: + post: + displayName: Add Content Type Draft Field definition + description: Creates a new Field definition for the given Content Type. + headers: + Accept: + description: If set, the new Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + Content-Type: + description: The Field Definition Create schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinitionCreate+xml + application/vnd.ez.api.FieldDefinitionCreate+json + body: + application/vnd.ez.api.FieldDefinitionCreate+xml: + type: FieldDefinitionCreate + example: !include examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinitionCreate.xml.example + responses: + 201: + description: Field definition created. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/POST/FieldDefinition.xml.example + 400: + description: Error - The input does not match the input schema definition or validation on the Field definition fails. + 401: + description: Error - The user is not authorized to add a Field definition. + 403: + description: Error - A Field definition with the same identifier already exists in the given Content Type. The Field definition is of singular type, already existing in the given Content Type. The Field definition you want to add is of a type that can't be added to a Content Type that already has content instances. + /{fieldDefinitionId}: + get: + displayName: Get Content Type Draft Field definition + description: Returns the Field definition by the given ID. + headers: + Accept: + description: If set, the Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + responses: + 200: + description: OK - returns the Field definition. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/GET/FieldDefinition.xml.example + 401: + description: Error - The user is not authorized to read the Content Type draft. + 404: + description: Error - The Content Type or draft does not exist. + patch: + displayName: Update Content Type Draft Field definition + description: Updates the attributes of a Field definition. + headers: + Accept: + description: If set, the updated Field definition is returned in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinition+xml + application/vnd.ez.api.FieldDefinition+json + Content-Type: + description: The Field definition update schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.FieldDefinitionUpdate+xml + application/vnd.ez.api.FieldDefinitionUpdate+json + body: + application/vnd.ez.api.FieldDefinitionUpdate+xml: + type: FieldDefinitionUpdate + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinitionUpdate.xml.example + responses: + 200: + description: OK - attributes updated. + body: + application/vnd.ez.api.FieldDefinition+xml: + type: FieldDefinition + example: !include examples/content/types/content_type_id/draft/field_definitions/field_definition_id/PATCH/FieldDefinition.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to update the Field definition. + 403: + description: Error - A Field definition with the given identifier already exists in the given Content Type. + delete: + displayName: Delete Content Type Draft Field definition + description: Deletes the provided Field definition. + responses: + 204: + description: No Content - Field definition deleted. + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - There is no draft of the Content Type assigned to the authenticated user. + /groups: + get: + displayName: Get groups of Content Type + description: Returns the Content Type group to which Content Type belongs to. + headers: + Accept: + description: If set, the Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroupRefList+xml: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/GET/ContentTypeGroupRefList.xml.example + 401: + description: Error - The user is not authorized to read this Content Type. + 404: + description: Error - The Content Type does not exist. + post: + displayName: Link group to Content Type + description: Links a Content Type group to the Content Type and returns the updated group list. + queryParameters: + group: + description: The URI of the group to which the Content Type should be linked to. + type: string + headers: + Accept: + description: If set, the updated Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroupRefList+xml: + type: ContentTypeGroupRefList + example: !include examples/content/types/content_type_id/groups/POST/ContentTypeGroupRefList.xml.example + 400: + description: Error - The input does not match the input schema definition. + 401: + description: Error - The user is not authorized to add a group. + 403: + description: Error - The Content Type is already assigned to the group. + /{id}: + delete: + displayName: Unlink group from Content Type + description: Removes the given group from the Content Type and returns the updated group list. + headers: + Accept: + description: If set, the updated Content Type group list is returned in XML or JSON format. + example: | + application/vnd.ez.api.ContentTypeGroupRefList+xml + application/vnd.ez.api.ContentTypeGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.ContentTypeGroup+xml: + type: ContentTypeGroup + example: !include examples/content/types/content_type_id/groups/id/DELETE/ContentTypeGroup.xml.example + 401: + description: Error - The user is not authorized to delete this Content Type. + 403: + description: Error - Content Type cannot be unlinked from the only remaining group. + 404: + description: Error - The resource does not exist. +/views: + displayName: Views + post: + displayName: Create View + description: Executes a query and returns a View including the results. The View input reflects the criteria model of the public API. + headers: + Accept: + description: The view in XML or JSON format. + example: | + application/vnd.ez.api.View+xml + application/vnd.ez.api.View+json + application/vnd.ez.api.View+xml; version=1.1 + application/vnd.ez.api.View+json; version=1.1 + Content-Type: + description: The view input in XML or JSON format. + example: | + application/vnd.ez.api.ViewInput+xml + application/vnd.ez.api.ViewInput+json + application/vnd.ez.api.ViewInput+xml; version=1.1 + application/vnd.ez.api.ViewInput+json; version=1.1 + body: + application/vnd.ez.api.ViewInput+xml: + type: ViewInput + example: !include examples/views/POST/ViewInput.xml.example + responses: + 200: + body: + application/vnd.ez.api.View+xml; version=1.1: + type: View + example: !include examples/views/POST/View.xml.v11.example + 400: + description: Error - the input does not match the input schema definition. +/user: + displayName: Managing users + /groups: + get: + displayName: Load User Groups + description: Loads User Groups for either an an ID or a remote ID or a Role. + queryParameters: + roleId: + description: Lists User Groups assigned to the given Role. + id: + description: Retrieves the User Groups for the given ID. + remoteID: + description: Retrieves the User Groups for the given remote ID. + headers: + Accept: + description: UserGroupList - If set, the User Group List is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupList+xml + application/vnd.ez.api.UserGroupList+json + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupList+xml: + type: UserGroupList + example: !include examples/user/groups/GET/UserGroupList.xml.example + 401: + description: Error - the user has no permission to read User Groups. + /root: + get: + # Currently does not work + displayName: Get root User Group + description: Redirects to the root User Group. + responses: + 301: + description: Moved permanently. + /{path}: + get: + displayName: Load User Group + description: Loads User Groups for the given {path}. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - loads User Groups. + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/GET/UserGroup.xml.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + patch: + displayName: Update User Group + description: Updates a User Group. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + Content-Type: + description: The UserGroupUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupUpdate+json + application/vnd.ez.api.UserGroupUpdate+xml + If-Match: + description: Performs the PATCH only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + body: + application/vnd.ez.api.UserGroupUpdate+xml: + type: UserGroupUpdate + example: !include examples/user/groups/path/PATCH/UserGroupUpdate.xml.example + responses: + 200: + description: OK - updated User Group. + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/PATCH/UserGroup.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the User Group. + 412: + description: Error - if the current ETag does not match with the one provided in the If-Match header. + delete: + displayName: Delete User Group + description: The given User Group is deleted. + responses: + 204: + description: No content - the given User Group is deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + 403: + description: Error - the User Group is not empty. + move: + displayName: Move User Group + description: Moves the User Group to another parent. MOVE or POST with header X-HTTP-Method-Override MOVE. + headers: + Destination: + description: A parent group resource to which the Location is moved. + responses: + 201: + description: Created - moves the User Group to another parent. + 401: + description: Error - the user is not authorized to update the User Group. + 403: + description: Error - the new parent does not exist. + 404: + description: Error - the User Group does not exist. + /users: + post: + displayName: Create User + description: Creates a new User in the given Group. + headers: + Accept: + description: If set, the new User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + Content-Type: + description: The UserCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserCreate+json + application/vnd.ez.api.UserCreate+xml + body: + application/vnd.ez.api.UserCreate+xml: + type: UserCreate + example: examples/user/groups/path/users/POST/UserCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.User+xml: + type: User + example: !include examples/user/groups/path/users/POST/User.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this User. + 403: + description: Error - a User with the same login already exists. + 404: + description: Error - the Group with the given ID does not exist. + /subgroups: + post: + displayName: Create User Group + description: Creates a new User Group under the given parent. To create a top level group use '/user/groups/subgroups'. + headers: + Accept: + description: If set, the new User Group is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroup+xml + application/vnd.ez.api.UserGroup+json + Content-Type: + description: The UserGroupCreate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupCreate+json + application/vnd.ez.api.UserGroupCreate+xml + body: + application/vnd.ez.api.UserGroupCreate+xml: + type: UserGroupCreate + example: !include examples/user/groups/path/subgroups/POST/UserGroupCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.UserGroup+xml: + type: UserGroup + example: !include examples/user/groups/path/subgroups/POST/UserGroup.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this User Group. + /Roles: + get: + displayName: Load Roles for User Group + description: Returns a list of all Roles assigned to the given User Group. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/GET/RoleAssignmentList.xml.example + 400: + description: Error - the user has no permission to read Roles. + post: + displayName: Assign Role to User Group + description: Assigns a Role to a User Group. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + Content-Type: + description: The RoleAssignInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignInput+json + application/vnd.ez.api.RoleAssignInput+xml + body: + application/vnd.ez.api.RoleAssignInput+xml: + type: RoleAssignInput + example: !include examples/user/groups/path/roles/POST/RoleAssignInput.xml.example + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/POST/RoleAssignmentList.xml.example + 400: + description: Error - validation of limitation in RoleAssignInput fails. + 401: + description: Error - the user is not authorized to assign this Role. + /{roleId}: + get: + displayName: Load User Group Role Assignment + description: Returns a Role assignment of the given User Group. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignment+xml + application/vnd.ez.api.RoleAssignment+json + responses: + 200: + description: OK - returns a Role assignment of the given User Group. + body: + application/vnd.ez.api.RoleAssignment+xml: + type: RoleAssignment + example: !include examples/user/groups/path/roles/role_id/GET/RoleAssignment.xml.example + 401: + description: Error - the user has no permission to read Roles. + delete: + displayName: Unassign Role from User Group + description: The given Role is removed from the User or User Group. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/groups/path/roles/role_id/DELETE/RoleAssignmentList.xml.example + 401: + description: Error - the user is not authorized to delete this Role assignment. + /{id}/users: + get: + displayName: Load Users of Group + description: Loads the Users of the Group with the given ID. + queryParameters: + limit: + description: Only 'limit' items will be returned started by offset. + offset: + description: Offset of the result set. + headers: + Accept: + description: UserList - If set, the User list returned in XML or JSON format. UserRefList - If set, the link list of Users returned in XML or JSON format. + example: | + application/vnd.ez.api.UserList+xml + application/vnd.ez.api.UserList+json + application/vnd.ez.api.UserRefList+xml + application/vnd.ez.api.UserRefList+json + responses: + 200: + description: OK - the Users of the Group with the given ID. + body: + application/vnd.ez.api.UserRefList+xml: + type: UserRefList + example: !include examples/user/groups/id/users/GET/UserRefList.xml.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + /{id}/subgroups: + get: + displayName: Load subgroups + description: Returns a list of the subgroups. + queryParameters: + limit: + description: The number of Locations returned. + offset: + description: The offset of the result set. + headers: + Accept: + description: UserGroupList - If set, the User Group list is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupList+xml + application/vnd.ez.api.UserGroupList+json + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + description: OK - list of the subgroups. + body: + application/vnd.ez.api.UserGroupRefList+xml: + type: UserGroupRefList + example: !include examples/user/groups/id/subgroups/GET/UserGroupRefList.xml.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the User Group does not exist. + /users: + get: + displayName: List Users + description: Load Users either for a given remote ID or Role. + queryParameters: + roleId: + description: Lists Users assigned to the given Role (e.g. GET /user/users?roleId=/user/roles/1). + remoteId: + description: Retrieves the User for the given remote ID (e.g. GET /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). + login: + description: Retrieves the User for the given login (e.g. GET /user/users?login=editor). + email: + description: Lists Users with the given email (e.g. GET /user/users?email=editor@example.com). + headers: + Accept: + description: UserList - If set, the User list is returned in XML or JSON format. UserRefList - If set, the link list of Users is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserList+xml + application/vnd.ez.api.UserList+json + application/vnd.ez.api.UserRefList+xml + application/vnd.ez.api.UserRefList+json + responses: + 200: + description: OK - Loads Users either for a given remote ID or Role. + body: + application/vnd.ez.api.UserRefList+xml: + type: UserRefList + example: !include examples/user/users/GET/UserRefList.xml.example + application/vnd.ez.api.UserList+xml: + type: UserList + example: !include examples/user/users/GET/UserList.xml.example + 404: + description: If there are no visibile Users matching the filter. + head: + displayName: Verify Users + description: Verifies if there are Users matching given filter. + queryParameters: + roleId: + description: Lists Users assigned to the given Role (e.g. GET /user/users?roleId=/user/roles/1). + remoteId: + description: Retrieves the User for the given remote ID (e.g. GET /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). + login: + description: Retrieves the User for the given login (e.g. GET /user/users?login=editor). + email: + description: Lists Users with the given email (e.g. GET /user/users?email=editor@example.com). + responses: + 200: + description: OK - verifies if there are Users matching the given filter. + 404: + description: Error - there are no visibile Users matching the filter. + /{userId}: + get: + displayName: Load User + description: Loads User with the given ID. + headers: + Accept: + description: If set, the User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - the User with the given ID. + body: + application/vnd.ez.api.User+xml: + type: UserList + example: !include examples/user/users/user_id/GET/User.xml.example + 401: + description: Error - the user has no permission to read Users. + 404: + description: Error - the User does not exist. + patch: + displayName: Update User + description: Updates a User. + headers: + Accept: + description: If set, the updated User is returned in XML or JSON format. + example: | + application/vnd.ez.api.User+xml + application/vnd.ez.api.User+json + Content-Type: + description: The UserUpdate schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.UserUpdate+json + application/vnd.ez.api.UserUpdate+xml + If-Match: + description: Performs a PATCH only if the specified ETag is the current one. + example: ETag + body: + application/vnd.ez.api.UserUpdate+xml: + type: UserUpdate + example: !include examples/user/users/user_id/PATCH/UserUpdate.xml.example + responses: + 200: + description: OK - User updated. + body: + application/vnd.ez.api.User+xml: + type: User + example: !include examples/user/users/user_id/PATCH/User.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the User. + 404: + description: Error - the User does not exist. + 412: + description: Error - the current ETag does not match with the provided one in the If-Match header. + delete: + displayName: Delete User + description: Deletes the given User. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to delete this User. + 403: + description: Error - the user is the same as the authenticated User. + 404: + description: Error - the User does not exist. + /groups: + get: + displayName: Load Groups of User + description: Returns a list of User Groups the User belongs to. The returned list includes the resources for unassigning a User Group if the User is in multiple groups. + queryParameters: + offset: + description: The offset of the result set. + type: integer + limit: + description: The number of Locations returned. + type: integer + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/GET/UserGroupRefList.xml.example + 401: + description: Error - the user has no permission to read User Groups. + 404: + description: Error - the user does not exist. + post: + displayName: Assign User Group + description: Assigns the User to a User Group. + queryParameters: + group: + description: The new parent group resource of the User. + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/POST/UserGroupRefList.xml.example + 401: + description: Error - the user is not authorized to assign User Groups. + 403: + description: Error - the new User Group does not exist or the User is already in this group. + 404: + description: Error - the User does not exist. + /{groupId}: + delete: + displayName: Unassign User Group + description: Unassigns the User from a User Group. + headers: + Accept: + description: If set, the link list of User Groups is returned in XML or JSON format. + example: | + application/vnd.ez.api.UserGroupRefList+xml + application/vnd.ez.api.UserGroupRefList+json + responses: + 200: + body: + application/vnd.ez.api.UserGroupRefList: + type: UserGroupRefList + example: !include examples/user/users/user_id/groups/group_id/DELETE/UserGroupRefList.xml.example + 401: + description: Error - the user is not authorized to unassign User Groups. + 403: + description: Error - the User is not in the given group. + 404: + description: Error - the User does not exist. + /Roles: + get: + displayName: Load Roles for User + description: Returns a list of all Roles assigned to the given User. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/GET/RoleAssignmentList.xml.example + 400: + description: Error - the user has no permission to read Roles + post: + displayName: Assign Role to User + description: Assigns a Role to a user. + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + Content-Type: + description: The RoleAssignInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignInput+json + application/vnd.ez.api.RoleAssignInput+xml + body: + application/vnd.ez.api.RoleAssignInput+xml: + type: RoleAssignInput + example: !include examples/user/users/user_id/roles/POST/RoleAssignInput.xml.example + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/POST/RoleAssignmentList.xml.example + 400: + description: Error - validation of limitation in RoleAssignInput fails. + 401: + description: Error - the user is not authorized to assign this Role. + /{roleId}: + get: + displayName: Load User Role Assignment + description: Returns a Role assignment to the given User. + headers: + Accept: + description: If set, the Role assignment list is returned in XML or JSON format. + example: | + application/vnd.ez.api.RoleAssignment+xml + application/vnd.ez.api.RoleAssignment+json + responses: + 200: + description: OK - Role assignment to the given User Group. + body: + application/vnd.ez.api.RoleAssignment+xml: + type: RoleAssignment + example: !include examples/user/users/user_id/roles/role_id/GET/RoleAssignment.xml.example + 401: + description: Error - the user has no permission to read Roles + delete: + displayName: Unassign Role from User + description: The given Role is removed from the user + headers: + Accept: + description: If set, the updated Role assignment list is returned in XML or JSON format + example: | + application/vnd.ez.api.RoleAssignmentList+xml + application/vnd.ez.api.RoleAssignmentList+json + responses: + 200: + body: + application/vnd.ez.api.RoleAssignmentList+xml: + type: RoleAssignmentList + example: !include examples/user/users/user_id/roles/role_id/DELETE/RoleAssignmentList.xml.example + 401: + description: Error - the user is not authorized to delete this Content Type + /roles: + get: + displayName: Load Roles + description: Returns a list of all Roles + queryParameters: + identifier: + description: Restricts the result to a list containing the Role with the given identifier. If the Role is not found an empty list is returned. + offset: + description: The offset of the result set + type: integer + limit: + description: Only limit items will be returned started by offset + type: integer + headers: + Accept: + description: If set, the user list returned in XML or JSON format + example: | + application/vnd.ez.api.RoleList+xml + application/vnd.ez.api.RoleList+json + responses: + 200: + description: OK - list of all Roles + body: + application/vnd.ez.api.RoleList+xml: + type: RoleList + example: !include examples/user/roles/GET/RoleList.xml.example + 401: + description: Error - the user has no permission to read Roles + post: + displayName: Create Role / Role Draft + description: Creates a new Role or Role draft + queryParameters: + publish: + type: boolean + description: If true the Role is published after creation + default: true + headers: + Accept: + description: If set, the new user is returned in XML or JSON format + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + application/vnd.ez.api.RoleDraft+xml + application/vnd.ez.api.RoleDraft+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: examples/user/roles/POST/RoleInput.xml.example + responses: + 201: + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/POST/Role.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to create this Role / Role draft + /{id}: + get: + displayName: Load Role + description: Loads a Role for the given ID + headers: + Accept: + description: If set, the user list returned in XML or JSON format + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - Role for the given ID + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/GET/Role.xml.example + 401: + description: Error - the user has no permission to read Roles + 404: + description: Error - the Role does not exist + post: + displayName: Create Role Draft + description: Creates a new Role draft from an existing Role. + headers: + Accept: + description: If set, the new user is returned in XML or JSON format + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + responses: + 201: + body: + application/vnd.ez.api.RoleDraft+xml: + type: RoleDraft + example: !include examples/user/roles/id/POST/RoleDraft.xml.example + 401: + description: Error - the user is not authorized to create this Role / Role draft + patch: + displayName: Update Role + description: Updates a Role. PATCH or POST with header X-HTTP-Method-Override PATCH + headers: + Accept: + description: If set, the new user is returned in XML or JSON format + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + If-Match: + description: ETag Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: !include examples/user/roles/id/PATCH/RoleInput.xml.example + responses: + 200: + description: OK - Role updated + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/PATCH/Role.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the Role + 412: + description: Error - the current ETag does not match with the provided one in the If-Match header + delete: + displayName: Delete Role + description: The given Role and all assignments to Users or User Groups are deleted. + responses: + 204: + description: No Content. + 401: + description: Error - the User is not authorized to delete this Role. + /draft: + get: + displayName: Load Role draft + description: Loads a Role draft by original Role ID. + headers: + Accept: + description: If set, the User list returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + If-None-Match: + description: ETag + responses: + 200: + description: OK - Role draft by original Role ID. + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/draft/GET/Role.xml.example + 401: + description: Error - the user has no permission to read Roles + 404: + description: Error - there is no draft or Role with the given ID + patch: + displayName: Update Role draft + description: Updates a Role draft. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Role is returned in XML or JSON format. + example: | + application/vnd.ez.api.Role+xml + application/vnd.ez.api.Role+json + Content-Type: + description: The RoleInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.RoleInput+json + application/vnd.ez.api.RoleInput+xml + If-Match: + description: Performs a PATCH only if the specified ETag is the current one. Otherwise a 412 is returned. + body: + application/vnd.ez.api.RoleInput+xml: + type: RoleInput + example: !include examples/user/roles/id/draft/PATCH/RoleInput.xml.example + responses: + 200: + description: OK - Role draft updated. + body: + application/vnd.ez.api.Role+xml: + type: Role + example: !include examples/user/roles/id/draft/PATCH/Role.xml.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the user is not authorized to update the Role. + 404: + description: Error - there is no draft or Role with the given ID. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + publish: + displayName: Publish Role draft + description: Publishes a Role draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to publish this Content Type draft. + 403: + description: Error - the Content Type draft is not complete e.g. there is no Field definition provided. + 404: + description: Error - there is no draft or Role with the given ID. + delete: + displayName: Delete Role draft + description: The given Role draft is deleted. + responses: + 204: + description: No Content. + 401: + description: Error - the user is not authorized to delete this Role. + /policies: + get: + displayName: Load Policies + description: Loads Policies for the given Role. + headers: + Accept: + description: If set, the Policy list is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyList+xml + application/vnd.ez.api.PolicyList+json + responses: + 200: + body: + application/vnd.ez.api.PolicyList+xml: + type: PolicyList + example: !include examples/user/roles/id/policies/GET/PolicyList.xml.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - the Role does not exist. + delete: + displayName: Delete Policies + description: All Policies of the given Role are deleted. + responses: + 204: + description: No Content - all Policies of the given Role are deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + post: + # Currently does not work + displayName: Create Policy + description: Creates a Policy + headers: + Accept: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + Content-Type: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyCreate+xml + application/vnd.ez.api.PolicyCreate+json + body: + application/vnd.ez.api.PolicyCreate+xml: + type: PolicyCreate + example: !include examples/user/roles/id/policies/POST/PolicyCreate.xml.example + responses: + 201: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/POST/Policy.xml.example + 400: + description: Error - the input does not match the input schema definition or validation of limitation in PolicyCreate fails. + 401: + description: Error - the user is not authorized to create the Policy. + 404: + description: Error - the Role does not exist. + /{id}: + patch: + # Currently does not work + displayName: Update Policy + description: Updates a Policy. PATCH or POST with header X-HTTP-Method-Override PATCH. + headers: + Accept: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + Content-Type: + description: If set, the updated Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyUpdate+xml + application/vnd.ez.api.PolicyUpdate+json + If-Match: + description: Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned. + example: ETag + body: + application/vnd.ez.api.PolicyUpdate+xml: + type: PolicyUpdate + example: !include examples/user/roles/id/policies/id/PATCH/PolicyUpdate.xml.example + responses: + 200: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/id/PATCH/Policy.xml.example + 400: + description: Error - the input does not match the input schema definition or validation of limitation in PolicyUpdate fails. + 401: + description: Error - the user is not authorized to update the Policy. + 404: + description: Error - the Role does not exist. + 412: + description: Error - the current ETag does not match with the one provided in the If-Match header. + get: + displayName: Load Policy + description: Loads a Policy for the given module and function. + headers: + Accept: + description: If set, the Policy is returned in XML or JSON format. + example: | + application/vnd.ez.api.Policy+xml + application/vnd.ez.api.Policy+json + If-None-Match: + description: ETag + responses: + 200: + body: + application/vnd.ez.api.Policy+xml: + type: Policy + example: !include examples/user/roles/id/policies/id/GET/Policy.xml.example + 401: + description: Error - the user has no permission to read Roles. + 404: + description: Error - the Role or Policy does not exist. + delete: + displayName: Delete Policy + description: Deletes given Policy. + responses: + 204: + description: No Content - the given Policy is deleted. + 401: + description: Error - the user is not authorized to delete this Content Type. + 404: + description: Error - the Role or Policy does not exist. + /policies: + get: + displayName: List Policies for User + description: Search all Policies which are applied to a given User. + queryParameters: + userId: + description: The User ID. + headers: + Accept: + description: If set, the Policy list is returned in XML or JSON format. + example: | + application/vnd.ez.api.PolicyList+xml + application/vnd.ez.api.PolicyList+json + responses: + 200: + description: OK - Policies which are applied to a given User. + body: + application/vnd.ez.api.PolicyList+xml: + type: PolicyList + example: !include examples/user/policies/GET/PolicyList.xml.example + 401: + description: Error - the user has no permission to read Roles. + /sessions: + displayName: User sessions + post: + displayName: Create session (login a User) + description: Performs a login for the user or checks if session exists and returns the session and session cookie. The client will need to remember both session name/ID and CSRF token as this is for security reasons not exposed via GET. + headers: + Accept: + description: If set, the session is returned in XML or JSON format. + example: | + application/vnd.ez.api.Session+xml + application/vnd.ez.api.Session+json + Content-Type: + description: The SessionInput schema encoded in XML or JSON format. + example: | + application/vnd.ez.api.SessionInput+xml + application/vnd.ez.api.SessionInput+json + Cookie: + description: Only needed for session's checking {sessionName}={sessionID}. + X-CSRF-Token: + description: Only needed for session's checking. The {csrfToken} needed on all unsafe http methods with session. + body: + application/vnd.ez.api.SessionInput+xml: + type: SessionInput + example: !include examples/user/sessions/POST/SessionInput.xml.example + application/vnd.ez.api.SessionInput+json: + type: SessionInput + example: !include examples/user/sessions/POST/SessionInput.json.example + responses: + 200: + description: Session already exists. + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/POST/Session.json.example + 201: + description: Session is created. + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/POST/Session.json.example + 400: + description: Error - the input does not match the input schema definition. + 401: + description: Error - the authorization failed. + 409: + description: Error - header contained a session cookie but different user was authorized. + /{sessionId}: + delete: + displayName: Delete session (logout a User) + description: The user session is removed i.e. the user is logged out. + headers: + Cookie: + description: "{sessionName}={sessionID}" + X-CSRF-Token: + description: The {csrfToken} needed on all unsafe http methods with session. + responses: + 204: + description: OK - session deleted. + 404: + description: Error - the session does not exist. + /refresh: + post: + displayName: Refresh session + description: Get the session's User information. + headers: + Cookie: + description: "{sessionName}={sessionID}" + X-CSRF-Token: + description: The {csrfToken} needed on all unsafe http methods with session. + responses: + 200: + body: + application/vnd.ez.api.Session+xml: + type: Session + example: !include examples/user/sessions/session_id/refresh/POST/Session.xml.example + application/vnd.ez.api.Session+json: + type: Session + example: !include examples/user/sessions/session_id/refresh/POST/Session.json.example + 404: + description: Error - the session does not exist. +/services: + displayName: Services + /countries: + get: + displayName: Countries list + description: Gives access to an ISO-3166 formatted list of world countries. It is useful when presenting a country options list from any application. + headers: + Accept: + description: If set, the country list is returned in XML or JSON format. + example: | + application/vnd.ez.api.CountriesLis+xml + application/vnd.ez.api.CountriesLis+json + responses: + 200: + body: + application/vnd.ez.api.CountriesList+xml: + type: CountryList + example: !include examples/services/countries/GET/CountriesList.xml.example + +types: !include ez-types.raml diff --git a/docs/api/rest_api_reference/rest_api_reference.html b/docs/api/rest_api_reference/rest_api_reference.html new file mode 100644 index 0000000000..620a6fe083 --- /dev/null +++ b/docs/api/rest_api_reference/rest_api_reference.html @@ -0,0 +1,88093 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> + <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/> + <link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet"> + <link href="assets/css/bootstrap.min.css" rel="stylesheet"> + <link href="assets/css/bootstrap-toc.min.css" rel="stylesheet"> + <link href="assets/fonts/MavenPro.css" rel="stylesheet"> + <link href="assets/css/github-gist.css" rel="stylesheet"> + <link href="assets/css/style.css" rel="stylesheet"> + <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> + <title>Ibexa REST API + + + +
    +
    +
    +
    + + ez-logo + + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +

    + Root resources + +

    +
    +
    +

    /

    + +
    +
    +
    +
    +
    +
    + List of root resources + +
    +
    +
    +

    + GET + / +

    +

    Lists the root resources of the Ibexa Platform installation.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the list is return in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Root+xml +application/vnd.ez.api.Root+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Root + + This class represents a root.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Root media-type="application/vnd.ez.api.Root+xml">
    +    <content media-type="" href="/api/ezp/v2/content/objects"/>
    +    <contentByRemoteId media-type="" href="/api/ezp/v2/content/objects{?remoteId}"/>
    +    <contentTypes media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/types"/>
    +    <contentTypeByIdentifier media-type="" href="/api/ezp/v2/content/types{?identifier}"/>
    +    <contentTypeGroups media-type="application/vnd.ez.api.ContentTypeGroupList+xml" href="/api/ezp/v2/content/typegroups"/>
    +    <contentTypeGroupByIdentifier media-type="" href="/api/ezp/v2/content/typegroups{?identifier}"/>
    +    <users media-type="application/vnd.ez.api.UserRefList+xml" href="/api/ezp/v2/user/users"/>
    +    <roles media-type="application/vnd.ez.api.RoleList+xml" href="/api/ezp/v2/user/roles"/>
    +    <rootLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2"/>
    +    <rootUserGroup media-type="app
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Root": {
    +        "_media-type": "application/vnd.ez.api.Root+json",
    +        "content": {
    +            "_href": "/api/ezp/v2/content/objects",
    +            "_media-type": ""
    +        },
    +        "contentByRemoteId": {
    +            "_href": "/api/ezp/v2/content/objects{?remoteId}",
    +            "_media-type": ""
    +        },
    +        "contentTypeByIdentifier": {
    +            "_href": "/api/ezp/v2/content/types{?identifier}",
    +            "_media-type": ""
    +        },
    +        "contentTypeGroupByIdentifier": {
    +            "_href": "/api/ezp/v2/content/typegroups{?identifier}",
    +            "_media-type": ""
    +        },
    +        "contentTypeGroups": {
    +            "_href": "/api/ezp/v2/content/typegroups",
    +            "_media-type": "application/vnd.ez.api.ContentTypeGroupList+json"
    +        },
    +        "contentTypes": {
    +            "_href": "/api/ezp/v2/content/types",
    +            "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json"
    +        },
    +        "createSession": {
    +            "_href
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Managing bookmarks + +

    +
    +
    +

    /bookmark

    + +
    +
    +
    +
    +
    +
    + List of bookmarks + +
    +
    +
    +

    + GET + /bookmark +

    +

    Lists bookmarked Locations for the current user.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.BookmarkList+xml +application/vnd.ez.api.BookmarkList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + offset + + + + integer + + + + + + The offset of the result set. +
    + limit + + + + integer + + + + + + The number of returned bookmarks. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to list bookmarks.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + BookmarkList + + List of bookmarked Locations.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<BookmarkList media-type="application/vnd.ez.api.BookmarkList+xml">
    +    <count>3</count>
    +    <Bookmark media-type="application/vnd.ez.api.Bookmark+xml" _href="/api/ezp/v2/bookmark/65">
    +        <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/57/65">
    +            <id>65</id>
    +            <priority>0</priority>
    +            <hidden>false</hidden>
    +            <invisible>false</invisible>
    +            <explicitlyHidden>false</explicitlyHidden>
    +            <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/57"/>
    +            <pathString>/1/2/57/65/</pathString>
    +            <depth>3</depth>
    +            <childCount>0</childCount>
    +            <remoteId>aa538e305aea472eb221ce23d1cc4b50</remoteId>
    +            <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/57/65/children"/>
    +            <Content media-type=
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "BookmarkList": {
    +        "_media-type": "application/vnd.ez.api.BookmarkList+json",
    +        "count": 3,
    +        "items": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Bookmark+json",
    +                "__href": "/api/ezp/v2/bookmark/65",
    +                "Location": {
    +                    "_media-type": "application/vnd.ez.api.Location+json",
    +                    "_href": "/api/ezp/v2/content/locations/1/2/57/65",
    +                    "id": 65,
    +                    "priority": 0,
    +                    "hidden": false,
    +                    "invisible": false,
    +                    "explicitlyHidden": false,
    +                    "ParentLocation": {
    +                        "_media-type": "application/vnd.ez.api.Location+json",
    +                        "_href": "/api/ezp/v2/content/locations/1/2/57"
    +                    },
    +                    "pathString": "/1/2/57/65/",
    +                    "depth": 3,
    +                    "childCount": 0,
    +                    "remoteId": "a
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /bookmark/{locationId}

    + +
    +
    +
    +
    +
    +
    + Create bookmark + +
    +
    +
    +

    + POST + /bookmark/{locationId} +

    +

    Add given Location to bookmarks of the current user.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Created.

    +
    + 401 + +

    Error - the user is not authorized to given Location.

    +
    + 404 + +

    Error - the given Location does not exist.

    +
    + 409 + +

    Error - Location is already bookmarked.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Check if Location is bookmarked + +
    +
    +
    +

    + HEAD + /bookmark/{locationId} +

    +

    Checks if the given Location is bookmarked by the current user.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 200 + +

    OK - bookmarked.

    +
    + 401 + +

    Error - the user is not authorized for the given Location.

    +
    + 404 + +

    Error - the given Location does not exist / is not bookmarked.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete bookmark + +
    +
    +
    +

    + DELETE + /bookmark/{locationId} +

    +

    Deletes the given Location from bookmarks of the current user.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    Deleted - no content.

    +
    + 401 + +

    Error - the user is not authorized for the given Location.

    +
    + 404 + +

    Error - the given Location does not exist / is not bookmarked.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Managing content + +

    +
    +

    /content/objects

    +
    +
    +
    +

    /content/objects

    + +
    +
    +
    +
    +
    +
    + Create Content item + +
    +
    +
    +

    + POST + /content/objects +

    +

    Creates a draft assigned to the authenticated user. If a different user ID is given in the input, the draft is assigned to the given user but this action requires special permissions for the authenticated user (this is useful for content staging where the transfer process does not have to authenticate with the user who created the Content item in the source server). The user needs to publish the Content item if it should be visible.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Content+xml +application/vnd.ez.api.Content+json +application/vnd.ez.api.ContentInfo+xml +application/vnd.ez.api.ContentInfo+json + +
    +
    +
    +
    +

    Content-Type

    +

    The ContentCreate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentCreate+xml +application/vnd.ez.api.ContentCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition or the validation on a field fails.

    +
    + 401 + +

    Error - the user is not authorized to create this Object in this Location.

    +
    + 404 + +

    Error - the parent Location specified in the request body does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentCreate + + This class is used for creating a new Content item.
    + + Content + + Content ID matcher class.
    + + ContentInfo + + This class provides all version independent information of the Content item.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentCreate xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    +  <ContentType href="/api/ezp/v2/content/types/2"/>
    +  <mainLanguageCode>eng-GB</mainLanguageCode>
    +  <LocationCreate>
    +    <ParentLocation href="/api/ezp/v2/content/locations/1/2"/>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <sortField>PATH</sortField>
    +    <sortOrder>ASC</sortOrder>
    +  </LocationCreate>
    +  <Section href="/api/ezp/v2/content/sections/1"/>
    +  <alwaysAvailable>true</alwaysAvailable>
    +  <remoteId></remoteId>
    +  <fields>
    +    <field>
    +      <fieldDefinitionIdentifier>title</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>draft article</fieldValue>
    +    </field>
    +    <field>
    +      <fieldDefinitionIdentifier>intro</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>
    +        <value key="xml"><![CDATA[<section xmlns="http://ez.no/namespaces/ezpublish5/xhtml5/edit"><p>draft draft</p></section>]
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentCreate": {
    +    "ContentType": {
    +      "_href": "/api/ezp/v2/content/types/2"
    +      },
    +    "mainLanguageCode": "eng-GB",
    +    "LocationCreate": {
    +      "ParentLocation": {
    +        "_href": "/api/ezp/v2/content/locations/1/2"
    +       },
    +      "priority": "0",
    +      "hidden": "false",
    +      "sortField": "PATH",
    +      "sortOrder": "ASC"
    +    },
    +    "Section": {
    +      "_href": "/api/ezp/v2/content/sections/1"
    +    },
    +    "alwaysAvailable": "true",
    +    "fields": {
    +      "field": [
    +        {
    +          "fieldDefinitionIdentifier": "title",
    +          "languageCode": "eng-GB",
    +          "fieldTypeIdentifier": "ezstring",
    +          "fieldValue": "draft article"
    +        },
    +        {
    +          "fieldDefinitionIdentifier": "intro",
    +          "languageCode": "eng-GB",
    +          "fieldTypeIdentifier": "ezrichtext",
    +          "fieldValue": 
    +           {
    +                "xml": "<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5/edit\"><p>draft draft</p></section>"
    +            }
    +        }
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/83" remoteId="a9c8f00b1dba880df7a5ed3e93fad421" id="83">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"/>
    +    <Name>draft article</Name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/83/versions"/>
    +    <CurrentVersion media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/83/currentversion">
    +        <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/83/versions/1">
    +            <VersionInfo>
    +                <id>547</id>
    +                <versionNo>1</versionNo>
    +                <status>DRAFT</status>
    +                <modificationDate>2019-02-19T15:55:37+01:00</modificationDate>
    +                <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +     
    +                            
    +
    + View more +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/67/versions/5">
    +    <VersionInfo>
    +        <id>1114</id>
    +        <versionNo>5</versionNo>
    +        <status>PUBLISHED</status>
    +        <modificationDate>2019-03-16T11:19:51+01:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2019-03-16T11:18:45+01:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB,fre-FR,ger-DE,nor-NO</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +            <Language>
    +                <languageCode>fre-FR</languageCode>
    +            </Language>
    +            <Language>
    +                <languageCode>ger-DE</languageCode>
    +            </Languag
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Content": {
    +        "_media-type": "application/vnd.ez.api.Content+json",
    +        "_href": "/api/ezp/v2/content/objects/57",
    +        "_remoteId": "055e5ed41cba5e8ec81d8846a4d1c673",
    +        "_id": 57,
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/2"
    +        },
    +        "Name": "draft article",
    +        "TranslatedName": "draft article",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/57/versions"
    +        },
    +        "CurrentVersion": {
    +            "_media-type": "application/vnd.ez.api.Version+json",
    +            "_href": "/api/ezp/v2/content/objects/57/currentversion",
    +            "Version": {
    +                "_media-type": "application/vnd.ez.api.Version+json",
    +                "_href": "/api/ezp/v2/content/objects/57/versions/1",
    +                "VersionInfo": {
    +                    "id": 517,
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Load content by remote ID + +
    +
    +
    +

    + GET + /content/objects +

    +

    Loads Content item for a given remote ID.

    +

    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + remoteId + + + + string + + + + + + The remote ID of the Content item. If present, the Content item with the given remote ID is returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 307 + +

    Temporary redirect.

    +
    + 404 + +

    Error - the content with the given remote ID does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}

    + +
    +
    +
    +
    +
    +
    + Load content + +
    +
    +
    +

    + GET + /content/objects/{contentId} +

    +

    Loads the Content item for the given ID. Depending on the Accept header the current version is embedded (i.e. the current published version or if it does not exist, the draft of the authenticated user).

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    Content - If set, all information for the Content item including the embedded current version is returned in XML or JSON format. ContentInfo - If set, all information for the Content item (excluding the current version) is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Content+xml +application/vnd.ez.api.Content+json +application/vnd.ez.api.ContentInfo+xml +application/vnd.ez.api.ContentInfo+json + +
    +
    +
    +
    +

    If-None-Match

    +

    If the provided ETag matches the current ETag then a "304 Not Modified" is returned. The ETag changes if the meta data has changed, this happens also if there is a new published version.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + languages + + + + string + + + + + + Restricts the output of translatable fields to the given languages. Comma separated list. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this object. This could also happen if there is no published version yet and another user owns a draft of this Content item.

    +
    + 404 + +

    Error - the ID is not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Content + + Content ID matcher class.
    + + ContentInfo + + This class provides all version independent information of the Content item.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/54" remoteId="9e863fbb0fb835ce050032b4f00de61d" id="54">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/1"/>
    +    <Name>Forms</Name>
    +    <TranslatedName>Forms</TranslatedName>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/54/versions"/>
    +    <CurrentVersion media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/54/currentversion">
    +        <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/54/versions/1">
    +            <VersionInfo>
    +                <id>514</id>
    +                <versionNo>1</versionNo>
    +                <status>PUBLISHED</status>
    +                <modificationDate>2018-09-17T06:48:13+00:00</modificationDate>
    +                <Creator media-type="application/vnd.ez.api.User+xml" h
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Content": {
    +        "_media-type": "application/vnd.ez.api.Content+json",
    +        "_href": "/api/ezp/v2/content/objects/54",
    +        "_remoteId": "9e863fbb0fb835ce050032b4f00de61d",
    +        "_id": 54,
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/1"
    +        },
    +        "Name": "Forms",
    +        "TranslatedName": "Forms",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/54/versions"
    +        },
    +        "CurrentVersion": {
    +            "_media-type": "application/vnd.ez.api.Version+json",
    +            "_href": "/api/ezp/v2/content/objects/54/currentversion",
    +            "Version": {
    +                "_media-type": "application/vnd.ez.api.Version+json",
    +                "_href": "/api/ezp/v2/content/objects/54/versions/1",
    +                "VersionInfo": {
    +                    "id": 514,
    +                
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update content + +
    +
    +
    +

    + PATCH + /content/objects/{contentId} +

    +

    This method updates the content metadata which is independent from a version. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, all information for the Content item (excluding the current version) is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentInfo+xml +application/vnd.ez.api.ContentInfo+json + +
    +
    +
    +
    +

    If-match

    +

    Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    +

    Content-Type

    +

    The ContentUpdate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentUpdate+xml +application/vnd.ez.api.ContentUpdate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update this object.

    +
    + 404 + +

    Error - the content ID does not exist.

    +
    + 412 + +

    Error - the current ETag does not match with the one provided in the If-Match header.

    +
    + 415 + +

    Error - the media-type is not one of those specified in headers.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentInfo + + This class provides all version independent information of the Content item.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentUpdate>
    +  <mainLanguageCode>eng-GB</mainLanguageCode>
    +  <Section href="/api/ezp/v2/content/sections/1"/>
    +  <MainLocation href="/api/ezp/v2/content/locations/2/63/66"/>
    +  <Owner href="/api/ezp/v2/user/users/11"/>
    +  <alwaysAvailable>false</alwaysAvailable>
    +  <remoteId>69848aeb86164c35e5c98202eebe9e05</remoteId>
    +</ContentUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/80" remoteId="69848aeb86164c35e5c98202eebe9e05" id="80">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2"/>
    +    <Name>article1</Name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/80/versions"/>
    +    <CurrentVersion media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/80/currentversion"/>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/1"/>
    +    <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/63/66"/>
    +    <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/80/locations"/>
    +    <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/11"/>
    +    <lastModi
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Content + +
    +
    +
    +

    + DELETE + /content/objects/{contentId} +

    +

    Deletes Content item. If Content item has multiple Locations, all of them will be deleted via delete a subtree.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    The Content item is deleted.

    +
    + 404 + +

    Error - Content item was not found.

    +
    + 401 + +

    Error - the user is not authorized to delete this Content item.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Copy content + +
    +
    +
    +

    + COPY + /content/objects/{contentId} +

    +

    Creates new Content item as a copy, under the given parent Location given in the destination header. COPY or POST with header X-HTTP-Method-Override COPY.

    +

    +
    +
    Header parameters
    +
    +

    destination

    +

    A Location resource to which the Content item should be copied.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Copy created.

    +
    + 401 + +

    Error - the user is not authorized to copy this Content item to the given Location.

    +
    + 404 + +

    Error - the source or destination resource does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/translations/{languageCode}

    + +
    +
    +
    +
    +
    +
    + Delete translation (permanently) + +
    +
    +
    +

    + DELETE + /content/objects/{contentId}/translations/{languageCode} +

    +

    Permanently deletes a translation from all versions of a Content item.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content

    +
    + 401 + +

    Error - the user is not authorized to delete Content item (content/remove policy).

    +
    + 404 + +

    Error - the Content item was not found.

    +
    + 406 + +

    Error - the given translation does not exist for the Content item.

    +
    + 409 + +

    Error - the specified translation is the only one any version has or is the main translation.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/currentversion

    + +
    +
    +
    +
    +
    +
    + Get current version + +
    +
    +
    +

    + GET + /content/objects/{contentId}/currentversion +

    +

    Redirects to the current version of the Content item.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 307 + +

    Temporary redirect.

    +
    + 404 + +

    Error - the resource does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Version + + Returns the VersionInfo for this version.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/61/versions/1">
    +    <VersionInfo>
    +        <id>521</id>
    +        <versionNo>1</versionNo>
    +        <status>PUBLISHED</status>
    +        <modificationDate>2021-06-28T11:33:49+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2021-06-28T11:33:31+00:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +        </VersionTranslationInfo>
    +        <names>
    +            <value languageCode="eng-GB">Art1</value>
    +        </names>
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/o
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Version": {
    +        "_media-type": "application/vnd.ez.api.Version+json",
    +        "_href": "/api/ezp/v2/content/objects/61/versions/1",
    +        "VersionInfo": {
    +            "id": 521,
    +            "versionNo": 1,
    +            "status": "PUBLISHED",
    +            "modificationDate": "2021-06-28T11:33:49+00:00",
    +            "Creator": {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            },
    +            "creationDate": "2021-06-28T11:33:31+00:00",
    +            "initialLanguageCode": "eng-GB",
    +            "languageCodes": "eng-GB",
    +            "VersionTranslationInfo": {
    +                "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json",
    +                "Language": [
    +                    {
    +                        "languageCode": "eng-GB"
    +                    }
    +                ]
    +            },
    +            "names": {
    +                "value": [
    +                    {
    +                        "_langu
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create a draft from current version + +
    +
    +
    +

    + COPY + /content/objects/{contentId}/currentversion +

    +

    The system creates a new draft as a copy of the current version. COPY or POST with header X-HTTP-Method-Override COPY.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated version is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Version+xml +application/vnd.ez.api.Version+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Created

    +
    + 401 + +

    Error - the user is not authorized to update this Content item.

    +
    + 403 + +

    Error - the current version is already a draft.

    +
    + 404 + +

    Error - the Content item was not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Version + + Returns the VersionInfo for this version.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/61/versions/2">
    +    <VersionInfo>
    +        <id>580</id>
    +        <versionNo>2</versionNo>
    +        <status>DRAFT</status>
    +        <modificationDate>2021-07-26T08:10:02+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2021-07-26T08:10:02+00:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +        </VersionTranslationInfo>
    +        <names>
    +            <value languageCode="eng-GB">Art1</value>
    +        </names>
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objec
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Version": {
    +        "_media-type": "application/vnd.ez.api.Version+json",
    +        "_href": "/api/ezp/v2/content/objects/61/versions/3",
    +        "VersionInfo": {
    +            "id": 581,
    +            "versionNo": 3,
    +            "status": "DRAFT",
    +            "modificationDate": "2021-07-26T08:16:50+00:00",
    +            "Creator": {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            },
    +            "creationDate": "2021-07-26T08:16:50+00:00",
    +            "initialLanguageCode": "eng-GB",
    +            "languageCodes": "eng-GB",
    +            "VersionTranslationInfo": {
    +                "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json",
    +                "Language": [
    +                    {
    +                        "languageCode": "eng-GB"
    +                    }
    +                ]
    +            },
    +            "names": {
    +                "value": [
    +                    {
    +                        "_languageC
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/versions

    + +
    +
    +
    +
    +
    +
    + List versions + +
    +
    +
    +

    + GET + /content/objects/{contentId}/versions +

    +

    Returns a list of all versions of the Content item. This method does not include fields and relations in the version elements of the response.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the version list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.VersionList+xml +application/vnd.ez.api.VersionList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user has no permission to read the versions.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + VersionList + + List of all versions of the content.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<VersionList media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/61/versions">
    +    <VersionItem>
    +        <Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/61/versions/1"/>
    +        <VersionInfo>
    +            <id>521</id>
    +            <versionNo>1</versionNo>
    +            <status>PUBLISHED</status>
    +            <modificationDate>2021-06-28T11:33:49+00:00</modificationDate>
    +            <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +            <creationDate>2021-06-28T11:33:31+00:00</creationDate>
    +            <initialLanguageCode>eng-GB</initialLanguageCode>
    +            <languageCodes>eng-GB</languageCodes>
    +            <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +                <Language>
    +                    <languageCode>eng-GB</languageCode>
    +                </Language>
    +            </VersionTran
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "VersionList": {
    +        "_media-type": "application/vnd.ez.api.VersionList+json",
    +        "_href": "/api/ezp/v2/content/objects/61/versions",
    +        "VersionItem": [
    +            {
    +                "Version": {
    +                    "_media-type": "application/vnd.ez.api.Version+json",
    +                    "_href": "/api/ezp/v2/content/objects/61/versions/1"
    +                },
    +                "VersionInfo": {
    +                    "id": 521,
    +                    "versionNo": 1,
    +                    "status": "PUBLISHED",
    +                    "modificationDate": "2021-06-28T11:33:49+00:00",
    +                    "Creator": {
    +                        "_media-type": "application/vnd.ez.api.User+json",
    +                        "_href": "/api/ezp/v2/user/users/14"
    +                    },
    +                    "creationDate": "2021-06-28T11:33:31+00:00",
    +                    "initialLanguageCode": "eng-GB",
    +                    "languageCodes": "eng-GB",
    +                    "VersionTranslationInfo": {
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/versions/{versionNo}

    + +
    +
    +
    +
    +
    +
    + Load version + +
    +
    +
    +

    + GET + /content/objects/{contentId}/versions/{versionNo} +

    +

    Loads a specific version of a Content item. This method returns Fields and relations.

    +

    +
    +
    Header parameters
    +
    +

    If-None-Match

    +

    Only return the version if the given ETag is the not current one, otherwise a 304 is returned.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    +

    Accept

    +

    If set, the version list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Version+xml +application/vnd.ez.api.Version+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + fields + + + + string + + + + + + Fields which should be returned in the response. Comma separated list. +
    + responseGroups + + + + string + + + + + + Alternative comma separated lists of predefined Field groups. +
    + languages + + + + string + + + + + + Restricts the output of translatable Fields to the given languages. Comma separated list. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 304 + +

    Error - the ETag does not match the current one.

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the ID or version is not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Version + + Returns the VersionInfo for this version.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/61/versions/3">
    +    <VersionInfo>
    +        <id>581</id>
    +        <versionNo>3</versionNo>
    +        <status>DRAFT</status>
    +        <modificationDate>2021-07-26T08:16:50+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2021-07-26T08:16:50+00:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +        </VersionTranslationInfo>
    +        <names>
    +            <value languageCode="eng-GB">Art1</value>
    +        </names>
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objec
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Version": {
    +        "_media-type": "application/vnd.ez.api.Version+json",
    +        "_href": "/api/ezp/v2/content/objects/61/versions/3",
    +        "VersionInfo": {
    +            "id": 581,
    +            "versionNo": 3,
    +            "status": "DRAFT",
    +            "modificationDate": "2021-07-26T08:16:50+00:00",
    +            "Creator": {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            },
    +            "creationDate": "2021-07-26T08:16:50+00:00",
    +            "initialLanguageCode": "eng-GB",
    +            "languageCodes": "eng-GB",
    +            "VersionTranslationInfo": {
    +                "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json",
    +                "Language": [
    +                    {
    +                        "languageCode": "eng-GB"
    +                    }
    +                ]
    +            },
    +            "names": {
    +                "value": [
    +                    {
    +                        "_languageC
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update version + +
    +
    +
    +

    + PATCH + /content/objects/{contentId}/versions/{versionNo} +

    +

    A specific draft is updated. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated version is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Version+xml +application/vnd.ez.api.Version+json + +
    +
    +
    +
    +

    If-match

    +

    Performs the patch only if the specified ETag is the current one.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    +

    Content-Type

    +

    The VersionUpdate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.VersionUpdate+xml +application/vnd.ez.api.VersionUpdate+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + languages + + + + string + + + + + + Restricts the output of translatable Fields to the given languages. Comma separated list. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update this version.

    +
    + 403 + +

    Error - the version is not allowed to change - i.e. version is not a DRAFT.

    +
    + 404 + +

    Error - the content ID or version ID does not exist.

    +
    + 412 + +

    Error - the current ETag does not match with the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + VersionUpdate + +
    + + Version + + Returns the VersionInfo for this version.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<VersionUpdate xmlns:p="http://ez.no/API/Values"
    +  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +  xsi:schemaLocation="http://ez.no/API/Values ../VersionUpdate.xsd ">
    +  <modificationDate>2012-02-20T12:00:00</modificationDate>
    +  <initialLanguageCode>ger-DE</initialLanguageCode>
    +  <fields>
    +    <field>
    +      <id>1234</id>
    +      <fieldDefinitionIdentifier>title</fieldDefinitionIdentifier>
    +      <languageCode>ger-DE</languageCode>
    +      <fieldValue>Neuer Titel</fieldValue>
    +    </field>
    +    <field>
    +      <id>1235</id>
    +      <fieldDefinitionIdentifier>summary</fieldDefinitionIdentifier>
    +      <languageCode>ger-DE</languageCode>
    +      <fieldValue>Dies ist eine neuse Zusammenfassungy</fieldValue>
    +    </field>
    +  </fields>
    +</VersionUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version href="/content/objects/23/versions/4" media-type="application/vnd.ez.api.Version+xml">
    +  <VersionInfo>
    +    <id>45</id>
    +    <versionNo>4</versionNo>
    +    <status>DRAFT</status>
    +    <modificationDate>2012-02-20T12:00:00</modificationDate>
    +    <Creator href="/user/users/44" media-type="application/vnd.ez.api.User+xml" />
    +    <creationDate>22012-02-20T12:00:00</creationDate>
    +    <initialLanguageCode>ger-DE</initialLanguageCode>
    +    <names>
    +      <value languageCode="ger-DE">Neuer Titel</value>
    +    </names>
    +    <Content href="/content/objects/23" media-type="application/vnd.ez.api.ContentInfo+xml" />
    +  </VersionInfo>
    +  <Fields>
    +    <field>
    +      <id>1234</id>
    +      <fieldDefinitionIdentifier>title</fieldDefinitionIdentifier>
    +      <languageCode>ger-DE</languageCode>
    +      <fieldValue>Neuer Titel</fieldValue>
    +    </field>
    +    <field>
    +      <id>1235</id>
    +      <fieldDefinitionIdentifier>summary</fieldDefinitionIdentifier>
    +      <languageCode>ger-
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create a draft from a version + +
    +
    +
    +

    + COPY + /content/objects/{contentId}/versions/{versionNo} +

    +

    The system creates a new draft as a copy of the given version. COPY or POST with header X-HTTP-Method-Override COPY.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated version is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Version+xml +application/vnd.ez.api.Version+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Created.

    +
    + 401 + +

    Error - the user is not authorized to update this Content item.

    +
    + 404 + +

    Error - the Content item was not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Version + + Returns the VersionInfo for this version.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/61/versions/3">
    +    <VersionInfo>
    +        <id>582</id>
    +        <versionNo>3</versionNo>
    +        <status>DRAFT</status>
    +        <modificationDate>2021-07-26T08:54:47+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2021-07-26T08:54:47+00:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +        </VersionTranslationInfo>
    +        <names>
    +            <value languageCode="eng-GB">Art1</value>
    +        </names>
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objec
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Version": {
    +        "_media-type": "application/vnd.ez.api.Version+json",
    +        "_href": "/api/ezp/v2/content/objects/61/versions/4",
    +        "VersionInfo": {
    +            "id": 583,
    +            "versionNo": 4,
    +            "status": "DRAFT",
    +            "modificationDate": "2021-07-26T08:55:27+00:00",
    +            "Creator": {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            },
    +            "creationDate": "2021-07-26T08:55:27+00:00",
    +            "initialLanguageCode": "eng-GB",
    +            "languageCodes": "eng-GB",
    +            "VersionTranslationInfo": {
    +                "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json",
    +                "Language": [
    +                    {
    +                        "languageCode": "eng-GB"
    +                    }
    +                ]
    +            },
    +            "names": {
    +                "value": [
    +                    {
    +                        "_languageC
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete content version + +
    +
    +
    +

    + DELETE + /content/objects/{contentId}/versions/{versionNo} +

    +

    Deletes the content version.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - the version is deleted.

    +
    + 404 + +

    Error - the Content item or version were not found.

    +
    + 401 + +

    Error - the user is not authorized to delete this version.

    +
    + 403 + +

    Error - the version is in published state.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Publish a content version + +
    +
    +
    +

    + PUBLISH + /content/objects/{contentId}/versions/{versionNo} +

    +

    Publishes the content version. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - the content version is published.

    +
    + 401 + +

    Error - the user is not authorized to publish this version.

    +
    + 403 + +

    Error - the version is not a draft.

    +
    + 404 + +

    Error - the Content item or version were not found.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/versions/{versionNo}/translations/{languageCode}

    + +
    +
    +
    +
    +
    +
    + Delete translation from version draft + +
    +
    +
    +

    + DELETE + /content/objects/{contentId}/versions/{versionNo}/translations/{languageCode} +

    +

    Removes a translation from a version draft.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - removes a translation from a version draft.

    +
    + 401 + +

    Error - the user is not authorized to delete this translation.

    +
    + 403 + +

    Error - the version is not in draft state.

    +
    + 404 + +

    Error - the Content item or version number were not found.

    +
    + 406 + +

    Error - the given translation does not exist for the version.

    +
    + 409 + +

    Error - the specified translation is the only one the version has or is the main translation.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/versions/{versionNo}/relations

    + +
    +
    +
    +
    +
    +
    + Load Relations of Content item version + +
    +
    +
    +

    + GET + /content/objects/{contentId}/versions/{versionNo}/relations +

    +

    Loads the Relations of the given version.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Relation is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RelationList+xml +application/vnd.ez.api.RelationList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + offset + + + + integer + + + + + + The offset of the result set. +
    + limit + + + + integer + + + + + + The number of bookmarks returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the Content item was not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RelationList + + Class representing a list of relations between content.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Version media-type="application/vnd.ez.api.Version+xml" href="/api/ezp/v2/content/objects/59/versions/1">
    +    <VersionInfo>
    +        <id>519</id>
    +        <versionNo>1</versionNo>
    +        <status>PUBLISHED</status>
    +        <modificationDate>2021-06-28T11:33:01+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <creationDate>2021-06-28T11:32:39+00:00</creationDate>
    +        <initialLanguageCode>eng-GB</initialLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <VersionTranslationInfo media-type="application/vnd.ez.api.VersionTranslationInfo+xml">
    +            <Language>
    +                <languageCode>eng-GB</languageCode>
    +            </Language>
    +        </VersionTranslationInfo>
    +        <names>
    +            <value languageCode="eng-GB">Art1</value>
    +        </names>
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/o
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Version": {
    +        "_media-type": "application/vnd.ez.api.Version+json",
    +        "_href": "/api/ezp/v2/content/objects/59/versions/1",
    +        "VersionInfo": {
    +            "id": 519,
    +            "versionNo": 1,
    +            "status": "PUBLISHED",
    +            "modificationDate": "2021-06-28T11:33:01+00:00",
    +            "Creator": {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            },
    +            "creationDate": "2021-06-28T11:32:39+00:00",
    +            "initialLanguageCode": "eng-GB",
    +            "languageCodes": "eng-GB",
    +            "VersionTranslationInfo": {
    +                "_media-type": "application/vnd.ez.api.VersionTranslationInfo+json",
    +                "Language": [
    +                    {
    +                        "languageCode": "eng-GB"
    +                    }
    +                ]
    +            },
    +            "names": {
    +                "value": [
    +                    {
    +                        "_langu
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create new Relation + +
    +
    +
    +

    + POST + /content/objects/{contentId}/versions/{versionNo}/relations +

    +

    Creates a new Relation of type COMMON for the given draft.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated version is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Relation+xml +application/vnd.ez.api.Relation+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RelationCreate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RelationCreate+xml +application/vnd.ez.api.RelationCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Relation + + Class representing a relation between content.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Relation media-type="application/vnd.ez.api.Relation+xml" href="/api/ezp/v2/content/objects/59/versions/2/relations/40">
    +    <SourceContent media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/59"/>
    +    <DestinationContent media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/59"/>
    +    <RelationType>COMMON</RelationType>
    +</Relation>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Relation": {
    +        "_media-type": "application/vnd.ez.api.Relation+json",
    +        "_href": "/api/ezp/v2/content/objects/59/versions/2/relations/38",
    +        "SourceContent": {
    +            "_media-type": "application/vnd.ez.api.ContentInfo+json",
    +            "_href": "/api/ezp/v2/content/objects/59"
    +        },
    +        "DestinationContent": {
    +            "_media-type": "application/vnd.ez.api.ContentInfo+json",
    +            "_href": "/api/ezp/v2/content/objects/59"
    +        },
    +        "RelationType": "COMMON"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/versions/{versionNo}/relations/{relationId}

    + +
    +
    +
    +
    +
    +
    + Load Relation + +
    +
    +
    +

    + GET + /content/objects/{contentId}/versions/{versionNo}/relations/{relationId} +

    +

    Loads a Relation for the given Content item.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Relation is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Relation+xml +application/vnd.ez.api.Relation+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 200 + +

    OK - loads a Relation for the given Content item.

    +
    + body + +

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the Content item with the given ID or the Relation does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Relation + +
    +
    +
    +

    + DELETE + /content/objects/{contentId}/versions/{versionNo}/relations/{relationId} +

    +

    Deletes a Relation of the given draft.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - deleted a Relation of the given draft.

    +
    + 401 + +

    Error - the user is not authorized to delete this Relation.

    +
    + 403 + +

    Error - the Relation is not of type COMMON or the given version is not a draft.

    +
    + 404 + +

    Error - Content item or the Relation were not found in the given version.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/relations

    + +
    +
    +
    +
    +
    +
    + Load Relations of Content item + +
    +
    +
    +

    + GET + /content/objects/{contentId}/relations +

    +

    Redirects to the Relations of the current version.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 307 + +

    Temporary redirect.

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the Content item was not found.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/locations

    + +
    +
    +
    +
    +
    +
    + Create new Location for Content item + +
    +
    +
    +

    + POST + /content/objects/{contentId}/locations +

    +

    Creates a new Location for the given Content item.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Location is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Location+xml +application/vnd.ez.api.Location+json + +
    +
    +
    +
    +

    Content-Type

    +

    The LocationCreate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.LocationCreate+json +application/vnd.ez.api.LocationCreate+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to create this Location.

    +
    + 403 + +

    Error - a Location under the given parent ID already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + LocationCreate + +
    + + Location + + This class represents a Location in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<LocationCreate>
    +  <ParentLocation href="/api/ezp/v2/content/locations/1/42" />
    +  <priority>0</priority>
    +  <hidden>false</hidden>
    +  <sortField>PATH</sortField>
    +  <sortOrder>ASC</sortOrder>
    +</LocationCreate>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "LocationCreate": {
    +        "ParentLocation": {
    +            "_href": "/api/ezp/v2/content/locations/1/59"
    +        },
    +     "priority": "0",
    +            "hidden": false,
    +            "sortField": "PATH",
    +            "sortOrder": "ASC"
    +       
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/42/96">
    +    <id>96</id>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <invisible>true</invisible>
    +    <explicitlyHidden>false</explicitlyHidden>
    +    <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/42"/>
    +    <pathString>/1/2/42/96/</pathString>
    +    <depth>3</depth>
    +    <childCount>0</childCount>
    +    <remoteId>58133c8c75230e5debe362a28b92d27a</remoteId>
    +    <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/42/96/children"/>
    +    <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/63"/>
    +    <sortField>PATH</sortField>
    +    <sortOrder>ASC</sortOrder>
    +    <UrlAliases media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/locations/1/2/42/96/urlaliases"/>
    +    <ContentInfo m
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Location": {
    +        "_media-type": "application/vnd.ez.api.Location+json",
    +        "_href": "/api/ezp/v2/content/locations/1/2/59/114",
    +        "id": 114,
    +        "priority": 0,
    +        "hidden": false,
    +        "invisible": false,
    +        "explicitlyHidden": false,
    +        "ParentLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/59"
    +        },
    +        "pathString": "/1/2/59/114/",
    +        "depth": 3,
    +        "childCount": 0,
    +        "remoteId": "47a1e4ee082fb64e93a822dcfe3a5614",
    +        "Children": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/59/114/children"
    +        },
    +        "Content": {
    +            "_media-type": "application/vnd.ez.api.Content+json",
    +            "_href": "/api/ezp/v2/content/objects/59"
    +        },
    +        "sortField": "PATH",
    +        "sortOrder": "ASC",
    +        "UrlAliases": {
    +           
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Get Locations for Content item + +
    +
    +
    +

    + GET + /content/objects/{contentId}/locations +

    +

    Loads all Locations for the given Content item.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Location list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.LocationList+xml +application/vnd.ez.api.LocationList+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the Content item with the given ID does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + LocationList + + This class represents a queried Location list holding a totalCount and a partial list of Locations (by offset/limit parameters and permission filters).
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<LocationList media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/63/locations">
    +    <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/57/65"/>
    +</LocationList>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "LocationList": {
    +        "_media-type": "application/vnd.ez.api.LocationList+json",
    +        "_href": "/api/ezp/v2/content/objects/63/locations",
    +        "Location": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Location+json",
    +                "_href": "/api/ezp/v2/content/locations/1/2/57/65"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/objectstates

    + +
    +
    +
    +
    +
    +
    + Get Object states of Content item + +
    +
    +
    +

    + GET + /content/objects/{contentId}/objectstates +

    +

    Returns the Object states of a Content item

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Object states are returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentObjectStates+xml +application/vnd.ez.api.ContentObjectStates+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Object state.

    +
    + 404 + +

    Error - The Content item does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentObjectStates + + Base struct for content create/update structs.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentObjectStates media-type="application/vnd.ez.api.ContentObjectStates+xml">
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates/1"/>
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/3/objectstates/3"/>
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/7/objectstates/7"/>
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/8/objectstates/8"/>
    +</ContentObjectStates>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentObjectStates": {
    +        "_media-type": "application/vnd.ez.api.ContentObjectStates+json",
    +        "ObjectState": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectState+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/1"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectState+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/3/objectstates/3"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectState+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates/7"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectState+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/8/objectstates/8"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Set Object states of Content item + +
    +
    +
    +

    + PATCH + /content/objects/{contentId}/objectstates +

    +

    Updates Object states of a Content item. An Object state in the input overrides the state of the Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Object state is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectState+xml +application/vnd.ez.api.ObjectState+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content item Object states input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateUpdate+xml +application/vnd.ez.api.ObjectStateUpdate+json + +
    +
    +
    +
    +

    If-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 204 + + +

    OK - Object state updated.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to set an Object state.

    +
    + 403 + +

    Error - The input contains multiple Object states of the same Object state group.

    +
    + 412 + +

    Error - The current ETag does not match the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateUpdate + +
    + + ObjectState + + This class represents a Object state value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/hide

    + +
    +
    +
    +
    +
    +
    + Hide Content item + +
    +
    +
    +

    + POST + /content/objects/{contentId}/hide +

    +

    Makes or keep the Content item invisible

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    OK - Object item is hidden.

    +
    + 401 + +

    Error - The user has no permission to change Object item visibility.

    +
    + 404 + +

    Error - The Content item was not found.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objects/{contentId}/reveal

    + +
    +
    +
    +
    +
    +
    + Reveal Content item + +
    +
    +
    +

    + POST + /content/objects/{contentId}/reveal +

    +

    Makes or keep the Content item visible

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    OK - Object item is revealed.

    +
    + 401 + +

    Error - The user has no permission to change Object item visibility.

    +
    + 404 + +

    Error - The Content item was not found.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/objectstategroups

    +
    +
    +
    +

    /content/objectstategroups

    + +
    +
    +
    +
    +
    +
    + List Object state groups + +
    +
    +
    +

    + GET + /content/objectstategroups +

    +

    Returns a list of all Object state groups.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Object state group list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroupList+xml +application/vnd.ez.api.ObjectStateGroupList+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list of Object state groups.

    +
    + 401 + +

    Error - The user has no permission to read Object state groups.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateGroupList + + List of Object state groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateGroupList media-type="application/vnd.ez.api.ObjectStateGroupList+xml" href="/api/ezp/v2/content/objectstategroups">
    +    <ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/2">
    +        <id>2</id>
    +        <identifier>ez_lock</identifier>
    +        <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <ObjectStates media-type="application/vnd.ez.api.ObjectStateList+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates"/>
    +        <names>
    +            <value languageCode="eng-GB">Lock</value>
    +        </names>
    +        <descriptions>
    +            <value languageCode="eng-GB"></value>
    +        </descriptions>
    +    </ObjectStateGroup>
    +    <ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/3">
    +        <id>3</id>
    +        <identifier>accessibility</identifier>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroupList": {
    +        "_media-type": "application/vnd.ez.api.ObjectStateGroupList+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups",
    +        "ObjectStateGroup": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/2",
    +                "id": 2,
    +                "identifier": "ez_lock",
    +                "defaultLanguageCode": "eng-GB",
    +                "languageCodes": "eng-GB",
    +                "ObjectStates": {
    +                    "_media-type": "application/vnd.ez.api.ObjectStateList+json",
    +                    "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates"
    +                },
    +                "names": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": "Lock"
    +                        }
    +                    ]
    +                },
    +                "
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Object state group + +
    +
    +
    +

    + POST + /content/objectstategroups +

    +

    Creates a new Object state group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Object state group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroup+xml +application/vnd.ez.api.ObjectStateGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Object state group input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroupCreate+json +application/vnd.ez.api.ObjectStateGroupCreate+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Object state group created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create an Object state group.

    +
    + 403 + +

    Error - An Object state group with the same identifier already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateGroupCreate + +
    + + ObjectStateGroup + + This class represents an Object state group value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateGroupCreate>
    +    <identifier>custom</identifier>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <names>
    +        <value languageCode="eng-GB">Custom State</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">Custom Object state</value>
    +    </descriptions>
    +</ObjectStateGroup>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroup": {
    +        "identifier": "custom-states",
    +        "defaultLanguageCode": "eng-GB",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom State"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom Object state"
    +                }
    +            ]
    +        }
    +    }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/5">
    +    <id>5</id>
    +    <identifier>custom-states</identifier>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <languageCodes>eng-GB</languageCodes>
    +    <ObjectStates media-type="application/vnd.ez.api.ObjectStateList+xml" href="/api/ezp/v2/content/objectstategroups/5/objectstates"/>
    +    <names>
    +        <value languageCode="eng-GB">Custom State</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">Custom Object state</value>
    +    </descriptions>
    +</ObjectStateGroup>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroup": {
    +        "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/7",
    +        "id": 7,
    +        "identifier": "custom-states",
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "ObjectStates": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateList+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates"
    +        },
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom State"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom Object state"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objectstategroups/{objectStateGroupId}

    + +
    +
    +
    +
    +
    +
    + Get Object state group + +
    +
    +
    +

    + GET + /content/objectstategroups/{objectStateGroupId} +

    +

    Returns the Object state group with the provided ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Object state group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroup+xml +application/vnd.ez.api.ObjectStateGroup+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Object state group.

    +
    + 401 + +

    Error - The user is not authorized to read this Object state group.

    +
    + 404 + +

    Error - The Object state group does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateGroup + + This class represents an Object state group value.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/7">
    +    <id>7</id>
    +    <identifier>custom-states</identifier>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <languageCodes>eng-GB</languageCodes>
    +    <ObjectStates media-type="application/vnd.ez.api.ObjectStateList+xml" href="/api/ezp/v2/content/objectstategroups/7/objectstates"/>
    +    <names>
    +        <value languageCode="eng-GB">Custom State</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">Custom Object state</value>
    +    </descriptions>
    +</ObjectStateGroup>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroup": {
    +        "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/7",
    +        "id": 7,
    +        "identifier": "custom-states",
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "ObjectStates": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateList+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates"
    +        },
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom State"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom Object state"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Object state group + +
    +
    +
    +

    + PATCH + /content/objectstategroups/{objectStateGroupId} +

    +

    Updates an Object state group. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Object state group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroup+xml +application/vnd.ez.api.ObjectStateGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Object state group input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateGroupUpdate+json +application/vnd.ez.api.ObjectStateGroupUpdate+xml + +
    +
    +
    +
    +

    If-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Object stated group updated.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to update an Object state group.

    +
    + 403 + +

    Error - An Object state group with the provided identifier already exists.

    +
    + 412 + +

    Error - The current ETag does not match the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateGroup + + This class represents an Object state group value.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/7">
    +    <id>7</id>
    +    <identifier>custom-states</identifier>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <languageCodes>eng-GB</languageCodes>
    +    <ObjectStates media-type="application/vnd.ez.api.ObjectStateList+xml" href="/api/ezp/v2/content/objectstategroups/7/objectstates"/>
    +    <names>
    +        <value languageCode="eng-GB">New Custom State name</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">Custom Object state</value>
    +    </descriptions>
    +</ObjectStateGroup>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroup": {
    +        "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/7",
    +        "id": 7,
    +        "identifier": "custom-states",
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "ObjectStates": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateList+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates"
    +        },
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New Custom State name"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Custom Object state"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Object state group + +
    +
    +
    +

    + DELETE + /content/objectstategroups/{objectStateGroupId} +

    +

    Deletes the given Object state group including Object states.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Object state group deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete an Object state group.

    +
    + 404 + +

    Error - The Object state group does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objectstategroups/{objectStateGroupId}/objectstates

    + +
    +
    +
    +
    +
    +
    + List Object states + +
    +
    +
    +

    + GET + /content/objectstategroups/{objectStateGroupId}/objectstates +

    +

    Returns a list of all Object states of the given group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Object state list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateList+xml +application/vnd.ez.api.ObjectStateList+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list of Object states.

    +
    + 401 + +

    Error - The user has no permission to read Object states.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateList + + List of Object states.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateList media-type="application/vnd.ez.api.ObjectStateList+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates">
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates/1">
    +        <id>1</id>
    +        <identifier>not_locked</identifier>
    +        <priority>0</priority>
    +        <ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/2"/>
    +        <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +        <languageCodes>eng-GB</languageCodes>
    +        <names>
    +            <value languageCode="eng-GB">Not locked</value>
    +        </names>
    +        <descriptions>
    +            <value languageCode="eng-GB"></value>
    +        </descriptions>
    +    </ObjectState>
    +    <ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates/2">
    +        <id>2</id>
    +     
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateList": {
    +        "_media-type": "application/vnd.ez.api.ObjectStateList+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates",
    +        "ObjectState": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ObjectState+json",
    +                "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/1",
    +                "id": 1,
    +                "identifier": "not_locked",
    +                "priority": 0,
    +                "ObjectStateGroup": {
    +                    "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +                    "_href": "/api/ezp/v2/content/objectstategroups/2"
    +                },
    +                "defaultLanguageCode": "eng-GB",
    +                "languageCodes": "eng-GB",
    +                "names": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": "Not locked"
    +                        }
    +                
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Object state + +
    +
    +
    +

    + POST + /content/objectstategroups/{objectStateGroupId}/objectstates +

    +

    Creates a new Object state.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Object state is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectState+xml application/vnd.ez.api.ObjectState+json +
    +
    +
    +
    +

    Content-Type

    +

    The Object state input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateCreate+json +application/vnd.ez.api.ObjectStateCreate+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Object state created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create an Object state.

    +
    + 403 + +

    Error - An Object state with the same identifier already exists in the given group.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateCreate + +
    + + ObjectState + + This class represents a Object state value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateCreate>
    +  <identifier>new-state</identifier>
    +  <priority>1</priority>
    +  <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +  <names>
    +    <value languageCode="eng-GB">New State</value>
    +  </names>
    +  <descriptions>
    +    <value languageCode="eng-GB">New Object State</value>
    +  </descriptions>
    +</ObjectStateCreate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateCreate": {
    +        "identifier": "new-state",
    +        "priority": "1",
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New State"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New Object State"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/7/objectstates/5">
    +    <id>5</id>
    +    <identifier>new-state</identifier>
    +    <priority>0</priority>
    +    <ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/7"/>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <languageCodes>eng-GB</languageCodes>
    +    <names>
    +        <value languageCode="eng-GB">New State</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">New Object State</value>
    +    </descriptions>
    +</ObjectState>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectState": {
    +        "_media-type": "application/vnd.ez.api.ObjectState+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/7/objectstates/7",
    +        "id": 7,
    +        "identifier": "new-state",
    +        "priority": 0,
    +        "ObjectStateGroup": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/7"
    +        },
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New State"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New Object State"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId}

    + +
    +
    +
    +
    +
    +
    + Get Object state + +
    +
    +
    +

    + GET + /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} +

    +

    Returns the Object state.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Object State is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectState+xml +application/vnd.ez.api.ObjectState+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Object state.

    +
    + 401 + +

    Error - The user is not authorized to read this Object state.

    +
    + 404 + +

    Error - The Object state does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectState + + This class represents a Object state value.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/6/objectstates/2">
    +<id>2</id>
    +<identifier>locked</identifier>
    +<priority>1</priority>
    +<ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/2"/>
    +<defaultLanguageCode>eng-GB</defaultLanguageCode>
    +<languageCodes>eng-GB</languageCodes>
    +<names>
    +    <value languageCode="eng-GB">Locked</value>
    +</names>
    +<descriptions>
    +    <value languageCode="eng-GB"></value>
    +</descriptions>
    +</ObjectState>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectState": {
    +        "_media-type": "application/vnd.ez.api.ObjectState+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/2/objectstates/2",
    +        "id": 2,
    +        "identifier": "locked",
    +        "priority": 1,
    +        "ObjectStateGroup": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/2"
    +        },
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Locked"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": ""
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Object state + +
    +
    +
    +

    + PATCH + /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} +

    +

    Updates an Object state. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Object state is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectState+xml +application/vnd.ez.api.ObjectState+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Object state input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ObjectStateUpdate+json +application/vnd.ez.api.ObjectStateUpdate+xml + +
    +
    +
    +
    +

    If-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Object State updated

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to update the Object state.

    +
    + 403 + +

    Error - An Object state with the provided identifier already exists in this group.

    +
    + 412 + +

    Error - The current ETag does not match the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ObjectStateUpdate + +
    + + ObjectState + + This class represents a Object state value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectStateCreate>
    +  <priority>3</priority>
    +  <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +  <names>
    +    <value languageCode="eng-GB">New Object State name</value>
    +  </names>
    +</ObjectStateCreate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectStateGroup": {
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New Object State name"
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ObjectState media-type="application/vnd.ez.api.ObjectState+xml" href="/api/ezp/v2/content/objectstategroups/2/objectstates/2">
    +    <id>2</id>
    +    <identifier>locked</identifier>
    +    <priority>1</priority>
    +    <ObjectStateGroup media-type="application/vnd.ez.api.ObjectStateGroup+xml" href="/api/ezp/v2/content/objectstategroups/2"/>
    +    <defaultLanguageCode>eng-GB</defaultLanguageCode>
    +    <languageCodes>eng-GB</languageCodes>
    +    <names>
    +        <value languageCode="eng-GB">New Object State name</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB"></value>
    +    </descriptions>
    +</ObjectState>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ObjectState": {
    +        "_media-type": "application/vnd.ez.api.ObjectState+json",
    +        "_href": "/api/ezp/v2/content/objectstategroups/6/objectstates/9",
    +        "id": 9,
    +        "identifier": "closed",
    +        "priority": 1,
    +        "ObjectStateGroup": {
    +            "_media-type": "application/vnd.ez.api.ObjectStateGroup+json",
    +            "_href": "/api/ezp/v2/content/objectstategroups/6"
    +        },
    +        "defaultLanguageCode": "eng-GB",
    +        "languageCodes": "eng-GB",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "New Object State name"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": ""
    +                }
    +            ]
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Object state + +
    +
    +
    +

    + DELETE + /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} +

    +

    Deletes provided Object state.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Object state deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete an Object state.

    +
    + 404 + +

    Error - The Object state does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/locations

    +
    +
    +
    +

    /content/locations

    + +
    +
    +
    +
    +
    +
    + Load Locations by id/remoteId/urlAlias + +
    +
    +
    +

    + GET + /content/locations +

    +

    Loads the Location for a given ID (x), remote ID or URL alias.

    +

    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + id + + + + string + + + + + + The ID of the Location. If present, the Location with the given ID is returned. +
    + remoteId + + + + string + + + + + + The remote ID of the Location. If present, the Location with the given remote ID is returned. +
    + urlAlias + + + + string + + + + + + One of the URL aliases of the Location. If present, the Location with given URL Alias is returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 307 + +

    Temporary redirect to the main resource URL.

    +
    + + 200 + + +

    +
    + 404 + +

    Error - the Location with the given ID (remote ID or URL Alias) does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Location + + This class represents a Location in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/58">
    +    <id>58</id>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <invisible>false</invisible>
    +    <explicitlyHidden>false</explicitlyHidden>
    +    <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2"/>
    +    <pathString>/1/2/58/</pathString>
    +    <depth>2</depth>
    +    <childCount>3</childCount>
    +    <remoteId>0cfe62f27753448d79aaa8acd8d01699</remoteId>
    +    <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/58/children"/>
    +    <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/57"/>
    +    <sortField>PATH</sortField>
    +    <sortOrder>ASC</sortOrder>
    +    <UrlAliases media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/locations/1/2/58/urlaliases"/>
    +    <ContentInfo media-type="app
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Location": {
    +        "_media-type": "application/vnd.ez.api.Location+json",
    +        "_href": "/api/ezp/v2/content/locations/1/2/58",
    +        "id": 58,
    +        "priority": 0,
    +        "hidden": false,
    +        "invisible": false,
    +        "explicitlyHidden": false,
    +        "ParentLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2"
    +        },
    +        "pathString": "/1/2/58/",
    +        "depth": 2,
    +        "childCount": 3,
    +        "remoteId": "0cfe62f27753448d79aaa8acd8d01699",
    +        "Children": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/58/children"
    +        },
    +        "Content": {
    +            "_media-type": "application/vnd.ez.api.Content+json",
    +            "_href": "/api/ezp/v2/content/objects/57"
    +        },
    +        "sortField": "PATH",
    +        "sortOrder": "ASC",
    +        "UrlAliases": {
    +            "_media-type": 
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/locations/{path}

    + +
    +
    +
    +
    +
    +
    + Load Location + +
    +
    +
    +

    + GET + /content/locations/{path} +

    +

    Loads the Location for the given path e.g. '/content/locations/1/2/61'.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Location is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Location+xml +application/vnd.ez.api.Location+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this Location.

    +
    + 404 + +

    Error - the Location with the given path does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Location + + This class represents a Location in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/61">
    +    <id>61</id>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <invisible>false</invisible>
    +    <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2"/>
    +    <pathString>/1/2/61/</pathString>
    +    <depth>2</depth>
    +    <childCount>0</childCount>
    +    <remoteId>2cfa66027e3806b113d994c9c26d3a66</remoteId>
    +    <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/61/children"/>
    +    <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/60"/>
    +    <sortField>NAME</sortField>
    +    <sortOrder>ASC</sortOrder>
    +    <UrlAliases media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/locations/1/2/61/urlaliases"/>
    +    <ContentInfo media-type="application/vnd.ez.api.ContentInfo+xml" href="/api
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Location": {
    +        "_media-type": "application/vnd.ez.api.Location+json",
    +        "_href": "/api/ezp/v2/content/locations/1/2/58",
    +        "id": 58,
    +        "priority": 0,
    +        "hidden": false,
    +        "invisible": false,
    +        "explicitlyHidden": false,
    +        "ParentLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2"
    +        },
    +        "pathString": "/1/2/58/",
    +        "depth": 2,
    +        "childCount": 3,
    +        "remoteId": "0cfe62f27753448d79aaa8acd8d01699",
    +        "Children": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/58/children"
    +        },
    +        "Content": {
    +            "_media-type": "application/vnd.ez.api.Content+json",
    +            "_href": "/api/ezp/v2/content/objects/57"
    +        },
    +        "sortField": "PATH",
    +        "sortOrder": "ASC",
    +        "UrlAliases": {
    +            "_media-type": 
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Move subtree + +
    +
    +
    +

    + MOVE + /content/locations/{path} +

    +

    Moves Location to a different parent. The destination can also be '/content/trash' where the Location is put into the trash. (NOTE - Be aware that the user might lose access to the item after it has been moved, for example when read access is limited by a subtree). MOVE or POST with header X-HTTP-Method-Override MOVE.

    +

    +
    +
    Header parameters
    +
    +

    Destination

    +

    A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Created. If destination is '/api/ezp/v2/content/trash' and content only has one Location (NOTE - Like on normal subtree moves, be aware that the user might lose access to the item after it has been moved to Trash.)

    +
    + 204 + +

    No Content. If destination is '/api/ezp/v2/content/trash' and content still has other Locations (no trash item is created).

    +
    + 401 + +

    Error - the user is not authorized to move this Location.

    +
    + 404 + +

    Error - the Location with the given ID does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Copy subtree + +
    +
    +
    +

    + COPY + /content/locations/{path} +

    +

    Copies the subtree to a different parent. COPY or POST with header X-HTTP-Method-Override COPY.

    +

    +
    +
    Header parameters
    +
    +

    Destination

    +

    A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Created. Copied the subtree to a different parent.

    +
    + 401 + +

    Error - the user is not authorized to move this Location.

    +
    + 404 + +

    Error - the Location with the given ID does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete subtree + +
    +
    +
    +

    + DELETE + /content/locations/{path} +

    +

    Deletes the complete subtree for the given path. Every Content item which does not have any other Location is deleted. Otherwise the deleted Location is removed from the Content item. The children are recursively deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - deleted.

    +
    + 401 + +

    Error - the user is not authorized to delete this subtree.

    +
    + 404 + +

    Error - the Location with the given ID does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Location + +
    +
    +
    +

    + PATCH + /content/locations/{path} +

    +

    Updates the Location. This method can also be used to hide/reveal a Location via the hidden field in the LocationUpdate. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Location is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Location+xml +application/vnd.ez.api.Location+json + +
    +
    +
    +
    +

    Content-Type

    +

    The LocationUpdate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.LocationUpdate+xml +application/vnd.ez.api.LocationUpdate+json + +
    +
    +
    +
    +

    If-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to update this Location.

    +
    + 404 + +

    Error - the Location with the given ID does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + LocationUpdateStruct + + This class is used for updating Location meta data.
    + + Location + + This class represents a Location in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<LocationUpdate>
    +  <priority>3</priority>
    +  <hidden>true</hidden>
    +  <remoteId>remoteId-qwert999</remoteId>
    +  <sortField>CLASS</sortField>
    +  <sortOrder>DESC</sortOrder>
    +</LocationUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "LocationUpdate": {
    +        "priority": "3",
    +        "hidden": true,
    +        "remoteId": "remoteId-qwert999",
    +        "sortField": "NAME",
    +        "sortOrder": "DESC"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/58">
    +    <id>58</id>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <invisible>false</invisible>
    +    <explicitlyHidden>false</explicitlyHidden>
    +    <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2"/>
    +    <pathString>/1/2/58/</pathString>
    +    <depth>2</depth>
    +    <childCount>3</childCount>
    +    <remoteId>0cfe62f27753448d79aaa8acd8d01699</remoteId>
    +    <Children media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/2/58/children"/>
    +    <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/57"/>
    +    <sortField>PATH</sortField>
    +    <sortOrder>ASC</sortOrder>
    +    <UrlAliases media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/locations/1/2/58/urlaliases"/>
    +    <ContentInfo media-type="app
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Location": {
    +        "_media-type": "application/vnd.ez.api.Location+json",
    +        "_href": "/api/ezp/v2/content/locations/1/2/59",
    +        "id": 59,
    +        "priority": 3,
    +        "hidden": true,
    +        "invisible": true,
    +        "explicitlyHidden": true,
    +        "ParentLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2"
    +        },
    +        "pathString": "/1/2/59/",
    +        "depth": 2,
    +        "childCount": 5,
    +        "remoteId": "remoteId-qwert999",
    +        "Children": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/59/children"
    +        },
    +        "Content": {
    +            "_media-type": "application/vnd.ez.api.Content+json",
    +            "_href": "/api/ezp/v2/content/objects/58"
    +        },
    +        "sortField": "NAME",
    +        "sortOrder": "DESC",
    +        "UrlAliases": {
    +            "_media-type": "application/vnd.
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Swap Location + +
    +
    +
    +

    + SWAP + /content/locations/{path} +

    +

    Swaps the Location of a Content item with the given Location of another Content item. SWAP or POST with header X-HTTP-Method-Override SWAP.

    +

    +
    +
    Header parameters
    +
    +

    Destination

    +

    A parent Location resource to which the Location is moved e.g. '/api/ezp/v2/content/locations/1/63'.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content. Swapped the Location of a Content item with the given Location of another Content item.

    +
    + 401 + +

    Error - the user is not authorized to swap this Location.

    +
    + 404 + +

    Error - the Location with the given ID does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /content/locations/{path}/children

    + +
    +
    +
    +
    +
    +
    + Get child Locations. + +
    +
    +
    +

    + GET + /content/locations/{path}/children +

    +

    Loads all child Locations for the given parent Location.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Location list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.LocationList+xml +application/vnd.ez.api.LocationList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + offset + + + + integer + + + + + + The offset of the result set. +
    + limit + + + + integer + + + + + + The number of Locations returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this Content item.

    +
    + 404 + +

    Error - the Content item with the given ID does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + LocationList + + This class represents a queried Location list holding a totalCount and a partial list of Locations (by offset/limit parameters and permission filters).
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<LocationList media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/locations/1/63/children">
    +    <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/63/60"/>
    +    <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/63/61"/>
    +    <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/63/62"/>
    +    <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/63/67"/>
    +</LocationList>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/locations/{path}/urlaliases

    + +
    +
    +
    +
    +
    +
    + List URL aliases for Location + +
    +
    +
    +

    + GET + /content/locations/{path}/urlaliases +

    +

    Returns the list of URL aliases for a Location.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the URL alias list contains only references and is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlAliasRefList+xml +application/vnd.ez.api.UrlAliasRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + custom + + + + boolean + + + + + + Indicates whether autogenerated (false) or manual URL aliases (true) should be returned (default true). +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the list of URL aliases.

    +
    + 400 + +

    Error - The user has no permission to read URL aliases.

    +
    + 401 + +

    Error - The Location was not found.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UrlAliasRefList + + List of URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/views

    +
    +
    +
    +

    /content/views

    + +
    +
    +
    +
    +
    +
    + Create View + +
    +
    +
    +

    + POST + /content/views +

    +

    Executes a query and returns View including the results. The View input reflects the criteria model of the public API. Will respond with a 301, as the resource has been moved to /views (Platform 1.0) - DEPRECATED.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    The View in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.View+xml +application/vnd.ez.api.View+json +application/vnd.ez.api.View+xml; version=1.1 +application/vnd.ez.api.View+json; version=1.1 + +
    +
    +
    +
    +

    Content-Type

    +

    The View input in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ViewInput+xml +application/vnd.ez.api.ViewInput+json +application/vnd.ez.api.ViewInput+xml; version=1.1 +application/vnd.ez.api.ViewInput+json; version=1.1 + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 301 + +

    Moved permanently.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /content/sections

    +
    +
    +
    +

    /content/sections

    + +
    +
    +
    +
    +
    +
    + Create new Section + +
    +
    +
    +

    + POST + /content/sections +

    +

    Creates a new Section.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Section is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Section+xml +application/vnd.ez.api.Section+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Section input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.SectionInput+json +application/vnd.ez.api.SectionInput+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + SectionInput + +
    + + Section + + This class represents a Section.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<SectionInput>
    +  <identifier>restricted</identifier>
    +  <name>Restricted</name>
    +</SectionInput>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Section": {
    +        "_media-type": "application/vnd.ez.api.Section+json",
    +        "_href": "/api/ezp/v2/content/sections/13",
    +        "sectionId": 13,
    +        "identifier": "restricted",
    +        "name": "Restricted"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/7">
    +    <sectionId>7</sectionId>
    +    <identifier>restricted</identifier>
    +    <name>Restricted</name>
    +</Section>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "SectionInput": {
    +        "identifier": "restricted",
    +        "name": "Restricted"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Get Sections + +
    +
    +
    +

    + GET + /content/sections +

    +

    Returns a list of all Sections.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Section list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.SectionList+xml +application/vnd.ez.api.SectionList+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + identifer + + + + string + + + + + + Only the Section with the given identifier is returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - The user has no permission to read the Section.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + SectionList + + This class represents a Section list.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<SectionList media-type="application/vnd.ez.api.SectionList+xml" href="/api/ezp/v2/content/sections">
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/1">
    +        <sectionId>1</sectionId>
    +        <identifier>standard</identifier>
    +        <name>Standard</name>
    +    </Section>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2">
    +        <sectionId>2</sectionId>
    +        <identifier>users</identifier>
    +        <name>Users</name>
    +    </Section>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/3">
    +        <sectionId>3</sectionId>
    +        <identifier>media</identifier>
    +        <name>Media</name>
    +    </Section>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/6">
    +        <sectionId>6</sectionId>
    +        <identifier>form</identifier>
    +        <name>Form</name>
    +    </Section>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "SectionList": {
    +        "_media-type": "application/vnd.ez.api.SectionList+json",
    +        "_href": "/api/ezp/v2/content/sections",
    +        "Section": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Section+json",
    +                "_href": "/api/ezp/v2/content/sections/1",
    +                "sectionId": 1,
    +                "identifier": "standard",
    +                "name": "Standard"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.Section+json",
    +                "_href": "/api/ezp/v2/content/sections/2",
    +                "sectionId": 2,
    +                "identifier": "users",
    +                "name": "Users"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.Section+json",
    +                "_href": "/api/ezp/v2/content/sections/3",
    +                "sectionId": 3,
    +                "identifier": "media",
    +                "name": "Media"
    +            },
    +            {
    +                "_media-type": "applic
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/sections/{sectionId}

    + +
    +
    +
    +
    +
    +
    + Get Section + +
    +
    +
    +

    + GET + /content/sections/{sectionId} +

    +

    Returns the Section by given Section ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Section is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Section+xml +application/vnd.ez.api.Section+json + +
    +
    +
    +
    +

    If-None-match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - The user is not authorized to read this Section.

    +
    + 404 + +

    Error - The Section does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Section + + This class represents a Section.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/5">
    +    <sectionId>10</sectionId>
    +    <identifier>design</identifier>
    +    <name>Design</name>
    +</Section>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Section": {
    +        "_media-type": "application/vnd.ez.api.Section+json",
    +        "_href": "/api/ezp/v2/content/sections/10",
    +        "sectionId": 10,
    +        "identifier": "design",
    +        "name": "Design"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update a Section + +
    +
    +
    +

    + PATCH + /content/sections/{sectionId} +

    +

    Updates a Section. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Section is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Section+xml +application/vnd.ez.api.Section+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Section input schema encoded in XML or JSON.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.SectionInput+xml +application/vnd.ez.api.SectionInput+json + +
    +
    +
    +
    +

    If-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Section updated.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to create this Section.

    +
    + 403 + +

    Error - a Section with the given identifier already exists.

    +
    + 412 + +

    Error - the current ETag does not match with the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + SectionInput + +
    + + Section + + This class represents a Section.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<SectionInput>
    +  <identifier>template</identifier>
    +  <name>Template</name>
    +</SectionInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "SectionInput": {
    +    "identifier": "template",
    +    "name": "Template"
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/7">
    +    <sectionId>7</sectionId>
    +    <identifier>template</identifier>
    +    <name>Template</name>
    +</Section>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Section": {
    +        "_media-type": "application/vnd.ez.api.Section+json",
    +        "_href": "/api/ezp/v2/content/sections/7",
    +        "sectionId": 7,
    +        "identifier": "template",
    +        "name": "Template"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Section + +
    +
    +
    +

    + DELETE + /content/sections/{sectionId} +

    +

    The given Section is deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - given Section is deleted.

    +
    + 401 + +

    Error - the user is not authorized to delete this Section.

    +
    + 404 + +

    Error - the Section does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/trash

    +
    +
    +
    +

    /content/trash

    + +
    +
    +
    +
    +
    +
    + List Trash items + +
    +
    +
    +

    + GET + /content/trash +

    +

    Returns a list of all items in the Trash.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Trash item list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Trash+xml +application/vnd.ez.api.Trash+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + limit + + + + string + + + + + + Only limit. Items will be returned, starting with the offset. +
    + offset + + + + string + + + + + + Offset of the result set. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the list of items in the Trash.

    +
    + 401 + +

    Error - The user has no permission to read the Trash.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Trash + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Trash media-type="application/vnd.ez.api.Trash+xml" href="/api/ezp/v2/content/trash">
    +    <TrashItem media-type="application/vnd.ez.api.TrashItem+xml" href="/api/ezp/v2/content/trash/58">
    +        <id>58</id>
    +        <priority>0</priority>
    +        <hidden>false</hidden>
    +        <invisible>false</invisible>
    +        <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/56"/>
    +        <pathString>/1/2/56/58/</pathString>
    +        <depth>3</depth>
    +        <childCount>0</childCount>
    +        <remoteId>59800915ad2eb8514de0bebe84f6ccba</remoteId>
    +        <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/52"/>
    +        <sortField>PATH</sortField>
    +        <sortOrder>ASC</sortOrder>
    +        <ContentInfo media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/52">
    +            <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Trash": {
    +        "_media-type": "application/vnd.ez.api.Trash+json",
    +        "_href": "/api/ezp/v2/content/trash",
    +        "TrashItem": [
    +            {
    +                "_media-type": "application/vnd.ez.api.TrashItem+json",
    +                "_href": "/api/ezp/v2/content/trash/87",
    +                "id": 87,
    +                "priority": 0,
    +                "hidden": false,
    +                "invisible": false,
    +                "ParentLocation": {
    +                    "_media-type": "application/vnd.ez.api.Location+json",
    +                    "_href": "/api/ezp/v2/content/locations/1/2/57"
    +                },
    +                "pathString": "/1/2/57/87/",
    +                "depth": 3,
    +                "childCount": 0,
    +                "remoteId": "7cc6565354858f39a794bf64aa2c2761",
    +                "Content": {
    +                    "_media-type": "application/vnd.ez.api.Content+json",
    +                    "_href": "/api/ezp/v2/content/objects/91"
    +                },
    +                "sortField": "PAT
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Empty Trash + +
    +
    +
    +

    + DELETE + /content/trash +

    +

    Empties the Trash.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Trash emptied.

    +
    + 401 + +

    Error - The user is not authorized to empty all items from Trash.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/trash/{trashItemId}

    + +
    +
    +
    +
    +
    +
    + Get Trash item + +
    +
    +
    +

    + GET + /content/trash/{trashItemId} +

    +

    Returns the item in Trash with the provided ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the item in Trash is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.TrashItem+xml +application/vnd.ez.api.TrashItem+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - The user has no permission to read the item in Trash.

    +
    + 404 + +

    Error - An item in Trash with the provided ID does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + TrashItem + + This class represents a trash item, which is actually a trashed Location.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<TrashItem media-type="application/vnd.ez.api.TrashItem+xml" href="/api/ezp/v2/content/trash/81">
    +    <id>81</id>
    +    <priority>0</priority>
    +    <hidden>false</hidden>
    +    <invisible>false</invisible>
    +    <ParentLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2/42"/>
    +    <pathString>/1/2/42/81/</pathString>
    +    <depth>3</depth>
    +    <childCount>0</childCount>
    +    <remoteId>135e8a84b61848a67be36e9552d2724d</remoteId>
    +    <Content media-type="application/vnd.ez.api.Content+xml" href="/api/ezp/v2/content/objects/99"/>
    +    <sortField>PATH</sortField>
    +    <sortOrder>ASC</sortOrder>
    +    <ContentInfo media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/99">
    +        <Content media-type="application/vnd.ez.api.ContentInfo+xml" href="/api/ezp/v2/content/objects/99" remoteId="ae2b4568c0d2e7a4a9bca9495e73591a" id="99">
    +            <ContentType media-type="application/vnd.ez.api.Co
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "TrashItem": {
    +        "_media-type": "application/vnd.ez.api.TrashItem+json",
    +        "_href": "/api/ezp/v2/content/trash/87",
    +        "id": 87,
    +        "priority": 0,
    +        "hidden": false,
    +        "invisible": false,
    +        "ParentLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/2/57"
    +        },
    +        "pathString": "/1/2/57/87/",
    +        "depth": 3,
    +        "childCount": 0,
    +        "remoteId": "7cc6565354858f39a794bf64aa2c2761",
    +        "Content": {
    +            "_media-type": "application/vnd.ez.api.Content+json",
    +            "_href": "/api/ezp/v2/content/objects/91"
    +        },
    +        "sortField": "PATH",
    +        "sortOrder": "ASC",
    +        "ContentInfo": {
    +            "_media-type": "application/vnd.ez.api.ContentInfo+json",
    +            "_href": "/api/ezp/v2/content/objects/91",
    +            "Content": {
    +                "_media-type": "application/vnd.ez.api.ContentInfo+json",
    +          
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Untrash Content item + +
    +
    +
    +

    + MOVE + /content/trash/{trashItemId} +

    +

    Restores an item from Trash. MOVE or POST with header X-HTTP-Method-Override MOVE.

    +

    +
    +
    Header parameters
    +
    +

    Destination

    +

    If the destination Location URI is provided, the item from Trash is restored under this Location, otherwise it is restored under its original parent Location.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Item restored.

    +
    + 401 + +

    Error - The user is not authorized to restore this item from Trash.

    +
    + 403 + +

    Error - The provided parent Location does not exist.

    +
    + 404 + +

    Error - The provided item does not exist in Trash.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Trash item + +
    +
    +
    +

    + DELETE + /content/trash/{trashItemId} +

    +

    Deletes the provided item from Trash.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - item deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete the provided item.

    +
    + 404 + +

    Error - The provided item does not exist in Trash.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/urlaliases

    +
    +
    +
    +

    /content/urlaliases

    + +
    +
    +
    +
    +
    +
    + List global URL aliases + +
    +
    +
    +

    + GET + /content/urlaliases +

    +

    Returns the list of global URL aliases.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the URL alias list contains only references and is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlAliasRefList+xml +application/vnd.ez.api.UrlAliasRefList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the list of URL aliases.

    +
    + 401 + +

    Error - The user has no permission to read URL aliases.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UrlAliasRefList + + List of URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlAliasRefList media-type="application/vnd.ez.api.UrlAliasRefList+xml" href="/api/ezp/v2/content/urlaliases">
    +    <UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" />
    +</UrlAliasRefList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlAliasRefList": {
    +        "_media-type": "application/vnd.ez.api.UrlAliasRefList+json",
    +        "_href": "/api/ezp/v2/content/urlaliases",
    +        "UrlAlias": [
    +            {
    +                "_media-type": "application/vnd.ez.api.UrlAlias+json",
    +                "_href": "/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create URL alias + +
    +
    +
    +

    + POST + /content/urlaliases +

    +

    Creates a URL alias.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the created URL alias is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlAlias+xml +application/vnd.ez.api.UrlAlias+json + +
    +
    +
    +
    +

    Content-Type

    +

    The URL alias input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlAliasCreate+xml +application/vnd.ez.api.UrlAliasCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    URL alias created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create a URL alias.

    +
    + 403 + +

    Error - A URL alias with the same identifier already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UrlAliasCreate + +
    + + UrlAlias + + This class represents a URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlAliasCreate type="LOCATION">
    +  <location href='/api/ezp/v2/content/locations/1/2/59' />
    +  <path>example-global</path>
    +  <languageCode>eng-GB</languageCode>
    +  <alwaysAvailable>true</alwaysAvailable>
    +  <forward>true</forward>
    +</UrlAliasCreate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "UrlAliasCreate": {
    +    "_type": "GLOBAL",
    +    "resource": "module:content/view/full",
    +    "path": "example-global",
    +    "languageCode": "eng-GB",
    +    "alwaysAvailable": "true",
    +    "forward": "true"
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-deca3dadca45c3dff7861274b8e67ed7" id="0-deca3dadca45c3dff7861274b8e67ed7" type="LOCATION">
    +    <location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/59"/>
    +    <path>/example-urlalias</path>
    +    <languageCodes>eng-GB</languageCodes>
    +    <alwaysAvailable>true</alwaysAvailable>
    +    <isHistory>false</isHistory>
    +    <forward>true</forward>
    +    <custom>true</custom>
    +</UrlAlias>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlAlias": {
    +        "_media-type": "application/vnd.ez.api.UrlAlias+json",
    +        "_href": "/api/ezp/v2/content/urlaliases/0-f530173ad554787c1fe30dc929d98360",
    +        "_id": "0-f530173ad554787c1fe30dc929d98360",
    +        "_type": "RESOURCE",
    +        "resource": "content/view/full",
    +        "path": "/example-global",
    +        "languageCodes": "eng-GB",
    +        "alwaysAvailable": true,
    +        "isHistory": false,
    +        "forward": true,
    +        "custom": true
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/urlaliases/{urlAliasId}

    + +
    +
    +
    +
    +
    +
    + Get URL alias + +
    +
    +
    +

    + GET + /content/urlaliases/{urlAliasId} +

    +

    Returns the URL alias with the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the URL alias is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlAlias+xml +application/vnd.ez.api.UrlAlias+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the URL alias.

    +
    + 401 + +

    Error - The user is not authorized to read URL aliases.

    +
    + 404 + +

    Error - The URL alias does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UrlAlias + + This class represents a URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlAlias media-type="application/vnd.ez.api.UrlAlias+xml" href="/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36" id="0-2cecdff5f90b8595d76b68c417b09f36" type="RESOURCE">
    +    <resource>content/view/full</resource>
    +    <path>/example-global</path>
    +    <languageCodes>eng-GB</languageCodes>
    +    <alwaysAvailable>true</alwaysAvailable>
    +    <isHistory>false</isHistory>
    +    <forward>true</forward>
    +    <custom>true</custom>
    +</UrlAlias>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlAlias": {
    +        "_media-type": "application/vnd.ez.api.UrlAlias+json",
    +        "_href": "/api/ezp/v2/content/urlaliases/0-2cecdff5f90b8595d76b68c417b09f36",
    +        "_id": "0-2cecdff5f90b8595d76b68c417b09f36",
    +        "_type": "RESOURCE",
    +        "resource": "content/view/full",
    +        "path": "/example-global",
    +        "languageCodes": "eng-GB",
    +        "alwaysAvailable": true,
    +        "isHistory": false,
    +        "forward": true,
    +        "custom": true
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete URL alias + +
    +
    +
    +

    + DELETE + /content/urlaliases/{urlAliasId} +

    +

    Deletes the provided URL alias.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - URL alias deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete a URL alias.

    +
    + 404 + +

    Error - The URL alias does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/urlwildcards

    +
    +
    +
    +

    /content/urlwildcards

    + +
    +
    +
    +
    +
    +
    + List URL wildcards + +
    +
    +
    +

    + GET + /content/urlwildcards +

    +

    Returns a list of URL wildcards.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the URL wildcard is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlWildcardList+xml +application/vnd.ez.api.UrlWildcardList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list of URL wildcards.

    +
    + 401 + +

    Error - The user has no permission to read URL wildcards.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UrlWildcardList + + List of URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlWildcardList media-type="application/vnd.ez.api.UrlWildcardList+xml" href="/api/ezp/v2/content/urlwildcards">
    +    <UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/1" id="1">
    +        <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl>
    +        <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl>
    +        <forward>true</forward>
    +    </UrlWildcard>
    +</UrlWildcardList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlWildcardList": {
    +        "_media-type": "application/vnd.ez.api.UrlWildcardList+json",
    +        "_href": "/api/ezp/v2/content/urlwildcards",
    +        "UrlWildcard": [
    +            {
    +                "_media-type": "application/vnd.ez.api.UrlWildcard+json",
    +                "_href": "/api/ezp/v2/content/urlwildcards/1",
    +                "_id": 1,
    +                "sourceUrl": "/api/ezp/v2/content/location/2",
    +                "destinationUrl": "/api/ezp/v2/content/location/59",
    +                "forward": true
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create URL wildcard + +
    +
    +
    +

    + POST + /content/urlwildcards +

    +

    Creates a new URL wildcard.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new URL wildcard is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlWildcard+xml +application/vnd.ez.api.UrlWildcard+json + +
    +
    +
    +
    +

    Content-Type

    +

    The URL Wildcard input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlWildcardCreate+xml +application/vnd.ez.api.UrlWildcardCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    URL wildcard created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create a URL wildcard.

    +
    + 403 + +

    Error - A URL wildcard with the same identifier already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UrlWildcardCreate + +
    + + UrlWildcard + + This class represents a URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +    <UrlWildcardCreate>
    +    	<sourceUrl>/api/ezp/v2/content/location/2</sourceUrl>
    +    	<destinationUrl>/api/ezp/v2/content/location/59</destinationUrl>
    +    	<forward>true</forward>
    +</UrlWildcardCreate>
    +                            
    +
    + View more +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlWildCardCreate": {
    +        "sourceUrl": "/api/ezp/v2/content/location/2",
    +        "destinationUrl": "/api/ezp/v2/content/location/59",
    +        "forward": true
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/1" id="1">
    +    <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl>
    +    <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl>
    +    <forward>true</forward>
    +</UrlWildcard>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlWildcard": {
    +        "_media-type": "application/vnd.ez.api.UrlWildcard+json",
    +        "_href": "/api/ezp/v2/content/urlwildcards/6",
    +        "_id": 6,
    +        "sourceUrl": "/api/ezp/v2/content/location/2",
    +        "destinationUrl": "/api/ezp/v2/content/location/59",
    +        "forward": true
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/urlwildcards/{wildcardId}

    + +
    +
    +
    +
    +
    +
    + Get URL wildcard + +
    +
    +
    +

    + GET + /content/urlwildcards/{wildcardId} +

    +

    Returns the URL wildcard with the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the URL wildcard is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UrlWildcard+xml +application/vnd.ez.api.UrlWildcard+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the URL wildcard.

    +
    + 401 + +

    Error - The user is not authorized to read URL wildcards.

    +
    + 404 + +

    Error - The URL wildcard does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UrlWildcard + + This class represents a URL alias in the Repository.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UrlWildcard media-type="application/vnd.ez.api.UrlWildcard+xml" href="/api/ezp/v2/content/urlwildcards/4" id="4">
    +    <sourceUrl>/api/ezp/v2/content/location/2</sourceUrl>
    +    <destinationUrl>/api/ezp/v2/content/location/59</destinationUrl>
    +    <forward>true</forward>
    +</UrlWildcard>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UrlWildcard": {
    +        "_media-type": "application/vnd.ez.api.UrlWildcard+json",
    +        "_href": "/api/ezp/v2/content/urlwildcards/4",
    +        "_id": 4,
    +        "sourceUrl": "/api/ezp/v2/content/location/2",
    +        "destinationUrl": "/api/ezp/v2/content/location/59",
    +        "forward": true
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete URL wildcard + +
    +
    +
    +

    + DELETE + /content/urlwildcards/{wildcardId} +

    +

    Deletes the given URL wildcard.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - URL wildcard deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete a URL wildcard.

    +
    + 404 + +

    Error - The URL wildcard does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/typegroups

    +
    +
    +
    +

    /content/typegroups

    + +
    +
    +
    +
    +
    +
    + Get Content Type groups + +
    +
    +
    +

    + GET + /content/typegroups +

    +

    Returns a list of all Content Type groups. If an identifier is provided, loads the Content Type group for this identifier.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Content Type group list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroupList+xml +application/vnd.ez.api.ContentTypeGroupList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + identifier + + + + string + + + + + + The identifier of the Content Type group. If present, the Content Type group with this identifier is returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list of Content Type groups.

    +
    + 307 + +

    Temporary redirect.

    +
    + 401 + +

    Error - The user has no permission to read Content Types.

    +
    + 404 + +

    Error - The Content Type group with the given identifier does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeGroupList + + List of Content Type groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroupList media-type="application/vnd.ez.api.ContentTypeGroupList+xml" href="/api/ezp/v2/content/typegroups">
    +    <ContentTypeGroup media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1">
    +        <id>1</id>
    +        <identifier>Content</identifier>
    +        <created>2002-09-05T11:08:48+02:00</created>
    +        <modified>2002-10-06T18:35:06+02:00</modified>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <ContentTypes media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/typegroups/1/types"/>
    +    </ContentTypeGroup>
    +    <ContentTypeGroup media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/2">
    +        <id>2</id>
    +        <identifier>Users</identifier>
    +        <created>2002-09
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeGroupList": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeGroupList+json",
    +        "_href": "/api/ezp/v2/content/typegroups",
    +        "ContentTypeGroup": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ContentTypeGroup+json",
    +                "_href": "/api/ezp/v2/content/typegroups/1",
    +                "id": 1,
    +                "identifier": "Content",
    +                "created": "2002-09-05T09:08:48+00:00",
    +                "modified": "2002-10-06T16:35:06+00:00",
    +                "Creator": {
    +                    "_media-type": "application/vnd.ez.api.User+json",
    +                    "_href": "/api/ezp/v2/user/users/14"
    +                },
    +                "Modifier": {
    +                    "_media-type": "application/vnd.ez.api.User+json",
    +                    "_href": "/api/ezp/v2/user/users/14"
    +                },
    +                "ContentTypes": {
    +                    "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json",
    +      
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Content Type group + +
    +
    +
    +

    + POST + /content/typegroups +

    +

    Creates a new Content Type group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Content Type group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroup+xml +application/vnd.ez.api.ContentTypeGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content Type group input schema encoded in XML or JSON.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroupInput+xml +application/vnd.ez.api.ContentTypeGroupInput+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Content Type group created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create this Content Type group.

    +
    + 403 + +

    Error - A Content Type group with the same identifier already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeGroupInput + +
    + + ContentTypeGroup + + This class represents a Content Type group value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroupInput>
    +    <identifier>newContentTypeGroup</identifier>
    +</ContentTypeGroupInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentTypeGroupInput": {
    +    "identifier": "newContentTypeGroup"
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroup href="/content/typesgroups/7" media-type="application/vnd.ez.api.ContentTypeGroup+xml">
    +    <id>7</id>
    +    <identifier>newContentTypeGroup</identifier>
    +    <created>2012-02-31T12:45:00</created>
    +    <modified>2012-02-31T12:45:00</modified>
    +    <Creator href="/user/users/13" media-type="application/vnd.ez.api.User+xml"/>
    +    <Modifier href="/user/users/13" media-type="application/vnd.ez.api.User+xml"/>
    +    <ContentTypes href="/content/typegroups/7/types" media-type="application/vnd.ez.api.ContentTypeList+xml"/>
    +</ContentTypeGroup>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeGroup": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeGroup+json",
    +        "_href": "/api/ezp/v2/content/typegroups/6",
    +        "id": 6,
    +        "identifier": "newContentTypeGroup",
    +        "created": "2021-08-11T09:44:18+00:00",
    +        "modified": "2021-08-11T09:44:18+00:00",
    +        "Creator": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Modifier": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "ContentTypes": {
    +            "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json",
    +            "_href": "/api/ezp/v2/content/typegroups/6/types"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/typegroups/{contentTypeGroupId}

    + +
    +
    +
    +
    +
    +
    + Get Content Type group + +
    +
    +
    +

    + GET + /content/typegroups/{contentTypeGroupId} +

    +

    Returns the Content Type group with provided ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Content Type group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroup+xml +application/vnd.ez.api.ContentTypeGroup+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Content Type group.

    +
    + 401 + +

    Error - The user is not authorized to read this Content Type group.

    +
    + 404 + +

    Error - The Content Type group does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeGroup + + This class represents a Content Type group value.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeList media-type="application/vnd.ez.api.ContentTypeList+xml" href="/api/ezp/v2/content/typegroups/1/types">
    + <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2">
    +  <id>2</id>
    +  <status>DEFINED</status>
    +  <identifier>article</identifier>
    +  <names>
    +   <value languageCode="eng-GB">Article</value>
    +  </names>
    +  <descriptions>
    +   <value languageCode="eng-GB"></value>
    +  </descriptions>
    +  <creationDate>2002-06-18T09:21:38+00:00</creationDate>
    +  <modificationDate>2021-06-28T11:31:22+00:00</modificationDate>
    +  <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +  <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +  <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/>
    +  <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/d
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeList": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeList+json",
    +        "_href": "/api/ezp/v2/content/typegroups/1/types",
    +        "ContentType": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ContentType+json",
    +                "_href": "/api/ezp/v2/content/types/2",
    +                "id": 2,
    +                "status": "DEFINED",
    +                "identifier": "article",
    +                "names": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": "Article"
    +                        }
    +                    ]
    +                },
    +                "descriptions": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": null
    +                        }
    +                    ]
    +                },
    +                "creationDate": "2002-06-18T09:21:38+00:00",
    +   
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Content Type group + +
    +
    +
    +

    + PATCH + /content/typegroups/{contentTypeGroupId} +

    +

    Updates a Content Type group. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Content Type group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroup+xml +application/vnd.ez.api.ContentTypeGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content Type group input schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroupInput+xml +application/vnd.ez.api.ContentTypeGroupInput+json + +
    +
    +
    +
    +

    If-Match

    +

    ETag causes patching only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    Content Type group updated.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create this Content Type group.

    +
    + 403 + +

    Error - A Content Type group with the given identifier already exists.

    +
    + 412 + +

    Error - The current ETag does not match the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeGroupInput + +
    + + ContentTypeGroup + + This class represents a Content Type group value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroupInput>
    +    <identifier>updatedIdentifer</identifier>
    +</ContentTypeGroupInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentTypeGroupInput": {
    +    "identifier": "updatedIdentifer"
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroup media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1">
    +    <id>4</id>
    +    <identifier>updatedIdentifer</identifier>
    +    <created>2002-09-05T11:08:48+02:00</created>
    +    <modified>2019-02-22T14:42:55+01:00</modified>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <ContentTypes media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/typegroups/1/types"/>
    +</ContentTypeGroup>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeGroup": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeGroup+json",
    +        "_href": "/api/ezp/v2/content/typegroups/4",
    +        "id": 4,
    +        "identifier": "updatedIdentifer",
    +        "created": "2021-08-11T09:44:18+00:00",
    +        "modified": "2021-08-11T10:10:04+00:00",
    +        "Creator": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Modifier": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "ContentTypes": {
    +            "_media-type": "application/vnd.ez.api.ContentTypeInfoList+json",
    +            "_href": "/api/ezp/v2/content/typegroups/6/types"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Content Type group + +
    +
    +
    +

    + DELETE + /content/typegroups/{contentTypeGroupId} +

    +

    Deletes the provided Content Type group.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Content Type group deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete this Content Type group.

    +
    + 403 + +

    Error - The Content Type group is not empty.

    +
    + 404 + +

    Error - The Content Type group does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/typegroups/{contentTypeGroupId}/types

    + +
    +
    +
    +
    +
    +
    + List Content Types for group + +
    +
    +
    +

    + GET + /content/typegroups/{contentTypeGroupId}/types +

    +

    Returns a list of Content Types in the provided group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeInfoList+xml +application/vnd.ez.api.ContentTypeInfoList+json +application/vnd.ez.api.ContentTypeList+xml +application/vnd.ez.api.ContentTypeList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list on Content Types.

    +
    + 401 + +

    Error - The user has no permission to read the Content Types.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeInfoList + + List of Content Type information.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeInfoList media-type="application/vnd.ez.api.ContentTypeInfoList+xml" href="/api/ezp/v2/content/typegroups/1/types">
    +    <ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/2">
    +        <id>2</id>
    +        <status>DEFINED</status>
    +        <identifier>article</identifier>
    +        <names>
    +            <value languageCode="eng-GB">Article</value>
    +        </names>
    +        <descriptions/>
    +        <creationDate>2002-06-18T11:21:38+02:00</creationDate>
    +        <modificationDate>2004-04-20T11:56:29+02:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/>
    +        <Draft media-type="application/vnd.ez.api.ContentTyp
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeList": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeList+json",
    +        "_href": "/api/ezp/v2/content/types",
    +        "ContentType": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ContentType+json",
    +                "_href": "/api/ezp/v2/content/types/2",
    +                "id": 2,
    +                "status": "DEFINED",
    +                "identifier": "article",
    +                "names": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": "Article"
    +                        }
    +                    ]
    +                },
    +                "descriptions": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": null
    +                        }
    +                    ]
    +                },
    +                "creationDate": "2002-06-18T09:21:38+00:00",
    +                
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Content Type + +
    +
    +
    +

    + POST + /content/typegroups/{contentTypeGroupId}/types +

    +

    Creates a new Content Type draft in the given Content Type group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Content Type or draft is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentType+xml +application/vnd.ez.api.ContentType+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content Type Create schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeCreate+xml +application/vnd.ez.api.ContentTypeCreate+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + publish + + + + boolean + + + + + + If true, the Content Type is published after creating (default false). +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Content Type created.

    +
    + 400 + +

    Error - The input does not match the input schema definition. Validation on a Field definition fails. Validation of the Content Type fails, e.g. multiple Fields of a same singular Field Type are provided. Publish is set to true and the input is not complete e.g. no Field definitions are provided.

    +
    + 401 + +

    Error - The user is not authorized to create this Content Type.

    +
    + 403 + +

    Error - A Content Type with same identifier already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeCreate + +
    + + ContentType + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeCreate>
    +  <identifier>newContentType</identifier>
    +  <names>
    +    <value languageCode="eng-US">New Content Type</value>
    +  </names>
    +  <descriptions>
    +    <value languageCode="eng-US">This is a description</value>
    +  </descriptions>
    +  <remoteId>remoteId-qwert548</remoteId>
    +  <urlAliasSchema>&lt;title&gt;</urlAliasSchema>
    +  <nameSchema>&lt;title&gt;</nameSchema>
    +  <isContainer>true</isContainer>
    +  <mainLanguageCode>eng-US</mainLanguageCode>
    +  <defaultAlwaysAvailable>true</defaultAlwaysAvailable>
    +  <defaultSortField>PATH</defaultSortField>
    +  <defaultSortOrder>ASC</defaultSortOrder>
    +  <FieldDefinitions>
    +    <FieldDefinition>
    +      <identifier>title</identifier>
    +      <fieldType>ezstring</fieldType>
    +      <fieldGroup>content</fieldGroup>
    +      <position>1</position>
    +      <isTranslatable>true</isTranslatable>
    +      <isRequired>true</isRequired>
    +      <isInfoCollector>false</isInfoCollector>
    +      <defaultValue>New Title</defaultValue>
    +      <isS
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentTypeCreate": {
    +    "identifier": "new_content_type",
    +    "names": {
    +      "value": [
    +        {
    +          "_languageCode": "eng-GB",
    +          "#text": "New Content Type"
    +        }
    +      ]
    +    },
    +    "descriptions": {
    +      "value": [
    +        {
    +          "_languageCode": "eng-GB",
    +          "#text": "This is a description"
    +        }
    +      ]
    +    },
    +    "remoteId": "remoteId-qwerty548",
    +    "urlAliasSchema": "<title>",
    +    "nameSchema": "<title>",
    +    "isContainer": true,
    +    "mainLanguageCode": "eng-GB",
    +    "defaultAlwaysAvailable": true,
    +    "defaultSortField": "PATH",
    +    "defaultSortOrder": "ASC",
    +    "FieldDefinitions": {
    +      "FieldDefinition": [
    +        {
    +          "identifier": "title",
    +          "fieldType": "ezstring",
    +          "fieldGroup": "content",
    +          "position": 1,
    +          "isTranslatable": true,
    +          "isRequired": true,
    +          "isInfoCollector": false,
    +          "defaultValue": "New Title",
    +          "isSearchable": true,
    +          "names": 
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/20/draft">
    +    <id>20</id>
    +    <status>DRAFT</status>
    +    <identifier>newContentType</identifier>
    +    <names>
    +        <value languageCode="eng-GB">New Content Type</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">This is a description</value>
    +    </descriptions>
    +    <creationDate>2019-02-26T09:39:58+01:00</creationDate>
    +    <modificationDate>2019-02-26T09:39:58+01:00</modificationDate>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/20/groups"/>
    +    <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/20/draft"/>
    +    <remoteId>remoteId-qwert548<
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /content/types

    +
    +
    +
    +

    /content/types

    + +
    +
    +
    +
    +
    +
    + List Content Types + +
    +
    +
    +

    + GET + /content/types +

    +

    Returns a list of Content Types.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the list of Content Type info objects or Content Types (including Field definitions) is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeInfoList+xml +application/vnd.ez.api.ContentTypeInfoList+json +application/vnd.ez.api.ContentTypeList+xml +application/vnd.ez.api.ContentTypeList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + identifier + + + + string + + + + + + Retrieves the Content Type for the given identifer. +
    + remoteId + + + + string + + + + + + Retrieves the Content Type for the given remote ID. +
    + limit + + + + string + + + + + + Only 'limit' items will be returned, starting with the offset. +
    + offset + + + + string + + + + + + Offset of the result set. +
    + orderby + + + + string + + + + + + One of (name|lastmodified). +
    + sort + + + + string + + + + + + One of (asc|desc). +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a list of Content Types.

    +
    + 401 + +

    Error - The user has no permission to read the Content Types.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeInfoList + + List of Content Type information.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeList media-type="application/vnd.ez.api.ContentTypeList+xml" href="/api/ezp/v2/content/typegroups/1/types">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2">
    +        <id>2</id>
    +        <status>DEFINED</status>
    +        <identifier>article</identifier>
    +        <names>
    +            <value languageCode="eng-GB">Article</value>
    +        </names>
    +        <descriptions/>
    +        <creationDate>2002-06-18T09:21:38+00:00</creationDate>
    +        <modificationDate>2004-04-20T09:56:29+00:00</modificationDate>
    +        <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +        <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/>
    +        <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/ap
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeList": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeList+json",
    +        "_href": "/api/ezp/v2/content/typegroups/1/types",
    +        "ContentType": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ContentType+json",
    +                "_href": "/api/ezp/v2/content/types/2",
    +                "id": 2,
    +                "status": "DEFINED",
    +                "identifier": "article",
    +                "names": {
    +                    "value": [
    +                        {
    +                            "_languageCode": "eng-GB",
    +                            "#text": "Article"
    +                        }
    +                    ]
    +                },
    +                "descriptions": {
    +                    "value": []
    +                },
    +                "creationDate": "2002-06-18T09:21:38+00:00",
    +                "modificationDate": "2004-04-20T09:56:29+00:00",
    +                "Creator": {
    +                    "_media-type": "application/vnd.ez.api.User+json",
    +        
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}

    + +
    +
    +
    +
    +
    +
    + Get Content Type + +
    +
    +
    +

    + GET + /content/types/{contentTypeId} +

    +

    Returns the Content Type with the provided ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Content Type is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentType+xml +application/vnd.ez.api.ContentType+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Content Type.

    +
    + 401 + +

    Error - The user is not authorized to read this Content Type.

    +
    + 404 + +

    Error - The Content Type does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentType + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2">
    +    <id>2</id>
    +    <status>DEFINED</status>
    +    <identifier>article</identifier>
    +    <names>
    +        <value languageCode="eng-GB">Article</value>
    +    </names>
    +    <descriptions/>
    +    <creationDate>2002-06-18T11:21:38+02:00</creationDate>
    +    <modificationDate>2004-04-20T11:56:29+02:00</modificationDate>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/2/groups"/>
    +    <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/2/draft"/>
    +    <remoteId>c15b600eb9198b1924063b5a68758232</remoteId>
    +    <urlAliasSchema></urlAliasSchema>
    +    <nameSchema>&lt;short_title|title&gt;</nam
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentType": {
    +        "_media-type": "application/vnd.ez.api.ContentType+json",
    +        "_href": "/api/ezp/v2/content/types/2",
    +        "id": 2,
    +        "status": "DEFINED",
    +        "identifier": "article",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Article"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": null
    +                }
    +            ]
    +        },
    +        "creationDate": "2002-06-18T09:21:38+00:00",
    +        "modificationDate": "2021-06-28T11:31:22+00:00",
    +        "Creator": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Modifier": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +     
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Copy Content Type + +
    +
    +
    +

    + COPY + /content/types/{contentTypeId} +

    +

    Copies a Content Type. A new remote ID is generated, and the identifier of the copy is set to 'copy_of_originalBaseIdentifier_newTypeId' (or another random string). COPY or POST with header X-HTTP-Method-Override COPY.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Copy of the Content Type created.

    +
    + 401 + +

    Error - The user is not authorized to copy this Content Type.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Draft + +
    +
    +
    +

    + POST + /content/types/{contentTypeId} +

    +

    Creates a draft and updates it with the given data.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Content Type draft is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeInfo+xml +application/vnd.ez.api.ContentTypeInfo+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content Type Update schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeUpdate+xml +application/vnd.ez.api.ContentTypeUpdate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Draft created.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to create the draft.

    +
    + 403 + +

    Error - A Content Type with the given new identifier already exists. A draft already exists.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeUpdate + +
    + + ContentTypeInfo + + This class stores Content Type information.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeUpdate>
    +    <defaultAlwaysAvailable>true</defaultAlwaysAvailable>
    +</ContentTypeUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentTypeUpdate": {
    +    "defaultAlwaysAvailable": "true"
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/3/draft">
    +    <id>3</id>
    +    <status>DRAFT</status>
    +    <identifier>user_group</identifier>
    +    <names>
    +        <value languageCode="eng-GB">User group</value>
    +    </names>
    +    <descriptions/>
    +    <creationDate>2002-06-18T11:21:38+02:00</creationDate>
    +    <modificationDate>2019-02-25T14:41:53+01:00</modificationDate>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/3/groups"/>
    +    <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3/draft"/>
    +    <remoteId>25b4268cdcd01921b808a0d854b877ef</remoteId>
    +    <urlAliasSchema></urlAliasSchema>
    +    <nameSchema>&lt;name&gt;
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeInfo": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeInfo+json",
    +        "_href": "/api/ezp/v2/content/types/1/draft",
    +        "id": 1,
    +        "status": "DRAFT",
    +        "identifier": "folder",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Folder"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": []
    +        },
    +        "creationDate": "2002-06-18T09:21:38+00:00",
    +        "modificationDate": "2021-08-11T11:17:58+00:00",
    +        "Creator": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Modifier": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Groups": {
    +            "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json",
    +            "_href": "/
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Content Type + +
    +
    +
    +

    + DELETE + /content/types/{contentTypeId} +

    +

    Deletes the provided Content Type.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Content Type deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete this Content Type.

    +
    + 403 + +

    Error - There are object instances of this Content Type.

    +
    + 404 + +

    Error - The Content Type does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}/fieldDefinitions/{fieldDefinitionId}

    + +
    +
    +
    +
    +
    +
    + Get Field definition + +
    +
    +
    +

    + GET + /content/types/{contentTypeId}/fieldDefinitions/{fieldDefinitionId} +

    +

    Returns the Field definition by the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Field definition is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinition+xml +application/vnd.ez.api.FieldDefinition+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Field definition.

    +
    + 401 + +

    Error - The user is not authorized to read the Content Type.

    +
    + 404 + +

    Error - The Content Type does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + FieldDefinition + + This class represents a Field definition.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/195">
    +    <id>195</id>
    +    <identifier>image</identifier>
    +    <fieldType>ezimageasset</fieldType>
    +    <fieldGroup>content</fieldGroup>
    +    <position>7</position>
    +    <isTranslatable>false</isTranslatable>
    +    <isRequired>false</isRequired>
    +    <isInfoCollector>false</isInfoCollector>
    +    <defaultValue>
    +        <value key="destinationContentId"/>
    +        <value key="alternativeText"/>
    +        <value key="source"/>
    +    </defaultValue>
    +    <isSearchable>false</isSearchable>
    +    <names>
    +        <value languageCode="eng-GB">Image</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB"></value>
    +    </descriptions>
    +    <fieldSettings/>
    +    <validatorConfiguration/>
    +</FieldDefinition>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "FieldDefinition": {
    +        "_media-type": "application/vnd.ez.api.FieldDefinition+json",
    +        "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195",
    +        "id": 195,
    +        "identifier": "image",
    +        "fieldType": "ezimageasset",
    +        "fieldGroup": "content",
    +        "position": 7,
    +        "isTranslatable": false,
    +        "isRequired": false,
    +        "isInfoCollector": false,
    +        "defaultValue": {
    +            "destinationContentId": null,
    +            "alternativeText": null,
    +            "source": null
    +        },
    +        "isSearchable": false,
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Image"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": null
    +                }
    +            ]
    +        },
    +        "fieldSettings": [],
    +        "valid
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}/draft

    + +
    +
    +
    +
    +
    +
    + Get Content Type draft + +
    +
    +
    +

    + GET + /content/types/{contentTypeId}/draft +

    +

    Returns the draft of the Content Type with the provided ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Content Type is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentType+xml +application/vnd.ez.api.ContentType+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Content Type.

    +
    + 401 + +

    Error - The user is not authorized to read this Content Type.

    +
    + 404 + +

    Error - The Content Type does not exist or does not have a draft.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentType + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Content Type draft + +
    +
    +
    +

    + PATCH + /content/types/{contentTypeId}/draft +

    +

    Updates metadata of a draft. This method does not handle Field definitions. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Content Type draft is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeInfo+xml +application/vnd.ez.api.ContentTypeInfo+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Content Type update schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeUpdate+xml +application/vnd.ez.api.ContentTypeUpdate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    Draft metadata updated.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to update the draft.

    +
    + 403 + +

    Error - A Content Type with the given new identifier already exists.

    +
    + 404 + +

    Error - There is no draft for this Content Type.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeUpdate + +
    + + ContentTypeInfo + + This class stores Content Type information.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeUpdate>
    +    <names>
    +        <value languageCode="eng-GB">Updated Content Type name</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">This is an updated Content Type description</value>
    +    </descriptions>
    +</ContentTypeUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ContentTypeUpdate": {
    +    "names": {
    +      "value": [ { 
    +        "_languageCode": "eng-GB",
    +        "#text": "Updated Content Type name"
    +       } ]
    +    },
    +    "descriptions": {
    +      "value": [ {
    +        "_languageCode": "eng-GB",
    +        "#text": "This is an updated Content Type description"
    +      } ]
    +    }
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeInfo media-type="application/vnd.ez.api.ContentTypeInfo+xml" href="/api/ezp/v2/content/types/14/draft">
    +    <id>14</id>
    +    <status>DRAFT</status>
    +    <identifier>new_content_type</identifier>
    +    <names>
    +        <value languageCode="eng-GB">Updated Content Type name</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">This is an updated Content Type description</value>
    +    </descriptions>
    +    <creationDate>2019-02-06T10:56:36+01:00</creationDate>
    +    <modificationDate>2019-02-25T12:15:51+01:00</modificationDate>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/14/groups"/>
    +    <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14/
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeInfo": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeInfo+json",
    +        "_href": "/api/ezp/v2/content/types/2/draft",
    +        "id": 2,
    +        "status": "DRAFT",
    +        "identifier": "article",
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Updated Content Type name"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "This is an updated Content Type description"
    +                }
    +            ]
    +        },
    +        "creationDate": "2002-06-18T09:21:38+00:00",
    +        "modificationDate": "2021-08-11T11:58:24+00:00",
    +        "Creator": {
    +            "_media-type": "application/vnd.ez.api.User+json",
    +            "_href": "/api/ezp/v2/user/users/14"
    +        },
    +        "Modifier": {
    +            "_media-type": "application/vnd.ez.api.User+j
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Publish Content Type draft + +
    +
    +
    +

    + PUBLISH + /content/types/{contentTypeId}/draft +

    +

    Publishes a Content Type draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    Content Type draft published.

    +
    + 401 + +

    Error - The user is not authorized to publish this Content Type draft.

    +
    + 403 + +

    Error - The Content Type draft is not complete, e.g. there is no Field definition provided.

    +
    + 404 + +

    Error - If there is no draft or Content Type with the given ID.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentType + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14">
    +    <id>14</id>
    +    <status>DEFINED</status>
    +    <identifier>copy_of_article_14</identifier>
    +    <names>
    +        <value languageCode="eng-GB">Updated Content Type name</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB">This is an updated Content Type description</value>
    +    </descriptions>
    +    <creationDate>2019-02-06T10:56:36+01:00</creationDate>
    +    <modificationDate>2019-02-25T12:15:51+01:00</modificationDate>
    +    <Creator media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Modifier media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <Groups media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/types/14/groups"/>
    +    <Draft media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/14/draft"/>
    + 
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Content Type draft + +
    +
    +
    +

    + DELETE + /content/types/{contentTypeId}/draft +

    +

    Deletes the provided Content Type draft.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Content Type draft deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete this Content Type draft.

    +
    + 404 + +

    Error - The Content Type draft does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}/draft/fieldDefinitions

    + +
    +
    +
    +
    +
    +
    + Get Draft Field definition list + +
    +
    +
    +

    + GET + /content/types/{contentTypeId}/draft/fieldDefinitions +

    +

    Returns all Field definitions of the provided Content Type Draft.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 200 + +

    OK - return a list of Field definitions.

    +
    + 404 + +

    Error - The Content Type draft does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Add Content Type Draft Field definition + +
    +
    +
    +

    + POST + /content/types/{contentTypeId}/draft/fieldDefinitions +

    +

    Creates a new Field definition for the given Content Type.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new Field definition is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinition+xml +application/vnd.ez.api.FieldDefinition+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Field Definition Create schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinitionCreate+xml +application/vnd.ez.api.FieldDefinitionCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    Field definition created.

    +
    + 400 + +

    Error - The input does not match the input schema definition or validation on the Field definition fails.

    +
    + 401 + +

    Error - The user is not authorized to add a Field definition.

    +
    + 403 + +

    Error - A Field definition with the same identifier already exists in the given Content Type. The Field definition is of singular type, already existing in the given Content Type. The Field definition you want to add is of a type that can't be added to a Content Type that already has content instances.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + FieldDefinitionCreate + +
    + + FieldDefinition + + This class represents a Field definition.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinitionCreate>
    +    <identifier>name</identifier>
    +    <fieldType>ezstring</fieldType>
    +    <isRequired>true</isRequired>
    +    <validatorConfiguration>
    +    <value key="StringLengthValidator">
    +        <value key="maxStringLength">32</value>
    +        <value key="minStringLength">8</value>
    +    </value>
    +</validatorConfiguration>
    +</FieldDefinitionCreate>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/14/draft/fieldDefinitions/221">
    +    <id>221</id>
    +    <identifier>name2</identifier>
    +    <fieldType>ezstring</fieldType>
    +    <fieldGroup></fieldGroup>
    +    <position>0</position>
    +    <isTranslatable>true</isTranslatable>
    +    <isRequired>true</isRequired>
    +    <isInfoCollector>false</isInfoCollector>
    +    <defaultValue/>
    +    <isSearchable>true</isSearchable>
    +    <names/>
    +    <descriptions/>
    +    <fieldSettings/>
    +    <validatorConfiguration>
    +        <value key="StringLengthValidator">
    +            <value key="maxStringLength">32</value>
    +            <value key="minStringLength">8</value>
    +        </value>
    +    </validatorConfiguration>
    +</FieldDefinition>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId}

    + +
    +
    +
    +
    +
    +
    + Get Content Type Draft Field definition + +
    +
    +
    +

    + GET + /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} +

    +

    Returns the Field definition by the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Field definition is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinition+xml +application/vnd.ez.api.FieldDefinition+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns the Field definition.

    +
    + 401 + +

    Error - The user is not authorized to read the Content Type draft.

    +
    + 404 + +

    Error - The Content Type or draft does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + FieldDefinition + + This class represents a Field definition.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/2/fieldDefinitions/195">
    +    <id>195</id>
    +    <identifier>image</identifier>
    +    <fieldType>ezimageasset</fieldType>
    +    <fieldGroup>content</fieldGroup>
    +    <position>7</position>
    +    <isTranslatable>false</isTranslatable>
    +    <isRequired>false</isRequired>
    +    <isInfoCollector>false</isInfoCollector>
    +    <defaultValue>
    +        <value key="destinationContentId"/>
    +        <value key="alternativeText"/>
    +        <value key="source"/>
    +    </defaultValue>
    +    <isSearchable>false</isSearchable>
    +    <names>
    +        <value languageCode="eng-GB">Image</value>
    +    </names>
    +    <descriptions>
    +        <value languageCode="eng-GB"></value>
    +    </descriptions>
    +    <fieldSettings/>
    +    <validatorConfiguration/>
    +</FieldDefinition>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "FieldDefinition": {
    +        "_media-type": "application/vnd.ez.api.FieldDefinition+json",
    +        "_href": "/api/ezp/v2/content/types/2/fieldDefinitions/195",
    +        "id": 195,
    +        "identifier": "image",
    +        "fieldType": "ezimageasset",
    +        "fieldGroup": "content",
    +        "position": 7,
    +        "isTranslatable": false,
    +        "isRequired": false,
    +        "isInfoCollector": false,
    +        "defaultValue": {
    +            "destinationContentId": null,
    +            "alternativeText": null,
    +            "source": null
    +        },
    +        "isSearchable": false,
    +        "names": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": "Image"
    +                }
    +            ]
    +        },
    +        "descriptions": {
    +            "value": [
    +                {
    +                    "_languageCode": "eng-GB",
    +                    "#text": null
    +                }
    +            ]
    +        },
    +        "fieldSettings": [],
    +        "valid
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Content Type Draft Field definition + +
    +
    +
    +

    + PATCH + /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} +

    +

    Updates the attributes of a Field definition.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Field definition is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinition+xml +application/vnd.ez.api.FieldDefinition+json + +
    +
    +
    +
    +

    Content-Type

    +

    The Field definition update schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.FieldDefinitionUpdate+xml +application/vnd.ez.api.FieldDefinitionUpdate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - attributes updated.

    +
    + 400 + +

    Error - The input does not match the input schema definition.

    +
    + 401 + +

    Error - The user is not authorized to update the Field definition.

    +
    + 403 + +

    Error - A Field definition with the given identifier already exists in the given Content Type.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + FieldDefinitionUpdate + +
    + + FieldDefinition + + This class represents a Field definition.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinitionUpdate>
    +    <fieldGroup>new_field_group</fieldGroup>
    +    <position>10</position>
    +</FieldDefinitionUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<FieldDefinition media-type="application/vnd.ez.api.FieldDefinition+xml" href="/api/ezp/v2/content/types/15/draft/fieldDefinitions/197">
    +    <id>197</id>
    +    <identifier>author</identifier>
    +    <fieldType>ezauthor</fieldType>
    +    <fieldGroup>new_field_group</fieldGroup>
    +    <position>10</position>
    +    <isTranslatable>true</isTranslatable>
    +    <isRequired>false</isRequired>
    +    <isInfoCollector>false</isInfoCollector>
    +    <defaultValue/>
    +    <isSearchable>false</isSearchable>
    +    <names>
    +        <value languageCode="eng-GB">Author</value>
    +    </names>
    +    <descriptions/>
    +    <fieldSettings>
    +        <value key="defaultAuthor">1</value>
    +    </fieldSettings>
    +    <validatorConfiguration/>
    +</FieldDefinition>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Content Type Draft Field definition + +
    +
    +
    +

    + DELETE + /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} +

    +

    Deletes the provided Field definition.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - Field definition deleted.

    +
    + 401 + +

    Error - The user is not authorized to delete this Content Type.

    +
    + 403 + +

    Error - There is no draft of the Content Type assigned to the authenticated user.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /content/types/{contentTypeId}/groups

    + +
    +
    +
    +
    +
    +
    + Get groups of Content Type + +
    +
    +
    +

    + GET + /content/types/{contentTypeId}/groups +

    +

    Returns the Content Type group to which Content Type belongs to.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Content Type group list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentTypeGroupRefList+xml +application/vnd.ez.api.ContentTypeGroupRefList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - The user is not authorized to read this Content Type.

    +
    + 404 + +

    Error - The Content Type does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentTypeGroupRefList + + List of Content Type groups references.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ContentTypeGroupRefList media-type="application/vnd.ez.api.ContentTypeGroupRefList+xml" href="/api/ezp/v2/content/typegroups/2/types">
    +    <ContentTypeGroupRef media-type="application/vnd.ez.api.ContentTypeGroup+xml" href="/api/ezp/v2/content/typegroups/1"/>
    +</ContentTypeGroupRefList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ContentTypeGroupRefList": {
    +        "_media-type": "application/vnd.ez.api.ContentTypeGroupRefList+json",
    +        "_href": "/api/ezp/v2/content/typegroups/2/types",
    +        "ContentTypeGroupRef": [
    +            {
    +                "_media-type": "application/vnd.ez.api.ContentTypeGroup+json",
    +                "_href": "/api/ezp/v2/content/typegroups/1"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +

    /content/types/{contentTypeId}/groups/{id}

    + +
    +
    + +
    +

    /content/assets

    +
    +
    +
    +

    /content/assets/images/{assetId}/{assetSource}

    + +
    +
    +
    +
    +
    +
    + Fetch image asset value + +
    +
    +
    +

    + GET + /content/assets/images/{assetId}/{assetSource} +

    +

    Fetches the image asset value object. Depending on the provider, the asset metadata object can contain different parameters.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the value XML object with image asset metadata is returned.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Asset+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to read this image asset.

    +
    + 404 + +

    Error - asset Id/asset source pair does not match any image asset.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Asset + + Asset value object
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Asset media-type="application/vnd.ez.api.Asset+xml" href="/api/ezp/v2/content/assets/images/UtrE5DcgEyg/unsplash">
    +    <uri>https://images.unsplash.com/photo-1544568100-847a948585b9?ixlib=rb-1.2.1&amp;ixid=eyJhcHBfaWQiOjE1MDk5MH0</uri>
    +    <assetId>UtrE5DcgEyg</assetId>
    +    <assetSource>unsplash</assetSource>
    +    <assetMetadata>
    +        <alternativeText>medium-coated brown dog during daytime</alternativeText>
    +        <width>5184</width>
    +        <height>3888</height>
    +        <created_at>2018-12-11T17:46:12-05:00</created_at>
    +        <updated_at>2020-10-23T23:08:44-04:00</updated_at>
    +        <author>Jamie Street</author>
    +        <author_link_html>https://unsplash.com/@jamie452?utm_source=Ibexa+Platform+DAM+Connector&amp;utm_medium=referral&amp;utm_campaign=api-credit</author_link_html>
    +    </assetMetadata>
    +</Asset>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Calendar + +

    +
    +
    +

    /calendar/event

    + +
    +
    +
    +
    +
    +
    + Calendar list + +
    +
    +
    +

    + GET + /calendar/event +

    +

    Calendar event list.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the calendar event list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ContentInfo+xml +application/vnd.ez.api.ContentInfo+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + start + + + + datetime + + + + + + Query start date. +
    + end + + + + datetime + + + + + + Query end date. +
    + types + + + + string + + + + + + The types of events that are displayed. +
    + languages + + + + string + + + + + + Language code. Restricts the output of translatable Fields to the given languages. +
    + count + + + + string + + + + + + Number of parameters that are returned in the list. +
    + cursor + + + + string + + + + + + Starting point of calendar event list. It should be taken from the URL. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentInfo + + This class provides all version independent information of the Content item.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<EventList media-type="application/vnd.ez.api.EventList+xml" currentPage="/api/ezp/v2/calendar/event?start=1597104000&amp;end=1598745600&amp;count=10">
    +    <totalCount>5</totalCount>
    +</EventList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "EventList": {
    +        "_media-type": "application/vnd.ez.api.EventList+json",
    +        "_currentPage": "/api/ezp/v2/calendar/event?start=1597104000&end=1598745600&count=10",
    +        "totalCount": 5,
    +        "events": []
    +    }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /calendar/event/grouped-by-day

    + +
    +
    +
    +
    +
    +
    + Calendar list grouped by day + +
    +
    +
    +

    + GET + /calendar/event/grouped-by-day +

    +

    Calendar event list grouped by day.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the calendar event list grouped by day is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ScheduledVersion+xml +application/vnd.ez.api.ScheduledVersion+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + start + + + + integer + + + + + + Query start date. +
    + end + + + + integer + + + + + + Query end date. +
    + types + + + + string + + + + + + The types of events that are displayed. +
    + languages + + + + string + + + + + + Language code. Restricts the output of event list to the given languages. +
    + count + + + + integer + + + + + + Number of parameters that are returned in the list. +
    + cursor + + + + string + + + + + + Starting point of calendar event list. It should be taken from the URL. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ContentInfo + + This class provides all version independent information of the Content item.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<EventGroupList media-type="application/vnd.ez.api.EventGroupList+xml">
    +    <EventGroup media-type="application/vnd.ez.api.EventGroup+xml" currentPage="/api/ezp/v2/calendar/event?start=1597104000&amp;end=1597190400&amp;count=10">
    +        <DateRange media-type="application/vnd.ez.api.DateRange+xml">
    +            <startDate>1597104000</startDate>
    +            <endDate>1597190400</endDate>
    +        </DateRange>
    +        <totalCount>0</totalCount>
    +    </EventGroup>
    +    <EventGroup media-type="application/vnd.ez.api.EventGroup+xml" currentPage="/api/ezp/v2/calendar/event?start=1597190400&amp;end=1597276800&amp;count=10">
    +        <DateRange media-type="application/vnd.ez.api.DateRange+xml">
    +            <startDate>1597190400</startDate>
    +            <endDate>1597276800</endDate>
    +        </DateRange>
    +        <totalCount>0</totalCount>
    +    </EventGroup>
    +    <EventGroup media-type="application/vnd.ez.api.EventGroup+xml" currentPage="/api/ezp/v2/calendar/event?st
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "EventGroupList": {
    +        "_media-type": "application/vnd.ez.api.EventGroupList+json",
    +        "groups": [
    +            {
    +                "_media-type": "application/vnd.ez.api.EventGroup+json",
    +                "_currentPage": "/api/ezp/v2/calendar/event?start=1597104000&end=1597190400&count=10",
    +                "DateRange": {
    +                    "_media-type": "application/vnd.ez.api.DateRange+json",
    +                    "startDate": "1597104000",
    +                    "endDate": "1597190400"
    +                },
    +                "totalCount": 0,
    +                "events": []
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.EventGroup+json",
    +                "_currentPage": "/api/ezp/v2/calendar/event?start=1597190400&end=1597276800&count=10",
    +                "DateRange": {
    +                    "_media-type": "application/vnd.ez.api.DateRange+json",
    +                    "startDate": "1597190400",
    +                    "endDate": "1597276800"
    +             
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /calendar/event/{eventType}

    + +
    +
    +
    +
    +
    +
    + Calendar action + +
    +
    +
    +

    + POST + /calendar/event/{eventType} +

    +

    A calendar action that e.g. reschedules or unschedules calendar events. The event type should always be copied from an event.

    +

    +
    +
    Header parameters
    +
    +

    Content-Type

    +

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.calendar.future_publication.UnscheduleAction+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - the action has been unscheduled.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignInput + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "UnscheduleAction": {
    +    "events": [
    +      "future_publication:2"
    +    ]
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + Views + +

    +
    +
    +

    /views

    + +
    +
    +
    +
    +
    +
    + Create View + +
    +
    +
    +

    + POST + /views +

    +

    Executes a query and returns a View including the results.

    +

    View input reflects the criteria model of the public API.

    +

    Refer to Search Criteria Reference

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    The view in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.View+xml +application/vnd.ez.api.View+json +application/vnd.ez.api.View+xml; version=1.1 +application/vnd.ez.api.View+json; version=1.1 + +
    +
    +
    +
    +

    Content-Type

    +

    The view input in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.ViewInput+xml +application/vnd.ez.api.ViewInput+json +application/vnd.ez.api.ViewInput+xml; version=1.1 +application/vnd.ez.api.ViewInput+json; version=1.1 + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ViewInput + +
    + + View + + View.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<ViewInput>
    +  <identifier>TitleView</identifier>
    +  <Query>
    +    <Filter>
    +      <ContentTypeIdentifierCriterion>image</ContentTypeIdentifierCriterion>
    +      <SectionIdentifierCriterion>media</SectionIdentifierCriterion>
    +      <DateMetadataCriterion>
    +        <Target>modified</Target>
    +        <Value>1675681020</Value>
    +        <Operator>gte</Operator>
    +      </DateMetadataCriterion>
    +    </Filter>
    +    <limit>10</limit>
    +    <offset>0</offset>
    +    <SortClauses>
    +      <ContentName>ascending</ContentName>
    +    </SortClauses>
    +    <Aggregations>
    +      <Aggregation>
    +        <ContentTypeTermAggregation>
    +          <name>some name</name>
    +        </ContentTypeTermAggregation>
    +      </Aggregation>
    +    </Aggregations>
    +  </Query>
    +</ViewInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "ViewInput": {
    +        "identifier": "TitleView",
    +        "Query": {
    +            "Filter": {
    +                "ContentTypeIdentifierCriterion": "image",
    +                "SectionIdentifierCriterion": "media",
    +                "DateMetadataCriterion": {
    +                    "Target": "modified",
    +                    "Value": 1675681020,
    +                    "Operator": "gte"
    +                }
    +            },
    +            "limit": 10,
    +            "offset": 0,
    +            "SortClauses": {
    +                "ContentName": "ascending"
    +            },
    +            "Aggregations": [
    +                {
    +                    "ContentTypeTermAggregation": {
    +                        "name": "some name"
    +                    }
    +                }
    +            ]
    +        }
    +    }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<View href="/views/TitleView" media-type="application/vnd.ez.api.View+xml; version=1.1">
    +  <identifier>TitleView</identifier>
    +  <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/>
    +  <public>false</public>
    +  <LocationQuery>
    +    <Filter>
    +      <ParentLocationIdCriterion>2</ParentLocationIdCriterion>
    +    </Filter>
    +    <limit>10</limit>
    +    <offset>0</offset>
    +    <SortClauses>
    +      <ContentName>ascending</ContentName>
    +    </SortClauses>
    +    <FacetBuilders>
    +      <contentTypeFacetBuilder/>
    +    </FacetBuilders>
    +  </LocationQuery>
    +  <Result href="/content/views/view1234/results"
    +    media-type="application/vnd.ez.api.ViewResult+xml" count="34" time="31" maxScore="1.0">
    +    <searchHits>
    +      <searchHit score="1.0" index="installid1234567890">
    +        <hightlight/>
    +        <value>
    +          <Location media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/2">
    +            <id>2</id>
    +            <priority>0</priori
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Managing users + +

    +
    +

    /user/groups

    +
    +
    +
    +

    /user/groups

    + +
    +
    +
    +
    +
    +
    + Load User Groups + +
    +
    +
    +

    + GET + /user/groups +

    +

    Loads User Groups for either an an ID or a remote ID or a Role.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    UserGroupList - If set, the User Group List is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupList+xml +application/vnd.ez.api.UserGroupList+json +application/vnd.ez.api.UserGroupRefList+xml +application/vnd.ez.api.UserGroupRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + roleId + + + + string + + + + + + Lists User Groups assigned to the given Role (e.g. GET /user/groups?roleId=1). +
    + id + + + + string + + + + + + Retrieves the User Groups for the given ID. +
    + remoteID + + + + string + + + + + + Retrieves the User Groups for the given remote ID. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user has no permission to read User Groups.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupList + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupList media-type="application/vnd.ez.api.UserGroupList+xml" href="/api/ezp/v2/user/groups">
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13/15" id="14" remoteId="1bb4fe25487f05527efa8bfd394cecc7">
    +        <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/>
    +        <name>Administrator User</name>
    +        <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/14/versions"/>
    +        <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +        <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13/15"/>
    +        <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/14/locations"/>
    +        <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/us
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroupList": {
    +        "_media-type": "application/vnd.ez.api.UserGroupList+json",
    +        "_href": "/api/ezp/v2/user/groups",
    +        "UserGroup": [
    +            {
    +                "_media-type": "application/vnd.ez.api.UserGroup+json",
    +                "_href": "/api/ezp/v2/user/groups/1/5/13/15",
    +                "_id": 14,
    +                "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7",
    +                "ContentType": {
    +                    "_media-type": "application/vnd.ez.api.ContentType+json",
    +                    "_href": "/api/ezp/v2/content/types/4"
    +                },
    +                "name": "Administrator User",
    +                "Versions": {
    +                    "_media-type": "application/vnd.ez.api.VersionList+json",
    +                    "_href": "/api/ezp/v2/content/objects/14/versions"
    +                },
    +                "Section": {
    +                    "_media-type": "application/vnd.ez.api.Section+json",
    +                    "_href": "/api/ezp/v2/content/sections/2"
    +  
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/root

    + +
    +
    +
    +
    +
    +
    + Get root User Group + +
    +
    +
    +

    + GET + /user/groups/root +

    +

    Redirects to the root User Group.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + 301 + +

    Moved permanently.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/{path}

    + +
    +
    +
    +
    +
    +
    + Load User Group + +
    +
    +
    +

    + GET + /user/groups/{path} +

    +

    Loads User Groups for the given {path}.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new User Group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroup+xml +application/vnd.ez.api.UserGroup+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - loads User Groups.

    +
    + 401 + +

    Error - the user has no permission to read User Groups.

    +
    + 404 + +

    Error - the User Group does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroup + + This class represents a User group.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/>
    +    <name>Administrator users</name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/12/versions"/>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +    <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13"/>
    +    <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/12/locations"/>
    +    <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <publishDate>2002-10-06T16:12:55+00:00</publishDate>
    +    <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDa
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroup": {
    +        "_media-type": "application/vnd.ez.api.UserGroup+json",
    +        "_href": "/api/ezp/v2/user/groups/1/5/13",
    +        "_id": 12,
    +        "_remoteId": "9b47a45624b023b1a76c73b74d704acf",
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/3"
    +        },
    +        "name": "Administrator users",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/12/versions"
    +        },
    +        "Section": {
    +            "_media-type": "application/vnd.ez.api.Section+json",
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        },
    +        "MainLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/5/13"
    +        },
    +        "Locations": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_hr
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update User Group + +
    +
    +
    +

    + PATCH + /user/groups/{path} +

    +

    Updates a User Group. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new User Group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroup+xml +application/vnd.ez.api.UserGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The UserGroupUpdate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupUpdate+json +application/vnd.ez.api.UserGroupUpdate+xml + +
    +
    +
    +
    +

    If-Match

    +

    Performs the PATCH only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - updated User Group.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update the User Group.

    +
    + 412 + +

    Error - if the current ETag does not match with the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupUpdate + +
    + + UserGroup + + This class represents a User group.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupUpdate>
    +	<Section href="/api/ezp/v2/content/sections/2" />
    +</UserGroupUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroupUpdate":{
    +        "Section": {
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13" id="12" remoteId="9b47a45624b023b1a76c73b74d704acf">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/>
    +    <name>Administrator users</name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/12/versions"/>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +    <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/13"/>
    +    <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/12/locations"/>
    +    <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <publishDate>2002-10-06T16:12:55+00:00</publishDate>
    +    <lastModificationDate>2002-10-06T16:12:55+00:00</lastModificationDa
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroup": {
    +        "_media-type": "application/vnd.ez.api.UserGroup+json",
    +        "_href": "/api/ezp/v2/user/groups/1/5/13",
    +        "_id": 12,
    +        "_remoteId": "9b47a45624b023b1a76c73b74d704acf",
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/3"
    +        },
    +        "name": "Administrator users",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/12/versions"
    +        },
    +        "Section": {
    +            "_media-type": "application/vnd.ez.api.Section+json",
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        },
    +        "MainLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/5/13"
    +        },
    +        "Locations": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_hr
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete User Group + +
    +
    +
    +

    + DELETE + /user/groups/{path} +

    +

    The given User Group is deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No content - the given User Group is deleted.

    +
    + 401 + +

    Error - the user is not authorized to delete this Content Type.

    +
    + 403 + +

    Error - the User Group is not empty.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Move User Group + +
    +
    +
    +

    + MOVE + /user/groups/{path} +

    +

    Moves the User Group to another parent. MOVE or POST with header X-HTTP-Method-Override MOVE.

    +

    +
    +
    Header parameters
    +
    +

    Destination

    +

    A parent group resource to which the Location is moved.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 201 + +

    Created - moves the User Group to another parent.

    +
    + 401 + +

    Error - the user is not authorized to update the User Group.

    +
    + 403 + +

    Error - the new parent does not exist.

    +
    + 404 + +

    Error - the User Group does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/{path}/users

    + +
    +
    +
    +
    +
    +
    + Load Users of Group + +
    +
    +
    +

    + GET + /user/groups/{path}/users +

    +

    Loads the Users of the Group with the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    UserList - If set, the User list returned in XML or JSON format. UserRefList - If set, the link list of Users returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserList+xml +application/vnd.ez.api.UserList+json +application/vnd.ez.api.UserRefList+xml +application/vnd.ez.api.UserRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + limit + + + + string + + + + + + Only 'limit' items will be returned started by offset. +
    + offset + + + + string + + + + + + Offset of the result set. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - the Users of the Group with the given ID.

    +
    + 401 + +

    Error - the user has no permission to read User Groups.

    +
    + 404 + +

    Error - the User Group does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserRefList + + Returns a list of the users.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserRefList media-type="application/vnd.ez.api.UserRefList+xml" href="/api/ezp/v2/user/groups/13/users">
    +    <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/113"/>
    +    <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +</UserRefList>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserRefList": {
    +        "_media-type": "application/vnd.ez.api.UserRefList+json",
    +        "_href": "/api/ezp/v2/user/groups/13/users",
    +        "User": [
    +            {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/113"
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create User + +
    +
    +
    +

    + POST + /user/groups/{path}/users +

    +

    Creates a new User in the given Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new User is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.User+xml +application/vnd.ez.api.User+json + +
    +
    +
    +
    +

    Content-Type

    +

    The UserCreate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserCreate+json +application/vnd.ez.api.UserCreate+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to create this User.

    +
    + 403 + +

    Error - a User with the same login already exists.

    +
    + 404 + +

    Error - the Group with the given ID does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UserCreate + +
    + + User + + This class represents a User value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserCreate>
    +  <mainLanguageCode>eng-GB</mainLanguageCode>
    +  <remoteId>remoteId-qwert426</remoteId>
    +  <login>yura</login>
    +  <email>yurarajzer@example.net</email>
    +  <password>Secrepassword5!</password>
    +  <fields>
    +    <field>
    +      <fieldDefinitionIdentifier>first_name</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>Yura</fieldValue>
    +    </field>
    +    <field>
    +      <fieldDefinitionIdentifier>last_name</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>Rajzer</fieldValue>
    +    </field>
    +  </fields>
    +</UserCreate>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "UserCreate": {
    +    "mainLanguageCode": "eng-GB",
    +    "remoteId": "remoteId-qwert426",
    +    "login": "yura",
    +    "email": "yurarajzer@example.net",
    +    "password": "Password1!",
    +    "fields": {
    +      "field": [
    +        {
    +          "fieldDefinitionIdentifier": "first_name",
    +          "languageCode": "eng-GB",
    +          "fieldValue": "Yura"
    +        },
    +        {
    +          "fieldDefinitionIdentifier": "last_name",
    +          "languageCode": "eng-GB",
    +          "fieldValue": "Rajzer"
    +        }
    +      ]
    +    }
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/59" id="59" remoteId="remoteId-qwert426">
    +<ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/>
    +<name>Yura Rajzer</name>
    +<Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/59/versions"/>
    +<Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +<MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/65/67"/>
    +<Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/59/locations"/>
    +<Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/59/groups"/>
    +<Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +<publishDate>2019-02-27T11:23:42+01:00</publishDate>
    +<lastModificationDate>2019-02-2
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "User": {
    +        "_media-type": "application/vnd.ez.api.User+json",
    +        "_href": "/api/ezp/v2/user/users/57",
    +        "_id": 57,
    +        "_remoteId": "remoteId-qwert426",
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/4"
    +        },
    +        "name": "Yura Rajzer",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/57/versions"
    +        },
    +        "Section": {
    +            "_media-type": "application/vnd.ez.api.Section+json",
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        },
    +        "MainLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/5/13/58"
    +        },
    +        "Locations": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/content/objects/5
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/{path}/subgroups

    + +
    +
    +
    +
    +
    +
    + Load subgroups + +
    +
    +
    +

    + GET + /user/groups/{path}/subgroups +

    +

    Returns a list of the subgroups.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    UserGroupList - If set, the User Group list is returned in XML or JSON format. UserGroupRefList - If set, the link list of User Groups is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupList+xml +application/vnd.ez.api.UserGroupList+json +application/vnd.ez.api.UserGroupRefList+xml +application/vnd.ez.api.UserGroupRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + limit + + + + string + + + + + + The number of Locations returned. +
    + offset + + + + string + + + + + + The offset of the result set. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - list of the subgroups.

    +
    + 401 + +

    Error - the user has no permission to read User Groups.

    +
    + 404 + +

    Error - the User Group does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupRefList + + Returns a list of the sub groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/groups/13/subgroups">
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13/112"/>
    +</UserGroupRefList>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroupRefList": {
    +        "_media-type": "application/vnd.ez.api.UserGroupRefList+json",
    +        "_href": "/api/ezp/v2/user/groups/13/subgroups",
    +        "UserGroup": [
    +            {
    +                "_media-type": "application/vnd.ez.api.UserGroup+json",
    +                "_href": "/api/ezp/v2/user/groups/1/5/13/112"
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create User Group + +
    +
    +
    +

    + POST + /user/groups/{path}/subgroups +

    +

    Creates a new User Group under the given parent. To create a top level group use '/user/groups/subgroups'.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new User Group is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroup+xml +application/vnd.ez.api.UserGroup+json + +
    +
    +
    +
    +

    Content-Type

    +

    The UserGroupCreate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupCreate+json +application/vnd.ez.api.UserGroupCreate+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to create this User Group.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupCreate + +
    + + UserGroup + + This class represents a User group.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupCreate>
    +  <mainLanguageCode>eng-GB</mainLanguageCode>
    +  <remoteId>remoteId-qwert098</remoteId>
    +  <fields>
    +    <field>
    +      <fieldDefinitionIdentifier>name</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>UserGroup</fieldValue>
    +    </field>
    +    <field>
    +      <fieldDefinitionIdentifier>description</fieldDefinitionIdentifier>
    +      <languageCode>eng-GB</languageCode>
    +      <fieldValue>This is the description of the user group</fieldValue>
    +    </field>
    +  </fields>
    +</UserGroupCreate>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "UserGroupCreate": {
    +    "mainLanguageCode": "eng-GB",
    +    "remoteId": "remoteId-qwert098",
    +    "fields": {
    +      "field": [
    +        {
    +          "fieldDefinitionIdentifier": "name",
    +          "languageCode": "eng-GB",
    +          "fieldValue": "UserGroup2"
    +        },
    +        {
    +          "fieldDefinitionIdentifier": "description",
    +          "languageCode": "eng-GB",
    +          "fieldValue": "This is the description of the user group"
    +        }
    +      ]
    +    }
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/79" id="85" remoteId="remoteId-qwert098">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/3"/>
    +    <name>UserGroup2</name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/85/versions"/>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +    <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/79"/>
    +    <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/85/locations"/>
    +    <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +    <publishDate>2021-08-09T09:03:35+00:00</publishDate>
    +    <lastModificationDate>2021-08-09T09:03:35+00:00</lastModificationDate>
    +    <mainLanguageCod
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroup": {
    +        "_media-type": "application/vnd.ez.api.UserGroup+json",
    +        "_href": "/api/ezp/v2/user/groups/1/5/81",
    +        "_id": 87,
    +        "_remoteId": "remoteId-qwert098",
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/3"
    +        },
    +        "name": "UserGroup2",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/87/versions"
    +        },
    +        "Section": {
    +            "_media-type": "application/vnd.ez.api.Section+json",
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        },
    +        "MainLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/5/81"
    +        },
    +        "Locations": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/conten
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/{path}/roles

    + +
    +
    +
    +
    +
    +
    + Load Roles for User Group + +
    +
    +
    +

    + GET + /user/groups/{path}/roles +

    +

    Returns a list of all Roles assigned to the given User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/42/44/roles/1">
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1"/>
    +</RoleAssignment>
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Assign Role to User Group + +
    +
    +
    +

    + POST + /user/groups/{path}/roles +

    +

    Assigns a Role to a User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleAssignInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignInput+json +application/vnd.ez.api.RoleAssignInput+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - validation of limitation in RoleAssignInput fails.

    +
    + 401 + +

    Error - the user is not authorized to assign this Role.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignInput + +
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignInput>
    +  <Role href="/api/ezp/v2/user/roles/10" media-type="application/vnd.ez.api.RoleAssignInput+xml"/>
    +  <limitation identifier="Section">
    +      <values>
    +          <ref href="/api/ezp/v2/content/sections/1" media-type="application/vnd.ez.api.Section+xml" />
    +          <ref href="/api/ezp/v2/content/sections/4" media-type="application/vnd.ez.api.Section+xml" />
    +      </values>
    +  </limitation>
    +</RoleAssignInput>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "RoleAssignInput": {
    +    "Role": {
    +      "_href": "/api/ezp/v2/user/roles/2",
    +      "media-type": "application/vnd.ez.api.RoleAssignInput+json",
    +      "self-closing": "true"
    +    },
    +    "Limitation": {
    +      "identifier": "Section",
    +      "values": {
    +        "ref": [
    +          {
    +            "_href": "/api/ezp/v2/content/sections/1",
    +            "media-type": "application/vnd.ez.api.Section+json",
    +            "self-closing": "true"
    +          },
    +          {
    +            "_href": "/api/ezp/v2/content/sections/4",
    +            "media-type": "application/vnd.ez.api.Section+json",
    +            "self-closing": "true"
    +          }
    +        ]
    +      }
    +    }
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/65/roles">
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/3">
    +    <limitation identifier="Section">
    +        <values>
    +            <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +        </values>
    +    </limitation>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/>
    +</RoleAssignment>
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/10">
    +    <limitation identifier="Section">
    +        <values>
    +            <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +        </values>
    +    </limitation>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/10"/>
    +</RoleAssignment>
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssi
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignmentList": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignmentList+json",
    +        "_href": "/api/ezp/v2/user/groups/1/12/2/roles",
    +        "RoleAssignment": [
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/groups/1/12/2/roles/2",
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/2"
    +                }
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/groups/{path}/roles/{roleId}

    + +
    +
    +
    +
    +
    +
    + Load User Group Role Assignment + +
    +
    +
    +

    + GET + /user/groups/{path}/roles/{roleId} +

    +

    Returns a Role assignment of the given User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignment+xml +application/vnd.ez.api.RoleAssignment+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - returns a Role assignment of the given User Group.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignment + + This value object represents an assignment of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/11/12/roles/1">
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1"/>
    +</RoleAssignment>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignment": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +        "_href": "/api/ezp/v2/user/groups/1/11/12/roles/1",
    +        "Role": {
    +            "_media-type": "application/vnd.ez.api.Role+json",
    +            "_href": "/api/ezp/v2/user/roles/1"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Unassign Role from User Group + +
    +
    +
    +

    + DELETE + /user/groups/{path}/roles/{roleId} +

    +

    The given Role is removed from the User or User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to delete this Role assignment.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/groups/1/5/65/roles">
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/groups/1/5/65/roles/3">
    +    <limitation identifier="Section">
    +        <values>
    +            <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +        </values>
    +    </limitation>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/>
    +</RoleAssignment>
    +</RoleAssignmentList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignmentList": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignmentList+json",
    +        "_href": "/api/ezp/v2/user/groups/1/57/58/roles",
    +        "RoleAssignment": [
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/groups/1/57/58/roles/3",
    +                "limitation": {
    +                    "_identifier": "Section",
    +                    "values": {
    +                        "ref": [
    +                            {
    +                                "_media-type": "application/vnd.ez.api.ref+json",
    +                                "_href": "1"
    +                            }
    +                        ]
    +                    }
    +                },
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/3"
    +                }
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /user/users

    +
    +
    +
    +

    /user/users

    + +
    +
    +
    +
    +
    +
    + List Users + +
    +
    +
    +

    + GET + /user/users +

    +

    Load Users either for a given remote ID or Role.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    UserList - If set, the User list is returned in XML or JSON format. UserRefList - If set, the link list of Users is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserList+xml +application/vnd.ez.api.UserList+json +application/vnd.ez.api.UserRefList+xml +application/vnd.ez.api.UserRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + roleId + + + + string + + + + + + Lists Users assigned to the given Role (e.g. GET /user/users?roleId=/user/roles/1). +
    + remoteId + + + + string + + + + + + Retrieves the User for the given remote ID (e.g. GET /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). +
    + login + + + + string + + + + + + Retrieves the User for the given login (e.g. GET /user/users?login=editor). +
    + email + + + + string + + + + + + Lists Users with the given email (e.g. GET /user/users?email=editor@example.com). +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Loads Users either for a given remote ID or Role.

    +
    + 404 + +

    If there are no visible Users matching the filter.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UserRefList + + Returns a list of the users.
    + + UserList + + This class represents a list of users.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserRefList media-type="application/vnd.ez.api.UserRefList+xml" href="/api/ezp/v2/user/users">
    +    <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/14"/>
    +</UserRefList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserList": {
    +        "_media-type": "application/vnd.ez.api.UserList+json",
    +        "_href": "/api/ezp/v2/user/users",
    +        "User": [
    +            {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/14",
    +                "_id": 14,
    +                "_remoteId": "1bb4fe25487f05527efa8bfd394cecc7",
    +                "ContentType": {
    +                    "_media-type": "application/vnd.ez.api.ContentType+json",
    +                    "_href": "/api/ezp/v2/content/types/4"
    +                },
    +                "name": "Administrator User",
    +                "Versions": {
    +                    "_media-type": "application/vnd.ez.api.VersionList+json",
    +                    "_href": "/api/ezp/v2/content/objects/14/versions"
    +                },
    +                "Section": {
    +                    "_media-type": "application/vnd.ez.api.Section+json",
    +                    "_href": "/api/ezp/v2/content/sections/2"
    +                },
    +            
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Verify Users + +
    +
    +
    +

    + HEAD + /user/users +

    +

    Verifies if there are Users matching given filter.

    +

    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + roleId + + + + string + + + + + + Checks if there are Users directly assigned to the given Role (e.g. HEAD /user/users?roleId=/user/roles/1). +
    + remoteId + + + + string + + + + + + Checks if there is a User for the given remote ID (e.g. HEAD /user/users?remoteId=55dd9713db75145f374bbd0b4f60ad29). +
    + login + + + + string + + + + + + Checks if there is a User for the given login (e.g. HEAD /user/users?login=editor). +
    + email + + + + string + + + + + + Checks if there is a User with the given email (e.g. HEAD /user/users?email=editor@example.com). +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 200 + +

    OK - verifies if there are Users matching the given filter.

    +
    + 404 + +

    Error - there are no visible Users matching the filter.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /user/users/{userId}

    + +
    +
    +
    +
    +
    +
    + Load User + +
    +
    +
    +

    + GET + /user/users/{userId} +

    +

    Loads User with the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the User is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.User+xml +application/vnd.ez.api.User+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - the User with the given ID.

    +
    + 401 + +

    Error - the user has no permission to read Users.

    +
    + 404 + +

    Error - the User does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserList + + This class represents a list of users.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserList media-type="application/vnd.ez.api.UserList+xml" href="/api/ezp/v2/user/users">
    +    <User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13">
    +        <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/>
    +        <name>Jose Vargas</name>
    +        <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/79/versions"/>
    +        <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +        <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/12/79"/>
    +        <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/79/locations"/>
    +        <Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/79/groups"/>
    +      
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserList": {
    +        "_media-type": "application/vnd.ez.api.UserList+json",
    +        "_href": "/api/ezp/v2/user/users",
    +        "User": [
    +            {
    +                "_media-type": "application/vnd.ez.api.User+json",
    +                "_href": "/api/ezp/v2/user/users/79",
    +                "_id": 79,
    +                "_remoteId": "bcf0764b417f05af21852a1f03fb1f13",
    +                "ContentType": {
    +                    "_media-type": "application/vnd.ez.api.ContentType+json",
    +                    "_href": "/api/ezp/v2/content/types/4"
    +                },
    +                "name": "Jose Vargas",
    +                "Versions": {
    +                    "_media-type": "application/vnd.ez.api.VersionList+json",
    +                    "_href": "/api/ezp/v2/content/objects/79/versions"
    +                },
    +                "Section": {
    +                    "_media-type": "application/vnd.ez.api.Section+json",
    +                    "_href": "/api/ezp/v2/content/sections/2"
    +                },
    +                "Ma
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update User + +
    +
    +
    +

    + PATCH + /user/users/{userId} +

    +

    Updates a User.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated User is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.User+xml +application/vnd.ez.api.User+json + +
    +
    +
    +
    +

    Content-Type

    +

    The UserUpdate schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserUpdate+json +application/vnd.ez.api.UserUpdate+xml + +
    +
    +
    +
    +

    If-Match

    +

    Performs a PATCH only if the specified ETag is the current one.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - User updated.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update the User.

    +
    + 404 + +

    Error - the User does not exist.

    +
    + 412 + +

    Error - the current ETag does not match with the provided one in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + UserUpdate + +
    + + User + + This class represents a User value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserUpdate>
    +  <login>josevargas</login>
    +  <enabled>false</enabled>
    +</UserUpdate>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserUpdate": {
    +        "login": "josevargas",
    +        "enabled": false
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<User media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/79" id="79" remoteId="bcf0764b417f05af21852a1f03fb1f13">
    +    <ContentType media-type="application/vnd.ez.api.ContentType+xml" href="/api/ezp/v2/content/types/4"/>
    +    <name>Jose Vargas</name>
    +    <Versions media-type="application/vnd.ez.api.VersionList+xml" href="/api/ezp/v2/content/objects/79/versions"/>
    +    <Section media-type="application/vnd.ez.api.Section+xml" href="/api/ezp/v2/content/sections/2"/>
    +    <MainLocation media-type="application/vnd.ez.api.Location+xml" href="/api/ezp/v2/content/locations/1/5/12/79"/>
    +    <Locations media-type="application/vnd.ez.api.LocationList+xml" href="/api/ezp/v2/content/objects/79/locations"/>
    +    <Groups media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/79/groups"/>
    +    <Owner media-type="application/vnd.ez.api.User+xml" href="/api/ezp/v2/user/users/10"/>
    +    <publishDate>2021-06-29T08:23:42+
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "User": {
    +        "_media-type": "application/vnd.ez.api.User+json",
    +        "_href": "/api/ezp/v2/user/users/79",
    +        "_id": 79,
    +        "_remoteId": "bcf0764b417f05af21852a1f03fb1f13",
    +        "ContentType": {
    +            "_media-type": "application/vnd.ez.api.ContentType+json",
    +            "_href": "/api/ezp/v2/content/types/4"
    +        },
    +        "name": "Jose Vargas",
    +        "Versions": {
    +            "_media-type": "application/vnd.ez.api.VersionList+json",
    +            "_href": "/api/ezp/v2/content/objects/79/versions"
    +        },
    +        "Section": {
    +            "_media-type": "application/vnd.ez.api.Section+json",
    +            "_href": "/api/ezp/v2/content/sections/2"
    +        },
    +        "MainLocation": {
    +            "_media-type": "application/vnd.ez.api.Location+json",
    +            "_href": "/api/ezp/v2/content/locations/1/5/12/79"
    +        },
    +        "Locations": {
    +            "_media-type": "application/vnd.ez.api.LocationList+json",
    +            "_href": "/api/ezp/v2/co
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete User + +
    +
    +
    +

    + DELETE + /user/users/{userId} +

    +

    Deletes the given User.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content.

    +
    + 401 + +

    Error - the user is not authorized to delete this User.

    +
    + 403 + +

    Error - the user is the same as the authenticated User.

    +
    + 404 + +

    Error - the User does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/users/{userId}/groups

    + +
    +
    +
    +
    +
    +
    + Load Groups of User + +
    +
    +
    +

    + GET + /user/users/{userId}/groups +

    +

    Returns a list of User Groups the User belongs to. The returned list includes the resources for unassigning a User Group if the User is in multiple groups.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the link list of User Groups is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupRefList+xml +application/vnd.ez.api.UserGroupRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + offset + + + + integer + + + + + + The offset of the result set. +
    + limit + + + + integer + + + + + + The number of Locations returned. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user has no permission to read User Groups.

    +
    + 404 + +

    Error - the user does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupRefList + + Returns a list of the sub groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/55/groups">
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/12">
    +        <unassign href="/api/ezp/v2/user/users/55/groups/12" method="DELETE"/>
    +    </UserGroup>
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/14">
    +        <unassign href="/api/ezp/v2/user/users/55/groups/14" method="DELETE"/>
    +    </UserGroup>
    +</UserGroupRefList>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Assign User Group + +
    +
    +
    +

    + POST + /user/users/{userId}/groups +

    +

    Assigns the User to a User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the link list of User Groups is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupRefList+xml +application/vnd.ez.api.UserGroupRefList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + group + + + + string + + + + + + The new parent group resource of the User. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to assign User Groups.

    +
    + 403 + +

    Error - the new User Group does not exist or the User is already in this group.

    +
    + 404 + +

    Error - the User does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupRefList + + Returns a list of the sub groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupRefList media-type="application/vnd.ez.api.UserGroupRefList+xml" href="/api/ezp/v2/user/users/115/groups">
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/12">
    +        <unassign href="/api/ezp/v2/user/users/79/groups/12" method="DELETE"/>
    +    </UserGroup>
    +    <UserGroup media-type="application/vnd.ez.api.UserGroup+xml" href="/api/ezp/v2/user/groups/1/5/13">
    +        <unassign href="/api/ezp/v2/user/users/115/groups/13" method="DELETE"/>
    +    </UserGroup>
    +</UserGroupRefList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "UserGroupRefList": {
    +        "_media-type": "application/vnd.ez.api.UserGroupRefList+json",
    +        "_href": "/api/ezp/v2/user/users/115/groups",
    +        "UserGroup": [
    +            {
    +                "_media-type": "application/vnd.ez.api.UserGroup+json",
    +                "_href": "/api/ezp/v2/user/groups/1/5/12",
    +                "unassign": {
    +                    "_href": "/api/ezp/v2/user/users/115/groups/12",
    +                    "_method": "DELETE"
    +                }
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.UserGroup+json",
    +                "_href": "/api/ezp/v2/user/groups/1/5/13",
    +                "unassign": {
    +                    "_href": "/api/ezp/v2/user/users/115/groups/13",
    +                    "_method": "DELETE"
    +                }
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/users/{userId}/groups/{groupId}

    + +
    +
    +
    +
    +
    +
    + Unassign User Group + +
    +
    +
    +

    + DELETE + /user/users/{userId}/groups/{groupId} +

    +

    Unassigns the User from a User Group.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the link list of User Groups is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.UserGroupRefList+xml +application/vnd.ez.api.UserGroupRefList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to unassign User Groups.

    +
    + 403 + +

    Error - the User is not in the given group.

    +
    + 404 + +

    Error - the User does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + UserGroupRefList + + Returns a list of the sub groups.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<UserGroupRefList href="/user/users/45/groups"
    +  media-type="application/vnd.ez.api.UserGroupRefList">
    +  <UserGroup href="/user/groups/1/5/34" media-type="application/vnd.ez.api.UserGroup">
    +    <unassign href="/user/users/45/groups/34" method="DELETE" />
    +  </UserGroup>
    +  <UserGroup href="/user/groups/1/5/88" media-type="application/vnd.ez.api.UserGroup">
    +    <unassign href="/user/users/45/groups/88" method="DELETE" />
    +  </UserGroup>
    +</UserGroupRefList>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/users/{userId}/roles

    + +
    +
    +
    +
    +
    +
    + Load Roles for User + +
    +
    +
    +

    + GET + /user/users/{userId}/roles +

    +

    Returns a list of all Roles assigned to the given User.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/115/roles">
    +    <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2">
    +        <limitation identifier="Section">
    +            <values>
    +                <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +            </values>
    +        </limitation>
    +        <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/>
    +    </RoleAssignment>
    +</RoleAssignmentList>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignmentList": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignmentList+json",
    +        "_href": "/api/ezp/v2/user/users/115/roles",
    +        "RoleAssignment": [
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/users/115/roles/2",
    +                "limitation": {
    +                    "_identifier": "Section",
    +                    "values": {
    +                        "ref": [
    +                            {
    +                                "_media-type": "application/vnd.ez.api.ref+json",
    +                                "_href": "1"
    +                            }
    +                        ]
    +                    }
    +                },
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/2"
    +                }
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Assign Role to User + +
    +
    +
    +

    + POST + /user/users/{userId}/roles +

    +

    Assigns a Role to a user.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleAssignInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignInput+json +application/vnd.ez.api.RoleAssignInput+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - validation of limitation in RoleAssignInput fails.

    +
    + 401 + +

    Error - the user is not authorized to assign this Role.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignInput + +
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignInput>
    +    <Role href="/api/ezp/v2/user/roles/2" media-type="application/vnd.ez.api.RoleAssignInput+xml"/>
    +    <limitation identifier="Section">
    +        <values>
    +            <ref href="/api/ezp/v2/content/sections/1" media-type="application/vnd.ez.api.Section+xml" />
    +            <ref href="/api/ezp/v2/content/sections/2" media-type="application/vnd.ez.api.Section+xml" />
    +        </values>
    +    </limitation>
    +</RoleAssignInput>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "RoleAssignInput": {
    +    "Role": {
    +      "_href": "/api/ezp/v2/user/roles/2",
    +      "_media-type": "application/vnd.ez.api.RoleAssignInput+xml",
    +      "self-closing": "true"
    +    },
    +    "limitation": {
    +      "_identifier": "Section",
    +      "values": {
    +        "ref": [
    +          {
    +            "_href": "/api/ezp/v2/content/sections/1",
    +            "media-type": "application/vnd.ez.api.Section+xml",
    +            "self-closing": "true"
    +          },
    +          {
    +            "_href": "/api/ezp/v2/content/sections/2",
    +            "_media-type": "application/vnd.ez.api.Section+xml",
    +            "self-closing": "true"
    +          }
    +        ]
    +      }
    +    }
    +  }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignmentList media-type="application/vnd.ez.api.RoleAssignmentList+xml" href="/api/ezp/v2/user/users/115/roles">
    +    <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2">
    +        <limitation identifier="Section">
    +            <values>
    +                <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +            </values>
    +        </limitation>
    +        <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/>
    +    </RoleAssignment>
    +    <RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/115/roles/2">
    +        <limitation identifier="Section">
    +            <values>
    +                <ref media-type="application/vnd.ez.api.ref+xml" href="2"/>
    +            </values>
    +        </limitation>
    +        <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2"/>
    +    </RoleAssignment>
    +</RoleAss
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignmentList": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignmentList+json",
    +        "_href": "/api/ezp/v2/user/users/115/roles",
    +        "RoleAssignment": [
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/users/115/roles/2",
    +                "limitation": {
    +                    "_identifier": "Section",
    +                    "values": {
    +                        "ref": [
    +                            {
    +                                "_media-type": "application/vnd.ez.api.ref+json",
    +                                "_href": "2"
    +                            }
    +                        ]
    +                    }
    +                },
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/2"
    +                }
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssign
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/users/{userId}/roles/{roleId}

    + +
    +
    +
    +
    +
    +
    + Load User Role Assignment + +
    +
    +
    +

    + GET + /user/users/{userId}/roles/{roleId} +

    +

    Returns a Role assignment to the given User.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignment+xml +application/vnd.ez.api.RoleAssignment+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Role assignment to the given User Group.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignment + + This value object represents an assignment of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignment media-type="application/vnd.ez.api.RoleAssignment+xml" href="/api/ezp/v2/user/users/113/roles/3">
    +    <limitation identifier="Section">
    +        <values>
    +            <ref media-type="application/vnd.ez.api.ref+xml" href="6"/>
    +        </values>
    +    </limitation>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3"/>
    +</RoleAssignment>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignment": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +        "_href": "/api/ezp/v2/user/users/113/roles/3",
    +        "limitation": {
    +            "_identifier": "Section",
    +            "values": {
    +                "ref": [
    +                    {
    +                        "_media-type": "application/vnd.ez.api.ref+json",
    +                        "_href": "6"
    +                    }
    +                ]
    +            }
    +        },
    +        "Role": {
    +            "_media-type": "application/vnd.ez.api.Role+json",
    +            "_href": "/api/ezp/v2/user/roles/3"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Unassign Role from User + +
    +
    +
    +

    + DELETE + /user/users/{userId}/roles/{roleId} +

    +

    The given Role is removed from the user.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Role assignment list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleAssignmentList+xml +application/vnd.ez.api.RoleAssignmentList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user is not authorized to delete this Content Type.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleAssignmentList + + This value object represents a list of assignments of a User or User group to a role including a limitation.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleAssignmentList href="/user/groups/1/5/65/roles" media-type="application/vnd.ez.api.RoleAssignmentList+xml">
    +  <RoleAssignment href="/user/groups/1/5/65/roles/5" media-type="application/vnd.ez.api.RoleAssignment+xml">
    +    <Role href="/user/roles/5" media-type="application/vnd.ez.api.Role+xml"/>
    +  </RoleAssignment>
    +  <RoleAssignment href="/user/groups/1/5/65/roles/11" media-type="application/vnd.ez.api.RoleAssignment+xml">
    +    <limitation identifier="Section">
    +      <values>
    +          <ref href="/content/sections/1" media-type="application/vnd.ez.api.Section+xml" />
    +          <ref href="/content/sections/4" media-type="application/vnd.ez.api.Section+xml" />
    +      </values>
    +    </limitation>
    +    <Role href="/user/roles/11" media-type="application/vnd.ez.api.Role+xml"/>
    +  </RoleAssignment>
    +</RoleAssignmentList>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleAssignmentList": {
    +        "_media-type": "application/vnd.ez.api.RoleAssignmentList+json",
    +        "_href": "/api/ezp/v2/user/users/57/roles",
    +        "RoleAssignment": [
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/users/57/roles/1",
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/1"
    +                }
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.RoleAssignment+json",
    +                "_href": "/api/ezp/v2/user/users/57/roles/2",
    +                "Role": {
    +                    "_media-type": "application/vnd.ez.api.Role+json",
    +                    "_href": "/api/ezp/v2/user/roles/2"
    +                }
    +            }
    +        ]
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /user/roles

    +
    +
    +
    +

    /user/roles

    + +
    +
    +
    +
    +
    +
    + Load Roles + +
    +
    +
    +

    + GET + /user/roles +

    +

    Returns a list of all Roles.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the user list returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleList+xml +application/vnd.ez.api.RoleList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + identifier + + + + string + + + + + + Restricts the result to a list containing the Role with the given identifier. If the Role is not found an empty list is returned. +
    + offset + + + + integer + + + + + + The offset of the result set. +
    + limit + + + + integer + + + + + + Only limit items will be returned started by offset. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - list of all Roles.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleList + + This class represents a list roles.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleList media-type="application/vnd.ez.api.RoleList+xml" href="/api/ezp/v2/user/roles">
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/1">
    +        <identifier>Anonymous</identifier>
    +        <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/1/policies"/>
    +    </Role>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/2">
    +        <identifier>Administrator</identifier>
    +        <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/2/policies"/>
    +    </Role>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/3">
    +        <identifier>Editor</identifier>
    +        <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/3/policies"/>
    +    </Role>
    +    <Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/4">
    +        <identifi
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleList": {
    +        "_media-type": "application/vnd.ez.api.RoleList+json",
    +        "_href": "/api/ezp/v2/user/roles",
    +        "Role": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Role+json",
    +                "_href": "/api/ezp/v2/user/roles/1",
    +                "identifier": "Anonymous",
    +                "Policies": {
    +                    "_media-type": "application/vnd.ez.api.PolicyList+json",
    +                    "_href": "/api/ezp/v2/user/roles/1/policies"
    +                }
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.Role+json",
    +                "_href": "/api/ezp/v2/user/roles/2",
    +                "identifier": "Administrator",
    +                "Policies": {
    +                    "_media-type": "application/vnd.ez.api.PolicyList+json",
    +                    "_href": "/api/ezp/v2/user/roles/2/policies"
    +                }
    +            },
    +            {
    +                "_media-type": "application/vnd.ez.api.Role+json",
    +       
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Role or Role draft + +
    +
    +
    +

    + POST + /user/roles +

    +

    Creates a new Role or Role draft.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new user is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json +application/vnd.ez.api.RoleDraft+xml +application/vnd.ez.api.RoleDraft+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleInput schema encoded in XML or JSON.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleInput+json +application/vnd.ez.api.RoleInput+xml + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + publish + + + + boolean + + + + + + If true the Role is published after creation. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to create a Role or a Role draft.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Role + + This class represents a role.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/19">
    +    <identifier>NewRole</identifier>
    +    <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/19/policies"/>
    +</Role>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/21",
    +        "identifier": "NewRole",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/21/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/roles/{id}

    + +
    +
    +
    +
    +
    +
    + Load Role + +
    +
    +
    +

    + GET + /user/roles/{id} +

    +

    Loads a Role for the given ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the user list returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Role for the given ID.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    + 404 + +

    Error - the Role does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Role + + This class represents a role.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/21">
    +    <identifier>NewRole</identifier>
    +    <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/21/policies"/>
    +</Role>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/21",
    +        "identifier": "NewRole",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/21/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Role Draft + +
    +
    +
    +

    + POST + /user/roles/{id} +

    +

    Creates a new Role draft from an existing Role.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new user is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleInput schema encoded in XML or JSON.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleInput+json +application/vnd.ez.api.RoleInput+xml + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 401 + +

    Error - the user is not authorized to create a Role or a Role draft

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + RoleDraft + + This class represents a draft of a role, extends Role.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role href="/user/roles/11" media-type="application/vnd.ez.api.RoleDraft+xml">
    +  <identifier>MyRole</identifier>
    +  <Policies href="/user/roles/11/policies" media-type="application/vnd.ez.api.PolicyList+xml"/>
    +</Role>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/6",
    +        "identifier": "Editor",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/6/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Role + +
    +
    +
    +

    + PATCH + /user/roles/{id} +

    +

    Updates a Role. PATCH or POST with header X-HTTP-Method-Override PATCH

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the new user is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleInput+json +application/vnd.ez.api.RoleInput+xml + +
    +
    +
    +
    +

    If-Match

    +

    ETag Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Role updated

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update the Role.

    +
    + 412 + +

    Error - the current ETag does not match with the provided one in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + RoleInput + +
    + + Role + + This class represents a role.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleInput>
    +    <identifier>NewIdentifier</identifier>
    +</RoleInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleInput": {
    +        "identifier": "NewIdentifier"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/5">
    +    <identifier>NewIdentifier</identifier>
    +    <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/5/policies"/>
    +</Role>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/21",
    +        "identifier": "NewIdentifier",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/21/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Role + +
    +
    +
    +

    + DELETE + /user/roles/{id} +

    +

    The given Role and all assignments to Users or User Groups are deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content.

    +
    + 401 + +

    Error - the User is not authorized to delete this Role.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/roles/{id}/draft

    + +
    +
    +
    +
    +
    +
    + Load Role draft + +
    +
    +
    +

    + GET + /user/roles/{id}/draft +

    +

    Loads a Role draft by original Role ID.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the User list returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Role draft by original Role ID.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    + 404 + +

    Error - there is no draft or Role with the given ID.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Role + + This class represents a role.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/27">
    +    <identifier>Anonymous</identifier>
    +    <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/27/policies"/>
    +</Role>
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/27",
    +        "identifier": "Anonymous",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/27/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Update Role draft + +
    +
    +
    +

    + PATCH + /user/roles/{id}/draft +

    +

    Updates a Role draft. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Role is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Role+xml +application/vnd.ez.api.Role+json + +
    +
    +
    +
    +

    Content-Type

    +

    The RoleInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.RoleInput+json +application/vnd.ez.api.RoleInput+xml + +
    +
    +
    +
    +

    If-Match

    +

    Performs a PATCH only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Role draft updated.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the user is not authorized to update the Role.

    +
    + 404 + +

    Error - there is no draft or Role with the given ID.

    +
    + 412 + +

    Error - the current ETag does not match with the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + RoleInput + +
    + + Role + + This class represents a role.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<RoleInput>
    +    <identifier>UpdatedIdentifier</identifier>
    +</RoleInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "RoleInput": {
    +        "identifier": "UpdatedIdentifier"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Role media-type="application/vnd.ez.api.Role+xml" href="/api/ezp/v2/user/roles/9">
    +    <identifier>UpdatedIdentifier</identifier>
    +    <Policies media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/9/policies"/>
    +</Role>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Role": {
    +        "_media-type": "application/vnd.ez.api.Role+json",
    +        "_href": "/api/ezp/v2/user/roles/6",
    +        "identifier": "UpdatedIdentifier",
    +        "Policies": {
    +            "_media-type": "application/vnd.ez.api.PolicyList+json",
    +            "_href": "/api/ezp/v2/user/roles/6/policies"
    +        }
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Publish Role draft + +
    +
    +
    +

    + PUBLISH + /user/roles/{id}/draft +

    +

    Publishes a Role draft. PUBLISH or POST with header X-HTTP-Method-Override PUBLISH.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content.

    +
    + 401 + +

    Error - the user is not authorized to publish this Content Type draft.

    +
    + 403 + +

    Error - the Content Type draft is not complete e.g. there is no Field definition provided.

    +
    + 404 + +

    Error - there is no draft or Role with the given ID.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Role draft + +
    +
    +
    +

    + DELETE + /user/roles/{id}/draft +

    +

    The given Role draft is deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content.

    +
    + 401 + +

    Error - the user is not authorized to delete this Role.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/roles/{id}/policies

    + +
    +
    +
    +
    +
    +
    + Load Policies + +
    +
    +
    +

    + GET + /user/roles/{id}/policies +

    +

    Loads Policies for the given Role.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Policy list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.PolicyList+xml +application/vnd.ez.api.PolicyList+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    + 404 + +

    Error - the Role does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + PolicyList + + List of policies.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<PolicyList media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/roles/1/policies">
    +    <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/349">
    +        <id>349</id>
    +        <module>content</module>
    +        <function>read</function>
    +        <limitations>
    +            <limitation identifier="Section">
    +                <values>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="3"/>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="6"/>
    +                </values>
    +            </limitation>
    +        </limitations>
    +    </Policy>
    +    <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/350">
    +        <id>350</id>
    +        <module>user</module>
    +        <function>login</function>
    +        <limitations>
    +            <limitation iden
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "PolicyList": {
    +        "_media-type": "application/vnd.ez.api.PolicyList+json",
    +        "_href": "/api/ezp/v2/user/roles/1/policies",
    +        "Policy": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Policy+json",
    +                "_href": "/api/ezp/v2/user/roles/1/policies/349",
    +                "id": 349,
    +                "module": "content",
    +                "function": "read",
    +                "limitations": {
    +                    "limitation": [
    +                        {
    +                            "_identifier": "Section",
    +                            "values": {
    +                                "ref": [
    +                                    {
    +                                        "_media-type": "application/vnd.ez.api.ref+json",
    +                                        "_href": "1"
    +                                    },
    +                                    {
    +                                        "_media-type": "application/vnd.ez.api.ref+json",
    +            
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Policies + +
    +
    +
    +

    + DELETE + /user/roles/{id}/policies +

    +

    All Policies of the given Role are deleted.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - all Policies of the given Role are deleted.

    +
    + 401 + +

    Error - the user is not authorized to delete this Content Type.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Create Policy + +
    +
    +
    +

    + POST + /user/roles/{id}/policies +

    +

    Creates a Policy

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Policy is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Policy+xml +application/vnd.ez.api.Policy+json + +
    +
    +
    +
    +

    Content-Type

    +

    If set, the updated Policy is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.PolicyCreate+xml +application/vnd.ez.api.PolicyCreate+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 201 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition or validation of limitation in PolicyCreate fails.

    +
    + 401 + +

    Error - the user is not authorized to create the Policy.

    +
    + 404 + +

    Error - the Role does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + PolicyCreate + +
    + + Policy + + This class represents a policy value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<PolicyCreate>
    +    <module>content</module>
    +    <function>create</function>
    +    <limitations>
    +        <limitation identifier="Class">
    +            <values>
    +                <ref href="/api/ezp/v2/content/types/13"/>
    +            </values>
    +        </limitation>
    +        <limitation identifier="ParentClass">
    +            <values>
    +                <ref href="/api/ezp/v2/content/types/12"/>
    +            </values>
    +        </limitation>
    +    </limitations>
    +</PolicyCreate>
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <Policy href="/user/roles/11/policies/55" media-type="application/vnd.ez.api.Policy+xml">
    +  <id>55</id>
    +  <module>content</module>
    +  <function>create</function>
    +  <limitations>
    +    <limitation identifier="Class">
    +      <values>
    +        <ref href="/content/types/13"/>
    +      </values>
    +    </limitation>
    +    <limitation identifier="ParentClass">
    +      <values>
    +        <ref href="/content/types/12"/>
    +      </values>
    +    </limitation>
    +  </limitations>
    + </Policy>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/roles/{id}/policies/{id}

    + +
    +
    +
    +
    +
    +
    + Update Policy + +
    +
    +
    +

    + PATCH + /user/roles/{id}/policies/{id} +

    +

    Updates a Policy. PATCH or POST with header X-HTTP-Method-Override PATCH.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the updated Policy is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Policy+xml +application/vnd.ez.api.Policy+json + +
    +
    +
    +
    +

    Content-Type

    +

    If set, the updated Policy is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.PolicyUpdate+xml +application/vnd.ez.api.PolicyUpdate+json + +
    +
    +
    +
    +

    If-Match

    +

    Causes to patch only if the specified ETag is the current one. Otherwise a 412 is returned.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + ETag +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 400 + +

    Error - the input does not match the input schema definition or validation of limitation in PolicyUpdate fails.

    +
    + 401 + +

    Error - the user is not authorized to update the Policy.

    +
    + 404 + +

    Error - the Role does not exist.

    +
    + 412 + +

    Error - the current ETag does not match with the one provided in the If-Match header.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + PolicyUpdate + +
    + + Policy + + This class represents a policy value.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<PolicyUpdate>
    +    <limitations>
    +        <limitation identifier="Class">
    +            <values>
    +                <ref href="/content/types/14"/>
    +            </values>
    +        </limitation>
    +        <limitation identifier="ParentClass">
    +            <values>
    +                <ref href="/content/types/10"/>
    +            </values>
    +        </limitation>
    +    </limitations>
    +</PolicyUpdate>
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <Policy href="/user/roles/11/policies/55" media-type="application/vnd.ez.api.Policy+xml">
    +  <id>55</id>
    +  <module>content</module>
    +  <function>create</function>
    +  <limitations>
    +    <limitation identifier="Class">
    +      <values>
    +        <ref href="/content/types/14"/>
    +      </values>
    +    </limitation>
    +    <limitation identifier="ParentClass">
    +      <values>
    +        <ref href="/content/types/10"/>
    +      </values>
    +    </limitation>
    +  </limitations>
    + </Policy>
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Load Policy + +
    +
    +
    +

    + GET + /user/roles/{id}/policies/{id} +

    +

    Loads a Policy for the given module and function.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Policy is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Policy+xml +application/vnd.ez.api.Policy+json + +
    +
    +
    +
    +

    If-None-Match

    +

    ETag

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    + 404 + +

    Error - the Role or Policy does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Policy + + This class represents a policy value.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <Policy href="/user/roles/11/policies/45" media-type="application/vnd.ez.api.Policy+xml">
    +  <id>45</id>
    +  <module>content</module>
    +  <function>create</function>
    +  <limitations>
    +    <limitation identifier="Class">
    +      <values>
    +        <ref href="/content/types/10" media-type="application/vnd.ez.api.ContentType+xml" />
    +        <ref href="/content/types/11" media-type="application/vnd.ez.api.ContentType+xml" />
    +        <ref href="/content/types/12" media-type="application/vnd.ez.api.ContentType+xml" />
    +      </values>
    +    </limitation>
    +    <limitation identifier="ParentClass">
    +      <values>
    +        <ref href="/content/types/4" media-type="application/vnd.ez.api.ContentType+xml" />
    +      </values>
    +    </limitation>
    +  </limitations>
    +</Policy>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "Policy": {
    +        "_media-type": "application/vnd.ez.api.Policy+json",
    +        "_href": "/api/ezp/v2/user/roles/1/policies/352",
    +        "id": 352,
    +        "module": "user",
    +        "function": "register"
    +    }
    +}
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Policy + +
    +
    +
    +

    + DELETE + /user/roles/{id}/policies/{id} +

    +

    Deletes given Policy.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    No Content - the given Policy is deleted.

    +
    + 401 + +

    Error - the user is not authorized to delete this Content Type.

    +
    + 404 + +

    Error - the Role or Policy does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +

    /user/policies

    +
    +
    +
    +

    /user/policies

    + +
    +
    +
    +
    +
    +
    + List Policies for User + +
    +
    +
    +

    + GET + /user/policies +

    +

    Search all Policies which are applied to a given User.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the Policy list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.PolicyList+xml +application/vnd.ez.api.PolicyList+json + +
    +
    +
    +
    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + userId + + + + string + + + + + + The User ID. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    OK - Policies which are applied to a given User.

    +
    + 401 + +

    Error - the user has no permission to read Roles.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + PolicyList + + List of policies.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<PolicyList media-type="application/vnd.ez.api.PolicyList+xml" href="/api/ezp/v2/user/policies">
    +    <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/349">
    +        <id>349</id>
    +        <module>content</module>
    +        <function>read</function>
    +        <limitations>
    +            <limitation identifier="Section">
    +                <values>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="1"/>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="3"/>
    +                    <ref media-type="application/vnd.ez.api.ref+xml" href="6"/>
    +                </values>
    +            </limitation>
    +        </limitations>
    +    </Policy>
    +    <Policy media-type="application/vnd.ez.api.Policy+xml" href="/api/ezp/v2/user/roles/1/policies/350">
    +        <id>350</id>
    +        <module>user</module>
    +        <function>login</function>
    +        <limitations>
    +            <limitation identifier="
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "PolicyList": {
    +        "_media-type": "application/vnd.ez.api.PolicyList+json",
    +        "_href": "/api/ezp/v2/user/policies",
    +        "Policy": [
    +            {
    +                "_media-type": "application/vnd.ez.api.Policy+json",
    +                "_href": "/api/ezp/v2/user/roles/1/policies/349",
    +                "id": 349,
    +                "module": "content",
    +                "function": "read",
    +                "limitations": {
    +                    "limitation": [
    +                        {
    +                            "_identifier": "Section",
    +                            "values": {
    +                                "ref": [
    +                                    {
    +                                        "_media-type": "application/vnd.ez.api.ref+json",
    +                                        "_href": "1"
    +                                    },
    +                                    {
    +                                        "_media-type": "application/vnd.ez.api.ref+json",
    +                    
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /user/sessions

    +
    +
    +
    +

    /user/sessions

    + +
    +
    +
    +
    +
    +
    + Create session (login a User) + +
    +
    +
    +

    + POST + /user/sessions +

    +

    Performs a login for the user or checks if session exists and returns the session and session cookie. The client will need to remember both session name/ID and CSRF token as this is for security reasons not exposed via GET.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the session is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Session+xml +application/vnd.ez.api.Session+json + +
    +
    +
    +
    +

    Content-Type

    +

    The SessionInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.SessionInput+xml +application/vnd.ez.api.SessionInput+json + +
    +
    +
    +
    +

    Cookie

    +

    Only needed for session's checking {sessionName}={sessionID}.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    +

    X-CSRF-Token

    +

    Only needed for session's checking. The {csrfToken} needed on all unsafe http methods with session.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    Session already exists.

    +
    + + 201 + + +

    Session is created.

    +
    + 400 + +

    Error - the input does not match the input schema definition.

    +
    + 401 + +

    Error - the authorization failed.

    +
    + 409 + +

    Error - header contained a session cookie but different user was authorized.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + SessionInput + +
    + + Session + + Value for session.
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<SessionInput>
    +  <login>admin</login>
    +  <password>secret</password>
    +</SessionInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "SessionInput": {
    +    "login": "admin",
    +    "password": "secret"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Session href="/user/sessions/sessionID" media-type="application/vnd.ez.api.Session+xml">
    +  <name>eZSSID</name>
    +  <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier>
    +  <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken>
    +  <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/>
    +</Session>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Session": {
    +    "name": "eZSSID",
    +    "identifier": "go327ij2cirpo59pb6rrv2a4el2",
    +    "csrfToken": "23lkneri34ijajedfw39orj3j93",
    +    "User": {
    +      "_href": "/user/users/14",
    +      "_media-type": "application/vnd.ez.api.User+json"
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 201 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Session href="/user/sessions/sessionID" media-type="application/vnd.ez.api.Session+xml">
    +  <name>eZSSID</name>
    +  <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier>
    +  <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken>
    +  <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/>
    +</Session>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Session": {
    +    "name": "eZSSID",
    +    "identifier": "go327ij2cirpo59pb6rrv2a4el2",
    +    "csrfToken": "23lkneri34ijajedfw39orj3j93",
    +    "User": {
    +      "_href": "/user/users/14",
    +      "_media-type": "application/vnd.ez.api.User+json"
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /user/sessions/{sessionId}

    + +
    +
    +
    +
    +
    +
    + Delete session (logout a User) + +
    +
    +
    +

    + DELETE + /user/sessions/{sessionId} +

    +

    The user session is removed i.e. the user is logged out.

    +

    +
    +
    Header parameters
    +
    +

    Cookie

    +

    {sessionName}={sessionID}

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    +

    X-CSRF-Token

    +

    The {csrfToken} needed on all unsafe http methods with session.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 204 + +

    OK - session deleted.

    +
    + 404 + +

    Error - the session does not exist.

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    /user/sessions/{sessionId}/refresh

    + +
    +
    +
    +
    +
    +
    + Refresh session + +
    +
    +
    +

    + POST + /user/sessions/{sessionId}/refresh +

    +

    Get the session's User information.

    +

    +
    +
    Header parameters
    +
    +

    Cookie

    +

    {sessionName}={sessionID}

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    +

    X-CSRF-Token

    +

    The {csrfToken} needed on all unsafe http methods with session.

    +
    + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 404 + +

    Error - the session does not exist.

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Session + + Value for session.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<Session href="user/sessions/go327ij2cirpo59pb6rrv2a4el2/refresh" media-type="application/vnd.ez.api.Session+xml">
    +  <name>eZSSID</name>
    +  <identifier>go327ij2cirpo59pb6rrv2a4el2</identifier>
    +  <csrfToken>23lkneri34ijajedfw39orj3j93</csrfToken>
    +  <User href="/user/users/14" media-type="vnd.ez.api.User+xml"/>
    +</Session>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Session": {
    +    "name": "eZSSID",
    +    "identifier": "go327ij2cirpo59pb6rrv2a4el2",
    +    "csrfToken": "23lkneri34ijajedfw39orj3j93",
    +    "User": {
    +      "_href": "/user/users/14",
    +      "_media-type": "application/vnd.ez.api.User+json"
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /user/token

    +
    +
    +
    +

    /user/token/jwt

    + +
    +
    +
    +
    +
    +
    + Create JWT token + +
    +
    +
    +

    + POST + /user/token/jwt +

    +

    Creates JWT authentication token.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the token is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.JWT+xml +application/vnd.ez.api.JWT+json + +
    +
    +
    +
    +

    Content-Type

    +

    The SessionInput schema encoded in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.JWTInput+xml +application/vnd.ez.api.JWTInput+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + 401 + +

    Error - Unauthorized

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + JWTInput + + This class represents the input for a JWT authentication token
    + + JWT + + This class represents the JWT authentication token
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <JWTInput>
    +    <password>publish</password>
    +    <username>admin</username>
    +</JWTInput>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "JWTInput": {
    +        "username": "admin",
    +        "password": "publish"
    +    }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<JWT media-type="application/vnd.ez.api.JWT+xml" token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3NjEsImV4cCI6MTYwMjg0NDM2MSwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.LsmdVjad7wMwVQUo4vSftT0zHbJyArOMd23b417E2jI"/>
    +
    +                            
    +
    + View more +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +    "JWT": {
    +        "_media-type": "application/vnd.ez.api.JWT+json",
    +        "_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDI4NDA3ODAsImV4cCI6MTYwMjg0NDM4MCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoiYWRtaW4ifQ.0LHa799HwSwwfDBZd2V0q2xHwGt86PpyZamKnXHQyYI"
    +    }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Services + +

    +
    +
    +

    /services/countries

    + +
    +
    +
    +
    +
    +
    + Countries list + +
    +
    +
    +

    + GET + /services/countries +

    +

    Gives access to an ISO-3166 formatted list of world countries. It is useful when presenting a country options list from any application.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    If set, the country list is returned in XML or JSON format.

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.CountriesLis+xml +application/vnd.ez.api.CountriesLis+json + +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + CountryList + + This class is representing an ISO-3166 formatted list of world countries.
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                <?xml version="1.0" encoding="UTF-8"?>
    +<CountryList media-type="application/vnd.ez.api.CountryList+xml">
    +    <Country media-type="application/vnd.ez.api.Country+xml" id="AF">
    +        <name>Afghanistan</name>
    +        <Alpha2>AF</Alpha2>
    +        <Alpha3>AFG</Alpha3>
    +        <IDC>93</IDC>
    +    </Country>
    +    <Country media-type="application/vnd.ez.api.Country+xml" id="AX">
    +        <name>Åland</name>
    +        <Alpha2>AX</Alpha2>
    +        <Alpha3>ALA</Alpha3>
    +        <IDC>358</IDC>
    +    </Country>
    +    <Country media-type="application/vnd.ez.api.Country+xml" id="AL">
    +        <name>Albania</name>
    +        <Alpha2>AL</Alpha2>
    +        <Alpha3>ALB</Alpha3>
    +        <IDC>355</IDC>
    +    </Country>
    +    <Country media-type="application/vnd.ez.api.Country+xml" id="DZ">
    +        <name>Algeria</name>
    +        <Alpha2>DZ</Alpha2>
    +        <Alpha3>DZA</Alpha3>
    +        <IDC>213</IDC>
    +    </Country>
    +    <Country media-type="application/vnd.ez.api.Country+xml" id="AS">
    +        <name>American Samoa</name>
    +        <Alp
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + eCommerce API + +

    +
    +

    /commerce/basket

    +
    +
    +
    +

    /commerce/basket

    + +
    +
    +
    +
    +
    +
    + Read list of baskets + +
    +
    +
    +

    + GET + /commerce/basket +

    +

    Returns baskets for the current user. Only "storedBasket" and "wishList" types are handled. Others will return validation error.

    +

    + +
    +
    Query parameters
    +
    + + + + + + + + + + + + + + + +
    PropertyTypeValue
    + type + + + + string + + + + + + Either "storedBasket" or "wishList". "storedBasket" is assumed if not passed. +
    +
    +
    + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + BasketListResponse + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "BasketListResponse": {
    +    "_media-type": "application\/vnd.ez.api.BasketListResponse+json",
    +    "basketList": [
    +      {
    +        "_media-type": "application\/vnd.ez.api.stdClass+json",
    +        "basketId": 5,
    +        "state": "new",
    +        "type": "storedBasket",
    +        "sessionId": "mrt02u8u2rbh6aeeibm7rj0i1j",
    +        "userId": 14,
    +        "basketName": "test",
    +        "dateCreated": {
    +          "_media-type": "application\/vnd.ez.api.DateTime+json"
    +        },
    +        "dateLastModified": {
    +          "_media-type": "application\/vnd.ez.api.DateTime+json"
    +        },
    +        "totals": [
    +          {
    +            "_media-type": "application\/vnd.ez.api.BasketTotals+json"
    +          },
    +          {
    +            "_media-type": "application\/vnd.ez.api.BasketTotals+json"
    +          }
    +        ],
    +        "totalsSum": {
    +          "_media-type": "application\/vnd.ez.api.BasketTotals+json"
    +        },
    +        "dateLastPriceCalculation": {
    +          "_media-type": "application\/vnd.ez.api.DateTime
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current

    + +
    +
    +
    +
    +
    +
    + Get current basket + +
    +
    +
    +

    + GET + /commerce/basket/current +

    +

    Returns a Basket instance for the current session. If there isn't one, a new one will be created.

    +

    +
    +
    Header parameters
    +
    +

    Accept

    +

    +
    + + + + + + + + + + + + + + + + + +
    PropertyValue
    Type + + + string + + + + +
    Examples + application/vnd.ez.api.Basket+json +
    +
    +
    +
    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Basket + +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/{basketId}

    + +
    +
    +
    +
    +
    +
    + Copy basket to user session + +
    +
    +
    +

    + COPY + /commerce/basket/current/{basketId} +

    +

    Adds basket lines into current basket in user session, if any, using basket stored in permanent +storage.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 400 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ValidationResponse": {
    +    "_media-type": "application\/vnd.ez.api.ValidationResponse+json",
    +    "messages": {
    +      "_error": [
    +        "Access denied"
    +      ]
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/referencenumber

    + +
    +
    +
    +
    +
    +
    + Save the external reference number in basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/referencenumber +

    +

    + + + + +
    +
    + +
    +
    +
    +
    +
    +

    /commerce/basket/current/remark

    + +
    +
    +
    +
    +
    +
    + Save the remark in basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/remark +

    +

    + + + + +
    +
    + +
    +
    +
    +
    +
    +

    /commerce/basket/current/party/{partyType}

    + +
    +
    +
    +
    +
    +
    + Update party information in the basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/party/{partyType} +

    +

    Updates party (buyer, delivery, invoice) information in the basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + PartyTypeInput + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "PartyData": {
    +    "_data-type":"invoice",
    +    "_media-type":"application/vnd.ez.api.Party+json",
    +    "PartyIdentification":[],
    +    "PartyName":[
    +      {
    +        "_media-type":"application/vnd.ez.api.PartyPartyName+json",
    +        "Name":"Testerin"
    +      },
    +      {
    +        "_media-type":"application/vnd.ez.api.PartyPartyName+json",
    +        "Name":""
    +      }
    +    ],
    +    "PostalAddress":{
    +      "_media-type":"application/vnd.ez.api.PartyPostalAddress+json",
    +      "StreetName":"Teststr. 11",
    +      "AdditionalStreetName":"",
    +      "BuildingNumber":null,
    +      "CityName":"Testingen",
    +      "PostalZone":"1111",
    +      "CountrySubentity":"Dummy",
    +      "CountrySubentityCode":null,
    +      "AddressLine":[],
    +      "Country":{
    +        "_media-type":"application/vnd.ez.api.PartyPostalAddressCountry+json",
    +        "IdentificationCode":"NO",
    +        "Name":null
    +      },
    +      "Department":null,
    +      "SesExtension":{}
    +    },
    +    "Contact":{
    +      "_media-type":"application/vnd.ez.api.Contact+json",
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/shippingmethod

    + +
    +
    +
    +
    +
    +
    + Update shipping information in current basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/shippingmethod +

    +

    Updates shipping information in the current basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + ShippingMethodData + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ShippingMethodData": {
    +    "shippingMethod": "mail"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/paymentmethod

    + +
    +
    +
    +
    +
    +
    + Update payment information in current basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/paymentmethod +

    +

    Updates payment information in the current basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + PaymentMethodData + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "PaymentMethodData": {
    +    "paymentMethod": "invoice"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/voucher

    + +
    +
    +
    +
    +
    +
    + Update and check voucher in current basket + +
    +
    +
    +

    + PATCH + /commerce/basket/current/voucher +

    +

    Updates and checks voucher in current basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + VoucherData + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "VoucherData": {
    +    "voucherCode": "1234567"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/current/line

    + +
    +
    +
    +
    +
    +
    + Add products to current basket + +
    +
    +
    +

    + POST + /commerce/basket/current/line +

    +

    Adds products to the current basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + BasketLineData + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "BasketLineData": [
    +    {
    +      "sku":"1000",
    +      "quantity":"3",
    +      "isVariant":"0",
    +      "variantCode":""
    +    }
    +  ]
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 400 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ValidationResponse": {
    +    "_media-type": "application\/vnd.ez.api.ValidationResponse+json",
    +    "messages": {
    +      "_error": [
    +        "Product with the sku: foo not found"
    +      ]
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketId}

    + +
    +
    +
    +
    +
    +
    + Get Basket by ID + +
    +
    +
    +

    + GET + /commerce/basket/{basketId} +

    +

    Gets basket by ID.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + Basket + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 7,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "storedBasket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": "sbnla08g8seeoua51v46jns3vc",
    +    "userId": 14,
    +    "basketName": "foo",
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 07:40:00"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 07:40:00"
    +    },
    +    "shop": null,
    +    "requirePriceUpdate": false,
    +    "totals": null,
    +    "totalsSum": null,
    +    "currency": null,
    +    "totalsSumNet": null,
    +    "totalsSumGross": null,
    +    "additionalLines": null,
    +    "lines": [],
    +    "dateLastPriceCalculation": null,
    +    "shippingMethod": null,
    +    "pay
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + Delete Basket by ID (removed) + +
    +
    +
    +

    + DELETE + /commerce/basket/{basketId} +

    +

    Deletes basket by ID. Removed as of Ibexa DXP 4.4, use DELETE /cart/{identifier} instead.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + + + + + +
    CodeDescription
    + 200 + +

    OK - The basket has been deleted

    +
    + 400 + +

    Error - The basket does not exist

    +
    + 403 + +

    Error - Access denied

    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketId}/name

    + +
    +
    +
    +
    +
    +
    + Update the name of the stored basket + +
    +
    +
    +

    + PATCH + /commerce/basket/{basketId}/name +

    +

    Updates the name of the stored basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + BasketHeaderData + +
    + + Basket + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "BasketHeaderData": {
    +    "value": "foobar"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketId}/note

    + +
    +
    +
    +
    +
    +
    + Update the note of the stored basket + +
    +
    +
    +

    + PATCH + /commerce/basket/{basketId}/note +

    +

    Updates the note of the stored basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + BasketHeaderData + +
    + + Basket + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "BasketHeaderData": {
    +    "value": "foobar"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketId}/line

    + +
    +
    +
    +
    +
    +
    + Add products to stored basket + +
    +
    +
    +

    + POST + /commerce/basket/{basketId}/line +

    +

    Adds products to the stored basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + BasketLineData + +
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "BasketLineData": [
    +    {
    +      "sku":"1000",
    +      "quantity":"3",
    +      "isVariant":"0",
    +      "variantCode":""
    +    }
    +  ]
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 400 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ValidationResponse": {
    +    "_media-type": "application\/vnd.ez.api.ValidationResponse+json",
    +    "messages": {
    +      "_error": [
    +        "Product with the sku: foo not found"
    +      ]
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketId}/line/{lineId}

    + +
    +
    +
    +
    +
    +
    + Delete a line from a stored basket + +
    +
    +
    +

    + DELETE + /commerce/basket/{basketId}/line/{lineId} +

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 400 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ValidationResponse": {
    +    "_media-type": "application\/vnd.ez.api.ValidationResponse+json",
    +    "messages": {
    +      "_error": [
    +        "msg.invalid_basket_line_data"
    +      ]
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/basket/{basketName}

    + +
    +
    +
    +
    +
    +
    + Create a new persisted basket + +
    +
    +
    +

    + POST + /commerce/basket/{basketName} +

    +

    Creates a new persisted basket.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    + + 400 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + Basket + +
    + + ValidationResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "Basket": {
    +    "_media-type": "application\/vnd.ez.api.Basket+json",
    +    "basketId": 1,
    +    "originId": null,
    +    "erpOrderId": null,
    +    "guid": null,
    +    "state": "new",
    +    "type": "basket",
    +    "erpFailCounter": null,
    +    "erpFailErrorLog": null,
    +    "sessionId": null,
    +    "userId": 14,
    +    "basketName": null,
    +    "invoiceParty": null,
    +    "deliveryParty": null,
    +    "buyerParty": null,
    +    "remark": null,
    +    "dateCreated": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:33:26"
    +    },
    +    "dateLastModified": {
    +      "_media-type": "application\/vnd.ez.api.DateTime+json",
    +      "string": "2021-03-12 10:35:22"
    +    },
    +    "shop": "MAIN",
    +    "requirePriceUpdate": false,
    +    "totals": {
    +      "lines": {
    +        "_media-type": "application\/vnd.ez.api.BasketTotals+json",
    +        "name": "lines",
    +        "totalNet": 0,
    +        "totalGross": 0,
    +        "vatList": {
    +          "19": 0
    +        },
    +        "groupType": "order",
    +        "curre
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    + +
    + Code: 400 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ValidationResponse": {
    +    "_media-type": "application\/vnd.ez.api.ValidationResponse+json",
    +    "messages": {
    +      "_error": [
    +        "msg.duplicate_basket_name_not_allowed"
    +      ]
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /commerce/checkout

    +
    +
    +
    +

    /commerce/checkout/paymentmethods

    + +
    +
    +
    +
    +
    +
    + Get list of payment methods + +
    +
    +
    +

    + GET + /commerce/checkout/paymentmethods +

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + PaymentMethodDataResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "PaymentMethodDataResponse": {
    +    "_media-type": "application\/vnd.ez.api.PaymentMethodDataResponse+json",
    +    "paymentMethods": {
    +      "paypal": "paypal",
    +      "invoice": "invoice"
    +    },
    +    "defaultMethod": "invoice"
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    /commerce/checkout/shippingmethods

    + +
    +
    +
    +
    +
    +
    + Get list of shipping methods + +
    +
    +
    +

    + GET + /commerce/checkout/shippingmethods +

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ShippingMethodDataResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ShippingMethodDataResponse": {
    +    "_media-type": "application\/vnd.ez.api.ShippingMethodDataResponse+json",
    +    "shippingMethods": {
    +      "standard": "standard",
    +      "expressDel": "express_delivery"
    +    },
    +    "defaultMethod": "standard",
    +    "shippingMethodsSorted": []
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /commerce/customer/addresses/shipping

    +
    +
    +
    +

    /commerce/customer/addresses/shipping

    + +
    +
    +
    +
    +
    +
    + Get list of shipping addresses + +
    +
    +
    +

    + GET + /commerce/customer/addresses/shipping +

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + ShippingAddressesResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "ShippingAddressesResponse": {
    +    "_media-type": "application\/vnd.ez.api.ShippingAddressesResponse+json",
    +    "Parties": [
    +      {
    +        "_media-type": "application\/vnd.ez.api.Party+json",
    +        "PartyIdentification": [],
    +        "PartyName": [],
    +        "PostalAddress": {
    +          "_media-type": "application\/vnd.ez.api.PartyPostalAddress+json",
    +          "StreetName": null,
    +          "AdditionalStreetName": null,
    +          "BuildingNumber": null,
    +          "CityName": null,
    +          "PostalZone": null,
    +          "CountrySubentity": null,
    +          "CountrySubentityCode": null,
    +          "AddressLine": [],
    +          "Country": {
    +            "_media-type": "application\/vnd.ez.api.PartyPostalAddressCountry+json",
    +            "IdentificationCode": null,
    +            "Name": null
    +          },
    +          "Department": null,
    +          "SesExtension": {}
    +        },
    +        "Contact": {
    +          "_media-type": "application\/vnd.ez.api.Contact+json",
    +          "ID": null,
    +        
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /commerce/customerprice

    +
    +
    +
    +

    /commerce/customerprice

    + +
    +
    +
    +
    +
    +
    + Get customer prices for products + +
    +
    +
    +

    + POST + /commerce/customerprice +

    +

    Gets customer prices for the requested products.

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + + + + + +
    TypeDescription
    + + CustomerPriceData + +
    + + PriceResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "PriceResponse": {
    +    "_media-type": "application\/vnd.ez.api.PriceResponse+json",
    +    "priceResponse": {
    +      "_media-type": "application\/vnd.ez.api.priceResponse+json"
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "PriceResponse": {
    +    "_media-type": "application\/vnd.ez.api.PriceResponse+json",
    +    "priceResponse": {
    +      "_media-type": "application\/vnd.ez.api.priceResponse+json"
    +    }
    +  }
    +}
    +
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    /commerce/common/check_sku_file/{mode}

    +
    +
    +
    +

    /commerce/common/check_sku_file/{mode}

    + +
    +
    +
    +
    +
    +
    + Add to basket from CSV or Excel + +
    +
    +
    +

    + POST + /commerce/common/check_sku_file/{mode} +

    +

    Adds to basket a line from a CSV or Excel file.

    +

    + + + + +
    +
    + +
    +
    +
    +
    +

    /commerce/country

    +
    +
    +
    +

    /commerce/country

    + +
    +
    +
    +
    +
    +
    + Gets list of countries. + +
    +
    +
    +

    + GET + /commerce/country +

    +

    + + +
    +
    Possible responses
    +
    + + + + + + + + + + + + + +
    CodeDescription
    + + 200 + + +

    +
    +
    +
    + +
    +
    Types
    +
    + + + + + + + + + + + + + +
    TypeDescription
    + + CountrySelectionResponse + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    + Code: 200 +
    +
    +
    +
    +
    +
    +

    + file_copy + +

    +
    +                                {
    +  "CountrySelectionResponse": {
    +    "_media-type": "application\/vnd.ez.api.CountrySelectionResponse+json",
    +    "countryOptions": {
    +      "DZ": "Algeria",
    +      "AU": "Australia",
    +      "AT": "Austria",
    +      "BE": "Belgium",
    +      "BR": "Brazil",
    +      "BG": "Bulgaria",
    +      "CA": "Canada",
    +      "CY": "Cyprus",
    +      "CZ": "Czechia",
    +      "DK": "Denmark",
    +      "EE": "Estonia",
    +      "SZ": "Eswatini",
    +      "FI": "Finland",
    +      "FR": "France",
    +      "DE": "Germany",
    +      "GR": "Greece",
    +      "HU": "Hungary",
    +      "IS": "Iceland",
    +      "IN": "India",
    +      "ID": "Indonesia",
    +      "IE": "Ireland",
    +      "IT": "Italy",
    +      "KE": "Kenya",
    +      "LV": "Latvia",
    +      "LT": "Lithuania",
    +      "MO": "Macao SAR China",
    +      "MY": "Malaysia",
    +      "MT": "Malta",
    +      "MX": "Mexico",
    +      "MZ": "Mozambique",
    +      "NL": "Netherlands",
    +      "NZ": "New Zealand",
    +      "NG": "Nigeria",
    +      "NO": "Norway",
    +      "PH": "Philippines",
    +      "PL": "Poland",
    +      "PT": "Portugal",
    +                            
    +
    + View more +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +

    Copyright 1999- Ibexa AS and others.

    +
    +
    + + + + + + + + + + + + + + + diff --git a/docs/api/rest_api_requests.md b/docs/api/rest_api_requests.md new file mode 100644 index 0000000000..d1650f7697 --- /dev/null +++ b/docs/api/rest_api_requests.md @@ -0,0 +1,381 @@ +--- +description: REST API requests can have a generic or a custom header. It defines additional options in the request, such as the accepted Content Type of response. +--- + +# REST requests + +## Request method + +Depending on the HTTP method used, different actions will be possible on the same resource. Example: + +| Action | Description | +|-----------------------------------------|----------------------------------------------------------------------| +| `GET /content/objects/2/version/3` | Fetches data about version \#3 of Content item \#2 | +| `PATCH /content/objects/2/version/3` | Updates the version \#3 draft of Content item \#2 | +| `DELETE /content/objects/2/version/3` | Deletes the (draft or archived) version \#3 from Content item \#2 | +| `COPY /content/objects/2/version/3` | Creates a new draft version of Content item \#2 from its version \#3 | +| `PUBLISH /content/objects/2/version/3` | Promotes the version \#3 of Content item \#2 from draft to published | +| `OPTIONS /content/objects/2/version/3` | Lists all the methods usable with this resource, the 5 ones above | + +The following list of available methods gives an overview of the kind of action a method triggers on a resource, if available. +For method action details per resource, see the [REST API reference](rest_api_reference/rest_api_reference.html). + +| HTTP method | Status | Description | Safe | +|------------------------------------------------------------|----------|------------------------|------| +| [OPTIONS](https://tools.ietf.org/html/rfc2616#section-9.2) | Standard | List available methods | Yes | +| [GET](https://tools.ietf.org/html/rfc2616#section-9.3) | Standard | Collect data | Yes | +| [HEAD](https://tools.ietf.org/html/rfc2616#section-9.4) | Standard | Check existence | Yes | +| [POST](https://tools.ietf.org/html/rfc2616#section-9.5) | Standard | Create an item | No | +| [PATCH](http://tools.ietf.org/html/rfc5789) | Custom | Update an item | No | +| COPY | Custom | Duplicate an item | No | +| [MOVE](http://tools.ietf.org/html/rfc2518) | Custom | Move an item | No | +| SWAP | Custom | Swap two Locations | No | +| PUBLISH | Custom | Publish an item | No | +| [DELETE](https://tools.ietf.org/html/rfc2616#section-9.7) | Standard | Remove an item | No | + +!!! note "Caution with custom HTTP methods" + + Using custom HTTP methods can cause issues with several HTTP proxies, network firewall/security solutions and simpler web servers. + To avoid issues with this, REST API allows you to set these using the HTTP header `X-HTTP-Method-Override` alongside the standard `POST` method + instead of using a custom HTTP method. For example: `X-HTTP-Method-Override: PUBLISH` + + If applicable, both methods are always mentioned in the specifications. + +Unsafe methods will require a CSRF token if [session-based authentication](rest_api_authentication.md#session-based-authentication) is used. + +### OPTIONS method + +Any URI resource that the REST API responds to will respond to an `OPTIONS` request. + +The response contains an [`Allow` header](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7), which lists the methods accepted by the resource. + +```shell +curl -IX OPTIONS https://api.example.com/api/ezp/v2/content/objects/1 +``` + +``` +OPTIONS /content/objects/1 HTTP/1.1 +Host: api.example.com +``` + +``` +HTTP/1.1 200 OK +Allow: PATCH,GET,DELETE,COPY +``` + +```shell +curl -IX OPTIONS https://api.example.com/api/ezp/v2/content/locations/1/2 +``` + +``` +OPTIONS /content/locations/1/2 HTTP/1.1 +Host: api.example.com +``` + +``` +HTTP/1.1 200 OK +Allow: GET,PATCH,DELETE,COPY,MOVE,SWAP +``` + +## Request headers + +You can use the following HTTP headers with a REST request: + +- [`Accept`](https://tools.ietf.org/html/rfc2616#section-14.1) describing the desired response type and format; +- [`Content-Type`](https://toos.ietf.org/html/rfc2616#section-14.17) describing the payload type and format; +- [`X-Siteaccess`](#siteaccess) specifying the target SiteAccess; +- `X-HTTP-Method-Override` allowing to pass a custom method while using `POST` method as previously seen in [HTTP method](#request-method). +- [`Destination`](#destination) specifying where to move an item + +Other headers related to authentication methods can be found in [REST API authentication](rest_api_authentication.md). + +### SiteAccess + +In order to specify a SiteAccess when communicating with the REST API, provide a custom `X-Siteaccess` header. +Otherwise, the default SiteAccess is used. + +The following example shows what could be a SiteAccess called `restapi` dedicated to REST API accesses: + +``` +GET / HTTP/1.1 +Host: api.example.com +Accept: application/vnd.ez.api.Root+json +X-Siteaccess: restapi +``` + +One of the principles of REST is that the same resource (such as Content item, Location, Content Type) should be unique. +It allows caching your REST API using a reverse proxy such as Varnish. +If the same resource is available in multiple locations, cache purging is noticeably more complex. +This is why SiteAccess matching with REST is not enabled at URL level (or domain). + +### Media types + +On top of methods, HTTP request headers allow you to personalize the request's behavior. +On every resource, you can use the `Accept` header to indicate which format you want to communicate in, JSON or XML. +This header is also used to specify the response type you want the server to send when multiple types are available. + +- `Accept: application/vnd.ez.api.Content+xml` to get `Content` (full data, Fields included) as **[XML](http://www.w3.org/XML/)** +- `Accept: application/vnd.ez.api.ContentInfo+json` to get `ContentInfo` (metadata only) as **[JSON](http://www.json.org/)** + +Media types are also used with the [`Content-Type` header](rest_api_responses.md#content-type-header) to characterize a [request body](#request-body) or a [response body](rest_api_responses.md#response-body). +See [Creating content with binary attachments](#creating-content-with-binary-attachments) below. +Also see [Creating session](rest_api_authentication.md#creating-session) examples. + +If the resource only returns one media type, it is also possible to skip it and to just specify the format using `application/xml` or `application/json`. + +A response indicates `href`s to related resources and their media types. + +### Destination + +The `Destination` request header is the request counterpart of the `Location` response header. +It is used for a `COPY`, `MOVE` or `SWAP` operation to indicate where the resource should be moved, copied to or swapped with by using the ID of the parent or target Location. + +Examples of such requests are: + +- [copying a Content](rest_api_reference/rest_api_reference.html#managing-content-copy-content); +- [moving a Location and its subtree](rest_api_reference/rest_api_reference.html#managing-content-move-subtree) +- [swapping a Location with another](rest_api_reference/rest_api_reference.html#managing-content-swap-location) + +## Request body + +You can pass some short scalar parameters in the URIs or as GET parameters, but other resources need heavier structured payloads passed in the request body, +in particular the ones to create (`POST`) or update (`PATCH`) items. +In the [REST API reference](rest_api_reference/rest_api_reference.html), request payload examples are given when needed. + +One example is the [creation of an authentication session](rest_api_authentication.md#establishing-a-session). + +When creating a Content item, a special payload is needed if the ContentType has some [Image](field_types_reference/imagefield.md) or [BinaryFile](field_types_reference/binaryfilefield.md) Fields as files need to be attached. See the example of a [script uploading images](#creating-content-with-binary-attachments) below. + +When searching for Content items (or Locations), the query grammar is also particular. See the [Search section](#search-view) below. + +### Creating content with binary attachments + +The example below is a command-line script to upload images. + +This script: + +- receives an image path and optionally a name as command-line arguments, +- uses the [HTTP basic authentication](rest_api_authentication.md#http-basic-authentication), if it is enabled, +- creates a draft in the /Media/Images folder by posting (`POST`) data to [`/content/objects`](rest_api_reference/rest_api_reference.html#managing-content-create-content-item), +- and, publishes (`PUBLISH`) the draft through [`/content/objects/{contentId}/versions/{versionNo}`](rest_api_reference/rest_api_reference.html#managing-content-publish-a-content-version). + +=== "XML" + + ``` php + [[= include_file('code_samples/api/rest_api/create_image.xml.php', 0, None, ' ') =]] + ``` + +=== "JSON" + + ``` php + [[= include_file('code_samples/api/rest_api/create_image.json.php', 0, None, ' ') =]] + ``` + +### Search (`/views`) + +The `/views` route allows you to [search in the repository](../guide/search/search.md). It works similarly to its [PHP API counterpart](public_php_api_search.md). + +The model allows combining criteria using the logical operators `AND`, `OR` and `NOT`. + +Almost all [Search Criteria](../guide/search/criteria_reference/search_criteria_reference.md#search-criteria) are available in REST API. The suffix `Criterion` is added when used with REST API. + +Almost all [Sort Clauses](../guide/search/sort_clause_reference/sort_clause_reference.md#sort-clauses) are available too. They require no additional prefix or suffix. + +The search request has a `Content-Type: application/vnd.ez.api.ViewInput+xml` or `+json` header to specify the format of its body's payload. +The root node is `` and it has two mandatory children: `` and ``. + +You can add `version=1.1` to the `Content-Type` header to support the distinction between `ContentQuery` and `LocationQuery` instead of just `Query` which implicitly looks only for Content items. + +The following examples search for `article` and `news` typed Content items everywhere or for Content items of all types directly under Location `123`. All those Content items must be in the `standard` Section. + +=== "XML" + + ``` + Content-Type: application/vnd.ez.api.ViewInput+xml + ``` + + ``` xml + + + test + + + + + article + news + 123 + + standard + + + 10 + 0 + + ascending + + + + ``` + +=== "XML; 1.1" + + ``` + Content-Type: application/vnd.ez.api.ViewInput+xml; version=1.1 + ``` + + ``` xml + + + test + + + + + article + news + 123 + + standard + + + 10 + 0 + + ascending + + + + ``` + +=== "JSON" + + ``` + Content-Type: application/vnd.ez.api.ViewInput+json + ``` + + ``` json + { + "ViewInput": { + "identifier": "test", + "Query": { + "Filter": { + "AND": { + "OR": { + "ContentTypeIdentifierCriterion": [ + "article", + "news" + ], + "ParentLocationIdCriterion": 123 + }, + "SectionIdentifierCriterion": "standard" + } + }, + "limit": "10", + "offset": "0", + "SortClauses": { "ContentName": "ascending" } + } + } + } + ``` + +=== "JSON; 1.1" + + ``` + Content-Type: application/vnd.ez.api.ViewInput+json + ``` + + ``` json + { + "ViewInput": { + "identifier": "test", + "ContentQuery": { + "Filter": { + "AND": { + "OR": { + "ContentTypeIdentifierCriterion": [ + "article", + "news" + ], + "ParentLocationIdCriterion": 123 + }, + "SectionIdentifierCriterion": "standard" + } + }, + "limit": "10", + "offset": "0", + "SortClauses": { "ContentName": "ascending" } + } + } + } + ``` + +!!! note + + In JSON, the structure for `ContentTypeIdentifierCriterion` with multiple values has a slightly different format as keys must be unique. + In JSON, if there is only one item in `SortClauses`, it can be passed directly without an array to wrap it. + +You can omit logical operators. If Criteria are of mixed types, they are wrapped in an implicit `AND`. +If they are of the same type, they are wrapped in an implicit `OR`. + +For example, the `AND` operator from previous example's `Filter` could be removed. + +=== "XML Explicit `AND`" + + ``` xml + + + + article + news + 123 + + standard + + + ``` + +=== "XML Implicit `AND`" + + ``` xml + + + article + news + 123 + + standard + + ``` + +=== "JSON Explicit `AND`" + + ``` json + "Filter": { + "AND": { + "OR": { + "ContentTypeIdentifierCriterion": [ + "article", + "news" + ], + "ParentLocationIdCriterion": 123 + }, + "SectionIdentifierCriterion": "standard" + } + }, + ``` + +=== "JSON Implicit `AND`" + + ``` json + "Filter": { + "OR": { + "ContentTypeIdentifierCriterion": [ + "article", + "news" + ], + "ParentLocationIdCriterion": 123 + }, + "SectionIdentifierCriterion": "standard" + }, + ``` diff --git a/docs/api/rest_api_responses.md b/docs/api/rest_api_responses.md new file mode 100644 index 0000000000..84cc2ac8d6 --- /dev/null +++ b/docs/api/rest_api_responses.md @@ -0,0 +1,197 @@ +--- +description: REST API response code defines the status of the received response. +--- + +# REST Responses + +## Response code + +The following list of available HTTP response status codes gives an overview of the meaning of each code. +For code details per resource, see the [REST API reference](rest_api_reference/rest_api_reference.html). + +| Code | Message | Description | +|-------|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `200` | OK | The resource has been found. | +| `201` | Created | The request to create a new item has succeeded; the response `Location` header indicates where you can find the created item. | +| `204` | No Content | The request has succeeded and there is no additional information in the response header or body (for example when publishing or deleting). | +| `301` | Moved Permanently | The resource should not be accessed this way; the response `Location` header indicates the proper way. | +| `307` | Temporary Redirect | The resource is available at another URL considered as its main; the response `Location` header indicates this main URL. | +| `400` | Bad Request | The input (payload) doesn't have the proper schema for the resource. | +| `401` | Unauthorized | The user does not have the permission to make this request. | +| `403` | Forbidden | The user has the permission but action can't be performed because of Repository logic (for example, when trying to create an item with an already existing ID or identifier, when attempting to update a version in another state than draft). | +| `404` | Not Found | The requested object (or a request data like the parent of a new item) hasn't been found. | +| `405` | Method Not Allowed | The requested resource does not support the HTTP verb that was used. | +| `406` | Not Acceptable | The request's `Accept` header is not supported. | +| `409` | Conflict | The request is in conflict with another part of the repository (for example, trying to create a new item with an identifier already used). | +| `415` | Unsupported Media Type | The request payload media type doesn't match the media type specified in the request header. | +| `500` | Internal Server Error | The server encountered an unexpected condition, usually an exception, which prevents it from fulfilling the request, like database down, permissions or configuration error. | +| `501` | Not Implemented | Returned when the requested method has not yet been implemented. For [[= product_name =]], most of Users, User groups, Content items, Locations and Content Types have been implemented. Some of their methods, as well as other features, may return a 501. | + +## Response headers + +A resource's response may contain metadata in its HTTP headers. + +!!! note + + For information about the `Allow` response header, see the [`OPTIONS` method](rest_api_requests.md#options-method). + +### Content-Type header + +When a response contains an actual HTTP body, the `Content-Type` header specifies what the body contains. +The `Content-Type` header's value is a [media type](rest_api_requests.md#media-types), like with the request `Accept` and `Content-Type` headers. + +For example, the first following request without an `Accept` header returns a default format indicated in the response `Content-Type` header, while the second request shows that the response is in the requested format. + +``` +GET /content/objects/52 HTTP/1.1 +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.ContentInfo+xml +``` + +``` +GET /content/objects/52 HTTP/1.1 +Accept: application/vnd.ez.api.Content+json +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.Content+json +``` + +### Accept-Patch header + +When available, the `Accept-Patch` tells how the received item could be modified with `PATCH`. + +The following examples also shows that the format (XML or JSON) is adapted: + +``` +GET /content/objects/52 HTTP/1.1 +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.ContentInfo+xml +Accept-Patch: application/vnd.ez.api.ContentUpdate+xml +``` + +``` +GET /content/objects/52 HTTP/1.1 +Accept: application/vnd.ez.api.Content+json +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.Content+json +Accept-Patch: application/vnd.ez.api.ContentUpdate+json +``` + +Those example `Accept-Path` headers above indicate that the content could be modified by sending a [ContentUpdateStruct](https://github.com/ibexa/core/blob/main/src/contracts/Repository/Values/Content/ContentUpdateStruct.php) in XML or JSON. + +### Location header + +For example, [creating content](rest_api_reference/rest_api_reference.html#managing-content-create-content-type) and [getting a Content item's current version](rest_api_reference/rest_api_reference.html#managing-content-get-current-version) +both send a `Location` header to provide you with the requested resource's ID. + +Those particular headers generally match a specific list of HTTP response codes. +`Location` is mainly sent alongside `201 Created`, `301 Moved permanently`, `307 Temporary redirect responses`. + +In the following example, the Content item's remote ID 34720ff636e1d4ce512f762dc638e4ac corresponds to the ID 52: + +``` +GET /content/objects?remoteId=34720ff636e1d4ce512f762dc638e4ac" HTTP/1.1 +``` + +``` +HTTP/1.1 307 Temporary Redirect +Location: /content/objects/52 +``` + +In the following example, an erroneous slash has been added to demonstrate the 301 case: + +``` +GET /content/objects?remoteId=34720ff636e1d4ce512f762dc638e4ac" HTTP/1.1 +``` + +``` +HTTP/1.1 301 Moved Permanently +Location: /content/objects?remoteId=34720ff636e1d4ce512f762dc638e4ac +``` + +cURL can follow those redirections. On CLI, there is the `--location` option (or its shorthand `-L`). +In PHP, you can achieve the same effect with `CURLOPT_FOLLOWLOCATION`. +The following command-line example follows the two redirections above and the `Accept` header is propagated: + +```shell +curl --head --location --header "Accept: application/vnd.ez.api.Content+json" "https://api.example.com/api/ezp/v2/content/objects/?remoteId=34720ff636e1d4ce512f762dc638e4ac" +``` + +``` +HTTP/1.1 200 OK +Content-Type: application/vnd.ez.api.Content+json +``` + +### Cross-origin + +[Cross-Origin Resource Sharing (CORS)](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) can allow the REST API to be reached from a page on another domain. + +!!! tip "More information about CORS" + + - [CORS' W3C specification](http://www.w3.org/TR/cors/) + - [Overview of CORS on developer.mozilla.org](https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS) + +CORS support is provided by the third party [nelmio/cors-bundle](https://packagist.org/packages/nelmio/cors-bundle). You can read more about it in [NelmioCorsBundle's README](https://github.com/nelmio/NelmioCorsBundle/blob/master/README.md). + +Using CORS is not limited to REST API resources and can be used for any resource of the platform. + +The CORS bundle adds an `Access-Control-Allow-Origin` header to the response. + +#### Configuration + +To enable CORS, add regular expression for an allowed domain using the `.env` variable `CORS_ALLOW_ORIGIN`. + +For example, to allow the JS test above to be executed alongside this page, you could add the following to an `.env` file (like the `.env.local`): `CORS_ALLOW_ORIGIN=^https?://doc.ibexa.co`. + +To add several domains, filter on URIs, or change the default (like not allowing all the methods), +refer to [NelmioCorsBundle Configuration Documentation](https://github.com/nelmio/NelmioCorsBundle/blob/master/README.md#configuration) +to learn how to edit `config/packages/nelmio_cors.yaml`. + +## Response body + +The Response body is often a serialization in XML or JSON of an object as it could be retrieved using the Public PHP API. + +For example, the resource `/content/objects/52` with the `Accept: application/vnd.ez.api.ContentInfo+xml` header returns a serialized version of a [ContentInfo](https://github.com/ibexa/core/blob/main/src/contracts/Repository/Values/Content/ContentInfo.php) object. + +```shell +curl https://api.example.com/content/objects/52 --header 'Accept: application/vnd.ez.api.ContentInfo+xml'; +``` + +```xml + + + + Ibexa Digital Experience Platform + Ibexa Digital Experience Platform + + +
    + + + + 2015-09-17T09:22:23+00:00 + 2015-09-17T09:22:23+00:00 + eng-GB + 1 + true + false + PUBLISHED + + +``` + +The response body XML can contain two types of nodes: + +- Final nodes that fully give an information as a scalar value; +- Reference nodes which link to `href` where a new resource of a given `media-type` can be explored if you need to know more. diff --git a/docs/api/rest_api_testing.md b/docs/api/rest_api_testing.md new file mode 100644 index 0000000000..20282a0900 --- /dev/null +++ b/docs/api/rest_api_testing.md @@ -0,0 +1,71 @@ +--- +description: You can test operations in the REST API by using command line, PHP or JS code. +--- + +# Testing the API + +A standard web browser is not sufficient to fully test the API. +You can, however, try opening the root resource with it, using the session authentication: `http://example.com/api/ezp/v2/`. +Depending on how your browser understands XML, it either downloads the XML file, or opens it in the browser. + +The following examples show how to interrogate the REST API using cURL, PHP or JS. + +To test further, you can use browser extensions, like [Advanced REST client for Chrome](https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo) or [RESTClient for Firefox](https://addons.mozilla.org/firefox/addon/restclient/), or dedicated tools. For command line users, [HTTPie](https://github.com/jkbr/httpie) is a good tool. + +## CLI + +For examples of using `curl`, refer to: + +- [REST root](rest_api_usage.md#rest-root) +- [OPTIONS method](rest_api_requests.md#options-method) +- [Location header](rest_api_responses.md#location-header) +- [ContentInfo body](rest_api_responses.md#response-body) + +## PHP + +To test REST API with PHP, cURL can be used; open a PHP shell in a terminal with `php -a` and copy-paste this code into it: + +```php +$resource = 'https://api.example.com/api/ezp/v2/content/objects/52'; +$curl = curl_init($resource); +curl_setopt_array($curl, [ + CURLOPT_HTTPHEADER => ['Accept: application/vnd.ez.api.ContentInfo+json'], + CURLOPT_HEADERFUNCTION => function($curl, $header) { + if (!empty($cleanedHeader = trim($header))) { + var_dump($cleanedHeader); + } + return strlen($header); + }, + CURLOPT_RETURNTRANSFER => true, +]); +var_dump(json_decode(curl_exec($curl), true)); +``` + +`$resource` URI should be edited to address the right domain. + +On a freshly installed Ibexa DXP, `52` is the Content ID of the home page. If necessary, substitute `52` with the Content ID of an item from your database. + +For a content creation example using PHP, see [Creating content with binary attachments](rest_api_requests.md#creating-content-with-binary-attachments) + +## JS + +The REST API can help you implement JavaScript / AJAX interaction. +The following example of an AJAX call retrieves `ContentInfo` (that is, metadata) for a Content item: + +To test it, copy-paste this code into your browser console alongside a page from your website (to share the domain): + +```javascript +var resource = '/api/ezp/v2/content/objects/52', + request = new XMLHttpRequest(); + +request.open('GET', resource, true); +request.setRequestHeader('Accept', 'application/vnd.ez.api.ContentInfo+json'); +request.onload = function () { + console.log(request.getAllResponseHeaders(), JSON.parse(request.responseText)); +}; +request.send(); +``` + +On a freshly installed Ibexa DXP, `52` is the Content ID of the home page. If necessary, substitute `52` with the Content ID of an item from your database. + +You can edit the `resource` URI to address another domain, but [cross-origin requests](rest_api_responses.md#cross-origin) must be allowed first. diff --git a/docs/api/rest_api_usage.md b/docs/api/rest_api_usage.md new file mode 100644 index 0000000000..511a48719a --- /dev/null +++ b/docs/api/rest_api_usage.md @@ -0,0 +1,83 @@ +--- +description: The REST API covers objects in the Ibexa DXP Repository with regular and custom HTTP methods, such as GET or PUBLISH, as well as HTTP headers. +--- + +# REST API usage + +The REST API in [[= product_name =]] allows you to interact with an [[= product_name =]] installation using the HTTP protocol, +following a [REST](http://en.wikipedia.org/wiki/Representational_state_transfer) interaction model. + +Each resource (URI) interacts with a part of the system (content, users, search, and so on). +Every interaction with the Repository than you can do from Back Office or using the [Public PHP API](public_php_api.md) can also be done using the REST API. + +The REST API uses HTTP methods (`GET`, `PUBLISH` , and so on), as well as HTTP headers to specify the type of request. + +## URIs + +The REST API is designed in such a way that the client can explore the Repository without constructing any URIs to resources. +Starting from the [root resource](#rest-root), every response includes further links (`href`) to related resources. + +### URI prefix + +[REST reference](rest_api_reference/rest_api_reference.html), for the sake of readability, uses no prefixes in the URIs. +In practice, the `/api/ezp/v2` prefixes all REST hrefs. + +This prefix immediately follows the domain, and you can't use the [`URIElement` SiteAccess matcher](../guide/multisite/siteaccess_matching.md#urielement). +If you need to the select a SiteAccess, see the [`X-Siteaccess` HTTP header](rest_api_requests#siteaccess). + +### URI parameters + +URI parameters (query string) can be used on some resources. +They usually serve as options or filters for the requested resource. + +As an example, the request below would paginate the results and return the first 5 relations for version 3 of the Content item 59: + +``` +GET /content/objects/59/versions/3/relations?limit=5 HTTP/1.1 +Accept: application/vnd.ez.api.RelationList+xml +``` + +#### Working with value objects IDs + +Resources that accept a reference to another resource expect the reference to be given as a REST URI, not a single ID. +For example, the URI requesting a list of user groups assigned to the role with ID 1 is: + +``` +GET /api/ezp/v2/user/groups?roleId=/api/ezp/v2/user/roles/1 +``` + +### REST root + +The `/` root route is answered by a reference list with the main resource routes and media-types. +It is presented in XML by default, but you can also switch to JSON output. + +```shell +curl https://api.example.com/api/ezp/v2/ +curl -H "Accept: application/json" https://api.example.com/api/ezp/v2/ +``` + +### Country list + +Alongside regular Repository interactions, there is a REST service providing a list of countries with their names, [ISO-3166](http://en.wikipedia.org/wiki/ISO_3166) codes and International Dialing Codes (IDC). It could be useful when presenting a country options list from any application. + +This country list's URI is `/services/countries`. + +The ISO-3166 country codes can be represented as: + +- two-letter code (alpha-2) — recommended as the general purpose code +- three-letter code (alpha-3) — related to the country name +- three-digit numeric code (numeric-3) — useful if you need to avoid using Latin script + +For details, see the [ISO-3166 glossary](http://www.iso.org/iso/home/standards/country_codes/country_codes_glossary.htm). + +## REST communication summary + +* A REST route (URI) leads to a REST controller action. A REST route is composed of the root prefix (`ezpublish_rest.path_prefix: /api/ezp/v2`) and a resource path (for example, `/content/objects/{contentId}`). +* This controller action returns an `Ibexa\Rest\Value` descendant. + - This controller action might use the `Request` to build its result according to, for example, GET parameters, the `Accept` HTTP header, or the request payload and its `Content-Type` HTTP header. + - This controller action might wrap its return in a `CachedValue` which contains caching information for the reverse proxies. +* The `Ibexa\Bundle\Rest\EventListener\ResponseListener` attached to the `kernel.view event` is triggered, and passes the request and the controller action's result to the `AcceptHeaderVisitorDispatcher`. +* The `AcceptHeaderVisitorDispatcher` matches one of the `regexps` of an `ezpublish_rest.output.visitor` service (an `Ibexa\Contracts\Rest\Output\Visitor`). The role of this `Output\Visitor` is to transform the value returned by the controller into XML or JSON output format. To do so, it combines an `Output\Generator` corresponding to the output format and a `ValueObjectVisitorDispatcher`. This `Output\Generator` is also adding the `media-type` attributes. +* The matched `Output\Visitor` uses its `ValueObjectVisitorDispatcher` to select the right `ValueObjectVisitor` according to the fully qualified class name (FQCN) of the controller result. A `ValueObjectVisitor` is a service tagged `ezpublish_rest.output.value_object_visitor` and this tag has a property `type` pointing a FQCN. +* `ValueObjectVisitor`s will recursively help to transform the controller result thanks to the abstraction layer of the `Generator`. +* The `Output\Visitor` returns the `Response` to send back to the client. diff --git a/docs/api/shop_business_api.md b/docs/api/shop_business_api.md new file mode 100644 index 0000000000..7adf28748e --- /dev/null +++ b/docs/api/shop_business_api.md @@ -0,0 +1,124 @@ +--- +edition: commerce +--- + +# Shop API + +Shop business API is the layer between the application entry points (like controllers or CLI commands) and the particular shop-related services. + +To access the Shop business API, you have to use the Business API invocation service. +This service is the access point to the Business API and is defined by a service with the ID `ses_eshop.business_api.invocation`. + +To call the operation service, use the `call()` method. + +The method takes two parameters: + +- `string $operationIdentifier` +- `ValueObject $operationInput` + +The following example shows how to use the Business API basket operation service. + +``` php +use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as InputGetBasket; +use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as OutputGetBasket; + +/** @var InputGetBasket $input */ +$input = new InputGetBasket(array('request' => $request)); + +/** @var OutputGetBasket $output */ +$output = $this->get('ses_eshop.business_api.invocation')->call('basket.get_basket', $input); +``` + +## Methods + +| Method | Parameters | Returns | Description | Operation identifier | +| ------ | ----------- | ---------- | -------- | -------------------- | +| [`addProducts`](#addproducts) | `InputAddItemToBasket $operationInput` | `OutputAddItemToBasket $operationOutput` | Adds products to the basket. | `basket.add_products` | +| [`getBasket`](#getbasket) | `InputGetBasket $input` | `OutputGetBasket $output` | Returns current basket. | `basket.get_basket` | +| [`loadProducts`](#loadproducts) | `InputLoadList $input` | `OutputLoadList $input` | Loads products from catalog. | `catalog.load_products` | + +## addProducts + +`basket.add_products` adds one or more products to the basket. + +### Example + +``` php +$outputGetBasket = $this->getBusinessApi()->call('basket.get_basket', $inputGetBasket); +// Clear all messages for each request +$outputGetBasket->basket->clearAllMessages(); +$itemData = new ItemData( + array( + 'quantity' => '1', + 'isVariant' => '', + 'variantCode' => '', + 'sku' => '1000', + ) +); + +/** @var InputAddItemToBasket $inputAddItemToBasket */ +$inputAddItemToBasket = new InputAddItemToBasket( + array( + 'itemData' => $itemData, + 'basket' => $outputGetBasket->basket, + ) +); + +try { + $outputGetBasket = $this->getBusinessApi()->call('basket.add_products', $inputAddItemToBasket); +} catch (\InvalidArgumentException $e) { + // .... +} + +$message = $this->getBasketMessage($outputGetBasket->basket); +``` + +## getBasket + +`basket.get_basket` gets the current basket. + +### Example + +``` php +use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\InputValueObjects\GetBasket as InputGetBasket; +use Ibexa\Platform\Commerce\Checkout\Entities\BusinessLayer\OutputValueObjects\GetBasket as OutputGetBasket; + +/** @var InputGetBasket $inputGetBasket */ +$inputGetBasket = new InputGetBasket(array('request' => $request)); + +/** @var OutputGetBasket $outputGetBasket */ +$outputGetBasket = $this->getBusinessApi()->call('basket.get_basket', $inputGetBasket); + +$message = $this->getBasketMessage($outputGetBasket->basket); +``` + +## loadProducts + +`catalog.load_products` loads products from storage. + +### Example call to Business API + +``` php +/** @var InputLoadList $input */ +$input = new InputLoadList( + array( + 'locationId' => 136, + 'limit' => 3, + 'offset' => 3, + 'language' => 'de_DE', + 'filterType' => 'productList', + ) +); + +/** @var OutputLoadList $output */ +$output = $this->getBusinessApi()->call('catalog.load_products', $input); + +$html = $this->renderView( + '@SilversolutionsEshopBundle/Catalog/listProductNodes.html.twig', + array( + 'catalogList' => $output->catalogList, + 'params' => $data, + 'locationId' => $output->locationId, + ) +); +``` diff --git a/docs/cdp/cdp.md b/docs/cdp/cdp.md new file mode 100644 index 0000000000..00351ef86a --- /dev/null +++ b/docs/cdp/cdp.md @@ -0,0 +1,24 @@ +--- +description: Ibexa CDP is a software system designed to collect and organize customer data from multiple sources to build comprehensive customer profiles. +--- + +# Customer Data Platform (CDP) + +## What is Ibexa CDP + +Ibexa CDP helps you solve one of the hardest challenges facing business world today: +building unique experiences for your customers. +With Ibexa CDP you will be able to track and aggregate data of your customers' activity on multiple channels. +It will allow you to create individual customer profiles that enable you to personalize their experience on your platform. + +![Ibexa CDP control panel](img/cdp_control_panel.png) + +## How it works + +Ibexa CDP unifies customer data across your organization +to help you activate your users and provide them with real-time engagement. +With defined audiences you can target your user segments at the right time, +through the most used channel, with the relevant message, content, or products. + +The customer data are collected through the system of trackers embedded in different parts of your page. +For more information on activation and trackers, see [CDP activation documentation](cdp_activation.md). diff --git a/docs/cdp/cdp_activation.md b/docs/cdp/cdp_activation.md new file mode 100644 index 0000000000..9adcd6a3a9 --- /dev/null +++ b/docs/cdp/cdp_activation.md @@ -0,0 +1,200 @@ +--- +description: Step-by-step activation procedure of Ibexa CDP. +--- + +# CDP activation + +## Configuration + +To configure Ibexa CDP, edit the `config/packages/ibexa_cdp.yaml` file: + +```yaml +ezplatform: + system: + default: + cdp: + account_number: 123456 + user_data_streaming: + stream_id: 00000000-00000000-00000000-00000000 + activations: + client_id: '%env(CDP_ACTIVATION_CLIENT_ID)%' + client_secret: '%env(CDP_ACTIVATION_CLIENT_SECRET)%' + segment_group_identifier: example_segment_group_identifier +``` + +All configuration settings are described below. +You can follow them step by step to set up your Ibexa CDP. + +### Segment group + +First, create a segment group in the Back Office. +It will serve as a container for all segments data generated by Ibexa CDP. +Go to **Admin** -> **Segments** and select **Create**. +Fill in name and identifier for a segment group. +Choose wisely, as once connected to CDP Segment Group cannot be changed. + +![Creating a new segment group](img/cdp_create_segment_group.png) + +Next, add a segment group identifier to the configuration. + +!!! caution "Ibexa CDP Segment Group" + + After you create the Segment Group in the Back Office and connect it to Ibexa CDP, you cannot change it in any way, including edit its name. + +## Account number + +Now, fill in the account number. +Log in to Ibexa CDP and in the top right corner, select available accounts. + +![List of available accounts](img/cdp_accounts.png) + +A pop-up window displays with a list of all available accounts and their numbers. + +![Account number](img/cdp_account_number.png) + +## User Data Streaming + +You need to specify a source of the user data that Ibexa CDP will connect to. +To do so, go to **Data Manager** in **Tools** section and select **Create new dataflow**. +It will take you to a Dataflow Creator, where in five steps you will set up a data streaming. + +### General Information + +In the **General Information** section, specify dataflow name, +choose **Stream File** as a source of user data and **CDP** as a destination, +where they will be sent for processing. + +### Download + +In the **Download** section, select **Stream file**. +Copy generated steam ID and paste it into the `config/packages/ibexa_cdp.yaml` file under `stream_id`. +It allows you to establish a datastream from the Streaming API into the Data Manager. + +Next, you need to export your user data to the CDP. +Go to your installation and use this command: + +```bash +php bin/console ibexa:cdp:stream-user-data --draft +``` + +There are two versions of this command `--draft/--no-draft`. +The first one is used to send the test user data to the Data Manager. +If it passes a validation test in the **Activation** section, use the latter one to send a full version. + +Next, go back to Ibexa CDP and select **Validate & download**. +If the file passes, you will see a confirmation message. +Now, you can go to the **File mapping** section. + +### File mapping + +Mapping is completed automatically, the system fills all required information and shows available columns with datapoints on the right. +You can change their names if needed or disallow empty fields by checking **Mandatory**. +If the provided file contains empty values, this option is not available. + +If provided file is not recognized, the system will require you to fill in the parsing-options manually or select an appropriate format. +If you make any alterations, select the **Parse File** to generate columns with new data. + +### Transform & Map + +In the **Transform & Map** section you transform data and map it to a schema. +At this point, you can map **email** to **email** and **id** to **integer** fields to get custom columns. + +Next, select **Create schema based on the downloaded columns**. +It will move you to Schema Creator. +There, choose **PersonalData** as a parent and name the schema. + +![Create new schema](img/cdp_create_new_schema.png) + +Next, select all the columns and set Person Identifier as **userid**. + +![Person Identifier](img/cdp_person_identifier.png) + +If you used PersonData or Catalog type schemas, the system will require +specifying the Write Mode that will be applied to them. + +**Append** (default one) allows new data to overwrite the old one but leaves existing entries unaffected. +All entries are stored in the dataset, unchanged by updating dataflow. +For example, if a customer unsubscribes a newsletter, their email will remain in the system. +**Overwrite** completely removes the original dataset and replaces it with the new one every time the dataflow runs. + +Next, select **userid** from a **Schema columns section** on the right and map it to **id**. + +![Map userid to id](img/cdp_userid_mapid.png) + +### Activation + +In this section you will test the dataflow with provided test user data. +If everything passes, go to your installation and export production data with this command: + +```bash +php bin/console ibexa:cdp:stream-user-data --no-draft +``` + +Now you can run and activate the dataflow. + +### Build new Audience/Segment + +Go to the **Audience Builder** and select **Build new audience**. +When naming the audience remember, you will need to find it in a drop-down list during activation. +There, you can choose conditions from `did`, `did not` or `have`. +The conditions `did` and `did not` allow you to use events like buy, visit or add to a basket from online tracking. +- `have` conditions are tied to personal characteristics and can be used to track the sum of all buys or top-visited categories. + +In the Audience Builder, you can also connect created audiences to the activations. + +## Activation + +Activation synchronises data from Ibexa CDP to the Ibexa DXP. +When you specify a segment, you can activate it on multiple communication channels, such as newsletters or commercials. +You can configure multiple activations based data flows. + +First, from the menu bar, select **Activations** and create a new **Ibexa** activation. +Specify name of your activation, select `userid` as **Person Identifier** and click **Next**. + +![General Information - Activation](img/cdp_activation_general_info.png) + +Next, you can fill in **Ibexa information** they must match the ones provided in the YAML configuration: + +- **Client Secret** and **Client ID** - are used to authenticate against Webhook endpoint. In the configuration they are taken from environment variables in `.env` file. + +- **Segment Group Identifier** - identifier of the segment group in Ibexa DXP. It points to a segment group where all the CDP audiences will be stored. +- **Base URL** - URL of your instance with added `/cdp/webhook` at the end. + +![Ibexa Information - Activation](img/cdp_activation_ibexa_info.png) + +Finally, you can specify the audiences you wish to include. + +!!! note "CDP requests" + + All CDP requests are logged in with `debug` severity. + +## Add Client-side Tracking + +The final step is setting up a tracking script. +It requires a head tracking script between the `` tags on your website +and a main script after the head script, and cookie consent. +You can do it by following [tutorial in the documentation](https://support.raptorsmartadvisor.com/hc/en-us/articles/115000656909-Client-side-Tracking). + +Now, you need to add a tracker to specific places in your website where you want to track users. +For example, add this tracker to the Landing Page template if you want to track user entrances. + +```js +raptor.trackEvent('visit', ..., ...); +``` +or buys: + +```js + //Parameters for Product 1 +raptor.trackEvent('buy', ..., ...); + //Parameters for Product 2 +raptor.trackEvent('buy', ..., ...); +``` + +For tracing to be effective, you also need to send ID of a logged-in user in the same way. +Add the user ID information by using below script: + +```js +raptor.push("setRuid","USER_ID_HERE") +``` + +For more information on tracking events, see [the documentation](https://support.raptorsmartadvisor.com/hc/en-us/articles/201912411-Tracking-Events). \ No newline at end of file diff --git a/docs/cdp/cdp_installation.md b/docs/cdp/cdp_installation.md new file mode 100644 index 0000000000..9e7b704caf --- /dev/null +++ b/docs/cdp/cdp_installation.md @@ -0,0 +1,40 @@ +--- +description: Installation of standalone Ibexa CDP package. +--- + +# Ibexa CDP installation + +There are three steps required to install Ibexa CDP. +First, you need to register your Ibexa CDP account, then you can download a CDP package and update the configuration. + +## Register in Ibexa CDP dashboard + +If you decide to acquire Ibexa CDP contact your sales representative, +they will provide you with a registration link to Ibexa CDP. +After registration, you will get access to a separate instance +where you will find data required for configuring, activating and using this feature. + +## Install CDP package + +Ibexa CDP comes in an additional package that is opt-in and needs to be downloaded separately. + +To download it run: + +```bash +composer require ibexa/cdp +``` + +Flex will install and activate the package. +After an installation process is finished, go to `config/packages/security.yaml` +and uncomment `ibexa_cdp` rule. + +```yaml +ibexa_cdp: + pattern: /cdp/webhook + guard: + authenticator: 'Ibexa\Cdp\Security\CdpRequestAuthenticator' + stateless: true +``` + +Now, you can configure Ibexa CDP. +Go to [the activation documentation](cdp_activation.md) and follow the steps. diff --git a/docs/cdp/img/cdp_account_number.png b/docs/cdp/img/cdp_account_number.png new file mode 100644 index 0000000000..0d4613f0c5 Binary files /dev/null and b/docs/cdp/img/cdp_account_number.png differ diff --git a/docs/cdp/img/cdp_accounts.png b/docs/cdp/img/cdp_accounts.png new file mode 100644 index 0000000000..249236927b Binary files /dev/null and b/docs/cdp/img/cdp_accounts.png differ diff --git a/docs/cdp/img/cdp_activation_general_info.png b/docs/cdp/img/cdp_activation_general_info.png new file mode 100644 index 0000000000..813e2bf8c2 Binary files /dev/null and b/docs/cdp/img/cdp_activation_general_info.png differ diff --git a/docs/cdp/img/cdp_activation_ibexa_info.png b/docs/cdp/img/cdp_activation_ibexa_info.png new file mode 100644 index 0000000000..c7feec840a Binary files /dev/null and b/docs/cdp/img/cdp_activation_ibexa_info.png differ diff --git a/docs/cdp/img/cdp_control_panel.png b/docs/cdp/img/cdp_control_panel.png new file mode 100644 index 0000000000..51a253a083 Binary files /dev/null and b/docs/cdp/img/cdp_control_panel.png differ diff --git a/docs/cdp/img/cdp_create_new_schema.png b/docs/cdp/img/cdp_create_new_schema.png new file mode 100644 index 0000000000..6691d2ff1d Binary files /dev/null and b/docs/cdp/img/cdp_create_new_schema.png differ diff --git a/docs/cdp/img/cdp_create_segment_group.png b/docs/cdp/img/cdp_create_segment_group.png new file mode 100644 index 0000000000..709c2ee675 Binary files /dev/null and b/docs/cdp/img/cdp_create_segment_group.png differ diff --git a/docs/cdp/img/cdp_person_identifier.png b/docs/cdp/img/cdp_person_identifier.png new file mode 100644 index 0000000000..260753e771 Binary files /dev/null and b/docs/cdp/img/cdp_person_identifier.png differ diff --git a/docs/cdp/img/cdp_userid_mapid.png b/docs/cdp/img/cdp_userid_mapid.png new file mode 100644 index 0000000000..9166c0f870 Binary files /dev/null and b/docs/cdp/img/cdp_userid_mapid.png differ diff --git a/docs/community_resources/code.md b/docs/community_resources/code.md deleted file mode 100644 index 0b12578c35..0000000000 --- a/docs/community_resources/code.md +++ /dev/null @@ -1,41 +0,0 @@ -# Contribute code - -If you intend to change existing or introduce new code, start with a proper explanation. -It should be placed in a public ticket. -All the rules to follow can be found in [Contribute to documentation](documentation.md) section. - -Once you are done with describing your idea, focus on the main part - sharing the actual solution. -Ibexa uses a regular git workflow, so if you are familiar with the concept, the whole procedure should be pretty straightforward. - -[[= product_name =]] is divided into meta-repositories. -The core of our system is [`ezplatform-kernel`](https://github.com/ezsystems/ezplatform-kernel) -containing an advanced Content Model and aiming to provide additional features for the MVC layer (Symfony). -On the other hand, e.g. `ezplatform-admin-ui` is strictly dedicated to the Admin Panel purposes. -If you want to learn more about our code structure, take a look at [our organization page on GitHub](https://github.com/ezsystems) -or the list of [core bundles](../guide/bundles/#core-bundles). - -After finishing your work, fork repository which you want to contribute to. -Now you need to determine which version of the package your changes should target. -If you plan to fix something in your current project, check `composer.json` for the version of the package and pick proper branch. - -For example: you added a `try { } catch () {}` statement fixing an annoying error in `ezplatform-admin-ui` -and you are using version 2.5. You should aim for branch `1.5` then, as version `1.5.0` is used. - -Now you can follow the same procedure as in [Contributing through git](documentation/#contributing-through-git). - -!!! caution "Public repositories" - - You can contribute only to the public repositories. - This means that all repositories marked as `private` are not open to contributions outside our organization. - However, you can generate a patch from your own codebase and attach it to the Customer Ticket. - That will allow our engineers to open a pull request in a private repository on your behalf. - -To become a part of the product your changes must pass our team's review. -Not all pull requests can be approved. Be prepared that some will need changes before they can be accepted. -When you respond to questions and discussion around your PR and make changes to it as needed, -you increase the chance that it will be accepted and reduce the waiting period. -Keep in mind that not every suggestion meets requirements of the product or chosen business strategy. - -**Don't hesitate to share your work** even if you don't consider yourself an experienced developer. -Our Engineers will help you meet our standards, follow good coding practices, -adapt to our solutions/code conventions and pay attention to details. diff --git a/docs/community_resources/contributing.md b/docs/community_resources/contributing.md deleted file mode 100644 index 3aaf9ca801..0000000000 --- a/docs/community_resources/contributing.md +++ /dev/null @@ -1,16 +0,0 @@ -# How to Contribute - -Are you ready to become a part of the Ibexa Community? There are several ways in which you can contribute, from spotting and reporting bugs to committing to the documentation to discussing innovative uses on Slack to coding new bundles. - -If you're looking to contribute code, whether in form of corrections or separate bundles with features, the open-source nature of [[= product_name =]] lets you do this without any fuss using GitHub. Take a look at our [Development guidelines](development_guidelines.md) to get started. - -- [Report and follow issues](report_follow_issues.md) if you'd just like to let us know of a bug you've encountered - -- [Contribute code](code.md) if you've fixed a bug or implemented improvement/feature - -- [Contribute translations](translations.md) if you'd like to add a translation of [[= product_name =]] interface - -- [Contribute to documentation](documentation.md) if you've noticed any improvement needed in the documentation - -- Visit [Community website](http://share.ez.no) or the [eZ Community Slack team](https://ezcommunity.slack.com/) if what you're looking for is simply discussing the way you use [[= product_name =]] -  diff --git a/docs/community_resources/development_guidelines.md b/docs/community_resources/development_guidelines.md deleted file mode 100644 index caa1c69c0e..0000000000 --- a/docs/community_resources/development_guidelines.md +++ /dev/null @@ -1,153 +0,0 @@ -# Development guidelines - -These are the development/coding guidelines for [[= product_name =]] kernel, they are the same if you intend to write Bundles, hack on [[= product_name =]] itself or create new functionality for or on top of [[= product_name =]]. - -Like most development guidelines these aims to improve security, maintainability, performance and readability of our software. They follow industry standards but sometimes extend them to cater specifically to our needs for [[= product_name =]] ecosystem. The next sections will cover all relevant technologies from a high level point of view. - -!!! tip "Security checklist" - - See the [Security checklist](../guide/security_checklist.md) for a list of security-related issues - you should take care of before going live with a project. - -## HTTP - -[[= product_name =]] is a web software that is reached via HTTP in most cases, out of the box in [[= product_name =]] kernel this is specifically: web (usually HTML) or REST. - -We aim to follow the [latest](http://trac.tools.ietf.org/wg/httpbis/trac/wiki#HTTP1.1Deliverables) stable HTTP specification, and industry best practice: - -- **Expose our data in a RESTful way** - - GET, HEAD, OPTIONS and TRACE methods are [safe](http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21#section-5.2.1) (otherwise known as [nullipotent](http://en.wiktionary.org/wiki/nullipotent)), as in: should never cause changes to resources (note: things like writing a line in a log file are not considered resource changes) - - PUT and DELETE methods are [idempotent](http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21#section-5.2.2), as in multiple identical requests should all have the same result as a single request - - GET and HEAD methods should be [cacheable](http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21#section-5.2.3) both on client side, server-side and proxies between, as further defined in the HTTP [specification](http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21) - - As PUT is for replacing a resource, we should use [PATCH](http://tools.ietf.org/html/rfc5789) in cases where only partial replacement is intended -- **Authenticated traffic** - - Should use HTTPS -- **Session based traffic** - - Should follow recommendations for *Authenticated traffic* - - Should use a per user session [CSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) token on all requests using un-safe HTTP methods (POST, PUT, DELETE, PATCH, ...) - - Should expire session ID, session data and CSRF token on login, logout and session time out, except: - - On login session data from previous session ID is moved to new session ID, keeping for instance shopping basket on login - - Should avoid timing attacks by using a random amount of time for login operation - - Should never use Session ID in URI's. And this feature ("SID") must always be disabled on production servers -- **Sessions** - - Should not be used to store large amounts of data; store data in database and ID's in session if needed - - Should not store critical data: if user deletes his cookies or closes his browser session data is lost - - Should use an ID generated with enough randomness to prevent prediction or brute-force attacks -- **Cookies (especially session cookies)** - - Should never store sensitive data in cookies (only exception is session ID in session cookie) - - Should always set *Full domain* to avoid [cross-subdomain cooking](https://en.wikipedia.org/wiki/Session_fixation#Attacks_using_cross-subdomain_cookie) when on shared domain. - - Should set *HttpOnly* flag to reduce risk of attacks such as [cross-site cooking](https://en.wikipedia.org/wiki/Cross-site_cooking) and [cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting "Cross-site scripting") - - Should set *Secure flag* if HTTPS is used (as recommended above) - - Must never exceed 4kB -- **Headers** - - Should never include input data from user input or data from database without sanitizing it -- **Redirects** - - Should never take URL from user input (example: POST parameter), instead allow identifiers instead that are understood by the backend -- **User input** - - Should always be validated, sanitized, casted and filtered to avoid [XSS](http://en.wikipedia.org/wiki/Cross-site_scripting) and [clickjacking](http://en.wikipedia.org/wiki/Clickjacking) attacks - - Note: this includes variables in the php supervariable `$_SERVER` as well (e.g. hostname should not be trusted) -- **User file uploads** - - Should follow recommendations for "User input" to validate file name - - Should place uploaded files in a non public folder to avoid access to execute uploaded file or in case of assets white list the type - - Should be appropriately limited in size to avoid DOS attacks on disk space, CPU usage by antivirus tool etc... -- **File downloads** - - Should not rely on user provided file path for non public files, instead use a synthetic ID -- **Admin operations** - - May be placed on a different (sub)domain then the front end website to avoid session stealing across front and backend. -- **Fully support being placed behind a reverse proxy like [Varnish](https://www.varnish-cache.org/)** - -## REST - -For now see the living [REST v2 specification](https://doc.ezplatform.com/rest-api-reference) in our git repository for further details. - -## UI - -[[= product_name =]] is often used as a web content management software, so we always strive to use the HTML/CSS/EcmaScript specifications correctly, and keep new releases up to date on new revisions of those. We furthermore always try to make sure our software gracefully degrades making sure it is useful even on older or less capable web clients (browsers), the industry terms for this approach are: - -- [Progressive enhancement](http://en.wikipedia.org/wiki/Progressive_enhancement "Progressive enhancement") -- [Unobtrusive JavaScript](http://en.wikipedia.org/wiki/Unobtrusive_JavaScript) -- [Responsive Design](http://en.wikipedia.org/wiki/Responsive_Web_Design "Responsive Web Design") - -All these terms in general recommends aiming for the minimum standard first, and enhance with additional features/styling if the client is capable of doing so. In essence this allows [[= product_name =]] to be "Mobile first" if the design allows for it, which is recommended. But [[= product_name =]] should always also be fully capable of having different sets of web presentations for different devices using one or several sets of SiteAccess matching rules for the domain, port or URI, so any kind of device detection can be used together with [[= product_name =]], making it fully possible to write for instance [WAP](http://en.wikipedia.org/wiki/Wireless_Application_Protocol) based websites and interfaces on top of [[= product_name =]]. - -### WEB Forms/Ajax - -As stated in the HTTP section, all unsafe requests to the web server should have a CSRF token to protect against attacks; this includes web forms and ajax requests that don't use the GET http method. As also stated in the HTTP section and further defined in the PHP section, User input should always be validated to avoid XSS issues. - -### HTML/Templates - -All data that comes from backend and in return comes from user input should always be escaped, in case of Twig templates this done by default, but in case of PHP templates, Ajax and other not Twig based output this must be handled manually. - - -Output escaping must be properly executed according to the desired format, eg. JavaScript vs. HTML, but also taking into account the correct character set (see e.g. output escaping fallacy when not specifying charset encoding in [htmlspecialchars](http://www.php.net/htmlspecialchars)) - -### Admin - -Admin operations that can have a severe impact on the web applications should require providing password and require it again after some time has gone, normally 10-20 minutes, on all session-based interfaces. - -## PHP - -### Public API - -The [Public PHP API](../api/public_php_api) provided in [[= product_name =]] is in most cases in charge of checking permissions to data for you, but some API's are not documented to throw `UnauthorizedException`, which means that it is the consumer of the API's who is responsible for checking permissions. - -The following example shows how this is done in the case of loading users: - -**loadUser()** - -``` php -// Get a user -$userId = (int)$params['id']; -$userService = $repository->getUserService(); -$user = $userService->loadUser( $userId ); - -// Now check that current user has access to read this user -if ( !$repository->canUser( 'content', 'read', $user ) ) -{ - // Generates message: User does not have access to 'content' 'read' with id '10' - throw new \eZ\Publish\Core\Base\Exceptions\UnauthorizedException( 'content', 'read', [ 'id' => $userId ] ); -} -``` - -### Command line - -Output must always be escaped when displaying data from the database. - -## Data and databases - -- Values coming from variables should always be appropriately quoted or binded in SQL statements -- The SQL statements used should never be created by hand with one version per supported database, as this increases both the maintenance load and the chances for security-related problems -- Usage of temporary tables is discouraged, as their behavior is very different on different databases. Subselects should be preferred (esp. since recent mysql versions have much better support for them) -- Full table locking is discouraged - -### Sessions - -- Business logic should not depend on database connections being either persistent or not persistent -- The connection to the database should always be opened as late as possible during page execution. Ideally, to improve scalability, a web page executing no queries should not connect to the database at all (note that closing the database connection as soon as possible is a tricky problem, as we expect to support persistent database connections as well for absolute best performances) -- The same principle applies to configurations where a master/slave database setup is in use: the chance for a failure due to a database malfunction should not increase with the number of database servers at play, but actually decrease -- It is recommended to avoid as much as possible statements which alter the current session, as they slow down the application, are brittle and hard to debug. - Point in case; if a database session locks a table then is abruptly terminated, the table might stay locked for a long time - -### Transactions - -- Transactions should always be used to wrap sql statements which affect data in multiple tables: either all data changes go through or none of them -- Transactions are prone to locking issues, so the code executed within a transaction should be limited to the minimum necessary amount (ex. clearing caches should be done after the transaction is committed) -- When using transactions, always consider side effects on external system, such as on-disk storage. E.g. is a transaction relative to creating an image variation is rolled back, the corresponding file should not be left on disk -- Nested transactions are supported in the following way: - - a transaction within another one will not commit when requested, only the outermost transaction will commit - - a transaction within another one will roll back all the way to the start of the outermost transaction when requested - - as a result a transaction shall never be rolled back just as a means of cancelling its work - the side effect might be of cancelling other work which had just been done previously - -### Limitations in the SQL dialect supported - -Striving to support Mysql 5, PostgreSQL xx and Oracle 10, the following limitations apply: - -- Tables, columns and other database objects should not use names longer than 30 chars -- Varchar columns with a definition of *default "" not null* are discouraged -- For SELECTs, offset and limit have to be handled by the php layer, not hardcoded in the SQL -- Never treat a NULL varchar value as semantically different from an empty string value -- The select list of a query cannot contain the same field multiple times -- For GROUP BY statements, all fields in the group by clause should be in the select list as well -- For SELECTs, usage of the AS token is allowed in the select list, but not in the list of tables -- Do not put quotes around numeric values (use proper casting/escaping to avoid SQL injection) -  diff --git a/docs/community_resources/documentation.md b/docs/community_resources/documentation.md deleted file mode 100644 index bf2b535278..0000000000 --- a/docs/community_resources/documentation.md +++ /dev/null @@ -1,76 +0,0 @@ -# Contribute to documentation - -While we are doing our best to make sure our documentation fulfills all your needs, there is always place for improvement. If you'd like to contribute to our docs, you can do the following: - -## How to contribute to documentation - -This documentation is written on GitHub and generated into a static site. It is organized in branches. Each branch is a version of documentation (which in turn corresponds to a version of [[= product_name =]]). - -If you are familiar with the git workflow, you will find it easy to contribute. -Please create a Pull Request for any, even the smallest change you want to suggest. - -### Contributing through the GitHub website - -To quickly contribute a fix to a page, find the correct `*.md` file in the GitHub repository and select "Edit this file". - -Introduce your changes, at the bottom of the page provide a title and a description of what you modified and select "Propose file change". - -This will lead to a screen for creating a Pull Request. Enter a name and description for the pull request and select "Create pull request". - -Your pull request will be reviewed by the team and, when accepted, merged with the rest of the repository. -You will be notified of all activity related to the pull request by email. - -### Contributing through git - -You can also contribute to the documentation by using a regular git workflow. -If you are familiar with it, this should be quick work. - -1. Assuming that you have a GitHub account and a git command line tool installed, -fork the project and clone it into a folder: `git clone XXX .` - -1. Add your own fork as a remote: `git remote add fork
    `. - -1. Checkout the branch you want to contribute to: - -``` -git checkout -git fetch origin -git rebase origin/ -``` - -!!! note "Choosing a branch" - - Always contribute to the **earliest** branch that a change applies to. - For example, if a change concerns versions v1.7 and v.1.13, make your contribution to the `v1.7` branch. - The changes will be merged forward to be included in later versions as well. - -1. Create a new local branch: `git checkout -b `. - -1. Now introduce whatever changes you wish, either modifying existing files, or creating new ones. - -1. Once you are happy with your edits, add your files to the staging area. Use `git add .` to add all changes. - -1. Commit your changes, with a short, clear description of your changes: `git commit -m "Description of commit"`. - -1. Now push your changes to your fork: `git push fork `. - -1. Finally, you can go to the project's page on GitHub and you should see a "Compare and pull request" button. Activate it, write a description and select "Create pull request". If your contribution solves a JIRA issues, start the pull request's name with the issue number. Now you can wait for your changes to be reviewed and merged. - -### Contributing outside git and GitHub - -- **Create a JIRA issue.** You can also report any omissions or inaccuracies you find by creating a JIRA issue. See [Report and follow issues](report_follow_issues.md) on how to do this. Remember to add the "Documentation" component to your issue to make sure we don't lose track of it -- **Visit Slack.** The `\#documentation-contrib` channel on [eZ Community Slack team](http://ez-community-on-slack.herokuapp.com) is the place to drop your comments, suggestions, or proposals for things you'd like to see covered in documentation. (You can use the link to get an auto-invite to Slack) -- **Contact the Doc Team.** If you'd like to add to any part of the documentation, you can also contact the Doc Team directly at - -## Writing guidelines - -- Write in (GitHub-flavored) Markdown -- Try to keep lines no longer than 120 characters. If possible, break lines in logical places, for example at sentence end. -- Use simple language -- Call the user "you" (not "the user", "we", etc.). -Use gender-neutral language: the visitor has *their* account, not *his*, *her*, *his/her*, etc. - -**Do not be discouraged** if you are not a native speaker of English and/or are not sure about your style. -Our team will proofread your contribution and make sure any problems are fixed. Any edits we do are not intended to be a criticism of your work. -We may simply modify the language of your contributions according to our style guide, -to make sure the terminology is consistent throughout the docs, and so on. diff --git a/docs/community_resources/img/phpstorm_plugin_create_project.png b/docs/community_resources/img/phpstorm_plugin_create_project.png new file mode 100644 index 0000000000..dc29f6ed41 Binary files /dev/null and b/docs/community_resources/img/phpstorm_plugin_create_project.png differ diff --git a/docs/community_resources/img/phpstorm_plugin_file_template.png b/docs/community_resources/img/phpstorm_plugin_file_template.png new file mode 100644 index 0000000000..37dee6f286 Binary files /dev/null and b/docs/community_resources/img/phpstorm_plugin_file_template.png differ diff --git a/docs/community_resources/img/phpstorm_plugin_intention.png b/docs/community_resources/img/phpstorm_plugin_intention.png new file mode 100644 index 0000000000..b2308a6dff Binary files /dev/null and b/docs/community_resources/img/phpstorm_plugin_intention.png differ diff --git a/docs/community_resources/img/phpstorm_plugin_query_type_params.png b/docs/community_resources/img/phpstorm_plugin_query_type_params.png new file mode 100644 index 0000000000..9cfcc3e316 Binary files /dev/null and b/docs/community_resources/img/phpstorm_plugin_query_type_params.png differ diff --git a/docs/community_resources/img/phpstorm_plugin_settings.png b/docs/community_resources/img/phpstorm_plugin_settings.png new file mode 100644 index 0000000000..61604e745c Binary files /dev/null and b/docs/community_resources/img/phpstorm_plugin_settings.png differ diff --git a/docs/community_resources/installing-on-mac-os-and-windows.md b/docs/community_resources/installing-on-mac-os-and-windows.md deleted file mode 100644 index 34271c4f79..0000000000 --- a/docs/community_resources/installing-on-mac-os-and-windows.md +++ /dev/null @@ -1,185 +0,0 @@ -# Install [[= product_name =]] on macOS or Windows - -This page explains how to install [[= product_name =]] on macOS or Windows. - -!!! caution - - This procedure is **for development purposes only**. - Installing [[= product_name =]] for production purposes is supported only on Linux. - - If you want to use [[= product_name =]] in the production environment, see [Installing [[= product_name =]]](../getting_started/install_ez_platform.md). - -### Prepare work environment - -To install [[= product_name =]], you need a stack with MySQL and PHP. -Additionally, you need [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/lang/en/docs/install/) for asset management. -If you want to use a web server, you need to install it as well: - -- For Windows: Apache -- For macOS: Apache/nginx - -The instructions below assumes you are using Apache. - -??? "Windows" - - Locate the `php.ini` file and open it in a text editor. - Provide missing values to relevant parameters, e.g. `date.timezone` and `memory_limit`: - - ``` bash - date.timezone = "Europe/Warsaw" - memory_limit = 4G - ``` - - Uncomment or add extensions relevant to your project, e.g. `opcache` extension for PHP (recommended, not required): - - ``` bash - zend_extension=opcache.so - ``` - - Edit Apache configuration file `httpd.conf`. - Replace placeholder values with corresponding values from your project, e.g. `ServerName localhost:80`. - Uncomment relevant modules, e.g.: - - ``` bash - LoadModule rewrite_module modules/mod_rewrite.so - LoadModule vhost_alias_module libexec/apache2/mod_vhost_alias.so - ``` - - Start Apache by running: `httpd.exe`. - - !!! note - - You can install Apache as a Windows service by running the following command in CMD as administrator: - - ``` bash - httpd.exe -k -install - ``` - - You can then start it with: - - ``` bash - httpd.exe -k start - ``` - -## Get Composer - -??? "macOS" - - Install Composer using a package manager, for example [Homebrew.](https://brew.sh/) - -??? "Windows" - - Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe) - it will install the latest Composer version. - -## Download [[= product_name =]] - -Download and extract an archive into the location where you want your project root directory to be from [ezplatform.com](https://ezplatform.com/#download-option) (for open-source version) or from the [Support portal](https://support.ez.no/Downloads) (for [[= product_name_exp =]]), or clone the [GitHub repository](https://github.com/ezsystems/ezplatform): - -``` bash -git clone https://github.com/ezsystems/ezplatform . -``` - -!!! tip - - You can use any other folder name for your project in place of `ezplatform`. - Set its location as your project root directory in your virtual host configuration. - -To install Composer dependencies, from the folder into which you downloaded [[= product_name =]], run: - -``` bash -composer install -``` - -## Change installation parameters - -At this point you can configure your database via the `DATABASE_URL` in the `.env` file: -`DATABASE_URL=mysql://user:password@host:port/name`. - -Choose a [secret](http://symfony.com/doc/5.0/reference/configuration/framework.html#secret) -and provide it in the `APP_SECRET` parameter in the `.env` file. -The secret should be a random string, made up of at least 32 characters, numbers, and symbols. -This is used by Symfony for generating [CSRF tokens](https://symfony.com/doc/5.0/security/csrf.html), -[encrypting cookies](http://symfony.com/doc/5.0/cookbook/security/remember_me.html), -and creating signed URIs when using [ESI (Edge Side Includes)](https://symfony.com/doc/5.0/http_cache/esi.html). - -Alternatively, you can also change individual installation parameters in `.env`. - -!!! caution - - The app secret is crucial to the security of your installation. Be careful about how you generate it, and how you store it. - Here's one way to generate a 64 characters long, secure random string as your secret, in PHP: - - ``` php - print bin2hex(random_bytes(32)); - ``` - - Do not commit the secret to version control systems, or share it with anyone who does not strictly need it. - If you have any suspicion that the secret may have been exposed, replace it with a new one. - The same goes for other secrets, like database password, Varnish invalidate token, JWT passphrase, etc. - - It is recommended to store the database credentials in your `.env.local` file and not commit it to the Version Control System. - -The [configuration](https://symfony.com/doc/current/reference/configuration/doctrine.html#doctrine-dbal-configuration) requires providing the following parameters: - -- `DATABASE_USER` -- `DATABASE_PASSWORD` -- `DATABASE_NAME` -- `DATABASE_HOST` -- `DATABASE_PORT` -- `DATABASE_PLATFORM` — prefix for distinguishing the database you are connecting to (e.g. `mysql` or `pgsql`) -- `DATABASE_DRIVER` — driver used by Doctrine to connect to the database (e.g. `pdo_mysql` or `pdo_pgsql`) -- `DATABASE_VERSION` - database server version (for a MariaDB database, prefix the value with `mariadb-`) - -!!! tip "Using PostgreSQL" - - If you want an installation with PostgreSQL instead of MySQL, see [Using PostgreSQL](../guide/databases.md#using-postgresql). - -## Create database - -!!! tip - - You can omit this step. If you do not create a database now, it will be created automatically in the next step. - -To manually create a database, ensure that you [changed the installation parameters](#change-installation-parameters), then run the following Symfony command: - -``` bash -php ./bin/console doctrine:database:create -``` - -## Install [[= product_name =]] - -Before executing the following command, ensure that the user set during `composer install` has sufficient permissions. - -Install [[= product_name =]] by running: - -``` bash -composer ezplatform-install -``` - -!!! note - - Setting up folder permissions and virtual host is installation-specific. - Make sure to adapt the instructions below to your specific configuration. - -## Set up virtual host - -To set up virtual host, use the template provided with [[= product_name =]]: `/doc/apache2/vhost.template`. - -Copy the virtual host template under the name `.conf` into your Apache directory: - -- For Windows: `\conf\vhosts` -- For macOS: `/private/etc/apache2/users/` - -Modify `.conf` to fit it to your installation. Then restart the Apache server. - -## Set up permissions - -Directories `var` and `web/var` need to be writable by CLI and web server user. -Future files and directories created by these two users will need to inherit those permissions. - -For more information, see [Setting up or Fixing File Permissions.](http://symfony.com/doc/5.0/setup/file_permissions.html) - -!!! note "Security checklist" - - See the [Security checklist](../guide/security_checklist.md) for a list of security-related issues - that you should take care of before going live with a project. diff --git a/docs/community_resources/new_in_doc.md b/docs/community_resources/new_in_doc.md new file mode 100644 index 0000000000..375bd6d92e --- /dev/null +++ b/docs/community_resources/new_in_doc.md @@ -0,0 +1,292 @@ +--- +description: Overview of major recent additions to Ibexa DXP documentation. +--- + +# New in documentation + +This page contains recent highlights and notable changes in [[= product_name =]] documentation. + +## September 2023 + +### Commerce + +- Cart + - [Merge carts API](https://doc.ibexa.co/en/master/commerce/cart/cart_api/#merge-carts) +- Checkout + - [Reorder](https://doc.ibexa.co/en/master/commerce/checkout/reorder/) + - [Hide checkout step](https://doc.ibexa.co/en/master/commerce/checkout/customize_checkout/#hide-checkout-step) +- Order management + - [Define cancel order](https://doc.ibexa.co/en/master/commerce/order_management/configure_order_management/#define-cancel-order) + +### Personalization + +- [Updated configuration for triggers](https://doc.ibexa.co/en/master/personalization/api_reference/tracking_api/#tracking-events-based-on-recommendations) +- [Send messages with recommendations](https://doc.ibexa.co/en/master/personalization/integrate_recommendation_service/#send-messages-with-recommendations) +- [Email triggers](https://doc.ibexa.co/projects/userguide/en/master/personalization/triggers/) in user documentation + +### PIM + +- [Product availability Twig extension](https://doc.ibexa.co/en/master/templating/twig_function_reference/product_twig_functions/#ibexa_has_product_availability) +- [PriceQuery with its criteria](https://doc.ibexa.co/en/master/search/criteria_reference/price_search_criteria/) + - [Price API](https://doc.ibexa.co/en/master/pim/price_api/#prices) + +### REST API + +- Added GET endpoint for all available [Sales Representatives Users](https://doc.ibexa.co/en/master/api/rest_api/rest_api_reference/rest_api_reference.html#corporate-account-read-list-of-sales-representatives) + +### Storefront + +- [Display language name instead of its code in language swticher](https://doc.ibexa.co/en/master/templating/twig_function_reference/storefront_twig_functions/#ibexa_storefront_get_language_name_by_code) + +### Templating + +- [Render content in PHP](https://doc.ibexa.co/en/master/templating/render_content/render_content_in_php/) + +### Others + +- Product guides integrated into developer documentation + - [Content management](https://doc.ibexa.co/en/master/content_management/content_management_guide/) + - [Customer portal](https://doc.ibexa.co/en/master/customer_management/customer_portal/) + - [Form Builder](https://doc.ibexa.co/en/master/content_management/forms/form_builder_guide/) + - [Online editor](https://doc.ibexa.co/en/master/content_management/rich_text/online_editor_guide/) + - [Personalization](https://doc.ibexa.co/en/master/personalization/personalization_brochure/) + - [PIM](https://doc.ibexa.co/en/master/pim/pim_guide/) + +- [Updated bundles list](https://doc.ibexa.co/en/master/administration/project_organization/bundles/) + +## August 2023 + +### New home page + +- Redesigned [home page for the user documentation](https://doc.ibexa.co/projects/userguide/en/latest/) + +### Administration + +- [Install [[= product_name =]] with DDEV](https://doc.ibexa.co/en/master/getting_started/install_with_ddev/) +- [Update from v3.3.x to v3.3.latest](https://doc.ibexa.co/en/master/update_and_migration/from_3.3/update_from_3.3/) + +### Commerce + +- [Importing data](https://doc.ibexa.co/en/master/content_management/data_migration/importing_data/#commerce) +- Cart + - [Quick order](https://doc.ibexa.co/en/master/commerce/cart/quick_order/) +- Checkout + - [Create custom strategy](https://doc.ibexa.co/en/master/commerce/checkout/customize_checkout/#create-custom-strategy) +- Payments + - [Implement payment method filtering](https://doc.ibexa.co/en/master/commerce/payment/payment_method_filtering/) + - [Filter payment methods](https://doc.ibexa.co/projects/userguide/en/master/commerce/payment/work_with_payment_methods/#filter-payment-methods) +- Shipping + - [Extend shipping](https://doc.ibexa.co/en/master/commerce/shipping_management/extend_shipping/) + - [Filter shipping methods](https://doc.ibexa.co/projects/userguide/en/master/commerce/shipping_management/work_with_shipping_methods/#filter-shipping-methods) + +### Online Editor + +- [Add CKEditor plugins](https://doc.ibexa.co/en/master/content_management/rich_text/extend_online_editor/#add-ckeditor-plugins) + +### PIM + +- [Custom name schema strategy](https://doc.ibexa.co/en/master/pim/create_custom_name_schema_strategy/) +- [IsVirtual Search Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/isvirtual_criterion/) + +### Security + +- [Hidden state clarification](https://doc.ibexa.co/en/master/infrastructure_and_maintenance/security/security_checklist/#do-not-use-hide-for-read-access-restriction) +- [Add timeouts information](https://doc.ibexa.co/en/master/infrastructure_and_maintenance/security/security_checklist/#protect-against-brute-force-attacks) + +## July 2023 + +### v4.5.1 + +- [v4.5.1 release notes](https://doc.ibexa.co/en/master/release_notes/ibexa_dxp_v4.5/#v451) + +### New home page + +- Redesigned [home page for the developer documentation](https://doc.ibexa.co/en/latest/) + +### Getting started + +- New cautions in [Install on Ibexa Cloud](https://doc.ibexa.co/en/master/getting_started/install_on_ibexa_cloud/) about using `cloud.ibexa.co` instead of `platform.sh` + +### Content management + +- New Page block [Ibexa Connect scenario block](https://doc.ibexa.co/en/master/content_management/pages/ibexa_connect_scenario_block/) +- Updated [Create custom Page blocks](https://doc.ibexa.co/en/master/content_management/pages/create_custom_page_block/#add-block-javascript) + +### Customer Portal + +- Updated [Creating a Customer Portal](https://doc.ibexa.co/en/master/customer_management/cp_page_builder/) + +### Personalization + +- [Multiple attributes in submodel computation](https://doc.ibexa.co/en/master/personalization/api_reference/recommendation_api/#submodel-parameters) +- [Multiple attributes in submodel computation](https://doc.ibexa.co/projects/userguide/en/master/personalization/recommendation_models/#submodels) in user documentation + +### PIM + +- Updated [Enable purchasing products](https://doc.ibexa.co/en/master/pim/enable_purchasing_products/#region-and-currency) +- [Virtual products](https://doc.ibexa.co/en/master/pim/products/#product-types) +- [Virtual products in user documentation](https://doc.ibexa.co/projects/userguide/en/master/pim/create_virtual_product/) +- [Work with product attributes](https://doc.ibexa.co/projects/userguide/en/master/pim/work_with_product_attributes/) in user documentation + +### REST API +- Added example of input payload in JSON format for [ContentTypeCreate in REST API reference](https://doc.ibexa.co/en/master/api/rest_api/rest_api_reference/rest_api_reference.html#managing-content-create-content-type) +- [Expected user](https://doc.ibexa.co/en/master/api/rest_api/rest_api_usage/rest_requests/#expected-user) header support + +### Commerce + +- [Virtual products in checkout](https://doc.ibexa.co/en/master/commerce/checkout/checkout/#virtual-products-checkout) +- New Order and Shipment Search Criteria: + - [Order Owner Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/order_owner_criterion/) + - [Shipment Owner Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/shipment_owner_criterion/) + +### Search + +- REST API examples in multiple [existing Search Criteria descriptions](https://doc.ibexa.co/en/master/search/search_criteria_and_sort_clauses/) +- New REST API-only Search Criteria: + - Content search: + - [ParentLocationRemoteId Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/parentlocationremoteId_criterion/) + - Product search: + - [AttributeGroupIdentifier Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/attributegroupidentifier_criterion/) + - [AttributeName Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/attributename_criterion/) + - [CatalogIdentifier Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/catalogidentifier_criterion/) + - [CatalogName Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/catalogname_criterion/) + - [CatalogStatus Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/catalogstatus_criterion/) + - [FloatAttributeRange Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/floatattributerange_criterion/) + - [IntegerAttributeRange Criterion](https://doc.ibexa.co/en/master/search/criteria_reference/integerattributerange_criterion/) + +### Infrastructure and maintenance + +- [Configure and customize Fastly](https://doc.ibexa.co/en/master/infrastructure_and_maintenance/cache/http_cache/fastly/) +- Updated Security checklist: + - [Block upload of unwanted file types](https://doc.ibexa.co/en/master/infrastructure_and_maintenance/security/security_checklist/#block-upload-of-unwanted-file-types) + - [Minimise exposure](https://doc.ibexa.co/en/master/infrastructure_and_maintenance/security/security_checklist/#minimize-exposure) + +## June 2023 + +### Personalization + +- [Email triggers](https://doc.ibexa.co/en/master/personalization/integrate_recommendation_service/#send-emails-with-recommendations) +- [Email triggers](https://doc.ibexa.co/projects/userguide/en/master/personalization/triggers/) in user documentation + +### Search + +- [Updated search engines documentation](https://doc.ibexa.co/en/master/search/search_engines/search_engines/): + - [Elasticsearch search engine](https://doc.ibexa.co/en/master/search/search_engines/elastic_search/elastic_search_overview/) + - [Solr search engine](https://doc.ibexa.co/en/master/search/search_engines/solr_search_engine/solr_overview/) + - [Legacy search engine](https://doc.ibexa.co/en/master/search/search_engines/legacy_search_engine/legacy_search_overview/#legacy-search-engine) + +### Commerce + +- [Shipping methods management](https://doc.ibexa.co/projects/userguide/en/master/commerce/shipping_management/work_with_shipping_methods/) in user documentation +- [Payment methods management](https://doc.ibexa.co/projects/userguide/en/master/commerce/payment/work_with_payments/) in user documentation +- Stock Search Criteria and Aggregation: + - [ProductStockRangeAggregation](https://doc.ibexa.co/en/master/search/aggregation_reference/productstockrange_aggregation/) + - [ProductStock](https://doc.ibexa.co/en/master/search/criteria_reference/productstock_criterion/) + - [ProductStockRange](https://doc.ibexa.co/en/master/search/criteria_reference/productstockrange_criterion/) + +## May 2023 + +### v4.5 + +- [v4.5 release notes](https://doc.ibexa.co/en/master/release_notes/ibexa_dxp_v4.5/) and guide on how to [update to v4.5](https://doc.ibexa.co/en/master/update_and_migration/from_4.4/update_from_4.4/) + +### Customer Portal + +- [Corporate account company and member REST API reference](https://doc.ibexa.co/en/master/api/rest_api/rest_api_reference/rest_api_reference.html#corporate-account) +- [Creating a Customer Portal](https://doc.ibexa.co/en/master/customer_management/cp_page_builder/) + +### Commerce + +- [Extending payments](https://doc.ibexa.co/en/master/commerce/payment/extend_payment/) +- Reference for commerce-related events: + - [Cart events](https://doc.ibexa.co/en/master/api/event_reference/cart_events/) + - [Order management events](https://doc.ibexa.co/en/master/api/event_reference/order_management_events/) + - [Payment events](https://doc.ibexa.co/en/master/api/event_reference/payment_events/) + +## April 2023 + +### Payment + +- [Payment management](https://doc.ibexa.co/en/master/commerce/payment/payment/), +including [configuring payment workflow](https://doc.ibexa.co/en/master/commerce/payment/configure_payment/), +as well as [payment](https://doc.ibexa.co/en/master/commerce/payment/payment_api/) +and [payment method PHP API](https://doc.ibexa.co/en/master/commerce/payment/payment_method_api/). + +### Orders + +- [Order management](https://doc.ibexa.co/en/master/commerce/order_management/order_management/), +including [configuring order workflow](https://doc.ibexa.co/en/master/commerce/order_management/configure_order_management/) +and [Orders REST API reference](https://doc.ibexa.co/en/master/api/rest_api/rest_api_reference/rest_api_reference.html#orders). + +### Shipping + +- [Shipping management](https://doc.ibexa.co/en/master/commerce/shipping_management/shipping_management/), +including [configuring shipment workflow](https://doc.ibexa.co/en/master/commerce/shipping_management/configure_shipment/), +as well as [shipment](https://doc.ibexa.co/en/master/commerce/shipping_management/shipment_api/) +and [shipping method PHP API](https://doc.ibexa.co/en/master/commerce/shipping_management/shipping_method_api/). + +### Search + +- Search Criteria and Sort Clauses covering the new commerce features: + - Order [Search Criteria](https://doc.ibexa.co/en/master/search/criteria_reference/order_search_criteria/) + and [Sort Clauses](https://doc.ibexa.co/en/master/search/sort_clause_reference/order_sort_clauses/) + - Payment [Search Criteria](https://doc.ibexa.co/en/master/search/criteria_reference/payment_search_criteria/) + and [Sort Clauses](https://doc.ibexa.co/en/master/search/sort_clause_reference/payment_sort_clauses/) + - Payment method [Search Criteria](https://doc.ibexa.co/en/master/search/criteria_reference/payment_method_search_criteria/) + and [Sort Clauses](https://doc.ibexa.co/en/master/search/sort_clause_reference/payment_method_sort_clauses/) + - Shipment [Search Criteria](https://doc.ibexa.co/en/master/search/criteria_reference/shipment_search_criteria/) + and [Sort Clauses](https://doc.ibexa.co/en/master/search/sort_clause_reference/shipment_sort_clauses/) + +### New Page blocks + +- [React app Page block](https://doc.ibexa.co/en/master/content_management/pages/react_app_block/) +- [Bestsellers block](https://doc.ibexa.co/projects/userguide/en/master/content_management/block_reference/#bestsellers-block) + +### Others + +- [Translation comparison](https://doc.ibexa.co/projects/userguide/en/master/content_management/translate_content/#translation-comparison) +- [Managing Segments](https://doc.ibexa.co/projects/userguide/en/master/personalization/segment_management/) + +## March 2023 + +- [Order management API](https://doc.ibexa.co/en/master/commerce/order_management/order_management_api/) +- [Customizing checkout](https://doc.ibexa.co/en/master/commerce/checkout/customize_checkout/) +- Extended [table reusable component documentation](https://doc.ibexa.co/en/master/administration/back_office/back_office_elements/reusable_components/#tables) +- How to [add GraphQL support to custom Field Types](https://doc.ibexa.co/en/master/api/graphql/graphql_custom_ft/) +- How to [customize Field Type metadata](https://doc.ibexa.co/en/master/content_management/field_types/customize_field_type_metadata/) + +## February 2023 + +### Storefront + +- [Storefront](https://doc.ibexa.co/en/master/commerce/storefront/storefront/) documentation, +including how to [configure](https://doc.ibexa.co/en/master/commerce/storefront/configure_storefront/) +and [extend Storefront](https://doc.ibexa.co/en/master/commerce/storefront/extend_storefront/). + +### Cart + +- [Cart](https://doc.ibexa.co/en/master/commerce/cart/cart/) documentation, including +[PHP API](https://doc.ibexa.co/en/master/commerce/cart/cart_api/). + +### Checkout + +- [Checkout](https://doc.ibexa.co/en/master/commerce/checkout/checkout/) documentation, +including how to [configure checkout](https://doc.ibexa.co/en/master/commerce/checkout/configure_checkout/). +Description of main [PHP API methods](https://doc.ibexa.co/en/master/commerce/checkout/checkout_api/) +as well as [checkout-related Twig functions](https://doc.ibexa.co/en/master/templating/twig_function_reference/checkout_twig_functions/). + +### Other + +- How to [create a Form Builder Form attribute](https://doc.ibexa.co/en/master/content_management/forms/create_form_attribute/) +- [Update guide for v4.4](https://doc.ibexa.co/en/master/update_and_migration/from_4.3/update_from_4.3/) + +## January 2023 + +### Page Builder + +- Description of new Page Builder blocks: [Catalog](https://doc.ibexa.co/projects/userguide/en/master/content_management/block_reference/#catalog-block) and [Product collection](https://doc.ibexa.co/projects/userguide/en/master/content_management/block_reference/#product-collection-block). + +### Other + +- [Fastly Image Optimizer](https://doc.ibexa.co/en/master/content_management/images/fastly_io/) +- [Storing Field Type settings externally](https://doc.ibexa.co/en/master/content_management/field_types/field_type_storage/#storing-field-type-settings-externally) diff --git a/docs/community_resources/package_structure.md b/docs/community_resources/package_structure.md index cd4f21c2be..72eb8f64cf 100644 --- a/docs/community_resources/package_structure.md +++ b/docs/community_resources/package_structure.md @@ -1,5 +1,12 @@ +--- +description: All code contributions to Ibexa DXP must follow package and bundle structure and namespace standards. +--- + # Package and bundle structure and namespaces +If you wish to contribute to [[= product_name =]] development, +you need to adhere to the package and bundle structure and namespace standards. + The following conventions apply to contributions to Ibexa core code, not to third party packages. !!! note diff --git a/docs/community_resources/phpstorm_plugin.md b/docs/community_resources/phpstorm_plugin.md new file mode 100644 index 0000000000..eb29e0763d --- /dev/null +++ b/docs/community_resources/phpstorm_plugin.md @@ -0,0 +1,252 @@ +--- +description: The Ibexa DXP PHPStorm plugin helps you speed up your development by providing file templates, autocompletion, a quick installation wizard, and more. +--- + +# Ibexa DXP plugin for PhpStorm + +Ibexa DXP plugin for PhpStorm helps you to work with Ibexa DXP by speeding up installation +and providing file templates, intentions, autocompletion, and other features. + +## Requirements + +- PhpStorm 2021.2 or newer +- Enabled Symfony support plugin + +## Install PhpStorm plugin + +You can install the Ibexa DXP plugin for PhpStorm from the JetBrains Marketplace, +or manually, from a downloaded .jar file. + +### Install from JetBrains Marketplace + +To install plugin from JetBrains marketplace: + +Look for "Ibexa DXP" in the plugin browser and click **Install**. + +### Install from file + +You can also install the plugin manually from a `.jar` file: + +1\. Download the latest version of the plugin from [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/17239-ibexa-dxp/versions). + +2\. In PhpStorm settings/preferences (depending on your system), select **Plugins** > (gear icon) > **Install plugin from Disk...** +and select the downloaded file. + +## Configuration + +Plugin configuration is available in PhpStorm settings/preferences (depending on your system), +under **PHP** > **Frameworks** > **Ibexa DXP**. + +You can use it to: + +- Enable and disable plugin features for the current project +- Change product edition and version by the current project + +![Intention](img/phpstorm_plugin_settings.png) + +!!! note + + Some plugin features depends on the selected product edition and version. + For example, "deprecated namespaces usage" inspection is enabled only if the project uses v4.x. + +Plugin configuration is automatically resolved when opening Ibexa DXP project for the first time. +If detection is successful, a notification appears with an "Enable Ibexa DXP support for this project" link. + +If you created your project by using Ibexa DXP project wizard, the plugin is automatically enabled and configured based +on wizard data. + +## Features + +### Project wizard + +The plugin enables creating a new Ibexa DXP project directly from PhpStorm. +To do it, select **File** > **New Project...** > **Ibexa DXP**. + +In project settings form you can choose: + +- Location of the project +- Product edition: Ibexa OSS, Ibexa Content, Ibexa Experience, Ibexa Commerce +- Authentication token (for Content, Experience and Commerce editions) +- Product version: Default (latest LTS version), Latest (fast track or LTS), Latest LTS and "Next 3.x" (unstable, based on the 3.x branch) and "Next 4.x" (unstable, based on the 4.x branch) +- Generate [Ibexa Cloud configuration](getting_started/install_on_ibexa_cloud) +- Composer settings + +![Create a project](img/phpstorm_plugin_create_project.png) + +If you do not provide credentials for https://updates.ibexa.co/, the plugin uses the installation key and token password stored in global Composer configuration. Otherwise, it creates an `auth.json` file. + +You can find details of the installation procedure in Composer log window. + +### File templates + +The plugin provides the following built-in file templates: + +| Name | Comment | +|---|---| +| Back Office tab | Class implementing `EzSystems\EzPlatformAdminUi\Tab\AbstractTab` | +| Block event subscriber | Event subscriber for `BlockRenderEvents::getBlockPreRenderEventName(...)` event | +| Command | Symfony command that uses content repository | +| Composite Criterion | Criterion class based on `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\CompositeCriterion` | +| Field definition form mapper | Class implementing `EzSystems\EzPlatformAdminUi\FieldType\FieldDefinitionFormMapperInterface` | +| Field Type | Field Type class based on `eZ\Publish\SPI\FieldType\Generic\Type` | +| Field Type Comparable | Class implementing `EzSystems\EzPlatformVersionComparison\FieldType\Comparable` | +| Field Type Indexable | Class implementing `eZ\Publish\SPI\FieldType\Indexable` | +| Field value form mapper | Class implementing `EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface` | +| Field value object | Field Type value class | +| Menu configuration event subscriber | Event subscriber for `EzSystems\EzPlatformAdminUi\Menu\Event\ConfigureMenuEvent::MAIN_MENU` | +| Policy provider | Class implementing `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface` | +| Policy provider (YAML) | Policy provider class based on `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider` | +| Query Type | Query Type class based on `eZ\Publish\Core\QueryType\OptionsResolverBasedQueryType` | +| Schema builder subscriber | Event subscriber for `EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvent::BUILD_SCHEMA` event | +| SiteAccess-aware configuration | SiteAccess-aware configuration definition class based on `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser` | +| Value object input parser | REST input parser class based on `EzSystems\EzPlatformRest\Input\BaseParser` | +| Value object visitor | REST value visitor class based on `EzSystems\EzPlatformRest\Output\ValueObjectVisitor` | +| Workflow action listener | Workflow action listener class based on `EzSystems\EzPlatformWorkflow\Event\Action\AbstractTransitionWorkflowActionListener` | + +The templates are available in, for example, the context menu in **Project window** > **New** > **Ibexa DXP**. + +The list of available file templates depends on the Ibexa DXP edition used by the project. + +For all file templates you can customize: + +- class name +- class namespace +- file name +- directory + +![File template](img/phpstorm_plugin_file_template.png) + +To customize file templates, go to **File** > **Settings**/**Preferences** > **Editor** > **File and Code templates**. + +!!! tip + + For more information about file templates, see [JetBrains documentation](https://www.jetbrains.com/help/phpstorm/settings-file-and-code-templates.html). + +### Live templates + +The plugin provides the following built-in live templates in Twig files: + +| Abbreviation | Comment | +|---|---| +| `ezcn` | `ez_content_name` | +| `ezfd` | `ez_field_description` | +| `ezfd?` | `ez_field_description` wrapped in an `ez_field_is_empty` check | +| `ezfn` | `ez_field_name` | +| `ezfn?` | `ez_field_name` wrapped in an `ez_field_is_empty` check | +| `ezrc` | `ez_render_content` | +| `ezrcq` | `ez_render_content_query` | +| `ezrf` | `ez_render_field` | +| `ezrf?` | `ez_render_field` wrapped in an `ez_field_is_empty` check | +| `ezrl` | `ez_render_location` | +| `ezrlq` | `ez_render_location_query` | + +and in PHP files: + +| Abbreviation | Comment | +|---|---| +| `ibx_create_c` | Create content | +| `ibx_create_cd` | Create content draft | +| `ibx_create_ct` | Create content type | +| `ibx_find_c` | Create and execute content query | +| `ibx_find_ci` | Create and execute content info query | +| `ibx_find_l` | Create and execute location query | +| `ibx_load_c` | Load content by ID | +| `ibx_load_ci` | Load content info by ID | +| `ibx_load_ct` | Load content type by identifier | +| `ibx_load_l` | Load location by ID | +| `ibx_param` | Get SiteAccess parameter value | +| `ibx_pub` | Publish content draft | +| `ibx_switch_user` | Switch user context | +| `ibx_trans` | Repository transaction | +| `ibx_update_c` | Update content | +| `ibx_update_ct` | Update content type | + +To customize live templates, go to **File** > **Settings**/**Preferences** > **Editor** > **Live Templates**. + +!!! tip + + For more information about live templates, see [JetBrains documentation](https://www.jetbrains.com/help/idea/using-live-templates.html). + +### Autocompletion in configuration files + +Plugin provides autocompletion for Ibexa DXP configuration structure in YAML files placed in `config/packages/`. + +Besides configuration structure, for the following YAML keys addition suggestions are available: + +- List of available view matchers, for: + - `ezplatform..content_view...match` + - `ezplatform..content_create_view...match` + - `ezplatform..content_edit_view...match` + - `ezplatform..content_translate_view...match` +- List of available SiteAccess matchers, for: + - `ezplatform.siteaccess.match` +- List of available block attribute types, for: + - `ezplatform_page_fieldtype.blocks..attributes..type` +- List of available configuration scopes, for: + - `ezplatform` +- List of available siteaccess names, for: + - `ezplatform.siteaccess.default_siteaccess` + - `ezplatform.siteaccess.groups` + - `ezplatform.system..translation_siteaccesses$` +- List of available design names, for: + - `ezdesign.design_list` + - `ezplatform.system..design` +- List of available repositories, for: + - `ezplatform.system..repository` +- List of available search engines, for: + - `ezplatform.repositories..search.engine` +- List of available custom tags, for: + - `ezplatform.system..fieldtypes.ezrichtext.custom_tags` +- List of available view types, for: + - `ezplatform..content_view` + - `ezplatform..content_create_view` + - `ezplatform..content_edit_view` + - `ezplatform..content_translate_view` + +### Structure autocompletion in DBAL schema file + +Autocompletion is also available for DBAL schema file structure. + +To enable autocompletion, you must place the file in the `config` directory and name it `schema.yaml`. + +### Dynamic settings autocompletion + +Parameter names suggestions are available in `\eZ\Publish\Core\MVC\ConfigResolverInterface::{hasParameter,getParameter}` method calls. + +Suggested results take into account namespace argument, if its value can be resolved without running interpreter +(for example, string literal or const reference). + +### Query type name autocompletion + +Query type name suggestions are available in `\eZ\Publish\Core\QueryType\QueryTypeRegistry::getQueryType` method calls. + +Suggestions are based on service definitions tagged as `ezplatform.query_type`. + +### Query type parameter autocompletion + +Parameter name suggestions are available for Query types which implement the `eZ\Publish\Core\QueryType\QueryType` interface +or extend the `eZ\Publish\Core\QueryType\OptionsResolverBasedQueryType` class in the following places: + +* `\eZ\Publish\Core\QueryType\QueryType::getQuery` method calls +* `\eZ\Publish\Core\QueryType\QueryType::getQuery` method definition +* `\eZ\Publish\Core\QueryType\OptionsResolverBasedQueryType::doGetQuery` method definition + +![Query Type parameter autocompletion](img/phpstorm_plugin_query_type_params.png) + +### Intentions and inspections + +The plugin also brings several new intentions and inspections (with related quick fixes where possible). + +For example, when plugin detects deprecated configuration key usage, it marks the key as deprecated and suggests a replacement: + +![Intention](img/phpstorm_plugin_intention.png) + +## Known issues + +It is not possible to create new project with Docker as PHP remote interpreter. +See [related JetBrains issue](https://youtrack.jetbrains.com/issue/WI-61330) for more details. + +## Feedback + +You can report bugs and feature suggestions on [https://issues.ibexa.co/](https://issues.ibexa.co/issues/?jql=project%20%3D%20IBX%20AND%20component%20%3D%20%22PHPStorm%20plugin%22) by +selecting the "PHPStorm plugin" component, or on the `#phpstorm-plugin` Ibexa Community Slack channel. diff --git a/docs/community_resources/release_process.md b/docs/community_resources/release_process.md index 13992d96b8..8c41308ecd 100644 --- a/docs/community_resources/release_process.md +++ b/docs/community_resources/release_process.md @@ -1,62 +1,47 @@ +--- +description: "Ibexa DXP releases new versions periodically in different flavors: Ibexa Content, Ibexa Experience and Ibexa Commerce, plus open-source Ibexa OSS." +--- + # [[= product_name =]] release process and roadmap ## Release process [[= product_name =]] has three distributions: -- [[= product_name =]] is an open source Content Management System (CMS) developed by Ibexa together with the open source community. [[= product_name =]]'s code base is available on GitHub under the GPLv2 license. [[= product_name =]] comes with no commercial support and maintenance services. It is supported by the community on public channels. -- [[= product_name_exp =]] is commercial software available under eZ Business User License (BUL) to [[= product_name_exp =]] subscribers. It is comprised of [[= product_name =]], additional enterprise support and maintenance services, as well as additional features which are not available in the open source software. +- [[= product_name_content =]] is a multichannel and headless content management system. +- [[= product_name_exp =]] is a modern modular Digital Experience Platform to build outstanding customer experiences +- [[= product_name_com =]] is a commerce-ready B2B DXP designed to digitalize your business from customer awareness to purchase and beyond. + +Additionally, [[= product_name =]] also has an open-source version called Ibexa OSS. +Ibexa OSS is developed by Ibexa together with the open source community. +The Ibexa OSS code is available on GitHub under the GPLv2 license. +It comes with no commercial support and maintenance services. We manage the release of [[= product_name =]] using an agile iterative process and a continuous software development model, which is why we provide two kinds of [[= product_name =]] releases: -- Long Term Support releases (LTS) which are supported by Ibexa for a long period of time (see [support lifecycle below](#support-lifecycle)), for [[= product_name_exp =]] subscribers. -- Fast Track releases (FT) give access to the latest features and are supported for a short period of time. They are maintained only until the next FT release is introduced. These are supported for both the open source community and [[= product_name_exp =]] subscribers. +- Long Term Support releases (LTS) which are supported by Ibexa for a long period of time. +- Fast Track releases (FT) give access to the latest features and are supported for a short period of time. They are maintained only until the next FT release is introduced. FT releases are tailored for those who want to stay up-to-date with newest functionalities, while LTS releases are suitable for highly stable enterprise rollouts. -We usually release [[= product_name =]] four times a year following the seasons (winter, spring, summer and fall). This usually includes one LTS release and three FT releases. - ## Versioning conventions -Both [[= product_name =]] editions use [semantic versioning](http://semver.org/). +All [[= product_name =]] editions use [semantic versioning](http://semver.org/). The version number of [[= product_name =]] and all its internal components follows the semantic versioning conventions: vX.Y.Z. -- Changes to X indicate breaking changes. They usually concern mostly internal things, but developers should check in our change logs if they need to adjust their code to continue using the API or features. If there are larger breaks (like the new Back Office in v2), this is announced well in advance of the upcoming release. +- Changes to X indicate breaking changes. They usually concern mostly internal things, but developers should check in our change logs if they need to adjust their code to continue using the API or features. If there are larger breaks, this is announced well in advance of the upcoming release. - Y represents new features and functionalities. - Z represents patches, bug fixes, smaller improvements, etc. -Distribution files of our two editions are as follows: - -- for [[= product_name =]]: ezplatform-vX.Y.Z.tgz -- for [[= product_name_exp =]]: ezplatformenterprise-vX.Y.Z.tgz - -## Support lifecycle +Distribution files of our three editions are as follows: -Our software products are continuously evolving. -With each release we strive to release stable products with cutting-edge technology. -This means there is need for software maintenance services to provide bug fixes and adjustments. -As our products constantly provide new features and possibilities, our documentation and user forums may not always be able to provide an answer to all questions that may arise. +- for [[= product_name_content =]]: ibexa-content-vX.Y.Z.tgz +- for [[= product_name_exp =]]: ibexa-experience-vX.Y.Z.tgz +- for [[= product_name_com =]]: ibexa-commerce-vX.Y.Z.tgz -That is why our support and consulting professional services teams are available to assist -as part of an [[= product_name_exp =]] subscription or as part of a specific statement of work. -[Contact our Sales team](https://ez.no/Forms/Request-a-Consultation) for more information. - -Over time, existing product versions mature and new versions become the center of attention for customers looking for the latest features. -We adapt to this continuous evolution by phasing out services for the old versions while commencing services for the new ones. -This means that our support and maintenance services specific to each release -are only available from a given start date until an end date. +Our support and maintenance services specific to each release are only available from a given start date until an end date. The time in between the start and end dates is what we call the product's **Service Life**. -You can find the specific dates of service life for each release on our [support portal service life page](https://support.ez.no/Public/Service-Life). - -## Roadmap - -[Our roadmap](https://ezplatform.com/product-feedback) is updated continuously following our iterative development methodology (our own adaptation and combination of Scrum and Kanban). -Our agile boards offer a clear view of the ongoing and upcoming development and are open to the public. -Progress is based on the prioritized stories from a living backlog into phases of specification and design, development and documentation, and QA. -The final phase of development includes a dedicated period of Certification and Quality Assurance, -which ensures our ability to deliver a stable first version of the professionally supported software. - -If you want to know more, please contact productmanagement@ez.no +You can find the specific dates of service life for each release on our [support portal service life page](https://support.ibexa.co/Public/Service-Life). diff --git a/docs/community_resources/report_follow_issues.md b/docs/community_resources/report_follow_issues.md index 63631a277f..a05e8fb6ef 100644 --- a/docs/community_resources/report_follow_issues.md +++ b/docs/community_resources/report_follow_issues.md @@ -1,63 +1,39 @@ +--- +description: You can report encountered issues to Ibexa DXP JIRA or use it to follow the development of new features and fixes. +--- + # Report and follow issues -The development of Ibexa projects is organized using a bugtracker. It can be found here: . Its role is to centralize references to all improvements, bug fixes and documentation being added to Ibexa projects. +[[= product_name =]] uses [JIRA](https://issues.ibexa.co) to track product development, improvements, and bugs. + +## Finding an existing issue -The first thing you should do in order to be able to get involved and have feedback on what is happening on Ibexa projects is to create a JIRA account. +Before you create a new bug report or an improvement suggestion, +search the JIRA project for similar reported issues. +If you find any, update them with your comment or additional information instead of creating a new one. -**Note:** The term "issue" is used to refer to a bugtracker item regardless of its type (bug, improvement, story, etc.) +## Reporting an issue !!! caution "Security issues" If you discover a security issue, please do not report it using regular channels, but instead take a look at [Security section](../guide/reporting_issues.md). -## How to find an existing issue - -When you have a great idea or if you have found a bug, you may want to create a new issue to let everyone know about it. Before doing that, you should make sure no one has made a similar report before. - -In order to do that, you should use the search page available in the top menu (under **Issues/Search for issues**) or the search box in the top right corner. Using filters and keywords you should be able to search and maybe find an issue to update instead of creating a new one. - -## How to improve existing issues - -Existing issues need to be monitored, sorted and updated in order to be processed in the best way possible. - -In case of bugs, trying to reproduce them, in order to confirm that they are (still) valid, is a great help for developers who will later troubleshoot and fix them. By doing that you can also provide extra information, if needed, such as: - -- Extra steps to reproduce -- Context/environment-specific information -- Links to duplicate or related issues - -In case of improvements, you can add extra use cases and spot behaviors that might be tricky or misleading. - -## How to follow an issue - -Every issue has a "Start watching this issue" link. It lets you receive notifications each time the issue is updated. - -This way you can get and provide feedback during the issue's life. You are also informed about ongoing development regarding the issue and can try out patches before they are integrated into the product. - -## How to report an issue - -!!! note "Issues in [[= product_name_exp =]] [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]" - - If you have an Enterprise subscription, report your issues through the [support portal](https://support.ez.no) - instead of JIRA. This ensures the issue can be quickly prioritized according to its impact. +If you have an [[= product_name =]] subscription, report your issues through the [support portal](https://support.ibexa.co) instead of JIRA. +This ensures the issue can be quickly prioritized according to its impact. -If you cannot find an issue matching what you are about to report using the search page, you can create a new one. -Click **Create** at the top of the bugtracker window and fill in the form: +If you cannot find an existing ticket matching what your issue, you can create a new one. +Click **Create** at the top of the bugtracker window and fill in the following fields: ||| |------|------| -|**Project**|Select **eZ Publish/Platform** if your issue affects platform as a standalone project, or **eZ Platform Enterprise Edition** if it is needed in order to reproduce the issue.| +|**Project**|Select **Ibexa IBX**.| |**Issue type**|Choose **Bug** or **Improvement** depending on what you are reporting, do not use other issue types (they are for internal use only).| |**Summary**|Write a short sentence describing what you are reporting.| -|**Security level**|Select security if you are reporting a security issue. It will make your issue visible only to you and the core dev team until it is fixed and distributed.| |**Priority**|Select the priority you consider the issue to be. Please try to keep a cool head while selecting it. A 1 pixel alignment bug is not a "blocker".| |**Component/s**|This is important, as it will make your issue appear on the radar (dashboards, filters) of people dealing with various parts of Ibexa projects.| |**Affect version/s**|Add the versions of the application you experienced the issue on.| -|**Fix version/s**|Leave blank.| |**Assignee**|Leave blank, unless you are willing to work on the issue yourself.| |**Reporter**|Leave as is (yourself).| |**Environment**|Enter specific information regarding your environment that could be relevant in the context of the issues.| |**Description**|This is the most important part of the issue report. In case of a bug, it **must** contain explicit steps to reproduce your issue. Anybody should be able to reproduce it at first try. In case of an improvement, it needs to contain use cases and detailed information regarding the expected behavior.| -|**Labels**|Leave blank.| -|**Epic Link**|Leave blank.| -|**Sprint**|Leave blank.| +|**Product**|Select which flavor of [[= product_name =]] the issue concerns.| diff --git a/docs/community_resources/resources.md b/docs/community_resources/resources.md deleted file mode 100644 index 5b249372fb..0000000000 --- a/docs/community_resources/resources.md +++ /dev/null @@ -1,20 +0,0 @@ -# Resources - -### Let's Connect! - -In the course of its development [[= product_name =]] has been deeply rooted in its community. This remains true, with many invaluable contributions by dedicated community members who play a key role in the continuous development of [[= product_name =]].  - -eZ partners, customers, independent developers, designers, and digital content enthusiasts can contribute to important web projects, influence the [eZ roadmap](https://doc.ez.no/display/MAIN/eZ+Platform+Release+Process+and+Roadmap), and ensure the platform stays on top of market trends and organizations' needs. - -This section provides information about the benefits you can draw from the community and about how you can contribute to [[= product_name =]]'s development yourself. - -- ** - The Developer Hub**, - the best place to start for all things [[= product_name =]]! - - Downloads - - Developer Blog -- **[eZ Community Slack team](http://ez-community-on-slack.herokuapp.com/)**, the place to discuss [[= product_name =]] with Community members, developers, and other like-minded individuals. -- **[ibexa.co](http://ibexa.co) - The corporate website for Ibexa**, - here you can find overviews of the products, contact information, and a wealth of additional [resources](http://ez.no/Resources/) such as use cases, webinars and e-books. - - [eZ Blog](http://ez.no/Blog) - Current insights from the eZ world. - - [Partner Portal](http://ez.no/Partner-Portal) - For eZ Business Partners with a knowledge and sales resource repository. - -  - The Legacy (ez Publish) Portal diff --git a/docs/community_resources/support_maintenance_faq.md b/docs/community_resources/support_maintenance_faq.md index c3801f0292..bec952e152 100644 --- a/docs/community_resources/support_maintenance_faq.md +++ b/docs/community_resources/support_maintenance_faq.md @@ -1,4 +1,8 @@ -# Support and Maintenance FAQ +--- +description: See how you can resolve common issues and report a Customer Support ticket. +--- + +# Support and maintenance FAQ This page contains answers to most common questions and tips around support and maintenance, as well as references to important parts of the documentation and tools useful for developers in their daily work. @@ -36,8 +40,8 @@ If you do not have a language defined in the browser, it will be selected based To read more about language managing in [[= product_name =]], see the following doc pages: -- [Back Office languages](../guide/internationalization/#back-office-languages) -- [Multi-language SiteAccesses and corresponding translations](../guide/multi_language_siteaccesses.md) +- [Back Office languages](../guide/back_office_translations.md) +- [Multi-language SiteAccesses and corresponding translations](../guide/multisite/set_up_translation_siteaccess.md) #### How can I apply patches to the installation? @@ -58,7 +62,7 @@ They can be manually removed from `composer.json` now. #### How to clear the cache properly? -Clearing cache is covered by our [documentation](../guide/devops/#cache-clearing), it applies to file and content (HTTP/persistence) cache. +Clearing cache is covered by our [documentation](../guide/devops.md#cache-clearing), it applies to file and content (HTTP/persistence) cache. Useful commands: diff --git a/docs/community_resources/translations.md b/docs/community_resources/translations.md index 498dbe1518..7077cc5449 100644 --- a/docs/community_resources/translations.md +++ b/docs/community_resources/translations.md @@ -1,3 +1,7 @@ +--- +description: You can contribute Back Office translations to Ibexa DXP by using Crowdin. +--- + # Contribute translations If you'd like to see [[= product_name =]] in your language, you can contribute to the translations. @@ -5,7 +9,7 @@ If you'd like to see [[= product_name =]] in your language, you can contribute t [`ezplatform-i18n`](https://github.com/ezsystems/ezplatform-i18n) contains the XLIFF files providing translations. You can use an XLIFF editor of your choice to contribute strings in your language. -## How to translate the interface using Crowdin +## Translating interface using Crowdin If you wish to contribute to an existing translation of Back Office or start a new one you can: - [translate in-context with bookmarks](#using-bookmarks) @@ -38,7 +42,7 @@ To enable in-context translation you need to create Crowdin account and join [[[ Strings in the interface that can be translated will be outlined in red (untranslated), blue (translated) or green (approved). When moving over them, an edit button will show up on the top left corner of the outline. Click on it, and edit the string in the window that shows up. -![In-context translation of Admin UI](img/crowdin_translation.png "In-context translation of Admin UI") +![In-context translation of Back Office](img/crowdin_translation.png "In-context translation of Back Office") #### Troubleshooting diff --git a/docs/css/bootstrap-iso.css b/docs/css/bootstrap-iso.css index 9da6998a71..917c6c88f4 100644 --- a/docs/css/bootstrap-iso.css +++ b/docs/css/bootstrap-iso.css @@ -5693,23 +5693,23 @@ border-color: #fff !important; } .bootstrap-iso .rounded { - border-radius: 0.25rem !important; + border-radius: 2.5px !important; } .bootstrap-iso .rounded-top { border-top-left-radius: .25rem!important; - border-top-right-radius: 0.25rem !important; + border-top-right-radius: 2.5px !important; } .bootstrap-iso .rounded-right { border-top-right-radius: .25rem!important; - border-bottom-right-radius: 0.25rem !important; + border-bottom-right-radius: 2.5px !important; } .bootstrap-iso .rounded-bottom { border-bottom-right-radius: .25rem!important; - border-bottom-left-radius: 0.25rem !important; + border-bottom-left-radius: 2.5px !important; } .bootstrap-iso .rounded-left { border-top-left-radius: .25rem!important; - border-bottom-left-radius: 0.25rem !important; + border-bottom-left-radius: 2.5px !important; } .bootstrap-iso .rounded-circle { border-radius: 50% !important; @@ -6791,99 +6791,99 @@ margin-left: 0 !important; } .bootstrap-iso .m-1 { - margin: 0.25rem !important; + margin: 2.5px !important; } .bootstrap-iso .mt-1, .bootstrap-iso .my-1 { - margin-top: 0.25rem !important; + margin-top: 2.5px !important; } .bootstrap-iso .mr-1, .bootstrap-iso .mx-1 { - margin-right: 0.25rem !important; + margin-right: 2.5px !important; } .bootstrap-iso .mb-1, .bootstrap-iso .my-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 2.5px !important; } .bootstrap-iso .ml-1, .bootstrap-iso .mx-1 { - margin-left: 0.25rem !important; + margin-left: 2.5px !important; } .bootstrap-iso .m-2 { - margin: 0.5rem !important; + margin: 5px !important; } .bootstrap-iso .mt-2, .bootstrap-iso .my-2 { - margin-top: 0.5rem !important; + margin-top: 5px !important; } .bootstrap-iso .mr-2, .bootstrap-iso .mx-2 { - margin-right: 0.5rem !important; + margin-right: 5px !important; } .bootstrap-iso .mb-2, .bootstrap-iso .my-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 5px !important; } .bootstrap-iso .ml-2, .bootstrap-iso .mx-2 { - margin-left: 0.5rem !important; + margin-left: 5px !important; } .bootstrap-iso .m-3 { - margin: 1rem !important; + margin: 10px !important; } .bootstrap-iso .mt-3, .bootstrap-iso .my-3 { - margin-top: 1rem !important; + margin-top: 10px !important; } .bootstrap-iso .mr-3, .bootstrap-iso .mx-3 { - margin-right: 1rem !important; + margin-right: 10px !important; } .bootstrap-iso .mb-3, .bootstrap-iso .my-3 { - margin-bottom: 1rem !important; + margin-bottom: 10px !important; } .bootstrap-iso .ml-3, .bootstrap-iso .mx-3 { - margin-left: 1rem !important; + margin-left: 10px !important; } .bootstrap-iso .m-4 { - margin: 1.5rem !important; + margin: 15px !important; } .bootstrap-iso .mt-4, .bootstrap-iso .my-4 { - margin-top: 1.5rem !important; + margin-top: 15px !important; } .bootstrap-iso .mr-4, .bootstrap-iso .mx-4 { - margin-right: 1.5rem !important; + margin-right: 15px !important; } .bootstrap-iso .mb-4, .bootstrap-iso .my-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 15px !important; } .bootstrap-iso .ml-4, .bootstrap-iso .mx-4 { - margin-left: 1.5rem !important; + margin-left: 15px !important; } .bootstrap-iso .m-5 { - margin: 3rem !important; + margin: 30px !important; } .bootstrap-iso .mt-5, .bootstrap-iso .my-5 { - margin-top: 3rem !important; + margin-top: 30px !important; } .bootstrap-iso .mr-5, .bootstrap-iso .mx-5 { - margin-right: 3rem !important; + margin-right: 30px !important; } .bootstrap-iso .mb-5, .bootstrap-iso .my-5 { - margin-bottom: 3rem !important; + margin-bottom: 30px !important; } .bootstrap-iso .ml-5, .bootstrap-iso .mx-5 { - margin-left: 3rem !important; + margin-left: 30px !important; } .bootstrap-iso .p-0 { padding: 0 !important; @@ -6905,99 +6905,99 @@ padding-left: 0 !important; } .bootstrap-iso .p-1 { - padding: 0.25rem !important; + padding: 2.5px !important; } .bootstrap-iso .pt-1, .bootstrap-iso .py-1 { - padding-top: 0.25rem !important; + padding-top: 2.5px !important; } .bootstrap-iso .pr-1, .bootstrap-iso .px-1 { - padding-right: 0.25rem !important; + padding-right: 2.5px !important; } .bootstrap-iso .pb-1, .bootstrap-iso .py-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 2.5px !important; } .bootstrap-iso .pl-1, .bootstrap-iso .px-1 { - padding-left: 0.25rem !important; + padding-left: 2.5px !important; } .bootstrap-iso .p-2 { - padding: 0.5rem !important; + padding: 5px !important; } .bootstrap-iso .pt-2, .bootstrap-iso .py-2 { - padding-top: 0.5rem !important; + padding-top: 5px !important; } .bootstrap-iso .pr-2, .bootstrap-iso .px-2 { - padding-right: 0.5rem !important; + padding-right: 5px !important; } .bootstrap-iso .pb-2, .bootstrap-iso .py-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 5px !important; } .bootstrap-iso .pl-2, .bootstrap-iso .px-2 { - padding-left: 0.5rem !important; + padding-left: 5px !important; } .bootstrap-iso .p-3 { - padding: 1rem !important; + padding: 10px !important; } .bootstrap-iso .pt-3, .bootstrap-iso .py-3 { - padding-top: 1rem !important; + padding-top: 10px !important; } .bootstrap-iso .pr-3, .bootstrap-iso .px-3 { - padding-right: 1rem !important; + padding-right: 10px !important; } .bootstrap-iso .pb-3, .bootstrap-iso .py-3 { - padding-bottom: 1rem !important; + padding-bottom: 10px !important; } .bootstrap-iso .pl-3, .bootstrap-iso .px-3 { - padding-left: 1rem !important; + padding-left: 10px !important; } .bootstrap-iso .p-4 { - padding: 1.5rem !important; + padding: 15px !important; } .bootstrap-iso .pt-4, .bootstrap-iso .py-4 { - padding-top: 1.5rem !important; + padding-top: 15px !important; } .bootstrap-iso .pr-4, .bootstrap-iso .px-4 { - padding-right: 1.5rem !important; + padding-right: 15px !important; } .bootstrap-iso .pb-4, .bootstrap-iso .py-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 15px !important; } .bootstrap-iso .pl-4, .bootstrap-iso .px-4 { - padding-left: 1.5rem !important; + padding-left: 15px !important; } .bootstrap-iso .p-5 { - padding: 3rem !important; + padding: 30px !important; } .bootstrap-iso .pt-5, .bootstrap-iso .py-5 { - padding-top: 3rem !important; + padding-top: 30px !important; } .bootstrap-iso .pr-5, .bootstrap-iso .px-5 { - padding-right: 3rem !important; + padding-right: 30px !important; } .bootstrap-iso .pb-5, .bootstrap-iso .py-5 { - padding-bottom: 3rem !important; + padding-bottom: 30px !important; } .bootstrap-iso .pl-5, .bootstrap-iso .px-5 { - padding-left: 3rem !important; + padding-left: 30px !important; } .bootstrap-iso .m-auto { margin: auto !important; @@ -7039,99 +7039,99 @@ margin-left: 0 !important; } .bootstrap-iso .m-sm-1 { - margin: 0.25rem !important; + margin: 2.5px !important; } .bootstrap-iso .mt-sm-1, .bootstrap-iso .my-sm-1 { - margin-top: 0.25rem !important; + margin-top: 2.5px !important; } .bootstrap-iso .mr-sm-1, .bootstrap-iso .mx-sm-1 { - margin-right: 0.25rem !important; + margin-right: 2.5px !important; } .bootstrap-iso .mb-sm-1, .bootstrap-iso .my-sm-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 2.5px !important; } .bootstrap-iso .ml-sm-1, .bootstrap-iso .mx-sm-1 { - margin-left: 0.25rem !important; + margin-left: 2.5px !important; } .bootstrap-iso .m-sm-2 { - margin: 0.5rem !important; + margin: 5px !important; } .bootstrap-iso .mt-sm-2, .bootstrap-iso .my-sm-2 { - margin-top: 0.5rem !important; + margin-top: 5px !important; } .bootstrap-iso .mr-sm-2, .bootstrap-iso .mx-sm-2 { - margin-right: 0.5rem !important; + margin-right: 5px !important; } .bootstrap-iso .mb-sm-2, .bootstrap-iso .my-sm-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 5px !important; } .bootstrap-iso .ml-sm-2, .bootstrap-iso .mx-sm-2 { - margin-left: 0.5rem !important; + margin-left: 5px !important; } .bootstrap-iso .m-sm-3 { - margin: 1rem !important; + margin: 10px !important; } .bootstrap-iso .mt-sm-3, .bootstrap-iso .my-sm-3 { - margin-top: 1rem !important; + margin-top: 10px !important; } .bootstrap-iso .mr-sm-3, .bootstrap-iso .mx-sm-3 { - margin-right: 1rem !important; + margin-right: 10px !important; } .bootstrap-iso .mb-sm-3, .bootstrap-iso .my-sm-3 { - margin-bottom: 1rem !important; + margin-bottom: 10px !important; } .bootstrap-iso .ml-sm-3, .bootstrap-iso .mx-sm-3 { - margin-left: 1rem !important; + margin-left: 10px !important; } .bootstrap-iso .m-sm-4 { - margin: 1.5rem !important; + margin: 15px !important; } .bootstrap-iso .mt-sm-4, .bootstrap-iso .my-sm-4 { - margin-top: 1.5rem !important; + margin-top: 15px !important; } .bootstrap-iso .mr-sm-4, .bootstrap-iso .mx-sm-4 { - margin-right: 1.5rem !important; + margin-right: 15px !important; } .bootstrap-iso .mb-sm-4, .bootstrap-iso .my-sm-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 15px !important; } .bootstrap-iso .ml-sm-4, .bootstrap-iso .mx-sm-4 { - margin-left: 1.5rem !important; + margin-left: 15px !important; } .bootstrap-iso .m-sm-5 { - margin: 3rem !important; + margin: 30px !important; } .bootstrap-iso .mt-sm-5, .bootstrap-iso .my-sm-5 { - margin-top: 3rem !important; + margin-top: 30px !important; } .bootstrap-iso .mr-sm-5, .bootstrap-iso .mx-sm-5 { - margin-right: 3rem !important; + margin-right: 30px !important; } .bootstrap-iso .mb-sm-5, .bootstrap-iso .my-sm-5 { - margin-bottom: 3rem !important; + margin-bottom: 30px !important; } .bootstrap-iso .ml-sm-5, .bootstrap-iso .mx-sm-5 { - margin-left: 3rem !important; + margin-left: 30px !important; } .bootstrap-iso .p-sm-0 { padding: 0 !important; @@ -7153,99 +7153,99 @@ padding-left: 0 !important; } .bootstrap-iso .p-sm-1 { - padding: 0.25rem !important; + padding: 2.5px !important; } .bootstrap-iso .pt-sm-1, .bootstrap-iso .py-sm-1 { - padding-top: 0.25rem !important; + padding-top: 2.5px !important; } .bootstrap-iso .pr-sm-1, .bootstrap-iso .px-sm-1 { - padding-right: 0.25rem !important; + padding-right: 2.5px !important; } .bootstrap-iso .pb-sm-1, .bootstrap-iso .py-sm-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 2.5px !important; } .bootstrap-iso .pl-sm-1, .bootstrap-iso .px-sm-1 { - padding-left: 0.25rem !important; + padding-left: 2.5px !important; } .bootstrap-iso .p-sm-2 { - padding: 0.5rem !important; + padding: 5px !important; } .bootstrap-iso .pt-sm-2, .bootstrap-iso .py-sm-2 { - padding-top: 0.5rem !important; + padding-top: 5px !important; } .bootstrap-iso .pr-sm-2, .bootstrap-iso .px-sm-2 { - padding-right: 0.5rem !important; + padding-right: 5px !important; } .bootstrap-iso .pb-sm-2, .bootstrap-iso .py-sm-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 5px !important; } .bootstrap-iso .pl-sm-2, .bootstrap-iso .px-sm-2 { - padding-left: 0.5rem !important; + padding-left: 5px !important; } .bootstrap-iso .p-sm-3 { - padding: 1rem !important; + padding: 10px !important; } .bootstrap-iso .pt-sm-3, .bootstrap-iso .py-sm-3 { - padding-top: 1rem !important; + padding-top: 10px !important; } .bootstrap-iso .pr-sm-3, .bootstrap-iso .px-sm-3 { - padding-right: 1rem !important; + padding-right: 10px !important; } .bootstrap-iso .pb-sm-3, .bootstrap-iso .py-sm-3 { - padding-bottom: 1rem !important; + padding-bottom: 10px !important; } .bootstrap-iso .pl-sm-3, .bootstrap-iso .px-sm-3 { - padding-left: 1rem !important; + padding-left: 10px !important; } .bootstrap-iso .p-sm-4 { - padding: 1.5rem !important; + padding: 15px !important; } .bootstrap-iso .pt-sm-4, .bootstrap-iso .py-sm-4 { - padding-top: 1.5rem !important; + padding-top: 15px !important; } .bootstrap-iso .pr-sm-4, .bootstrap-iso .px-sm-4 { - padding-right: 1.5rem !important; + padding-right: 15px !important; } .bootstrap-iso .pb-sm-4, .bootstrap-iso .py-sm-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 15px !important; } .bootstrap-iso .pl-sm-4, .bootstrap-iso .px-sm-4 { - padding-left: 1.5rem !important; + padding-left: 15px !important; } .bootstrap-iso .p-sm-5 { - padding: 3rem !important; + padding: 30px !important; } .bootstrap-iso .pt-sm-5, .bootstrap-iso .py-sm-5 { - padding-top: 3rem !important; + padding-top: 30px !important; } .bootstrap-iso .pr-sm-5, .bootstrap-iso .px-sm-5 { - padding-right: 3rem !important; + padding-right: 30px !important; } .bootstrap-iso .pb-sm-5, .bootstrap-iso .py-sm-5 { - padding-bottom: 3rem !important; + padding-bottom: 30px !important; } .bootstrap-iso .pl-sm-5, .bootstrap-iso .px-sm-5 { - padding-left: 3rem !important; + padding-left: 30px !important; } .bootstrap-iso .m-sm-auto { margin: auto !important; @@ -7288,99 +7288,99 @@ margin-left: 0 !important; } .bootstrap-iso .m-md-1 { - margin: 0.25rem !important; + margin: 2.5px !important; } .bootstrap-iso .mt-md-1, .bootstrap-iso .my-md-1 { - margin-top: 0.25rem !important; + margin-top: 2.5px !important; } .bootstrap-iso .mr-md-1, .bootstrap-iso .mx-md-1 { - margin-right: 0.25rem !important; + margin-right: 2.5px !important; } .bootstrap-iso .mb-md-1, .bootstrap-iso .my-md-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 2.5px !important; } .bootstrap-iso .ml-md-1, .bootstrap-iso .mx-md-1 { - margin-left: 0.25rem !important; + margin-left: 2.5px !important; } .bootstrap-iso .m-md-2 { - margin: 0.5rem !important; + margin: 5px !important; } .bootstrap-iso .mt-md-2, .bootstrap-iso .my-md-2 { - margin-top: 0.5rem !important; + margin-top: 5px !important; } .bootstrap-iso .mr-md-2, .bootstrap-iso .mx-md-2 { - margin-right: 0.5rem !important; + margin-right: 5px !important; } .bootstrap-iso .mb-md-2, .bootstrap-iso .my-md-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 5px !important; } .bootstrap-iso .ml-md-2, .bootstrap-iso .mx-md-2 { - margin-left: 0.5rem !important; + margin-left: 5px !important; } .bootstrap-iso .m-md-3 { - margin: 1rem !important; + margin: 10px !important; } .bootstrap-iso .mt-md-3, .bootstrap-iso .my-md-3 { - margin-top: 1rem !important; + margin-top: 10px !important; } .bootstrap-iso .mr-md-3, .bootstrap-iso .mx-md-3 { - margin-right: 1rem !important; + margin-right: 10px !important; } .bootstrap-iso .mb-md-3, .bootstrap-iso .my-md-3 { - margin-bottom: 1rem !important; + margin-bottom: 10px !important; } .bootstrap-iso .ml-md-3, .bootstrap-iso .mx-md-3 { - margin-left: 1rem !important; + margin-left: 10px !important; } .bootstrap-iso .m-md-4 { - margin: 1.5rem !important; + margin: 15px !important; } .bootstrap-iso .mt-md-4, .bootstrap-iso .my-md-4 { - margin-top: 1.5rem !important; + margin-top: 15px !important; } .bootstrap-iso .mr-md-4, .bootstrap-iso .mx-md-4 { - margin-right: 1.5rem !important; + margin-right: 15px !important; } .bootstrap-iso .mb-md-4, .bootstrap-iso .my-md-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 15px !important; } .bootstrap-iso .ml-md-4, .bootstrap-iso .mx-md-4 { - margin-left: 1.5rem !important; + margin-left: 15px !important; } .bootstrap-iso .m-md-5 { - margin: 3rem !important; + margin: 30px !important; } .bootstrap-iso .mt-md-5, .bootstrap-iso .my-md-5 { - margin-top: 3rem !important; + margin-top: 30px !important; } .bootstrap-iso .mr-md-5, .bootstrap-iso .mx-md-5 { - margin-right: 3rem !important; + margin-right: 30px !important; } .bootstrap-iso .mb-md-5, .bootstrap-iso .my-md-5 { - margin-bottom: 3rem !important; + margin-bottom: 30px !important; } .bootstrap-iso .ml-md-5, .bootstrap-iso .mx-md-5 { - margin-left: 3rem !important; + margin-left: 30px !important; } .bootstrap-iso .p-md-0 { padding: 0 !important; @@ -7402,99 +7402,99 @@ padding-left: 0 !important; } .bootstrap-iso .p-md-1 { - padding: 0.25rem !important; + padding: 2.5px !important; } .bootstrap-iso .pt-md-1, .bootstrap-iso .py-md-1 { - padding-top: 0.25rem !important; + padding-top: 2.5px !important; } .bootstrap-iso .pr-md-1, .bootstrap-iso .px-md-1 { - padding-right: 0.25rem !important; + padding-right: 2.5px !important; } .bootstrap-iso .pb-md-1, .bootstrap-iso .py-md-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 2.5px !important; } .bootstrap-iso .pl-md-1, .bootstrap-iso .px-md-1 { - padding-left: 0.25rem !important; + padding-left: 2.5px !important; } .bootstrap-iso .p-md-2 { - padding: 0.5rem !important; + padding: 5px !important; } .bootstrap-iso .pt-md-2, .bootstrap-iso .py-md-2 { - padding-top: 0.5rem !important; + padding-top: 5px !important; } .bootstrap-iso .pr-md-2, .bootstrap-iso .px-md-2 { - padding-right: 0.5rem !important; + padding-right: 5px !important; } .bootstrap-iso .pb-md-2, .bootstrap-iso .py-md-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 5px !important; } .bootstrap-iso .pl-md-2, .bootstrap-iso .px-md-2 { - padding-left: 0.5rem !important; + padding-left: 5px !important; } .bootstrap-iso .p-md-3 { - padding: 1rem !important; + padding: 10px !important; } .bootstrap-iso .pt-md-3, .bootstrap-iso .py-md-3 { - padding-top: 1rem !important; + padding-top: 10px !important; } .bootstrap-iso .pr-md-3, .bootstrap-iso .px-md-3 { - padding-right: 1rem !important; + padding-right: 10px !important; } .bootstrap-iso .pb-md-3, .bootstrap-iso .py-md-3 { - padding-bottom: 1rem !important; + padding-bottom: 10px !important; } .bootstrap-iso .pl-md-3, .bootstrap-iso .px-md-3 { - padding-left: 1rem !important; + padding-left: 10px !important; } .bootstrap-iso .p-md-4 { - padding: 1.5rem !important; + padding: 15px !important; } .bootstrap-iso .pt-md-4, .bootstrap-iso .py-md-4 { - padding-top: 1.5rem !important; + padding-top: 15px !important; } .bootstrap-iso .pr-md-4, .bootstrap-iso .px-md-4 { - padding-right: 1.5rem !important; + padding-right: 15px !important; } .bootstrap-iso .pb-md-4, .bootstrap-iso .py-md-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 15px !important; } .bootstrap-iso .pl-md-4, .bootstrap-iso .px-md-4 { - padding-left: 1.5rem !important; + padding-left: 15px !important; } .bootstrap-iso .p-md-5 { - padding: 3rem !important; + padding: 30px !important; } .bootstrap-iso .pt-md-5, .bootstrap-iso .py-md-5 { - padding-top: 3rem !important; + padding-top: 30px !important; } .bootstrap-iso .pr-md-5, .bootstrap-iso .px-md-5 { - padding-right: 3rem !important; + padding-right: 30px !important; } .bootstrap-iso .pb-md-5, .bootstrap-iso .py-md-5 { - padding-bottom: 3rem !important; + padding-bottom: 30px !important; } .bootstrap-iso .pl-md-5, .bootstrap-iso .px-md-5 { - padding-left: 3rem !important; + padding-left: 30px !important; } .bootstrap-iso .m-md-auto { margin: auto !important; @@ -7537,99 +7537,99 @@ margin-left: 0 !important; } .bootstrap-iso .m-lg-1 { - margin: 0.25rem !important; + margin: 2.5px !important; } .bootstrap-iso .mt-lg-1, .bootstrap-iso .my-lg-1 { - margin-top: 0.25rem !important; + margin-top: 2.5px !important; } .bootstrap-iso .mr-lg-1, .bootstrap-iso .mx-lg-1 { - margin-right: 0.25rem !important; + margin-right: 2.5px !important; } .bootstrap-iso .mb-lg-1, .bootstrap-iso .my-lg-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 2.5px !important; } .bootstrap-iso .ml-lg-1, .bootstrap-iso .mx-lg-1 { - margin-left: 0.25rem !important; + margin-left: 2.5px !important; } .bootstrap-iso .m-lg-2 { - margin: 0.5rem !important; + margin: 5px !important; } .bootstrap-iso .mt-lg-2, .bootstrap-iso .my-lg-2 { - margin-top: 0.5rem !important; + margin-top: 5px !important; } .bootstrap-iso .mr-lg-2, .bootstrap-iso .mx-lg-2 { - margin-right: 0.5rem !important; + margin-right: 5px !important; } .bootstrap-iso .mb-lg-2, .bootstrap-iso .my-lg-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 5px !important; } .bootstrap-iso .ml-lg-2, .bootstrap-iso .mx-lg-2 { - margin-left: 0.5rem !important; + margin-left: 5px !important; } .bootstrap-iso .m-lg-3 { - margin: 1rem !important; + margin: 10px !important; } .bootstrap-iso .mt-lg-3, .bootstrap-iso .my-lg-3 { - margin-top: 1rem !important; + margin-top: 10px !important; } .bootstrap-iso .mr-lg-3, .bootstrap-iso .mx-lg-3 { - margin-right: 1rem !important; + margin-right: 10px !important; } .bootstrap-iso .mb-lg-3, .bootstrap-iso .my-lg-3 { - margin-bottom: 1rem !important; + margin-bottom: 10px !important; } .bootstrap-iso .ml-lg-3, .bootstrap-iso .mx-lg-3 { - margin-left: 1rem !important; + margin-left: 10px !important; } .bootstrap-iso .m-lg-4 { - margin: 1.5rem !important; + margin: 15px !important; } .bootstrap-iso .mt-lg-4, .bootstrap-iso .my-lg-4 { - margin-top: 1.5rem !important; + margin-top: 15px !important; } .bootstrap-iso .mr-lg-4, .bootstrap-iso .mx-lg-4 { - margin-right: 1.5rem !important; + margin-right: 15px !important; } .bootstrap-iso .mb-lg-4, .bootstrap-iso .my-lg-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 15px !important; } .bootstrap-iso .ml-lg-4, .bootstrap-iso .mx-lg-4 { - margin-left: 1.5rem !important; + margin-left: 15px !important; } .bootstrap-iso .m-lg-5 { - margin: 3rem !important; + margin: 30px !important; } .bootstrap-iso .mt-lg-5, .bootstrap-iso .my-lg-5 { - margin-top: 3rem !important; + margin-top: 30px !important; } .bootstrap-iso .mr-lg-5, .bootstrap-iso .mx-lg-5 { - margin-right: 3rem !important; + margin-right: 30px !important; } .bootstrap-iso .mb-lg-5, .bootstrap-iso .my-lg-5 { - margin-bottom: 3rem !important; + margin-bottom: 30px !important; } .bootstrap-iso .ml-lg-5, .bootstrap-iso .mx-lg-5 { - margin-left: 3rem !important; + margin-left: 30px !important; } .bootstrap-iso .p-lg-0 { padding: 0 !important; @@ -7651,99 +7651,99 @@ padding-left: 0 !important; } .bootstrap-iso .p-lg-1 { - padding: 0.25rem !important; + padding: 2.5px !important; } .bootstrap-iso .pt-lg-1, .bootstrap-iso .py-lg-1 { - padding-top: 0.25rem !important; + padding-top: 2.5px !important; } .bootstrap-iso .pr-lg-1, .bootstrap-iso .px-lg-1 { - padding-right: 0.25rem !important; + padding-right: 2.5px !important; } .bootstrap-iso .pb-lg-1, .bootstrap-iso .py-lg-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 2.5px !important; } .bootstrap-iso .pl-lg-1, .bootstrap-iso .px-lg-1 { - padding-left: 0.25rem !important; + padding-left: 2.5px !important; } .bootstrap-iso .p-lg-2 { - padding: 0.5rem !important; + padding: 5px !important; } .bootstrap-iso .pt-lg-2, .bootstrap-iso .py-lg-2 { - padding-top: 0.5rem !important; + padding-top: 5px !important; } .bootstrap-iso .pr-lg-2, .bootstrap-iso .px-lg-2 { - padding-right: 0.5rem !important; + padding-right: 5px !important; } .bootstrap-iso .pb-lg-2, .bootstrap-iso .py-lg-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 5px !important; } .bootstrap-iso .pl-lg-2, .bootstrap-iso .px-lg-2 { - padding-left: 0.5rem !important; + padding-left: 5px !important; } .bootstrap-iso .p-lg-3 { - padding: 1rem !important; + padding: 10px !important; } .bootstrap-iso .pt-lg-3, .bootstrap-iso .py-lg-3 { - padding-top: 1rem !important; + padding-top: 10px !important; } .bootstrap-iso .pr-lg-3, .bootstrap-iso .px-lg-3 { - padding-right: 1rem !important; + padding-right: 10px !important; } .bootstrap-iso .pb-lg-3, .bootstrap-iso .py-lg-3 { - padding-bottom: 1rem !important; + padding-bottom: 10px !important; } .bootstrap-iso .pl-lg-3, .bootstrap-iso .px-lg-3 { - padding-left: 1rem !important; + padding-left: 10px !important; } .bootstrap-iso .p-lg-4 { - padding: 1.5rem !important; + padding: 15px !important; } .bootstrap-iso .pt-lg-4, .bootstrap-iso .py-lg-4 { - padding-top: 1.5rem !important; + padding-top: 15px !important; } .bootstrap-iso .pr-lg-4, .bootstrap-iso .px-lg-4 { - padding-right: 1.5rem !important; + padding-right: 15px !important; } .bootstrap-iso .pb-lg-4, .bootstrap-iso .py-lg-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 15px !important; } .bootstrap-iso .pl-lg-4, .bootstrap-iso .px-lg-4 { - padding-left: 1.5rem !important; + padding-left: 15px !important; } .bootstrap-iso .p-lg-5 { - padding: 3rem !important; + padding: 30px !important; } .bootstrap-iso .pt-lg-5, .bootstrap-iso .py-lg-5 { - padding-top: 3rem !important; + padding-top: 30px !important; } .bootstrap-iso .pr-lg-5, .bootstrap-iso .px-lg-5 { - padding-right: 3rem !important; + padding-right: 30px !important; } .bootstrap-iso .pb-lg-5, .bootstrap-iso .py-lg-5 { - padding-bottom: 3rem !important; + padding-bottom: 30px !important; } .bootstrap-iso .pl-lg-5, .bootstrap-iso .px-lg-5 { - padding-left: 3rem !important; + padding-left: 30px !important; } .bootstrap-iso .m-lg-auto { margin: auto !important; @@ -7786,99 +7786,99 @@ margin-left: 0 !important; } .bootstrap-iso .m-xl-1 { - margin: 0.25rem !important; + margin: 2.5px !important; } .bootstrap-iso .mt-xl-1, .bootstrap-iso .my-xl-1 { - margin-top: 0.25rem !important; + margin-top: 2.5px !important; } .bootstrap-iso .mr-xl-1, .bootstrap-iso .mx-xl-1 { - margin-right: 0.25rem !important; + margin-right: 2.5px !important; } .bootstrap-iso .mb-xl-1, .bootstrap-iso .my-xl-1 { - margin-bottom: 0.25rem !important; + margin-bottom: 2.5px !important; } .bootstrap-iso .ml-xl-1, .bootstrap-iso .mx-xl-1 { - margin-left: 0.25rem !important; + margin-left: 2.5px !important; } .bootstrap-iso .m-xl-2 { - margin: 0.5rem !important; + margin: 5px !important; } .bootstrap-iso .mt-xl-2, .bootstrap-iso .my-xl-2 { - margin-top: 0.5rem !important; + margin-top: 5px !important; } .bootstrap-iso .mr-xl-2, .bootstrap-iso .mx-xl-2 { - margin-right: 0.5rem !important; + margin-right: 5px !important; } .bootstrap-iso .mb-xl-2, .bootstrap-iso .my-xl-2 { - margin-bottom: 0.5rem !important; + margin-bottom: 5px !important; } .bootstrap-iso .ml-xl-2, .bootstrap-iso .mx-xl-2 { - margin-left: 0.5rem !important; + margin-left: 5px !important; } .bootstrap-iso .m-xl-3 { - margin: 1rem !important; + margin: 10px !important; } .bootstrap-iso .mt-xl-3, .bootstrap-iso .my-xl-3 { - margin-top: 1rem !important; + margin-top: 10px !important; } .bootstrap-iso .mr-xl-3, .bootstrap-iso .mx-xl-3 { - margin-right: 1rem !important; + margin-right: 10px !important; } .bootstrap-iso .mb-xl-3, .bootstrap-iso .my-xl-3 { - margin-bottom: 1rem !important; + margin-bottom: 10px !important; } .bootstrap-iso .ml-xl-3, .bootstrap-iso .mx-xl-3 { - margin-left: 1rem !important; + margin-left: 10px !important; } .bootstrap-iso .m-xl-4 { - margin: 1.5rem !important; + margin: 15px !important; } .bootstrap-iso .mt-xl-4, .bootstrap-iso .my-xl-4 { - margin-top: 1.5rem !important; + margin-top: 15px !important; } .bootstrap-iso .mr-xl-4, .bootstrap-iso .mx-xl-4 { - margin-right: 1.5rem !important; + margin-right: 15px !important; } .bootstrap-iso .mb-xl-4, .bootstrap-iso .my-xl-4 { - margin-bottom: 1.5rem !important; + margin-bottom: 15px !important; } .bootstrap-iso .ml-xl-4, .bootstrap-iso .mx-xl-4 { - margin-left: 1.5rem !important; + margin-left: 15px !important; } .bootstrap-iso .m-xl-5 { - margin: 3rem !important; + margin: 30px !important; } .bootstrap-iso .mt-xl-5, .bootstrap-iso .my-xl-5 { - margin-top: 3rem !important; + margin-top: 30px !important; } .bootstrap-iso .mr-xl-5, .bootstrap-iso .mx-xl-5 { - margin-right: 3rem !important; + margin-right: 30px !important; } .bootstrap-iso .mb-xl-5, .bootstrap-iso .my-xl-5 { - margin-bottom: 3rem !important; + margin-bottom: 30px !important; } .bootstrap-iso .ml-xl-5, .bootstrap-iso .mx-xl-5 { - margin-left: 3rem !important; + margin-left: 30px !important; } .bootstrap-iso .p-xl-0 { padding: 0 !important; @@ -7900,99 +7900,99 @@ padding-left: 0 !important; } .bootstrap-iso .p-xl-1 { - padding: 0.25rem !important; + padding: 2.5px !important; } .bootstrap-iso .pt-xl-1, .bootstrap-iso .py-xl-1 { - padding-top: 0.25rem !important; + padding-top: 2.5px !important; } .bootstrap-iso .pr-xl-1, .bootstrap-iso .px-xl-1 { - padding-right: 0.25rem !important; + padding-right: 2.5px !important; } .bootstrap-iso .pb-xl-1, .bootstrap-iso .py-xl-1 { - padding-bottom: 0.25rem !important; + padding-bottom: 2.5px !important; } .bootstrap-iso .pl-xl-1, .bootstrap-iso .px-xl-1 { - padding-left: 0.25rem !important; + padding-left: 2.5px !important; } .bootstrap-iso .p-xl-2 { - padding: 0.5rem !important; + padding: 5px !important; } .bootstrap-iso .pt-xl-2, .bootstrap-iso .py-xl-2 { - padding-top: 0.5rem !important; + padding-top: 5px !important; } .bootstrap-iso .pr-xl-2, .bootstrap-iso .px-xl-2 { - padding-right: 0.5rem !important; + padding-right: 5px !important; } .bootstrap-iso .pb-xl-2, .bootstrap-iso .py-xl-2 { - padding-bottom: 0.5rem !important; + padding-bottom: 5px !important; } .bootstrap-iso .pl-xl-2, .bootstrap-iso .px-xl-2 { - padding-left: 0.5rem !important; + padding-left: 5px !important; } .bootstrap-iso .p-xl-3 { - padding: 1rem !important; + padding: 10px !important; } .bootstrap-iso .pt-xl-3, .bootstrap-iso .py-xl-3 { - padding-top: 1rem !important; + padding-top: 10px !important; } .bootstrap-iso .pr-xl-3, .bootstrap-iso .px-xl-3 { - padding-right: 1rem !important; + padding-right: 10px !important; } .bootstrap-iso .pb-xl-3, .bootstrap-iso .py-xl-3 { - padding-bottom: 1rem !important; + padding-bottom: 10px !important; } .bootstrap-iso .pl-xl-3, .bootstrap-iso .px-xl-3 { - padding-left: 1rem !important; + padding-left: 10px !important; } .bootstrap-iso .p-xl-4 { - padding: 1.5rem !important; + padding: 15px !important; } .bootstrap-iso .pt-xl-4, .bootstrap-iso .py-xl-4 { - padding-top: 1.5rem !important; + padding-top: 15px !important; } .bootstrap-iso .pr-xl-4, .bootstrap-iso .px-xl-4 { - padding-right: 1.5rem !important; + padding-right: 15px !important; } .bootstrap-iso .pb-xl-4, .bootstrap-iso .py-xl-4 { - padding-bottom: 1.5rem !important; + padding-bottom: 15px !important; } .bootstrap-iso .pl-xl-4, .bootstrap-iso .px-xl-4 { - padding-left: 1.5rem !important; + padding-left: 15px !important; } .bootstrap-iso .p-xl-5 { - padding: 3rem !important; + padding: 30px !important; } .bootstrap-iso .pt-xl-5, .bootstrap-iso .py-xl-5 { - padding-top: 3rem !important; + padding-top: 30px !important; } .bootstrap-iso .pr-xl-5, .bootstrap-iso .px-xl-5 { - padding-right: 3rem !important; + padding-right: 30px !important; } .bootstrap-iso .pb-xl-5, .bootstrap-iso .py-xl-5 { - padding-bottom: 3rem !important; + padding-bottom: 30px !important; } .bootstrap-iso .pl-xl-5, .bootstrap-iso .px-xl-5 { - padding-left: 3rem !important; + padding-left: 30px !important; } .bootstrap-iso .m-xl-auto { margin: auto !important; diff --git a/docs/css/codehilite.css b/docs/css/codehilite.css new file mode 100644 index 0000000000..df7773d93c --- /dev/null +++ b/docs/css/codehilite.css @@ -0,0 +1,184 @@ +/* Keywords */ +.highlight .k, +.highlight .kd, +.highlight .kn, +.highlight .kp, +.highlight .kr, +.highlight .kt, +/* Other names (types) */ +.highlight .nx +/* Keyword constants */ +.highlight .kc, +.highlight .n { + color: #4191FF; +} + +/* Operators and punctuation */ +.highlight .o, +.highlight .ow, +.highlight .p, +.highlight .cp +{ + color: var(--ibexa-dusk-black); +} + +/* HTML tags and YAML keys */ +.highlight .nt { + color: #32707D; +} + +/* Function names */ +.highlight .nc, +.highlight .nb, +.highlight .ne, +.highlight .nf, +.highlight .fm, +.highlight .nn { + color: var(--ibexa-jazzberry); +} + +/* Variable names */ +.highlight .nv, +.highlight .vc, +.highlight .vg, +.highlight .vi { + color: #41000F; +} + +/* Attributes */ +.highlight .na { + color: var(--ibexa-jazzberry); +} + +/* Strings */ +.highlight .l, +.highlight .s, +.highlight .sb, +.highlight .sc, +.highlight .s2, +.highlight .si, +.highlight .s1, +.highlight .ss { + color: #A8604A; +} + +/* Numbers */ +.highlight .m, +.highlight .mf, +.highlight .mh, +.highlight .mi, +.highlight .il, +.highlight .mo { + color: #A8604A; +} + +/* Comments */ +.highlight .c, +.highlight .cm, +.highlight .c1, +.highlight .ch, +.highlight .cs, +.highlight .sd { + color: gray; +} + +.highlight .gd, .highlight .gi { + margin: 0 -0.125em; + padding: 0 .125em; + border-radius: .1rem +} + +.highlight .gd { + background-color: var(--md-typeset-del-color) +} + +.highlight .gi { + background-color: var(--md-typeset-ins-color) +} + +.highlight .hll { + display: block; + margin: 0 -1.1764705882em; + padding: 0 1.1764705882em; + background-color: var(--md-code-hl-color) +} + +.highlighttable { + display: block; + overflow: unset; +} + +.highlighttable tbody, .highlighttable td { + display: block; + padding: 0 +} + +.highlighttable tr { + display: flex +} + +.highlighttable pre { + margin: 0 +} + +.highlighttable .linenos { + padding: .525rem 1.1764705882em; + padding-right: 0; + font-size: .85em; + background-color: var(--md-code-bg-color); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none +} + +.highlighttable .linenodiv { + padding-right: .5882352941em; + box-shadow: -0.05rem 0 var(--md-default-fg-color--lighter) inset +} + +.highlighttable .linenodiv pre { + color: var(--md-default-fg-color--light); + text-align: right +} + +.highlighttable .code { + flex: 1; + overflow: unset; +} + +.md-typeset .highlighttable, +.md-typeset .tabbed-block>.highlight:first-child>.highlighttable { + margin: 1em 0; + direction: ltr; + border-radius: .1rem +} + +.md-typeset .highlighttable code { + border-radius: 0 +} + +@media screen and (max-width: 44.9375em) { + .md-typeset>.highlight { + margin:1em -0.8rem + } + + .md-typeset>.highlight .hll { + margin: 0 -0.8rem; + padding: 0 .8rem + } + + .md-typeset>.highlight code { + border-radius: 0 + } + + .md-typeset>.highlighttable { + margin: 1em -0.8rem; + border-radius: 0 + } + + .md-typeset>.highlighttable .hll { + margin: 0 -0.8rem; + padding: 0 .8rem + } +} diff --git a/docs/css/custom.css b/docs/css/custom.css index ad92ee03bd..9cd2a8839d 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -1,4 +1,4 @@ -:root { +:root > * { --ibexa-orange: rgb(255,71,19); --ibexa-red: rgb(219,0,50); --ibexa-jazzberry: rgb(174,17,100); @@ -12,210 +12,207 @@ --apricot: rgb(241,138,107); --sherpa-blue: rgb(0,77,93); + --mid-grey: rgb(201,201,208); + --dark-grey: rgb(151,151,151); + --caution-border: var(--ibexa-orange); - --note-border: rgb(201,201,208); + --note-border: var(--mid-grey); --enterprise-border: var(--ibexa-jazzberry); --codeblock: rgb(89,96,103); --inline-code: rgb(242,242,245); - --table-header: rgb(89,96,103); - --code-highlight: rgb(253,230,198); + --table-header: var(--codeblock); + --code-highlight: #D9E9FF; --link: rgb(0,71,206); + + /* Override Material colors */ + --md-accent-fg-color: var(--ibexa-jazzberry); + + + --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--caution: url('data:image/svg+xml;charset=utf-8,'); } @font-face { - font-family: "Noto Sans"; - src: url("../fonts/Noto_Sans/NotoSans-Regular.ttf") format("truetype"); - font-weight: normal; + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-Regular.ttf'); + font-weight: 400; font-style: normal; } @font-face { - font-family: "Work Sans"; - src: url("../fonts/Work_Sans/WorkSans-Regular.ttf") format("truetype"); - font-weight: normal; + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-SemiBold.ttf'); + font-weight: 600; font-style: normal; } -body, input { - font-family: "Noto Sans", sans-serif; - color: var(--ibexa-dusk-black); -} - -body { - -webkit-font-smoothing: initial; -} - -.md-grid { - max-width: 100%; -} - -.md-sidebar--secondary { - margin-left: 100%; -} - -[dir=rtl] .md-sidebar--secondary { - margin-right: 100%; +@font-face { + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-Bold.ttf'); + font-weight: 700; + font-style: normal; } -.md-typeset { - font-size: 16px; - line-height: 1.5; - -webkit-print-color-adjust: exact; +@font-face { + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-Italic.ttf'); + font-weight: 400; + font-style: italic; } -.md-typeset a:hover { - color: var(--ibexa-orange); +@font-face { + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-SemiBoldItalic.ttf'); + font-weight: 600; + font-style: italic; } -.md-nav__title { - display: block; - padding: 1.6rem 1.2rem 0; +@font-face { + font-family: 'Noto Sans'; + src: url('../fonts/Noto_Sans/NotoSans-BoldItalic.ttf'); font-weight: 700; - text-overflow: ellipsis; - overflow: hidden; - color: var(--ibexa-orange); + font-style: italic; } -.md-nav__heading { - font-weight: 700; +@font-face { + font-family: 'Work Sans'; + src: url('../fonts/Work_Sans/WorkSans-Regular.ttf'); + font-weight: 400; + font-style: normal; } -.md-nav__link:hover { - color: var(--ibexa-orange); +@font-face { + font-family: 'Work Sans'; + src: url('../fonts/Work_Sans/WorkSans-SemiBold.ttf'); + font-weight: 600; + font-style: normal; } -.md-nav__link::after { - font-weight: 700; +body, input { + font-family: "Noto Sans", sans-serif; + color: var(--ibexa-dusk-black); } -.md-nav--secondary { - transition: border-left .25s; - border-left: none; - padding-left: 10px; - padding-bottom: 10px; +body { + -webkit-font-smoothing: initial; } -.md-nav__item { - padding: 0 1.75rem; -} +.md-content { + padding: 0 3rem; -.md-nav--primary > .md-nav__list > .md-nav__item:first-of-type { - font-weight: 700; - color: var(--ibexa-orange); } -.md-header-nav__button { - padding: .5rem; +.md-main__inner { + margin-top: 0; } -.md-sidebar__inner { - font-family: "Work Sans", sans-serif; +.md-main__inner article { + padding-bottom: 6rem; } -.md-logo { - margin-top: 0; - padding-top: .2rem; +.md-grid { + max-width: 100%; } +.md-typeset { + font-size: 16px; + line-height: 1.5; + -webkit-print-color-adjust: exact; +} -.md-header, -[data-md-color-primary=blue-grey] .md-header, -[data-md-color-primary=blue-grey] .md-hero -{ +[data-md-color-primary=blue-grey] .md-header { background-color: var(--ibexa-dusk-black); color: #fff; + height: 56px; } -[data-md-color-primary=blue-grey] .md-typeset a { +.md-typeset a { color: var(--link); } -[data-md-color-primary=blue-grey] .md-typeset th a { - color: #fff; +.md-typeset a:hover, +.md-typeset a:focus { + color: var(--ibexa-jazzberry); } -[data-md-color-accent=deep-orange] .md-nav__link:focus, -[data-md-color-accent=deep-orange] .md-nav__link:hover, -[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref, -[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:active:before, -[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:hover:before, -[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus, -[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink, -html .md-footer-meta.md-typeset a:hover -{ - color: var(--ibexa-orange); +.md-typeset th a { + color: #fff; } -[data-md-color-primary=blue-grey] .md-typeset .md-icon { +.md-typeset .md-icon { color: rgba(0,0,0,.26); } /* Headings */ .md-typeset h1, .md-typeset h2, .md-typeset h3, .md-typeset h4, .md-typeset h5, .md-typeset h6 { - font-family: "Work Sans", sans-serif; color: var(--ibexa-dusk-black); line-height: 1; } .md-typeset h1 { margin: 0 0 1rem; - font-size: 42px; + font-size: 34px; } .md-typeset h2 { - font-size: 34px; + font-size: 28px; } .md-typeset h3 { - font-size: 26px; + font-size: 24px; } .md-typeset h4 { - font-size: 23px; + font-size: 22px; } .md-typeset h5 { - font-size: 20px; + font-size: 18px; text-transform: none; } .md-typeset h6 { - font-size: 18px; + font-size: 16px; } .md-typeset h1, .md-typeset h2, .md-typeset h3, .md-typeset h4, .md-typeset h5, .md-typeset h6 { font-weight: 600; } -.md-footer-meta { - background: #fff; +ul li::marker { + color: var(--ibexa-jazzberry); } -.md-footer-nav { - background-color: #fff; - border-top: 1px solid #e0e0e8; +ul li li { + list-style-type: square; } -.md-footer-nav, .md-footer-nav__direction { - color: var(--ibexa-dusk-black); +ul li li::marker { + color: black; } -.md-footer-nav span:hover { - color: var(--ibexa-orange); +ul li li li { + list-style-type: circle; } -.md-footer-copyright, .md-footer-copyright__highlight { - color: var(--ibexa-dusk-black); +.md-content__inner>:last-child { + margin-bottom: -24px; } -html .md-footer-meta.md-typeset a { - color: var(--link); +.md-footer { + background-color: #f9f9f9; + color: #c3c3c3; + padding: 1rem 1.5rem; + bottom: 0; + width: 200%; + position: absolute; + left: 15rem; + border-left: 1px solid #d7d7d7; } .md-icon--home { @@ -224,87 +221,72 @@ html .md-footer-meta.md-typeset a { /* Admonitions */ -.md-typeset details { - font-size: 16px; +.md-typeset :is(.admonition,details):is(.tip,.note,.caution), +.md-typeset :is(.admonition-title,summary), +.md-typeset :is(.tip,.note)>:is(.admonition-title,summary) { + background-color: #ffffff; box-shadow: none; + font-size: 16px; } -.md-typeset details { - border: .4rem solid; -} - -/* DXP */ - -.admonition.dxp .admonition-title, -.admonition.enterprise .admonition-title { - padding-left: 1.2rem; -} - -.admonition .admonition>.admonition-title { - padding-left: 3.8rem; -} - -.admonition.dxp, .admonition.dxp>.admonition-title, -.admonition.enterprise, .admonition.enterprise>.admonition-title { - border-color: var(--enterprise-border); - background-color: #ffffff; +.md-typeset :is(.admonition,details):is(.tip,.note) +{ + border: 0.1rem solid var(--note-border); + border-radius: 0.1rem; } -/* Caution */ - -.md-typeset .admonition.caution, .md-typeset .admonition.caution>.admonition-title { - border-color: var(--caution-border); +.md-typeset :is(.admonition,details):is(.caution) +{ + border: 0.1rem solid var(--caution-border); + border-radius: 0.1rem; } -.md-typeset .admonition.caution>.admonition-title:before { - color: var(--caution-border); +.md-typeset .admonition.caution>.admonition-title { + border-color: var(--caution-border); } -/* Note, tip and expand box */ - -.md-typeset .admonition.tip, -.md-typeset .admonition.tip>.admonition-title, -.md-typeset .admonition.note, -.md-typeset .admonition.note>.admonition-title, -.md-typeset details.tip, .md-typeset details.note -{ +.md-typeset .admonition:is(.note,.tip)>:is(.admonition-title) { border-color: var(--note-border); } -.md-typeset .admonition.dxp>.admonition-title:before, .md-typeset .admonition.enterprise>.admonition-title:before { - content: ""; +.md-typeset :is(.admonition,details[open]):is(.note,.tip,.caution) :is(.admonition-title,summary) { + border-bottom: 1px solid var(--note-border); } -.md-typeset .admonition.tip>.admonition-title:before, .md-typeset details.tip>summary:before { - content: "lightbulb_outline"; +/* Admonition icons */ + +.md-typeset .note > .admonition-title::before, +.md-typeset .tip > .admonition-title::before, +.md-typeset .note > summary::before, +.md-typeset .tip > summary::before { + background-color: var(--note-border); } -.md-typeset .admonition.tip>.admonition-title:before, .md-typeset details.tip>summary:before, -.md-typeset .admonition.note>.admonition-title:before, .md-typeset details.note>summary:before { - color: var(--ibexa-dusk-black); +.md-typeset :is(.admonition,details).caution>:is(.admonition-title,summary):before { + background-color: var(--caution-border); + -webkit-mask-image: var(--md-admonition-icon--caution); + mask-image: var(--md-admonition-icon--caution); } -/* General admonitions */ +/* Tile admonition */ -.md-typeset .admonition.caution>.admonition-title, .md-typeset .admonition.note>.admonition-title, -.md-typeset .admonition.tip>.admonition-title, .md-typeset .admonition.dxp>.admonition-title, -.md-typeset .admonition.enterprise>.admonition-title, -.md-typeset details.tip, .md-typeset details.tip>summary, -.md-typeset details.note, .md-typeset details.note>summary { +.md-typeset :is(.admonition):is(.tile) { background-color: #ffffff; -} - -.md-typeset .admonition { - font-size: 16px; + border: 1px solid #D4D4D4; + border-radius: 5px; + padding: 0.7rem; + margin-bottom: 0.4rem; + overflow: auto; box-shadow: none; + font-size: 16px; } -.admonition { - border: .4rem solid; +.md-typeset :is(.admonition).tile>:is(.admonition-title) { + padding-left: 0.7rem; } -.bootstrap-iso .admonition p { - margin-top: 1em; +.md-typeset :is(.admonition).tile>:is(.admonition-title):before { + content: none; } /* Fix scrollbar in admonitions when containing a table */ @@ -318,43 +300,52 @@ html .md-footer-meta.md-typeset a { color: var(--ibexa-dusk-black); } -.md-typeset .codehilite code, .md-typeset .codehilite pre { +.md-typeset .highlighttable code, .md-typeset .highlighttable pre { background-color: #fff; padding: 0; } -.md-typeset .codehilite code, .md-typeset .codehilite pre code { +.md-typeset .highlighttable code, .md-typeset .highlighttable pre code { padding: 1.05rem 1.2rem; } -.md-typeset .codehilitetable { +.md-typeset .highlighttable { border: 1px solid var(--codeblock); } /* Code block scrollbar */ -.md-typeset .codehilite pre code::-webkit-scrollbar { +.md-typeset .highlighttable pre code::-webkit-scrollbar { height: 10px; } -.md-typeset .codehilite pre code::-webkit-scrollbar-track { +.md-typeset .highlighttable pre code::-webkit-scrollbar-track { background-color: var(--ibexa-snow); } -.md-typeset .codehilite pre code::-webkit-scrollbar-thumb { +.md-typeset .highlighttable pre code::-webkit-scrollbar-thumb { background: var(--codeblock); } -.md-typeset .codehilitetable .linenos { +.md-typeset .highlighttable code, .md-typeset .highlighttable pre code { + padding: 0.5rem 0.5rem; +} + +.md-typeset .highlighttable .linenos { + padding: 0; background-color: var(--codeblock); - color: #fff; } -.md-typeset .codehilitetable .linenodiv { - padding: 1.05rem 1.2rem +.md-typeset .highlighttable .linenodiv { + padding: 0.5rem 0.5rem } -.md-typeset .codehilite, .md-typeset .highlight pre { +.md-typeset .highlighttable .linenodiv pre { + color: var(--ibexa-snow); + background-color: var(--codeblock); +} + +.md-typeset .highlighttable, .md-typeset .highlight pre { background-color: var(--inline-code); } @@ -366,10 +357,16 @@ html .md-footer-meta.md-typeset a { font-family: monospace; } -.codehilite .hll { +.highlighttable .hll { background-color: var(--code-highlight); } +/* Code block titles */ + +.highlighttable span.filename { + background-color: var(--ibexa-snow); +} + /* Tables */ .md-typeset table:not([class]) { @@ -378,6 +375,7 @@ html .md-footer-meta.md-typeset a { } .md-typeset table:not([class]) th { + color: var(--ibexa-snow); background-color: var(--table-header); } @@ -385,24 +383,6 @@ html .md-footer-meta.md-typeset a { color: rgb(19, 28, 38); } -div.rst-versions { - width: 250px; - border-top: 0; - background: var(--ibexa-dusk-black); - font-size: 10pt; - padding-top: 6px; - position: relative; -} - -.rst-versions span.rst-current-version { - background-color: rgb(66,73,81); - height: 3.6rem; -} - -.rst-versions div.rst-other-versions { - background-color: rgba(0,0,0,.26) !important; -} - figure { margin: 0; } @@ -413,9 +393,8 @@ figcaption { text-align: center; } -a.external:after { - content: 'open_in_new'; - font-family: Material Icons; +a.external:not(.card):after { + content: url(../images/open-in-new.svg); font-style: normal; padding-left: 2px; position: relative; @@ -432,46 +411,6 @@ a.external:after { font-weight: 700; } -.md-search__form { - font-size: 11pt; -} - -.md-search-result__item { - list-style: none; -} - -.md-search__output { - display: none; -} - -[data-md-toggle="search"]:checked ~ .md-header .md-search__inner { - box-sizing: border-box; -} - -.md-search__input { - background-color: rgb(66,73,81); -} - -.md-search__input::placeholder, .md-search__input:-ms-input-placeholder { - color: var(--white); -} - -.md-search__input:hover { - background-color: rgb(84,91,98); -} - -.md-search__icon { - color: var(--white); -} - -.ds-dataset-1 { - max-height: calc(100vh - 5.5rem); -} - -.algolia-autocomplete { - display: block !important; - box-sizing: border-box; -} /* Image enlagement Modal */ #imageModal { @@ -489,7 +428,7 @@ a.external:after { .modal-content { margin: auto; display: block; - max-width: 60%; + max-width: 100%; max-height: 90%; background-color: #fff; } @@ -513,6 +452,11 @@ a.external:after { cursor: pointer; } +.md-content img.inline-image { + display: inline; + max-height: 1rem; +} + /* UI guidelines */ .ez-code-example { @@ -542,80 +486,146 @@ a.external:after { /* Tabs */ -.tabbed-set { - display: flex; - position: relative; - flex-wrap: wrap; - margin-top: 1.2rem; -} - -.tabbed-set .highlight { - background: #dee2e6; -} - -.tabbed-set .tabbed-content { - display: none; +.md-typeset .tabbed-set .tabbed-content { order: 99; - width: 100%; border: 1px solid var(--ibexa-snow); padding: 4px 4px 4px 1rem; border-radius: 0 .2rem .2rem .2rem; } -.tabbed-set label { - width: auto; - /* margin: 0 0.5em 0 0; */ - padding: 0.25em 1rem; - cursor: pointer; +.md-typeset .tabbed-set label { + font-size: .64rem; + font-weight: 700; + padding: 0.9375em 1.25em 0.78125em; border: 1px solid var(--ibexa-snow); border-bottom: 2px solid var(--ibexa-snow); border-radius: 4px 4px 0 0; } -.tabbed-set input { - position: absolute; - opacity: 0; +.js .md-typeset .tabbed-labels:before { + height: 4px; } -.tabbed-set input:nth-child(n+1) { - color: #333333; - font-weight: 700; -} - -.tabbed-set input:nth-child(n+1):checked + label { - color: var(--ibexa-orange); - font-weight: 700; - border-bottom: 4px solid var(--ibexa-orange); +.md-typeset .tabbed-labels>label:hover { + color: var(--ibexa-dusk-black); + border-bottom: 2px solid var(--ibexa-dusk-black); } -.tabbed-set input:nth-child(n+1):checked + label + .tabbed-content { - display: block; +div.pills { + float: right; } -.github-icon { - padding: 1rem; -} - -.badge { +.pill { + background-color: var(--ibexa-jazzberry); border-radius: 2.18px; padding: 3px 8px; color: var(--white); - font-size: 12px; + font-size: 10px; font-weight: 500; } -.experience-badge::after { +.experience-pill::after { content: "Experience"; } -.experience-badge { +.experience-pill { background-color: var(--ibexa-orange); } -.commerce-badge::after { +.commerce-pill::after { content: "Commerce"; } -.commerce-badge { +.commerce-pill { background-color: var(--ibexa-jazzberry); } + +.md-clipboard:focus, .md-clipboard:hover { + color: var(--ibexa-jazzberry); +} + +.md-typeset .headerlink:focus, .md-typeset .headerlink:hover, .md-typeset :target>.headerlink { + color: var(--ibexa-jazzberry); +} + +/** Cards */ +.cards>div, .cards p, +.path>div, .path p { + margin: 0px !important; +} + +div.cards, +div.path { + display: flex !important; + flex-wrap: wrap; + justify-content: space-evenly; + align-content: stretch; + gap: 0.5rem; + padding-top: 1rem; +} + +div.path { + margin: 0 auto; + width: 20rem; + gap: 0; + flex-direction: column; +} + +.cards div.card-wrapper div div, +.cards div.card-wrapper div div{ + + border-radius: 5px; + background-color: #FFFFFF; + padding: 0.7rem; + margin-bottom: 0.4rem; + border: 1px solid #E0E0E8; + min-width: 230px; + height: 240px; + box-sizing: border-box; + overflow: auto; + justify-content: center; +} + +.cards div.card-wrapper div div, +.path div.card-wrapper div div { + border: 1px solid #D4D4D4; + border-radius: 5px; + background-color: #FFFFFF; + padding: 0.7rem; + margin-bottom: 0.4rem; + overflow: auto; +} + +.cards div.card-wrapper div div:hover, +.path div.card-wrapper div div:hover { + border-color: var(--ibexa-dusk-black); +} +.cards div.card-wrapper div div { + max-width: 10rem; +} + +.path div.card-wrapper:not(:last-child)::after { + content:url(../images/arrow_down.svg); + display: block; + width: 100%; + text-align: center; +} +.cards p.title, +.path p.title { + font-size: 1em; + min-height: 2em; + font-weight: 550; + line-height: 1; + padding-bottom: 0.5em; + box-sizing: border-box; + margin-bottom: 2em; + justify-content: center; +} + +.cards p.description, +.path p.description { + color: var(--ibexa-dusk-black); +} + + + diff --git a/docs/css/docs.switcher.css b/docs/css/docs.switcher.css index 34570737b4..cb4e8073c7 100644 --- a/docs/css/docs.switcher.css +++ b/docs/css/docs.switcher.css @@ -1,21 +1,62 @@ -.ez-docs-switcher { - line-height: 1.25; +.switcher { + font-size: 13px; + padding-top: 0.2rem; } -.ez-docs-switcher__selected-item { - padding-top: 6px; - line-height: calc(33 / 19.8); +.docs-switcher { + padding-right: 1rem; +} + +.md-header .switcher__selected-item { + padding: 0 0.5rem; +} + +.md-header .switcher__selected-item:hover { + background-color: var(--codeblock); +} + +.switcher__list dl { + margin: 0; +} + +.switcher__list dd { + margin: 0; + line-height: 1; + color: var(--ibexa-dusk-black); +} + +.switcher__list dd:focus, +.switcher__list dd:hover { + background-color: var(--mid-grey); +} + +.switcher__list dd.rtd-current-item { + background-image: url(../images/check.svg); + background-size: 15px 18px; + background-repeat: no-repeat; + background-position: 14px; +} + +.switcher__list dd a { + padding: 8px 14px 8px 34px; + line-height: 2; + display: block; +} + +.switcher__selected-item { position: relative; cursor: pointer; display: flex; align-items: center; } -.ez-docs-switcher__selected-item .ez-docs-switcher__label { - display: inline-block; +.switcher__selected-item .switcher__label { + display: block; + padding: 12px; + line-height: 1rem; } -.ez-docs-switcher__selected-item:after { +.switcher__selected-item:after { content: ''; width: 0; height: 0; @@ -25,45 +66,59 @@ transition: transform .4s,-webkit-transform .4s; } -.ez-docs-switcher__selected-item--expanded:after { - transform: rotateX(180deg); - transition: transform .4s,-webkit-transform .4s; +.md-header__title[data-md-state=active] .switcher__label, +.switcher__label { + position: relative; + transition: none; + display: block; + opacity: 1; + z-index: initial; + pointer-events: initial; + transform: none; + padding-right: .3rem; + color: #fff; + width: 100%; } -.ez-docs-switcher__list { +.switcher__list { + min-width: 100px; margin: 0; - padding: .5rem 0; opacity: 0; list-style: none; - background: #3a3b3c; - max-height: 0; + background: #fff; overflow: hidden; - transition: - max-height .3s cubic-bezier(0.25, 0.8, 0.25, 1), - opacity .2s cubic-bezier(0.25, 0.8, 0.25, 1); + border-radius: 2px; + max-height: 0; + border: 1px solid var(--mid-grey); } -.ez-docs-switcher__selected-item--expanded + .ez-docs-switcher__list { +.switcher__selected-item--expanded + .switcher__list { opacity: 1; max-height: 500px; } -.ez-docs-switcher__item:focus, -.ez-docs-switcher__item:hover { - background: #313233; +.version-switcher { + padding-left: 0; + padding-right: 1rem; + min-width: 100px; } -.md-header-nav__title[data-md-state=active] .ez-docs-switcher__label, -.ez-docs-switcher__label { - position: relative; - transition: none; - display: block; - padding: .5rem; - margin: 0; - font-size: 1.8rem; - font-weight: normal; - opacity: 1; - z-index: initial; - pointer-events: initial; - transform: none; +.md-header__switcher .rst-versions { + position: relative !important; + min-width: 100px; + width: auto; + text-align: left; + line-height: 1rem; + background-color: var(--ibexa-dusk-black); +} + +.md-header__switcher .rst-versions .rst-current-version { + color: #fff; + background-color: transparent; + text-align: right; + font-size: 100%; +} + +.md-header__switcher .rst-other-versions dt { + display: none; } diff --git a/docs/css/ez-guidelines-badges.css b/docs/css/ez-guidelines-badges.css deleted file mode 100644 index dfdcefc173..0000000000 --- a/docs/css/ez-guidelines-badges.css +++ /dev/null @@ -1,82 +0,0 @@ -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .badge { - padding: 4px; - font-size: 13px; - font-weight: 700; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .badge.badge-info { - color: #fff; - background: #17a2b8; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .badge.badge-warning { - color: #555; - background: #f7d000; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .badge.badge-danger { - color: #fff; - background: #d92d42; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .badge.badge-secondary { - color: #fff; - background: #106d95; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .ez-badge.ez-badge--small { - font-size: 12px; - font-weight: 400; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .ez-icon { - margin-right: 4px; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges .ez-icon--small { - width: 16px; - height: 16px; - vertical-align: middle; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample .ez-badge:last-of-type .ez-icon { - fill: #fff; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items { - padding: 0 3em; - max-height: 2em; - background: #ededed; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__connector { - display: inline-block; - color: #878787; - font-size: 12px; - font-weight: 600; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__pill { - display: inline-block; - margin: 2px 3px; - border-radius: 4px; - padding: 4px; - font-size: 12px; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-icon.ez-icon--small { - width: 16px; - height: 16px; - vertical-align: middle; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__pill.ez-details-items__pill--content-type { - color: #646464; - background: #fff; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__pill.ez-details-items__pill--language { - color: #fff; - font-style: italic; - background: #878787; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__pill.ez-details-items__pill--location { - color: #0a5574; - background: #fff; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__connector { - display: inline-block; - color: #878787; - font-size: 12px; - font-weight: 600; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-badges--sample-contextual .ez-details-items__connector.ez-details-items__connector--small { - font-size: 11px; - font-weight: 100; -} diff --git a/docs/css/ez-guidelines-buttons.css b/docs/css/ez-guidelines-buttons.css deleted file mode 100644 index 45b08b463b..0000000000 --- a/docs/css/ez-guidelines-buttons.css +++ /dev/null @@ -1,160 +0,0 @@ -.ez-guidelines .ez-code-example { - margin-top: -1.25em; -} -.ez-guidelines .ez-code-example .btn { - display: inline-flex; - text-align: center; - vertical-align: middle; - user-select: none; - line-height: 1.5; - border-radius: .25em; - padding: 0.375em 0.75em; - white-space: nowrap; - transition: opacity 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), background 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); - font-size: 1em; - color: #fff; -} -.ez-guidelines .ez-code-example .btn:disabled, -.ez-guidelines .ez-code-example .btn[disabled] { - cursor: not-allowed; - opacity: .3; -} -.ez-guidelines .ez-code-example .btn-primary, -.ez-guidelines .ez-code-example .btn-primary:disabled { - background-color: #f15a10; - border-color: #f15a10; - color: #fff; -} -.ez-guidelines .ez-code-example .btn-primary:hover { - background-color: #cf4c0c; - border-color: #c3480b; -} -.ez-guidelines .ez-code-example .btn-primary:focus { - box-shadow: 0 0 0 0.2rem rgba(241, 90, 16, 0.5); -} -.ez-guidelines .ez-code-example .btn-primary:disabled { - cursor: not-allowed; -} -.ez-guidelines .ez-code-example .btn-dark { - background-color: #555; - border-color: #555; -} -.ez-guidelines .ez-code-example .btn-dark:hover { - background-color: #424242; - border-color: #3c3c3c; -} -.ez-guidelines .ez-code-example .btn-dark:focus { - box-shadow: 0 0 0 0.2rem rgba(85, 85, 85, 0.5); -} -.ez-guidelines .ez-code-example .btn-secondary { - background-color: #0f6d95; - border-color: #0f6d95; - color: #fff; -} -.ez-guidelines .ez-code-example .btn-secondary:hover { - background-color: #0c5472; - border-color: #0b4b67; -} -.ez-guidelines .ez-code-example .btn-secondary:focus { - box-shadow: 0 0 0 0.2rem rgba(16, 109, 149, 0.5); -} -.ez-guidelines .ez-code-example .btn-danger { - background-color: #d92d42; - border-color: #d92d42; - color: #fff; -} -.ez-guidelines .ez-code-example .btn-danger:hover { - background-color: #bd2235; - border-color: #b32032; -} -.ez-guidelines .ez-code-example .btn-danger:focus { - box-shadow: 0 0 0 0.2rem rgba(217, 45, 66, 0.5); -} -.ez-guidelines .ez-code-example .btn-outline-secondary { - background-color: transparent; - border-color: 1px solid #0f6d95; - color: #0f6d95; -} -.ez-guidelines .ez-code-example .btn-outline-secondary:hover { - background-color: #0c5472; - border-color: #0c5472; -} -.ez-guidelines .ez-code-example .btn-outline-secondary:focus { - background: #0f6d95; - color: #fff; -} - -.ez-guidelines-buttons__two-buttons { - display: block; - position: relative; -} - -.ez-guidelines-buttons__two-buttons .btn:first-of-type { - margin-right: .625em; -} - -.ez-guidelines-buttons__two-buttons .btn:last-of-type { - margin-left: .625em; -} - -.ez-guidelines .ez-code-example .btn.ez-btn--wide { - padding: .5rem 3rem; - font-size: 13px; -} - -.ez-guidelines .ez-guidelines-buttons__button-icon .ez-code-example .btn { - font-weight: 700; -} - -.ez-guidelines-buttons__button-icon .ez-icon { - margin-right: .5em; - fill: #fff; -} - -.ez-guidelines .ez-code-example .btn-block { - width: 5em; - border-radius: 0; - font-size: 0.6875em; - color: #fff; - line-height: 1.4; -} - -.ez-guidelines-buttons__side-menu .ez-code-example .btn { - display: flex; - flex-direction: column; - align-items: center; - padding: 0.5em 0.3125em; - color: #fff; -} - -.ez-guidelines-buttons__side-menu .ez-code-example .ez-icon { - width: 1.75em; - height: 1.75em; - margin-bottom: .1875em; - fill: #fff; -} - -.ez-guidelines-buttons__side-menu.btn-block--white .ez-code-example { - color: white; -} - -.ez-guidelines-buttons__table-header .ez-code-example .btn { - line-height: .85; - padding: .5em .55em; -} - -.ez-guidelines-buttons__table-header .ez-code-example .ez-icon { - width: 1.5em; - height: 1.5em; - fill: #fff; -} - -.ez-guidelines-buttons__table-row .ez-code-example .btn-icon { - padding: 0; -} - -.ez-guidelines-buttons__table-row .ez-code-example .ez-icon { - fill: #f15a10; - width: 1.5em; - height: 1.5em; -} diff --git a/docs/css/ez-guidelines-colors.css b/docs/css/ez-guidelines-colors.css deleted file mode 100644 index 499532c641..0000000000 --- a/docs/css/ez-guidelines-colors.css +++ /dev/null @@ -1,101 +0,0 @@ -.ez-guidelines .color-box { - margin: .75em 1.25em 1em 0; - width: 7em; -} -.ez-guidelines .color-box .color-frame { - height: 5em; - width: 100%; - margin-bottom: 1em; - border: 1px solid transparent; - border-radius: .25em; -} -.ez-guidelines .color-box .color-frame-light { - border: 1px solid #ddd; -} -.ez-guidelines .color-box .color-code { - text-align: center; - margin-top: 1.65em; -} -.ez-guidelines .color-box .color-label { - margin-top: -.75em; - margin-bottom: 0; - font-size: .8125em; -} -.ez-guidelines .ez-color-primary { - background-color: #f15a10; -} -.ez-guidelines .ez-color-secondary { - background-color: #0f6d95; -} -.ez-guidelines .ez-color-hyperlink { - background-color: #0645ad; -} -.ez-guidelines .ez-color-positive { - background-color: #00825c; -} -.ez-guidelines .ez-color-warning { - background-color: #f7d000; -} -.ez-guidelines .ez-color-danger { - background-color: #d92d42; -} -.ez-guidelines .ez-ground-base-pale { - background-color: #fafafa; -} -.ez-guidelines .ez-ground-base-light { - background-color: #f5f5f5; -} -.ez-guidelines .ez-ground-base { - background-color: #f3f3f3; -} -.ez-guidelines .ez-ground-base-medium { - background-color: #ededed; -} -.ez-guidelines .ez-ground-base-dark { - background-color: #e5e3e3; -} -.ez-guidelines .ez-color-base-pale{ - background-color: #dbdbdb; -} -.ez-guidelines .ez-color-base-light { - background-color: #878787; -} -.ez-guidelines .ez-color-base-medium { - background-color: #646464; -} -.ez-guidelines .ez-color-base-dark { - background-color: #555; -} -.ez-guidelines .ez-color-white { - background-color: #fff; -} -.ez-guidelines .ez-color-black { - background-color: #333; -} -.ez-guidelines .ez-secondary-gound-pale { - background-color: #e1f5ff; -} -.ez-guidelines .ez-ground-primary { - background-color: #a8c8d5; -} -.ez-guidelines .ez-secondary-ground { - background-color: #2b84b1; -} -.ez-guidelines .ez-secondary-ground-dark { - background-color: #0a5574; -} -.ez-guidelines .ez-ground-primary-inverted { - background-color: #565e63; -} -.ez-guidelines .ez-ground-primary-inverted-light { - background-color: #445a64; -} -.ez-guidelines .ez-color-warning-pale { - background-color: #fceaec; -} -.ez-guidelines .ez-color-warning-dark { - background-color: #aa0000; -} -.ez-guidelines .ez-alloyeditor-color-hover { - background-color: #65b6f0; -} diff --git a/docs/css/ez-guidelines-form-components.css b/docs/css/ez-guidelines-form-components.css deleted file mode 100644 index 49981ca31c..0000000000 --- a/docs/css/ez-guidelines-form-components.css +++ /dev/null @@ -1,63 +0,0 @@ -.ez-guidelines .ez-code-example .ez-label { - margin-bottom: 0; - font-size: 1em; - font-weight: 700; - color: #333; -} -.ez-guidelines .ez-code-example .ez-label:after { - content: ": "; -} -.ez-guidelines .ez-code-example .form-group .form-control { - display: block; - width: 100%; - height: 38px; - padding: 0.375em 0.75em; - font-size: 1em; - line-height: 1.5; - color: #878787; - background-clip: padding-box; - border: 1px solid #ededed; - border-radius: 0.25em; - transition: cubic-bezier(0.25, 0.8, 0.25, 1); -} -.ez-guidelines .ez-guidelines-formcomponent-number .ez-code-example .form-control { - width: 10%; -} - -.ez-guidelines .ez-guidelines-formcomponent-multiple .ez-code-example .form-group .form-control { - height: 104px; -} -.ez-guidelines .ez-code-example .form-check-inline { - display: inline-flex; - align-items: center; - margin-right: 0.75em; - width: 100%; - padding-left: 0; -} -.ez-guidelines .ez-code-example .form-check-inline .form-check-input { - position: relative; - margin-top: 0; - margin-right: 0.3125em; - margin-left: 0; -} -.ez-guidelines .ez-guidelines-formcomponent-radioExample .ez-code-example .example-group { - margin-top: 0.5em; -} -.ez-guidelines .ez-guidelines-formcomponent-radioExample .ez-code-example .example-group .form-control { - font-size: initial; -} -.ez-guidelines .ez-guidelines-formcomponent-radioExample .ez-code-example .ez-icon { - margin-right: 0.25em; -} -.ez-guidelines.md-typeset.bootstrap-iso .ez-code-example .form-group, -.ez-guidelines.md-typeset.bootstrap-iso .ez-guidelines-formcomponent .ez-code-example .form-group { - margin-bottom: 0; -} -.ez-guidelines.md-typeset.bootstrap-iso .ez-guidelines-formcomponent .ez-code-example .form-group .form-control { - width: auto; - height: 38px; -} -.ez-guidelines.md-typeset.bootstrap-iso label { - margin-top: 0.25em; - margin-bottom: 0; -} diff --git a/docs/css/ez-guidelines-icons.css b/docs/css/ez-guidelines-icons.css deleted file mode 100644 index 42fdf6f517..0000000000 --- a/docs/css/ez-guidelines-icons.css +++ /dev/null @@ -1,61 +0,0 @@ -.ez-guidelines .ez-code-example .ez-icon { - width: 2em; - height: 2em; -} -.ez-guidelines .ez-icon.ez-icon--small { - width: 1em; - height: 1em; -} -.ez-guidelines .ez-icon.ez-icon--small-medium { - width: 1.25em; - height: 1.25em; -} -.ez-guidelines .ez-icon.ez-icon--medium { - width: 1.5em; - height: 1.5em; -} -.ez-guidelines .ez-icon.ez-icon--large { - width: 3em; - height: 3em; -} -.ez-guidelines .ez-icon.ez-icon--extra-large { - width: 4em; - height: 4em; -} -.ez-guidelines .ez-icon.ez-icon--dark { - fill: #333; -} -.ez-guidelines .ez-icon.ez-icon--light { - fill: #fff; -} -.ez-guidelines .ez-icon.ez-icon--secondary { - fill: #106d95; -} -.ez-guidelines-icons__colored .ez-code-example .ez-icon { - margin-right: .25em; -} - -.ez-guidelines .icon-box { - margin: .5em .5em 0 0; - height: 7em; - width: 8em; - background-color: #ededed; - border-radius: 4px; - text-align: center; -} -.ez-guidelines .icon-box .ez-icon { - display: inline-block; - margin-top: 1em; - width: 2.5em; - height: 2.5em; - fill: #333; -} -.ez-guidelines .icon-box .icon-label { - margin-top: .75em; - padding: 0 .5em; - font-size: .75em; - line-height: 14px; - color: #333; - overflow-wrap: break-word; - word-wrap: break-word; -} diff --git a/docs/css/ez-guidelines-modals.css b/docs/css/ez-guidelines-modals.css deleted file mode 100644 index 806c25ff5e..0000000000 --- a/docs/css/ez-guidelines-modals.css +++ /dev/null @@ -1,233 +0,0 @@ -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__sample .modal { - position: relative; - display: block; - z-index: 1; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__sample .modal-dialog { - max-width: none; - margin: 3em auto; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__sample p { - margin-bottom: 0; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals .modal-dialog { - margin: 1.75em auto; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals .modal.fade { - opacity: 1; - transition: opacity 0.15s linear; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals .modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: rgba(0,0,0,0.45); - width: 100%; - height: 100%; - z-index: 1040; -} -.ez-guidelines-modals .ez-modal .modal-content { - background-color: #ededed; - border: none; - border-radius: 0.25em; -} -.ez-guidelines-modals .ez-modal--send-to-trash .modal-header { - border-bottom: none; - padding: 0.9375em 0.9375em 0 0.9375em; - position: relative; - min-height: 2.5em; -} -.ez-guidelines-modals .ez-modal--send-to-trash .modal-body { - padding: 0.9375em 1.875em 0.5em; -} -.ez-guidelines-modals .ez-modal--send-to-trash .modal-body__main { - margin-bottom: 0; -} -.ez-guidelines-modals .ez-modal--send-to-trash .modal-footer { - border-top: none; - padding: 1em; -} -.ez-guidelines-modals .ez-modal--send-to-trash .modal-footer .btn { - line-height: 1.25; - padding: 0.5em 0.75em; - font-size: 1em; -} -.ez-guidelines-modals .ez-modal .modal-footer .btn { - margin: 0 0.625em; -} -.ez-guidelines-modals .ez-modal .modal-footer .btn:last-of-type { - margin-right: 0; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__send-to-trash .modal { - position: fixed; - display: none; - z-index: 1050; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__send-to-trash .modal-dialog { - max-width: 65%; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-sample .ez-guidelines-sample--images { - padding: 1.5em 1em 2.5em 1em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-dialog { - max-width: none; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-content { - background-color: #ededed; - border: none; - border-radius: 0.25em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-header { - align-items: center; - background-color: #dbdbdb; - border-bottom: 1px solid #f3f3f3; - padding: 1em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-title { - margin-bottom: 0; - font-size: 1.5em; - font-weight: 700; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-title:after { - content: attr(data-notifications-total); -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .modal-body { - padding: 2em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .ez-notifications-modal__spinner { - display: none; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-table--notifications { - margin-bottom: 0; - width: 100%; - max-width: 100%; - background-color: #fff; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header th { - border: none; - padding: 0.6875em 1.375em; - background-color: #555; - color: #fff; - font-weight: 700; - font-size: 0.9375em; - vertical-align: top; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__type, -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__description, -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__time - { - border-top: 1px solid #555; - padding: 0.7em 1.4em; - vertical-align: middle; - font-size: 0.875em; - line-height: 20px; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header tr { - color: #878787; - cursor: pointer; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__type { - display: flex; - align-items: center; - border-top: 1px solid #555; - width: 100%; - padding-top: 1em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__type .type__icon { - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - width: 2em; - height: 2em; - background-color: #878787; - fill: #fafafa; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__type .ez-icon { - width: 1.25em; - height: 1.25em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__type .type__text { - margin-left: 1em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__description .description__title, -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__description .description__text { - margin-bottom: 0; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header .n-notifications-modal__description .description__title__item { - font-weight: 700; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .ez-translation { - width: 100%; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .ez-translation .modal-dialog { - max-width: initial; - display: inline-block; - left: 50%; - transform: translateX(-50%); -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .ez-translation .modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100vw; - background-color: #ededed; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - padding: 1em; - border-bottom: none; - border-top-left-radius: 0.3em; - border-top-right-radius: 0.3em; - background: #dbdbdb; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-title { - margin-bottom: 0; - line-height: 1.5; - font-size: 1.375em; - font-weight: 700; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-header .close { - padding-top: 1.25em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body { - padding: 2em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body .ez-translation__title { - font-weight: bold; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body .ez-translation__language-wrapper { - display: flex; - flex-wrap: nowrap; - max-width: 70vw; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body .ez-translation__language-wrapper:not(:last-child) { - margin-bottom: 2em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body .ez-translation__input-wrapper { - width: 15em; - background: #fff; - margin: 0.5em; - border-radius: 5px; - padding: 0.75em; - line-height: 1; - color: #106d95; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-body .ez-translation__label { - margin: 0; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-footer { - display: flex; - align-items: center; - padding: 1em; - border-top: 1px solid #f3f3f3; - border-bottom-right-radius: 0.3em; - border-bottom-left-radius: 0.3em; -} -.ez-guidelines.bootstrap-iso .ez-guidelines-modals__with-header-footer .modal-footer .btn { - margin: 0 0.625em; -} diff --git a/docs/css/ez-guidelines-pagination.css b/docs/css/ez-guidelines-pagination.css deleted file mode 100644 index 9159674efa..0000000000 --- a/docs/css/ez-guidelines-pagination.css +++ /dev/null @@ -1,55 +0,0 @@ -.ez-guidelines .ez-guidelines-pagination .ez-code-example { - background-color: #f5f5f5; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .ez-pagination { - display: flex; - justify-content: center; - align-items: center; - margin-left: 0; - font-size: 1em; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .ez-pagination__text { - font-size: 0.75em; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item { - margin-left: 0; - margin-bottom: 0; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item.prev { - margin-left: -0.5em; - margin-right: 0.5em; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item.active { - border-radius: 50%; - border: transparent; - background-color: #ffffff; - font-weight: 700; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .ez-pagination .page-item .page-link { - margin-left: 0.5em; - border-radius: 50%; - border: transparent; - background-color: transparent; - color: #333; - line-height: 1.25; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .page-item.prev .page-link, -.ez-guidelines .ez-guidelines-pagination .ez-code-example .page-item.next .page-link { - border: #106d95; - border-radius: 0.3em; - padding: 0.5em 0.75em; - background-color: #106d95; - color: #fff; - line-height: 1.25; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item.active .page-link { - margin: 0.25em; - padding: 0.25em 0.5em; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item.next { - margin-bottom: auto; -} -.ez-guidelines .ez-guidelines-pagination .ez-code-example .pagination .page-item.disabled { - opacity: 0.3; - cursor: not-allowed; -} diff --git a/docs/css/ez-guidelines-switchers.css b/docs/css/ez-guidelines-switchers.css deleted file mode 100644 index b4bdc07f98..0000000000 --- a/docs/css/ez-guidelines-switchers.css +++ /dev/null @@ -1,148 +0,0 @@ -.ez-guidelines .ez-guidelines-switcher .ez-data-source__label { - position: relative; - top: calc(50% - 15px); - width: 4em; - height: calc(2em + 0.25em); - border-radius: 50px; - cursor: pointer; -} -.ez-guidelines .ez-guidelines-switcher .ez-data-source__input { - opacity: 0; - height: 1px; - display: none; -} -.ez-guidelines .ez-guidelines-switcher .ez-data-source__input:checked + .ez-data-source__indicator { - background: #2b84b1; - opacity: 1; -} -.ez-guidelines .ez-guidelines-switcher .ez-data-source__input:checked + .ez-data-source__indicator::before { - transform: translateX(1.75em); -} -.ez-guidelines .ez-guidelines-switcher .ez-data-source__indicator { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #dbdbdb; - border-radius: 50px; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); -} -.ez-guidelines .ez-guidelines-switcher .ez-data-source__indicator::before { - content: ""; - position: absolute; - width: 2em; - height: 2em; - top: 2px; - left: 2px; - border-radius: 50px; - background: #fff; - transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); -} -.ez-guidelines .ez-guidelines.md-typeset.bootstrap-iso label { - margin: 0; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon { - position: relative; - margin: 0; - border: 2px solid #106d95; - border-radius: 0.25em; - min-width: 4.6875em; - padding: 4px; - line-height: 0.5; - z-index: 1; - cursor: pointer; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon:before { - content: ''; - position: absolute; - top: 50%; - left: 0; - border-radius: 0.25em; - width: 2em; - height: 2em; - background-color: #106d95; - z-index: -1; - transform: translate(calc(100% + 0.3em), -50%); - transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon .ez-icon { - margin: 0.125em; - height: 1.5em; - width: 1.5em; - fill: #106d95; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon .ez-icon + .ez-icon { - margin: 0.125em 0.125em 0.125em 0.3125em; - fill: #fff; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.is-checked:before { - transform: translate(.15em, -50%); -} - -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.is-checked .ez-icon { - fill: #fff; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.is-checked .ez-icon + .ez-icon { - fill: #106d95; -} -.ez-guidelines .ez-guidelines-switcher-icons--primary .ez-checkbox-icon.is-checked .ez-icon + .ez-icon { - fill: #f15a10; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon .ez-checkbox-icon__checkbox { - display: none; -} - -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.ez-page-info-bar-switcher { - border: 2px solid #f15a10; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.ez-page-info-bar-switcher:before { - background-color: #f15a10; -} -.ez-guidelines .ez-guidelines-switcher-icons .ez-checkbox-icon.ez-page-info-bar-switcher:not(.is-checked) .ez-icon.ez-icon--view { - fill: #f15a10; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher { - display: flex; - justify-content: space-around; - align-items: center; - height: 44px; - width: 118px; - border: 2px solid #646464; - border-radius: 4px; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action { - display: flex; - align-items: center; - justify-content: center; - flex: 0 0 34px; - width: 34px; - height: 34px; - border: 0; - padding: 0; - margin: 0; - border-radius: 4px; - transition: background 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); - cursor: pointer; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action .ez-icon { - width: 24px; - height: 24px; - fill: #646464; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action:hover, -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action:focus { - background: #3e3e3e; - outline: none; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action:hover .ez-icon, -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action:focus .ez-icon { - fill: #fff; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action--selected { - background: #646464; -} -.ez-guidelines .ez-guidelines-switcher-preview .ez-preview-switcher__action--selected .ez-icon { - fill: #fff; -} diff --git a/docs/css/ez-guidelines-tables.css b/docs/css/ez-guidelines-tables.css deleted file mode 100644 index f0d842ffb1..0000000000 --- a/docs/css/ez-guidelines-tables.css +++ /dev/null @@ -1,197 +0,0 @@ -.ez-guidelines-tables__with-header { - color: #333; -} -.ez-guidelines-tables__with-header .ez-code-example .table, -.ez-guidelines-tables__with-header .ez-code-example .table.ez-table--no-border, -.ez-guidelines-tables__notifications - .ez-code-example - .table.n-table--notifications, -.ez-guidelines-tables__list .table.ez-table--list { - margin-bottom: 0; -} -.ez-guidelines-tables__with-header .ez-code-example .ez-table-header { - display: flex; - justify-content: space-between; - background-color: #a8c8d5; - align-items: center; - padding: 0 1.25em; - min-height: 50px; - border-radius: 0.25em 0.25em 0 0; -} -.ez-guidelines-tables__with-header .ez-code-example .ez-table-header__headline { - font-size: 1.0625em; - margin-bottom: 0; - font-weight: bold; - color: #333; -} -.ez-guidelines-tables__with-header .ez-code-example .table thead th { - font-size: 0.9375em; - font-weight: bold; - padding: 0.7em 1.4em; - line-height: 20px; - vertical-align: top; - border-top: none; - border-bottom: 2px solid #a8c8d5; -} -.ez-guidelines-tables__with-header .table tbody td { - padding: 0.7em 1.4em; - font-size: 0.875em; - line-height: 1.25em; - vertical-align: middle; - border-top: 1px solid #a8c8d5; -} -.ez-guidelines-tables__with-header .ez-code-example .ez-table-no-content { - margin-bottom: 0; - padding: 0.75em 1em; - background-color: #fff; - font-style: italic; - font-size: 0.875em; - color: #555; -} -.ez-guidelines-tables__with-header--action-btn .table td:last-of-type { - text-align: end; -} -.ez-guidelines-tables__with-header--action-btn .ez-code-example .ez-table__cell--has-checkbox { - width: 5%; -} -.ez-guidelines-tables__with-header--action-btn .ez-code-example .ez-table__cell--has-action-btns { - padding-right: 1.75em; - padding-left: .75em; -} -.ez-guidelines-tables__with-header--action-btn .ez-code-example .btn-icon { - padding: 0; -} -.ez-guidelines-tables__with-header--action-btn .table .btn-icon .ez-icon-edit { - fill: #f15a10; - width: 1.5em; - height: 1.5em; -} -.md-typeset .ez-guidelines-tables__with-header--action-btn .ez-code-example svg { - max-width: none; -} -.bootstrap-iso .ez-guidelines-tables__with-header--action-btn .ez-code-example svg:not(:root) { - overflow: visible; -} -.ez-guidelines-tables__with-header .ez-table-header .btn { - line-height: 0.85; - padding: 0.5em 0.55em; - margin-left: 0.15em; - margin-right: 0; -} -.ez-guidelines-tables__with-header .ez-table-header.ground-base { - background-color: #dbdbdb; -} -.ez-guidelines-tables__with-header .table td .btn { - font-size: 1.15em; -} -.ez-guidelines-tables__with-header .ez-table--no-border tbody td { - border: none; -} -.ez-guidelines-tables__notifications .ez-code-example .table thead th { - border-bottom: none; -} -.ez-guidelines-tables__notifications .n-table--notifications thead th { - border: none; - background-color: #555; - padding: 0.7em 1.4em; - color: #fff; - font-size: 0.9375em; - font-weight: 700; - vertical-align: top; - line-height: 20px; -} -.ez-guidelines-tables__notifications - .n-table__body - .n-notifications-modal__item { - border-top: 1px solid #555; - background-color: #fff; - cursor: pointer; -} -.ez-guidelines-tables__notifications .n-table--notifications .n-table__body td { - padding: 0.7em 1.4em; - font-size: 14px; - line-height: 20px; - vertical-align: middle; - border-top: none; -} -.ez-guidelines-tables__notifications .n-notifications-modal__type { - display: flex; - align-items: center; -} -.ez-guidelines-tables__notifications .n-notifications-modal__type .type__icon { - background-color: #f7d000; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - width: 2.5em; - height: 2.5em; -} -.ez-guidelines-tables__notifications - .n-notifications-modal__type - .type__icon - .ez-icon { - width: 1.25em; - height: 1.25em; -} -.ez-guidelines-tables__notifications .n-notifications-modal__type .type__text { - margin-left: 1em; -} -.ez-guidelines-tables__notifications - .n-notifications-modal__description - .description__title { - margin-bottom: 0; -} -.ez-guidelines-tables__notifications - .n-notifications-modal__description - .description__title__item { - font-weight: 700; -} -.ez-guidelines-tables__notifications - .n-notifications-modal__description - .description__text { - margin-bottom: 0; - max-width: 50ch; - float: left; -} -.ez-guidelines-tables__notifications - .n-notifications-modal__description - .description__read-more { - display: none; -} -.ez-guidelines-tables__list .table.ez-table thead th { - border-top: none; - border-bottom: none; -} -.ez-guidelines-tables__list .ez-table--list thead th { - border-radius: 0.3em 0.3em 0 0; - padding: 12px 24px; - background-color: #dbdbdb; - color: #093a50; - font-size: 0.9375em; - font-size: 15px; - font-weight: 700; - vertical-align: top; - line-height: 20px; -} -.ez-guidelines-tables__list .ez-table--list tbody td { - padding: 12px 24px; - font-size: 15px; - border-top: none; -} -.ez-guidelines-tables__list .ez-table--list td:first-child { - width: 33.33%; - background-color: #ededed; - font-weight: 700; - border-radius: 0 0 0 0.3em; -} -.ez-guidelines-tables__list .ez-table--list td:last-child { - width: 66.66%; - background-color: #fff; - font-weight: normal; - border-radius: 0 0 0.3em 0; -} - -.ez-guidelines-tables__notifications table tr td:first-child:not(.linenos) { - width: inherit; -} diff --git a/docs/css/ez-guidelines-tabs.css b/docs/css/ez-guidelines-tabs.css deleted file mode 100644 index 66b2416368..0000000000 --- a/docs/css/ez-guidelines-tabs.css +++ /dev/null @@ -1,36 +0,0 @@ -.ez-guidelines-tabs .ez-code-example .nav-tabs { - margin-left: 0; - border-bottom: 2px solid #e5e3e3; -} -.ez-guidelines-tabs .ez-code-example .nav-tabs .nav-item { - margin-bottom: -2px; - margin-left: 0; -} -.ez-guidelines-tabs .ez-code-example .nav-link { - border: 1px solid transparent; - padding: 0.5em 1em; - border-top-left-radius: 0.25em; - border-top-right-radius: 0.25em; - color: #646464; -} -.ez-guidelines-tabs .ez-code-example .nav-tabs .nav-link:hover { - background-color: #dbdbdb; - border-color: #dbdbdb; - color: #646464; -} -.ez-guidelines-tabs .ez-code-example .nav-tabs .nav-link.active { - background-color: #f5f5f5; - border-color: #f5f5f5; - color: #333; -} -.ez-guidelines-tabs--dashboard .ez-code-example .nav-tabs { - border-bottom: 2px solid #565e63; -} -.ez-guidelines-tabs--dashboard .ez-code-example .nav-tabs .nav-item { - margin-bottom: 0; -} -.ez-guidelines-tabs--dashboard .ez-code-example .nav-tabs .nav-link.active { - background-color: #565e63; - border-color: #565e63; - color: #fff; -} diff --git a/docs/css/ez-guidelines-tooltips.css b/docs/css/ez-guidelines-tooltips.css deleted file mode 100644 index 3ea66811ae..0000000000 --- a/docs/css/ez-guidelines-tooltips.css +++ /dev/null @@ -1,78 +0,0 @@ -.bootstrap-iso .ez-tooltip.show { - opacity: 1; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-top .ez-tooltip__arrow { - position: absolute; - display: block; - width: 12px; - height: 6px; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-top .ez-tooltip__arrow::before { - border-top-color: #fff; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-right .ez-tooltip__arrow { - width: 6px; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-right .ez-tooltip__arrow::before { - border-right-color: #fff; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-bottom .ez-tooltip__arrow { - height: 6px; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-bottom .ez-tooltip__arrow::before { - border-bottom-color: #fff; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-left .ez-tooltip__arrow { - width: 6px; -} -.bootstrap-iso .ez-tooltip.bs-tooltip-left .ez-tooltip__arrow::before { - border-left-color: #fff; -} -.bootstrap-iso .ez-tooltip .ez-tooltip__inner { - padding: 4px 10px; - max-width: 250px; - border: 1px solid #e5e3e3; - border-radius: 4px; - background-color: #fff; - color: #333; - font-size: 11px; - font-weight: 700; - text-align: left; - line-height: 14px; - box-shadow: 0 4px 6px rgba(135, 135, 135, 0.35); -} -.bootstrap-iso .ez-tooltip--medium .ez-tooltip__inner { - padding: 8px 16px; - font-size: 13px; - line-height: 15px; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn:first-of-type { - margin-top: 0; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn:not(:first-of-type) { - margin-top: 2em; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn-primary { - font-weight: 700; - padding: 0.375em 1.25em; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn-primary .ez-icon { - margin-right: 0.25em; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn-secondary, -.ez-guidelines .ez-guidelines-tooltips-sample .btn-dark { - margin: 0.375em; - height: 2.5em; - width: 2.5em; - border-radius: 50%; - padding: 0; - display: flex; - align-items: center; - justify-content: center; -} -.ez-guidelines .ez-guidelines-tooltips-sample .btn-secondary .ez-icon, -.ez-guidelines .ez-guidelines-tooltips-sample .btn-dark .ez-icon { - width: 24px; - height: 24px; - fill: #fff; -} diff --git a/docs/css/ez-guidelines-typography.css b/docs/css/ez-guidelines-typography.css deleted file mode 100644 index 297a6fdcaa..0000000000 --- a/docs/css/ez-guidelines-typography.css +++ /dev/null @@ -1,40 +0,0 @@ -.ez-guidelines .ez-guidelines-font { - font-family: 'Noto Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - color: #333; -} -.ez-guidelines .ez-guidelines-h1 { - font-size: 1.75em; - margin-bottom: 0; -} -.ez-guidelines .ez-guidelines-h2 { - font-size: 1.5em; - margin-bottom: 0; -} -.ez-guidelines .ez-guidelines-h3 { - font-size: 1.25em; - margin-bottom: 0; -} -.ez-guidelines .ez-guidelines-h4 { - font-size: 1.125em; - margin-bottom: 0; -} -.ez-guidelines .ez-guidelines-h5 { - font-size: 1.0625em; - margin-bottom: 0; -} -.ez-guidelines .type-box { - width: 25%; - margin: .5em 2em 1em 0; - border: 1px solid #fce3da; - border-radius: .5em; - text-align: center; -} -.ez-guidelines .letter-sample { - font-size: 5em; -} -.ez-guidelines .letter-sample-italic { - font-style: italic; -} -.ez-guidelines .letter-sample-bold { - font-weight: 700; -} diff --git a/docs/css/ez-guidelines-utilities.css b/docs/css/ez-guidelines-utilities.css deleted file mode 100644 index 87cb7ad480..0000000000 --- a/docs/css/ez-guidelines-utilities.css +++ /dev/null @@ -1,63 +0,0 @@ -.ez-guidelines .mgt-5 { - margin-top: 2em; -} -.ez-guidelines .mgt-4 { - margin-top: 1.75em; -} -.ez-guidelines .mgt-3 { - margin-top: 1.5em; -} -.ez-guidelines .mgt-2 { - margin-top: 1em; -} -.ez-guidelines .mgt-1 { - margin-top: .5em; -} -.ez-guidelines .mgt-minus-5 { - margin-top: -2em; -} -.ez-guidelines .mgt-minus-4 { - margin-top: -1.75em; -} -.ez-guidelines .mgt-minus-3 { - margin-top: -1.5em; -} -.ez-guidelines .mgt-minus-2 { - margin-top: -1em; -} -.ez-guidelines .mgt-minus-1 { - margin-top: -.5em; -} -.ez-guidelines .mgb-5 { - margin-bottom: 2em; -} -.ez-guidelines .mgb-4 { - margin-bottom: 1.75em; -} -.ez-guidelines .mgb-3 { - margin-bottom: 1.5em; -} -.ez-guidelines .mgb-2 { - margin-bottom: 1em; -} -.ez-guidelines .mgb-1 { - margin-bottom: .5em; -} -.ez-guidelines .mgb-0 { - margin-bottom: 0; -} -.ez-guidelines .mgl-5 { - margin-left: 2em; -} -.ez-guidelines .mgl-4 { - margin-left: 1.75em; -} -.ez-guidelines .mgl-3 { - margin-left: 1.5em; -} -.ez-guidelines .mgl-2 { - margin-left: 1em; -} -.ez-guidelines .mgl-1 { - margin-left: .5em; -} diff --git a/docs/css/ez-guidelines.css b/docs/css/ez-guidelines.css deleted file mode 100644 index 7ad8f2617b..0000000000 --- a/docs/css/ez-guidelines.css +++ /dev/null @@ -1,53 +0,0 @@ -.ez-guidelines .ez-code-example { - font-family: "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; -} -.ez-guidelines .header-line { - display: block; - border-bottom: 1px solid #63605d; -} -.ez-guidelines .wrapper-samples { - display: flex; - justify-content: flex-start; - flex-wrap: wrap; - align-content: space-around; -} -.ez-guidelines .header-h1 { - font-size: 34px; - font-weight: 500; - line-height: 1.2; - color: #f15a10; -} -.ez-guidelines-sample { - position: relative; - border: 1px solid #00825c; - border-radius: 4px; - padding: 2em 1em; -} - -.ez-guidelines-sample-negative { - border-color: #d92d42; -} -.ez-guidelines-sample--images { - padding: 1.5em 1em 3em; -} -.ez-guidelines-sample__correct-block { - position: absolute; - bottom: 0; - margin-left: -1.125em; - width: 100%; - padding: 0.25em 0; - border-radius: 0 0 4px 4px; - background-color: #00825c; - color: #fff; - text-align: center; - font-size: 14px; -} -.ez-guidelines-sample__correct-block-negative { - background-color: #d92d42; -} -.ez-guidelines-sample__correct-block .ez-icon { - margin-top: 1em; -} -.ez-guidelines.md-typeset .ez-code-example + .codehilitetable { - margin: 0.25em 0 1em 0; -} \ No newline at end of file diff --git a/docs/css/front-page.css b/docs/css/front-page.css index e42f926dc0..6189b60abf 100644 --- a/docs/css/front-page.css +++ b/docs/css/front-page.css @@ -1,101 +1,215 @@ -.front-page h1 { - font-size: 32px; - font-weight: 600; - padding-left: 1.4rem; - margin-bottom: 1.5rem; -} - -.tile { - box-sizing: border-box; - border: 1px solid #D4D4D4; - border-radius: 5px; - background-color: #FFFFFF; - padding: 1rem 3rem 2rem 2rem; +html { + height: 100%; } + +body { + margin: 0; + font-family: "Noto Sans"; + letter-spacing: 0.12px; + line-height: 1.5; } + +.md-typeset .front-page h1, .md-typeset .front-page h2 { + line-height: 1.2; + font-weight: 600; + font-family: "Work Sans"; } + +.md-typeset .front-page h1 { + font-size: 28px; + margin-bottom: 32px; } + +.md-typeset .front-page h2 { + font-size: 22px; + margin-top: 8px; + margin-bottom: 0; } + +.md-typeset .front-page .row { + padding-top: 12px; + padding-bottom: 12px; + margin: 0 -12px; + align-items: stretch; + row-gap: 24px; } + +.md-typeset .front-page .col-12 { + padding-left: 12px; + padding-right: 12px; } + +.md-typeset .front-page .info-tile { + border: 1px solid #ececf1; + border-radius: 12px; + padding: 12px; + display: flex; + align-items: center; + flex: 1; + box-shadow: 4px 22px 47px rgba(52, 116, 204, 0.05); + height: 100%; } + .md-typeset .front-page .info-tile--link-card { + padding: 24px; } + .md-typeset .front-page .info-tile--link-card .info-tile__content { + font-size: 14px; + margin-bottom: 0; } + .md-typeset .front-page .info-tile--link-card h3 svg { + fill: #ae1164; + width: 16px; + height: 16px; + margin-right: 4px; } + .md-typeset .front-page .info-tile--link-card a { + color: #131c26; + text-decoration: underline; } + .md-typeset .front-page .info-tile__circle { + width: 72px; + height: 72px; + border-radius: 50%; + margin: 12px; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; } + .md-typeset .front-page .info-tile__content { + padding: 0 4px; + margin-bottom: 18px; height: 100%; -} - -h5.tile-title { - color: #131C26; - font-family: "Noto Sans"; - font-size: 18px; - font-weight: bold; - line-height: 50px; - padding-left: 1rem; -} - -.tile-icon { - margin-right: 1.2rem; -} - -.tile-body { - margin-left: 5.2rem; -} - -.tile-body ul { - padding-left: 0; -} - -.tile-body ul li { - list-style-type: none; - background: url(../images/page.svg) no-repeat left top; - background-size: 15px 18px; - padding-left: 2rem; - margin-left: 0.5rem; -} - -.front-page ul li::marker { - color: var(--ibexa-jazzberry); -} - -.tile-body ol, .tile-body ul { - margin-left: 0; -} - -.most-popular h5 { - background-color: rgba(216,216,216,0.27); - font-size: 13px; - font-weight: bold; + font-size: 12px; line-height: 18px; - padding: 8px 14px; - margin-bottom: 2rem; -} - -.most-popular ul { - list-style-position: inside; - margin-left: 0px; -} - -.most-popular ul li { - border-bottom: 1px solid #D8D8D8; - margin-left: 0px; - padding-left: 1rem; -} - -.latest-release h5 { - border-bottom: 3px solid #D8D8D8; + color: #71767c; + display: flex; + flex-direction: column; + justify-content: center; + flex-grow: 1; } + .md-typeset .front-page .info-tile__content:first-child { + padding-left: 0; } + .md-typeset .front-page .info-tile__content:last-child { + padding-right: 0; } + .md-typeset .front-page .info-tile__content strong { + font-family: "Work Sans"; + display: block; + font-weight: 600; + font-size: 22px; + line-height: 26px; + margin-top: 8px; + color: #131c26; } + .md-typeset .front-page .info-tile__details { + display: flex; + justify-content: flex-end; + align-items: center; + font-size: 10px; + color: #ae1164; } + .md-typeset .front-page .info-tile__arrow-icon { + width: 13px; + height: 13px; + margin-left: 4px; + fill: #ae1164; } + .md-typeset .front-page .info-tile h3 { font-size: 18px; - font-weight: bold; - line-height: 24px; - padding-bottom: 15px; -} - -.latest-release { - line-height: 32px; -} - -.features { - column-count: 2; -} - -.features ul li { - margin-bottom: 0; -} - -.front-page .badge { - background-color: var(--ibexa-jazzberry); - border-radius: 2.18px; - padding: 3px 8px; - color: var(--white); - font-size: 9px; - font-weight: 500; -} + font-weight: 600; + margin: 0 0 8px; + padding: 4px; } + .md-typeset .front-page .info-tile h3 a { + text-decoration: none; } + .md-typeset .front-page .info-tile ul { + margin: 0 0 0 12px; + list-style: none; + margin-left: 24px; } + .md-typeset .front-page .info-tile ul li { + padding: 4px; + margin: 0; } + .md-typeset .front-page .info-tile ul li + li { + margin-top: 16px; } + +.md-typeset .front-page a.info-tile:hover { + border-color: #ae1164; + box-shadow: 0px 22px 24px 0px rgba(174, 17, 100, 0.1); + text-decoration: none; } + +.md-typeset .front-page .notification { + background-color: #f3f3f6; + border-radius: 12px; + color: #131c26; + font-size: 12px; + padding: 32px; + margin: 30px 40px 40px 0; + position: relative; } + .md-typeset .front-page .notification__content { + padding-right: 260px; + padding-bottom: 32px; } + .md-typeset .front-page .notification__content h2 { + margin: 0 0 14px; } + .md-typeset .front-page .notification__content a { + color: #131c26; + text-decoration: underline; } + .md-typeset .front-page .notification__content a:hover { + color: #3474cc; } + .md-typeset .front-page .notification__cta a { + display: inline-block; + background: linear-gradient(to right, #db0032 0%, #ae1164 100%); + color: #ffffff; + text-decoration: none; + padding: 15px 16px; + border-radius: 12px; + font-size: 14px; } + .md-typeset .front-page .notification__image { + position: absolute; + right: -40px; + top: -30px; + height: 272px; } + .md-typeset .front-page .notification__image img { + height: 275px; } + +.md-typeset .front-page .accordion { + border: 1px solid #ececf1; + border-radius: 12px; + padding: 12px; } + .md-typeset .front-page .accordion details { + margin: 0; + padding: 0; + border: none; } + .md-typeset .front-page .accordion details[open] { + box-shadow: none; } + .md-typeset .front-page .accordion details[open] summary { + border-bottom: 1px solid #e0e0e8; + padding-bottom: 16px; + margin-bottom: 16px; } + .md-typeset .front-page .accordion details[open] summary .accordion__toggler svg { + transform: rotate(180deg); } + .md-typeset .front-page .accordion details[open] .row { + padding: 8px; + font-size: 14px; } + .md-typeset .front-page .accordion details[open] .row a { + color: #131c26; + text-decoration: underline; } + .md-typeset .front-page .accordion details[open] .row a:hover { + color: #3474cc; } + .md-typeset .front-page .accordion details[open] .row ul { + margin: 0 0 0 12px; + list-style: none; } + .md-typeset .front-page .accordion details[open] .row ul li { + padding: 4px; + margin: 0; } + .md-typeset .front-page .accordion details[open] .row ul li + li { + margin-top: 16px; } + .md-typeset .front-page .accordion summary { + padding: 4px 8px; + margin: 0; + list-style: none; + border: none; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; } + .md-typeset .front-page .accordion summary::before, .md-typeset .front-page .accordion summary::after, .md-typeset .front-page .accordion summary::marker, .md-typeset .front-page .accordion summary::-webkit-details-marker { + display: none; } + .md-typeset .front-page .accordion summary h2 { + margin: 0; + flex-grow: 1; } + .md-typeset .front-page .accordion summary .accordion__toggler { + flex-shrink: 0; } + .md-typeset .front-page .accordion summary .accordion__toggler svg { + width: 13px; + height: 13px; + transform-origin: center; + transition: transform 0.3s; } + +@media (min-width: 1900px) { + .bootstrap-iso .col-fhd-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; } } diff --git a/docs/css/jquery-ui.min.css b/docs/css/jquery-ui.min.css new file mode 100644 index 0000000000..fc05515b21 --- /dev/null +++ b/docs/css/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.12.1 - 2021-01-20 +* http://jqueryui.com +* Includes: core.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} \ No newline at end of file diff --git a/docs/css/navigation.css b/docs/css/navigation.css new file mode 100644 index 0000000000..9eb92dc0f6 --- /dev/null +++ b/docs/css/navigation.css @@ -0,0 +1,449 @@ +.main_nav { + border-right: 1px solid var(--mid-grey); + margin-left: -15rem; +} + +.md-sidebar__scrollwrap { + margin: 0; +} + +@media only screen and (min-width: 76.1875em) { + .main_nav { + margin-left: 0; + position: sticky; + top: 56px; + height: calc(100vh - 56px); + } + + .md-sidebar { + width: 15rem; + height: 100%; + } + + .md-sidebar--primary { + padding-left: 0.6rem; + height: calc(100vh - 130px); + } + + .md-sidebar--primary .md-sidebar__scrollwrap { + height: 100%; + } + + .md-content { + max-width: calc(100% - 15rem); + } + + .md-sidebar--secondary + .md-content { + max-width: calc(100% - 15rem * 2); + } +} + +.md-nav--primary .md-nav__title, +.md-nav--secondary .md-nav__title { + display: block; + font-size: 16px; + padding: 0.5rem 0.6rem; + height: initial; + background-color: transparent; + line-height: 1.5; + font-weight: 700; + text-overflow: ellipsis; + overflow: hidden; + color: var(--ibexa-jazzberry); +} + +/* Page TOC */ +.md-nav--secondary { + padding-left: 10px; + padding-bottom: 10px; + padding-top: 7rem; +} + +.md-nav--secondary>ul { + border-left: 1px solid var(--mid-grey); + margin-left: 0.4rem; + padding-left: 0; +} + +.md-nav--secondary li.level-1 { + padding-left: 0.5rem; +} + +.md-nav--secondary li.level-1.with-children { + padding-bottom: 0.6rem; +} + +.md-nav--secondary li.level-1 a, +.md-nav--secondary li.level-1 a:hover, +.md-nav--secondary li.level-1 a:focus { + font-size: 16px; + font-weight: 600; + color: var(--ibexa-dusk-black); +} + +.md-nav--secondary li.level-2 { + padding-left: 0; +} + +.md-nav--secondary li.level-2 a, +.md-nav--secondary li.level-2 a:hover, +.md-nav--secondary li.level-2 a:focus { + font-size: 14px; + font-weight: 400; + color: var(--dark-grey); +} + +.md-nav--secondary .md-nav__item { + list-style-type: none; +} + +.md-nav--secondary li.level-1 a:hover, +.md-nav--secondary li.level-2 a:hover, +.md-nav--secondary li.level-1 a:focus, +.md-nav--secondary li.level-2 a:focus { + text-decoration: underline; + font-weight: 600; +} + +.md-nav--secondary .md-nav__link--active { + border-left: 2px solid var(--ibexa-jazzberry); + padding-left: 0.4rem; + margin-left: -0.55rem; +} +/* End Page TOC */ + +/* Main nav */ +.md-nav--primary .md-nav__link { + font-size: 16px; +} + +.md-nav__heading { + font-weight: 700; +} + +.md-nav--primary li a, +.md-nav--primary li label { + margin: 0; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.md-nav__link::after { + font-weight: 700; +} + +.md-nav--primary .md-nav__item { + list-style-type: none; + padding: 0; +} + +.md-nav--primary .md-nav__link { + background: no-repeat left top; +} + +@media only screen and (min-width: 76.1875em) { + .md-nav__item--nested>.md-nav>.md-nav__link-title { + display: none; + } +} + +.md-nav--primary .md-nav__item--active>label, +.md-nav--primary .level-4.md-nav__link--active { + background-color: #f1f1f1; +} + +.md-nav--primary .level-1 { + background-image: url(../images/page_lg.svg); + background-position: 0.2rem; + background-size: 12px 15px; + padding-left: 1.2rem; +} + +.md-nav--primary .md-nav__item--nested>.level-1 { + background-image: url(../images/plus.svg); + background-size: 15px 18px; + background-position: 3px 11px; +} + +.md-nav--primary .md-nav__item--nested>.md-nav__toggle:checked~.level-1, +.md-nav--primary .md-nav__item--nested>.md-nav__toggle:checked~.md-nav>.level-1 { + background-image: url(../images/minus.svg); + background-size: 15px 18px; +} + +.md-nav--primary .level-2 { + background-image: url(../images/page.svg); + background-position: 1.2rem; + background-size: 12px 15px; + padding-left: 2.2rem; +} + +.md-nav--primary .md-nav__item--nested>.level-2 { + background-image: url(../images/caret.svg); + background-size: 12px 15px; +} + +.md-nav--primary .md-nav__item--nested>.md-nav__toggle:checked~.level-2, +.md-nav--primary .md-nav__item--nested>.md-nav__toggle:checked~.md-nav>.level-2 { + background-image: url(../images/caret-down.svg); + background-size: 15px 15px; +} + +.md-nav--primary .level-3 { + background-image: url(../images/page.svg); + background-position: 2.2rem; + background-size: 12px 15px; + padding-left: 3.2rem; +} + +.md-nav--primary .md-nav__item--nested>.level-3 { + background-image: url(../images/caret.svg); + background-size: 12px 15px; +} + +.md-nav--primary .md-nav__item--nested>.level-3 { + background-image: url(../images/caret.svg); + background-size: 12px 15px; +} + +.md-nav--primary a.level-4 { + border-left: 1px solid var(--mid-grey); + margin-left: 2rem; + padding-left: 0.7rem; +} + +.md-nav--primary .level-4.md-nav__link--active { + border-left: 2px solid var(--ibexa-jazzberry); +} + +.md-nav[data-md-level="3"] ul li { + padding-left: 0.5rem; +} + +.md-nav--primary .level-4 { + padding-left: 0; +} + +.md-nav--primary li a:hover, +.md-nav--primary li label:hover, +.md-nav--primary li a:focus, +.md-nav--primary li label:focus, +.md-nav--primary .level-4:hover, +.md-nav--primary .level-4:focus { + color: var(--ibexa-jazzberry) !important; +} + +.md-nav--primary>.md-nav__list>li:first-child { + display: none; +} + +.md-nav--primary>.md-nav__list>li:nth-child(2) { + border-top: 0; +} + +/* End Main nav */ + +.md-sidebar__inner { + font-family: "Noto Sans", sans-serif; +} + +[dir=ltr] .md-sidebar__inner { + padding-right: 0; +} + + +.md-header__button { + height: 100%; +} + +.md-header__button.md-logo { + display: block; +} + +.md-header__button.md-logo img { + width: inherit; +} + +.md-header__source { + width: auto; + padding-right: 1rem; + padding-top: 0.2rem; +} + +.source-github { + font-size: 13px; +} + +.site-header { + width: 15rem; + font-size: 16px; + font-weight: 600; + padding: 1rem 0.8rem; + border-bottom: 1px solid var(--mid-grey); + background: white; +} + +.push { + margin-left: auto; +} + +/* Search */ + +.md-search__form { + font-size: 11pt; +} + +.md-search__form:hover { + background: none; +} + +.md-search__form label svg { + width: 16px; + height: 16px; +} + +.md-search__input { + height: 34px; + padding: 9px 16px 8px; + border: 1px solid var(--dark-grey); + border-radius: 12px; + font-size: 12px; + background-color: var(--ibexa-dusk-black); + letter-spacing: 0.12px; + margin-top: 3px; +} + +[dir=ltr] .md-search__input { + padding-left: 50px; +} + +.md-search__input:hover { + background-color: rgb(84,91,98); +} + +.md-search__form input::placeholder { + color: var(--white); + font-size: 13px; +} + +.md-search__icon { + color: var(--white); +} + +.md-search__icon[for=__search] { + top: 12px; + height: 16px; + width: 16px; +} + +[dir=ltr] .md-search__icon[for=__search] { + left: 16px; +} + +[dir=rtl] .md-search__icon[for=__search] { + right: 16px; +} + +.md-search__inner { + width: 260px; +} + +[data-md-toggle=search]:checked~.md-header .md-search__inner { +} + +.md-search-result__item { + list-style: none; +} + +.md-search__output { + display: none; +} + +[data-md-toggle="search"]:checked ~ .md-header .md-search__inner { + box-sizing: border-box; +} + +.ds-dataset-1 { + max-height: calc(100vh - 5.5rem); +} + +.algolia-autocomplete { + display: block !important; + box-sizing: border-box; +} + +/* Breadcrumbs */ +.md-typeset ul.breadcrumbs { + margin-left: 0; + margin-top: 0; + margin-bottom: 0; + padding-bottom: 2rem; +} + +ul.breadcrumbs li.breadcrumb-item { + display: inline-block; + list-style: none; + margin-left: 0; + font-size: 14px; + color: var(--dark-grey); +} + +ul.breadcrumbs li.breadcrumb-item-current { + color: var(--ibexa-dusk-black); + font-weight: 600; +} + +[data-md-toggle=search]:checked~.md-header .md-search__overlay { + background-color: rgba(0,0,0,.54); + pointer-events: initial; +} + +[data-md-toggle=search]:checked~.md-header .md-search__inner, +[data-md-toggle=search]:checked~.md-header .md-search__form, +[data-md-toggle=search]:checked~.md-header .md-search__input, +[data-md-toggle=search]:checked~.md-header .algolia-autocomplete { + padding-top: 0; + padding-bottom: 0; + width: 100%; + height: 100%; +} + +[data-md-toggle=search]:checked~.md-header .md-search__form { + background-color: transparent; +} + +[data-md-toggle=search]:checked~.md-header .md-search__inner { + height: 56px; + z-index: 401; +} + +[data-md-toggle=search]:checked~.md-header .md-search__input { + margin-top: 0; + padding-left: 1.5rem; + background-color: var(--md-default-bg-color); +} + +[data-md-toggle=search]:checked~.md-header .md-search__input, +[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder { + font-size: 21px; + color: var(--dark-grey); +} + +[data-md-toggle=search]:checked~.md-header .md-search__icon { + display: none; +} + +@media only screen and (min-width: 76.1875em) { + [data-md-toggle=search]:checked~.md-header .md-search { + position: absolute; + right: 0; + left: 15rem; + top: 0; + bottom: 0; + padding: 0; + } + + [data-md-toggle=search]:checked~.md-header .md-search__inner { + height: 100%; + } +} diff --git a/docs/css/page-not-found.css b/docs/css/page-not-found.css new file mode 100644 index 0000000000..0af53f3e7b --- /dev/null +++ b/docs/css/page-not-found.css @@ -0,0 +1,38 @@ +.page-not-found { + text-align: center; + margin: 50px; +} + +.page-not-found h1 { + font-weight: normal; + font-size: 37px; + margin: 30px 0 10px; +} + +.page-not-found h2 { + font-weight: normal; + font-size: 30px; + margin: 0 0 70px; +} + +.page-not-found h3 { + font-weight: normal; + font-size: 25px; + margin: 0 0 20px; + color: var(--dark-grey); +} + +.page-not-found ul { + display: inline-flex; + list-style-type: none; + margin: 0; +} + +.page-not-found ul li { + padding: 0 30px; + margin: 0; +} + +.page-not-found li + li { + border-left: 1px solid black; +} \ No newline at end of file diff --git a/docs/css/switcher.css b/docs/css/switcher.css new file mode 100644 index 0000000000..13fc3b6251 --- /dev/null +++ b/docs/css/switcher.css @@ -0,0 +1,272 @@ +.fa:before { + -webkit-font-smoothing: antialiased +} + +.clearfix { + *zoom:1} + +.clearfix:before,.clearfix:after { + display: table; + content: "" +} + +.clearfix:after { + clear: both +} + +@font-face { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + src: url("../fonts/fontawesome-webfont.eot"); + src: url("../fonts/fontawesome-webfont.eot?#iefix") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff") format("woff"),url("../fonts/fontawesome-webfont.ttf") format("truetype"),url("../fonts/fontawesome-webfont.svg#FontAwesome") format("svg") +} + +.fa:before { + display: inline-block; + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + line-height: 1; + text-decoration: inherit +} + +a .fa { + display: inline-block; + text-decoration: inherit +} + +li .fa { + display: inline-block +} + +li .fa-large:before,li .fa-large:before { + width: 1.875em +} + +ul.fas { + list-style-type: none; + margin-left: 2em; + text-indent: -0.8em +} + +ul.fas li .fa { + width: .8em +} + +ul.fas li .fa-large:before,ul.fas li .fa-large:before { + vertical-align: baseline +} + +.fa-book:before { + content: "" +} + +.icon-book:before { + content: "" +} + +.fa-caret-down:before { + content: "" +} + +.icon-caret-down:before { + content: "" +} + +.fa-caret-up:before { + content: "" +} + +.icon-caret-up:before { + content: "" +} + +.fa-caret-left:before { + content: "" +} + +.icon-caret-left:before { + content: "" +} + +.fa-caret-right:before { + content: "" +} + +.icon-caret-right:before { + content: "" +} + +.rst-versions { + position: fixed; + bottom: 0; + left: 0; + width: 300px; + color: #fcfcfc; + background: #1f1d1d; + font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif; + z-index: 400 +} + +.rst-versions a { + color: #2980B9; + text-decoration: none +} + +.rst-versions .rst-badge-small { + display: none +} + +.rst-versions .rst-current-version { + padding: 12px; + background-color: #272525; + display: block; + text-align: right; + font-size: 90%; + cursor: pointer; + color: #27AE60; + *zoom:1} + +.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after { + display: table; + content: "" +} + +.rst-versions .rst-current-version:after { + clear: both +} + +.rst-versions .rst-current-version .fa { + color: #fcfcfc +} + +.rst-versions .rst-current-version .fa-book { + float: left +} + +.rst-versions .rst-current-version .icon-book { + float: left +} + +.rst-versions .rst-current-version.rst-out-of-date { + background-color: #E74C3C; + color: #fff +} + +.rst-versions .rst-current-version.rst-active-old-version { + background-color: #F1C40F; + color: #000 +} + +.rst-versions.shift-up { + height: auto; + max-height: 100% +} + +.rst-versions.shift-up .rst-other-versions { + display: block +} + +.rst-versions .rst-other-versions { + font-size: 90%; + padding: 12px; + color: gray; + display: none +} + +.rst-versions .rst-other-versions hr { + display: block; + height: 1px; + border: 0; + margin: 20px 0; + padding: 0; + border-top: solid 1px #413d3d +} + +.rst-versions .rst-other-versions dd { + display: inline-block; + margin: 0 +} + +.rst-versions .rst-other-versions dd a { + display: inline-block; + padding: 6px; + color: #fcfcfc +} + +.rst-versions.rst-badge { + width: auto; + bottom: 20px; + right: 20px; + left: auto; + border: none; + max-width: 300px +} + +.rst-versions.rst-badge .icon-book { + float: none +} + +.rst-versions.rst-badge .fa-book { + float: none +} + +.rst-versions.rst-badge.shift-up .rst-current-version { + text-align: right +} + +.rst-versions.rst-badge.shift-up .rst-current-version .fa-book { + float: left +} + +.rst-versions.rst-badge.shift-up .rst-current-version .icon-book { + float: left +} + +.rst-versions.rst-badge .rst-current-version { + width: auto; + height: 30px; + line-height: 30px; + padding: 0 6px; + display: block; + text-align: center +} + +@media screen and (max-width: 768px) { + .rst-versions { + width:85%; + display: none + } + + .rst-versions.shift { + display: block + } +} + +.rst-versions.rst-badge { + display: block; + + bottom: 50px; + + /* Workaround for mkdocs which set a specific height for this element */ + height: auto; +} + +.rst-other-versions { + text-align: left; +} + +.rst-other-versions a { + border: 0; +} + +.rst-other-versions dl { + margin: 0; +} + + +/* Fix RTD theme bottom margin */ +.rst-content .line-block { + margin-bottom: 24px +} \ No newline at end of file diff --git a/docs/extending/add_user_setting.md b/docs/extending/add_user_setting.md new file mode 100644 index 0000000000..6014066bf6 --- /dev/null +++ b/docs/extending/add_user_setting.md @@ -0,0 +1,43 @@ +--- +description: Add the option to select a custom preference in user menu. +--- + +# Add user setting + +## Create new User setting + +You can add new preferences to the **User Settings** menu in the Back Office. + +To do so, create a setting class implementing two interfaces: +`ValueDefinitionInterface` and `FormMapperInterface`. + +In this example the class is located in `src/Setting/Unit.php` +and enables the user to select their preference for metric or imperial unit systems. + +``` php +[[= include_file('code_samples/back_office/settings/src/Setting/Unit.php') =]] +``` + +Register the setting as a service: + +``` yaml +[[= include_file('code_samples/back_office/settings/config/custom_services.yaml' )=]] +``` + +You can order the settings in the User menu by setting their `priority`. + +The value of the setting is accessible with `ez_user_settings['unit']`. + +## Create template for editing settings + +You can override a template used when editing the new setting: + +``` yaml +[[= include_file('code_samples/back_office/settings/config/packages/user_settings.yaml' )=]] +``` + +The `templates/User/Setting/update_unit.html.twig` template must extend the `@ezdesign/account/settings/update.html.twig` template: + +``` html+twig +[[= include_file('code_samples/back_office/settings/templates/User/Setting/update_unit.html.twig' )=]] +``` diff --git a/docs/extending/adding_tab_to_udw.md b/docs/extending/adding_tab_to_udw.md index 900faac84b..12fbe781ee 100644 --- a/docs/extending/adding_tab_to_udw.md +++ b/docs/extending/adding_tab_to_udw.md @@ -1,30 +1,21 @@ -# Creating a UDW tab +--- +description: Add a custom tab displaying selected data to the content browser. +--- + +# Add browser tab The Universal Discovery Widget (UDW) is a separate React module. By default, it contains three tabs: Browse, Bookmarks and Search. ![UDW default tabs](img/udw_tabs.png) -In this tutorial you will add a new tab called Images which will display all Content items of the type 'Image'. +Follow the instructions below to create and add a new tab called **Images** which displays all Content items of the type 'Image'. -## Create a tab +## Create tab -First add an `image.tab.module.js` file in `assets/js/image-tab/`. +First, in `assets/js/image-tab/`, add an `image.tab.module.js` file. -```js -import React, { useContext } from 'react'; - -import Tab from '../../../vendor/ezsystems/ezplatform-admin-ui/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab'; -import ImagesList from './components/images.list'; - -const ImageTabModule = () => { - return ( -
    - - - -
    - ); -}; +``` js +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js', 0,14) =]] ``` Next, add the tab to the configuration in the same file. @@ -32,62 +23,23 @@ Each tab definition is an object containing the following properties: |Property|Value|Definition| |-----------|------|----------| -|id|string|Tab ID, e.g. `image`| -|component|element|React component that represents the contents of a tab| -|label|string|Label text, e.g. `Images`| -|icon|string|Path to the icon, e.g. `/bundles/ezplatformadminui/img/ez-icons.svg#image`| +|id|string|Tab ID, for example, `image`.| +|component|element|React component that represents the contents of a tab.| +|label|string|Label text, for example, `Images`.| +|icon|string|Path to the icon, for example, `/bundles/ezplatformadminui/img/ez-icons.svg#image`.| ```js -eZ.addConfig( - 'adminUiConfig.universalDiscoveryWidget.tabs', - [ - { - id: 'image', - component: ImageTabModule, - label: 'Images', - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#image', - }, - ], - true -); +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js', 15,29) =]] ``` The module will govern the creation of the new tab. - -??? tip "Complete `image.tab.module.js` code" - - ```js - import React, { useContext } from 'react'; - - import Tab from '../../../vendor/ezsystems/ezplatform-admin-ui/src/bundle/ui-dev/src/modules/universal-discovery/components/tab/tab'; - import ImagesList from './components/images.list'; - - const ImageTabModule = () => { - return ( -
    - - - -
    - ); - }; - - eZ.addConfig( - 'adminUiConfig.universalDiscoveryWidget.tabs', - [ - { - id: 'image', - component: ImageTabModule, - label: 'Image', - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#image', - }, - ], - true - ); - - export default ImageTabModule; - ``` +
    +Complete image.tab.module.js code +```js +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/image.tab.module.js') =]] +``` +
    ## Add tab to webpack config @@ -103,7 +55,7 @@ eZConfigManager.add({ ## Provide ReactJS files -Next, you need to provide a set of files that will be used to render the module: +Next, you need to provide a set of files used to render the module: - `images.service.js` handles fetching the images - `images.list.js` renders the image list @@ -114,75 +66,7 @@ Next, you need to provide a set of files that will be used to render the module: Create a service for fetching the images by adding `images.service.js` to `assets/js/image-tab/services/`: ```js -const handleRequestResponse = (response) => { - if (!response.ok) { - throw Error(response.statusText); - } - - return response.json(); -}; - -export const getImages = ({ token, siteaccess, contentId }, callback) => { - const body = JSON.stringify({ - ViewInput: { - identifier: 'images', - public: false, - LocationQuery: { - Criteria: {}, - FacetBuilders: {}, - SortClauses: {}, - Filter: { ContentTypeIdCriterion: 5 }, - }, - }, - }); - const request = new Request('/api/ezp/v2/views', { - method: 'POST', - headers: { - Accept: 'application/vnd.ez.api.View+json; version=1.1', - 'Content-Type': 'application/vnd.ez.api.ViewInput+json; version=1.1', - 'X-Siteaccess': siteaccess, - 'X-CSRF-Token': token, - }, - body, - mode: 'cors', - }); - - fetch(request) - .then(handleRequestResponse) - .then(callback) - .catch((error) => console.log('error:load:images', error)); -}; - -export const loadImageContent = ({ token, siteaccess, contentId }, callback) => { - const body = JSON.stringify({ - ViewInput: { - identifier: `image-content-${contentId}`, - public: false, - ContentQuery: { - Criteria: {}, - FacetBuilders: {}, - SortClauses: {}, - Filter: { ContentIdCriterion: contentId }, - }, - }, - }); - const request = new Request('/api/ezp/v2/views', { - method: 'POST', - headers: { - Accept: 'application/vnd.ez.api.View+json; version=1.1', - 'Content-Type': 'application/vnd.ez.api.ViewInput+json; version=1.1', - 'X-Siteaccess': siteaccess, - 'X-CSRF-Token': token, - }, - body, - mode: 'cors', - }); - - fetch(request) - .then(handleRequestResponse) - .then(callback) - .catch((error) => console.log('error:load:images', error)); -}; +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/services/images.service.js') =]] ``` ### `images.list.js` @@ -190,105 +74,7 @@ export const loadImageContent = ({ token, siteaccess, contentId }, callback) => Next, create an image list by adding an `images.list.js` to `assets/js/image-tab/components/`: ```js -import React, { useState, useContext, useEffect } from 'react'; -import Image from './image'; -import { getImages } from '../services/images.service'; - -import { RestInfoContext } from '../../../../vendor/ezsystems/ezplatform-admin-ui/src/bundle/ui-dev/src/modules/universal-discovery/universal.discovery.module'; - -const ImagesList = () => { - const [images, setImages] = useState([]); - const [page, setPage] = useState(0); - const [itemsPerPage, setItemPerPage] = useState(5); - const [maxPageIndex, setMaxPageIndex] = useState(0); - const restInfo = useContext(RestInfoContext); - const updateImagesState = (response) => { - const images = response.View.Result.searchHits.searchHit.map((item) => item.value.Location); - const modulo = images.length % itemsPerPage; - const maxPageIndex = modulo ? (images.length - modulo) / itemsPerPage : images.length / itemsPerPage - 1; - - setImages(images); - setMaxPageIndex(maxPageIndex); - }; - const showPrevPage = () => { - const prevPage = page > 0 ? page - 1 : 0; - - setPage(prevPage); - }; - const showNextPage = () => { - const nextPage = maxPageIndex > page ? page + 1 : maxPageIndex; - - setPage(nextPage); - }; - const renderItems = () => { - const attrs = { - className: 'c-images-list__items', - style: { - transform: `translate3d(-${page * itemsPerPage * 316}px, 0, 0)`, - }, - }; - - return ( -
    -
    - {images.map((imageLocation) => ( - - ))} -
    -
    - ); - }; - const renderPrevBtn = () => { - const attrs = { - className: 'c-images-list__btn--prev', - onClick: showPrevPage, - }; - - if (page <= 0) { - attrs.disabled = true; - } - - return ( -
    - - - -
    - ); - }; - const renderNextBtn = () => { - const attrs = { - className: 'c-images-list__btn--next', - onClick: showNextPage, - }; - - if (page >= maxPageIndex) { - attrs.disabled = true; - } - - return ( -
    - - - -
    - ); - }; - - useEffect(() => { - getImages(restInfo, updateImagesState); - }, []); - - return ( -
    - {renderPrevBtn()} - {renderItems()} - {renderNextBtn()} -
    - ); -}; - -export default ImagesList; +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/components/images.list.js') =]] ``` ### `image.js` @@ -296,39 +82,7 @@ export default ImagesList; Finally, create an image view by adding an `image.js` to `assets/js/image-tab/components/`: ```js -import React, { useState, useEffect } from 'react'; -import { loadImageContent } from '../services/images.service'; - -const Image = ({ restInfo, location }) => { - const [content, setContent] = useState(null); - const updateVersionInfoState = (response) => { - - setContent(response.View.Result.searchHits.searchHit[0].value.Content); - }; - let src = - - 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI0MHB4IiBoZWlnaHQ9IjQwcHgiIHZpZXdCb3g9IjAgMCA0MCA0MCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWw6c3BhY2U9InByZXNlcnZlIiBzdHlsZT0iZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNDE0MjE7IiB4PSIwcHgiIHk9IjBweCI+CiAgICA8ZGVmcz4KICAgICAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPjwhW0NEQVRBWwogICAgICAgICAgICBALXdlYmtpdC1rZXlmcmFtZXMgc3BpbiB7CiAgICAgICAgICAgICAgZnJvbSB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIC13ZWJraXQtdHJhbnNmb3JtOiByb3RhdGUoLTM1OWRlZykKICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgQGtleWZyYW1lcyBzcGluIHsKICAgICAgICAgICAgICBmcm9tIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICAgIHRvIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogcm90YXRlKC0zNTlkZWcpCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIHN2ZyB7CiAgICAgICAgICAgICAgICAtd2Via2l0LXRyYW5zZm9ybS1vcmlnaW46IDUwJSA1MCU7CiAgICAgICAgICAgICAgICAtd2Via2l0LWFuaW1hdGlvbjogc3BpbiAxLjVzIGxpbmVhciBpbmZpbml0ZTsKICAgICAgICAgICAgICAgIC13ZWJraXQtYmFja2ZhY2UtdmlzaWJpbGl0eTogaGlkZGVuOwogICAgICAgICAgICAgICAgYW5pbWF0aW9uOiBzcGluIDEuNXMgbGluZWFyIGluZmluaXRlOwogICAgICAgICAgICB9CiAgICAgICAgXV0+PC9zdHlsZT4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSJvdXRlciI+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwwQzIyLjIwNTgsMCAyMy45OTM5LDEuNzg4MTMgMjMuOTkzOSwzLjk5MzlDMjMuOTkzOSw2LjE5OTY4IDIyLjIwNTgsNy45ODc4MSAyMCw3Ljk4NzgxQzE3Ljc5NDIsNy45ODc4MSAxNi4wMDYxLDYuMTk5NjggMTYuMDA2MSwzLjk5MzlDMTYuMDA2MSwxLjc4ODEzIDE3Ljc5NDIsMCAyMCwwWiIgc3R5bGU9ImZpbGw6YmxhY2s7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44NTc4Niw1Ljg1Nzg2QzcuNDE3NTgsNC4yOTgxNSA5Ljk0NjM4LDQuMjk4MTUgMTEuNTA2MSw1Ljg1Nzg2QzEzLjA2NTgsNy40MTc1OCAxMy4wNjU4LDkuOTQ2MzggMTEuNTA2MSwxMS41MDYxQzkuOTQ2MzgsMTMuMDY1OCA3LjQxNzU4LDEzLjA2NTggNS44NTc4NiwxMS41MDYxQzQuMjk4MTUsOS45NDYzOCA0LjI5ODE1LDcuNDE3NTggNS44NTc4Niw1Ljg1Nzg2WiIgc3R5bGU9ImZpbGw6cmdiKDIxMCwyMTAsMjEwKTsiLz4KICAgICAgICA8L2c+CiAgICAgICAgPGc+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMCwzMi4wMTIyQzIyLjIwNTgsMzIuMDEyMiAyMy45OTM5LDMzLjgwMDMgMjMuOTkzOSwzNi4wMDYxQzIzLjk5MzksMzguMjExOSAyMi4yMDU4LDQwIDIwLDQwQzE3Ljc5NDIsNDAgMTYuMDA2MSwzOC4yMTE5IDE2LjAwNjEsMzYuMDA2MUMxNi4wMDYxLDMzLjgwMDMgMTcuNzk0MiwzMi4wMTIyIDIwLDMyLjAxMjJaIiBzdHlsZT0iZmlsbDpyZ2IoMTMwLDEzMCwxMzApOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksMjguNDkzOUMzMC4wNTM2LDI2LjkzNDIgMzIuNTgyNCwyNi45MzQyIDM0LjE0MjEsMjguNDkzOUMzNS43MDE5LDMwLjA1MzYgMzUuNzAxOSwzMi41ODI0IDM0LjE0MjEsMzQuMTQyMUMzMi41ODI0LDM1LjcwMTkgMzAuMDUzNiwzNS43MDE5IDI4LjQ5MzksMzQuMTQyMUMyNi45MzQyLDMyLjU4MjQgMjYuOTM0MiwzMC4wNTM2IDI4LjQ5MzksMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxMDEsMTAxLDEwMSk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMy45OTM5LDE2LjAwNjFDNi4xOTk2OCwxNi4wMDYxIDcuOTg3ODEsMTcuNzk0MiA3Ljk4NzgxLDIwQzcuOTg3ODEsMjIuMjA1OCA2LjE5OTY4LDIzLjk5MzkgMy45OTM5LDIzLjk5MzlDMS43ODgxMywyMy45OTM5IDAsMjIuMjA1OCAwLDIwQzAsMTcuNzk0MiAxLjc4ODEzLDE2LjAwNjEgMy45OTM5LDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoMTg3LDE4NywxODcpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTUuODU3ODYsMjguNDkzOUM3LjQxNzU4LDI2LjkzNDIgOS45NDYzOCwyNi45MzQyIDExLjUwNjEsMjguNDkzOUMxMy4wNjU4LDMwLjA1MzYgMTMuMDY1OCwzMi41ODI0IDExLjUwNjEsMzQuMTQyMUM5Ljk0NjM4LDM1LjcwMTkgNy40MTc1OCwzNS43MDE5IDUuODU3ODYsMzQuMTQyMUM0LjI5ODE1LDMyLjU4MjQgNC4yOTgxNSwzMC4wNTM2IDUuODU3ODYsMjguNDkzOVoiIHN0eWxlPSJmaWxsOnJnYigxNjQsMTY0LDE2NCk7Ii8+CiAgICAgICAgPC9nPgogICAgICAgIDxnPgogICAgICAgICAgICA8cGF0aCBkPSJNMzYuMDA2MSwxNi4wMDYxQzM4LjIxMTksMTYuMDA2MSA0MCwxNy43OTQyIDQwLDIwQzQwLDIyLjIwNTggMzguMjExOSwyMy45OTM5IDM2LjAwNjEsMjMuOTkzOUMzMy44MDAzLDIzLjk5MzkgMzIuMDEyMiwyMi4yMDU4IDMyLjAxMjIsMjBDMzIuMDEyMiwxNy43OTQyIDMzLjgwMDMsMTYuMDA2MSAzNi4wMDYxLDE2LjAwNjFaIiBzdHlsZT0iZmlsbDpyZ2IoNzQsNzQsNzQpOyIvPgogICAgICAgIDwvZz4KICAgICAgICA8Zz4KICAgICAgICAgICAgPHBhdGggZD0iTTI4LjQ5MzksNS44NTc4NkMzMC4wNTM2LDQuMjk4MTUgMzIuNTgyNCw0LjI5ODE1IDM0LjE0MjEsNS44NTc4NkMzNS43MDE5LDcuNDE3NTggMzUuNzAxOSw5Ljk0NjM4IDM0LjE0MjEsMTEuNTA2MUMzMi41ODI0LDEzLjA2NTggMzAuMDUzNiwxMy4wNjU4IDI4LjQ5MzksMTEuNTA2MUMyNi45MzQyLDkuOTQ2MzggMjYuOTM0Miw3LjQxNzU4IDI4LjQ5MzksNS44NTc4NloiIHN0eWxlPSJmaWxsOnJnYig1MCw1MCw1MCk7Ii8+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K'; - let alt = 'Loading meta data ...'; - - useEffect(() => { - loadImageContent({ ...restInfo, contentId: location.ContentInfo.Content._id }, updateVersionInfoState); - }, []); - - if (content) { - const imageField = content.CurrentVersion.Version.Fields.field.find((field) => field.fieldTypeIdentifier === 'ezimage').fieldValue; - - src = imageField.uri; - alt = imageField.fileName; - } - - return ( -
    console.log(data)}> - {alt} -
    - ); -}; - -export default Image; +[[= include_file('code_samples/back_office/udw/assets/js/image-tab/components/image.js') =]] ``` ## Add styles @@ -338,113 +92,13 @@ Ensure that the new tab is styled by adding the following files to `assets/css/` ### `images.list.css` ```css -.c-images-list { - display: grid; - grid-template-areas: 'prev list next'; - grid-template-columns: 32px 1fr 32px; - grid-gap: 16px; - overflow: hidden; -} - -.c-images-list__items-wrapper { - overflow: hidden; - max-width: 1564px; -} - -[class*='c-images-list__btn--'] { - display: flex; - align-items: center; - justify-content: center; - background: #f15a10; - transition: background 0.2s ease-in-out, opacity 0.2s ease-in-out; -} - -[class*='c-images-list__btn--']:focus, -[class*='c-images-list__btn--']:hover { - background: #ab3f0a; -} - -[class*='c-images-list__btn--'][disabled], -[class*='c-images-list__btn--'][disabled]:focus, -[class*='c-images-list__btn--'][disabled]:hover { - background: #f15a10; - opacity: 0.5; -} - -[class*='c-images-list__btn--'] .ez-icon { - fill: #fff; -} - -.c-images-list__btn--prev { - grid-area: prev; -} - -.c-images-list__btn--next { - grid-area: next; -} - -.c-images-list__items { - grid-area: list; - display: flex; - flex-wrap: nowrap; - transition: transform 0.3s ease-in-out; -} - -.c-images-list__items .c-image { - flex: 0 0 300px; -} - -.c-images-list__items .c-image + .c-image { - margin-left: 1rem; -} +[[= include_file('code_samples/back_office/udw/assets/css/image.list.css') =]] ``` ### `image.css` ```css -.c-image { - width: 300px; - height: 200px; - background: #fff; - transition: box-shadow 0.3s ease-in-out; - position: relative; - cursor: pointer; - display: flex; -} - -.c-image:before { - content: attr(data-title); - display: flex; - background: rgba(0, 0, 0, 0.75); - color: #fff; - width: 300px; - align-items: center; - justify-content: center; - font-weight: 700; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - opacity: 0; - padding: 1rem; - transition: opacity 0.3s ease-in-out; - overflow: hidden; -} - -.c-image:hover:before, -.c-image:focus:before { - opacity: 1; -} - -.c-image__thumb { - display: block; - max-width: 300px; - max-height: 200px; - width: auto; - height: auto; - margin: auto; -} +[[= include_file('code_samples/back_office/udw/assets/css/image.css') =]] ``` ### Add css to webpack @@ -500,8 +154,8 @@ eZConfigManager.add({ ## Check results -At this point you can go to the Back Office and choose **Browse** under **Content/Content structure**. -In the UDW a new **Images** tab will appear, listing all images from the Repository. +In the Back Office go to **Content** -> **Content structure**. On the left panel, click **Browse**. +In the UDW a new **Images** tab appears, listing all images from the Repository. ![Image tab in UDW](img/udw_image_tab.png) diff --git a/docs/extending/config_back_office.md b/docs/extending/config_back_office.md new file mode 100644 index 0000000000..c149e1eb64 --- /dev/null +++ b/docs/extending/config_back_office.md @@ -0,0 +1,55 @@ +--- +title: Back Office configuration +description: Configure default upload locations, pagination limits, and more settings for the Back Office. +--- + +# Back Office configuration + +## Pagination limits + +Default pagination limits for different sections of the Back Office can be defined through respective settings in +[`ezplatform_default_settings.yaml`](https://github.com/ibexa/admin-ui/blob/main/src/bundle/Resources/config/ezplatform_default_settings.yaml#L7) + +You can set the pagination limit for user settings with the following configuration: + +``` yaml +ibexa: + system: + default: + pagination_user: + user_settings_limit: 6 +``` + +You can configure the following settings to manage the pagination limits for the product catalog: + +``` yaml +ezsettings.default.product_catalog.pagination.attribute_groups_limit: 25 +ezsettings.default.product_catalog.pagination.customer_groups_limit: 25 +ezsettings.default.product_catalog.pagination.products_limit: 25 +ezsettings.default.product_catalog.pagination.product_types_limit: 25 +``` + +## Copy subtree limit + +Copying large subtrees can cause performance issues, so you can limit the number of Content items +that can be copied at once using `ibexa.system..subtree_operations.copy_subtree.limit` +in `config/packages/ibexa_admin_ui.yaml`. + +The default value is `100`. You can set it to `-1` for no limit, +or to `0` to completely disable copying subtrees. + +You can copy subtree from CLI using the command: `bin/console ibexa:copy-subtree `. + +## Default Locations + +Default Location IDs for [Content structure, Media and Users](../guide/content_management.md#top-level-locations) in the menu are configured using the following settings: + +``` yaml +ibexa: + system: + default: + location_ids: + content_structure: 2 + media: 43 + users: 5 +``` diff --git a/docs/extending/content_tree.md b/docs/extending/content_tree.md new file mode 100644 index 0000000000..66bc992903 --- /dev/null +++ b/docs/extending/content_tree.md @@ -0,0 +1,41 @@ +--- +description: Configure SiteAccess, displayed Content items, depth and root location for the Content Tree. +--- + +# Content Tree + +With this configuration you can: + +- define configuration for a SiteAccess or a SiteAccess group +- decide how many Content items are displayed in the tree +- set maximum depth of expanded tree +- hide Content Types +- set a tree root Location +- override Content Tree's root for specific Locations + +```yaml +ibexa: + system: + # any SiteAccess or SiteAccess group + admin_group: + content_tree_module: + # defines how many children will be shown after expanding parent + load_more_limit: 15 + # users won't be able to load more children than that + children_load_max_limit: 200 + # maximum depth of expanded tree + tree_max_depth: 10 + # Content Types to display in Content Tree, value of '*' allows all CTs to be displayed + allowed_content_types: '*' + # Content Tree won't display these Content Types, can be used only when 'allowed_content_types' is set to '*' + ignored_content_types: + - post + - article + # ID of Location to use as tree root. If omitted - content.tree_root.location_id setting is used. + tree_root_location_id: 2 + # list of Location IDs for which Content Tree's root Location will be changed + contextual_tree_root_location_ids: + - 2 # Home (Content structure) + - 5 # Users + - 43 # Media +``` \ No newline at end of file diff --git a/docs/extending/creating_custom_dropdowns.md b/docs/extending/creating_custom_dropdowns.md index 8f61bdf8b5..71397b0d1d 100644 --- a/docs/extending/creating_custom_dropdowns.md +++ b/docs/extending/creating_custom_dropdowns.md @@ -1,11 +1,15 @@ -# Creating custom drop-downs +--- +description: Add custom drop down menus to Back Office interface. +--- -In [[= product_name =]], you can implement custom drop-downs anywhere in the Back Office. +# Add drop-downs + +In [[= product_name =]], you can create a reusable custom drop-down and implement it anywhere in the Back Office. Follow the steps below to learn how to integrate this component to fit it to your project needs. -## Prepare custom dropdown structure +## Prepare custom drop-down structure -First prepare the HTML code structure in the following way: +First prepare the component HTML code structure in the template inside the `content` section: ```html hl_lines="2 11 12"
    @@ -28,18 +32,18 @@ First prepare the HTML code structure in the following way:
    ``` -In line two, the code above contains a hidden native `select` input. It stores the selection values. +In line two, the code contains a hidden native `select` input. It stores the selection values. Input is hidden because a custom drop-down duplicates its functionality. !!! caution - Do not remove `select` input. Removing it would break the functionality of any submission form. + Do not remove the `select` input, otherwise form submission may not work. ![Dropdown expanded state](img/dropdown_expanded_state.jpg) ## Generate `` element. +Next, generate a standard select input with the `ez-custom-dropdown__select` CSS class added to the ` ``` -![Dropdown multiple selection](img/dropdown_multiple_selection.jpg) +![Drop-down multiple selection](img/dropdown_multiple_selection.jpg) ## Add attributes -Next, look into the `data-value` attribute in the code above (line 11 and 12) to duplicated options with the CSS class: `ez-custom-dropdown__item`. +Next, look at the `data-value` attribute in the code (line 11 and 12) for duplicated options with the CSS class: `ez-custom-dropdown__item`. It stores a value of an option from a select input. -You can provide placeholder text for your custom dropdown. To do so: +You can provide placeholder text for your custom drop-down. To do so: - put a `data-value` attribute with no value `data-value=""` -- add a `disabled` attribute to the item in the duplicated list of options +- add a `disabled` attribute to the item in the duplicated list of options to make it unclickable. -It will make it un-clickable. Example: @@ -95,13 +98,13 @@ Full list of options: |Name|Description|Required| |----|-----------|--------| -|`container`|contains a reference to a DOM node where custom dropdown is initialized|required| -|`sourceInput`|contains a reference to a DOM node where the value of selected option is stored. Presumably, it should be a reference to a select input node|required| -|`itemsContainer`|contains a reference to a duplicated items container|required| -|`hasDefaultSelection`|contains a boolean value. If set to `true` the first option will be selected as a placeholder or selected value|optional| -|`selectedItemTemplate`|contains a literal template string with placeholders for `value` and `label` data|optional| +|`container`|contains a reference to a DOM node where custom drop-down is initialized.|required| +|`sourceInput`|contains a reference to a DOM node where the value of selected option is stored. Preferably, it should be a reference to a select input node.|required| +|`itemsContainer`|contains a reference to a duplicated items container.|required| +|`hasDefaultSelection`|contains a boolean value. If set to `true` the first option is selected as a placeholder or selected value.|optional| +|`selectedItemTemplate`|contains a literal template string with placeholders for `value` and `label` data.|optional| -In the code samples provided above you will find 4 of 5 configuration options. +In the code samples you can find 4 of 5 configuration options. Default template HTML code structure for missing `selectedItemTemplate` looks like this: ```html diff --git a/docs/extending/custom_components.md b/docs/extending/custom_components.md index 4f3a634800..dbe83d1525 100644 --- a/docs/extending/custom_components.md +++ b/docs/extending/custom_components.md @@ -1,8 +1,12 @@ -# Injecting custom components +--- +description: Back Office components allow you to inject any custom widgets into selected places of the user interface. +--- + +# Custom components The Back Office has designated places where you can use your own components. -Components enable you to inject widgets (e.g. **My dashboard** blocks) and HTML code (e.g. a tag for loading JS or CSS files). +Components enable you to inject widgets (for example, **My dashboard** blocks) and HTML code (for example, a tag for loading JS or CSS files). A component is any class that implements the `Renderable` interface. It must be tagged as a service in `config/services.yaml`: @@ -12,7 +16,7 @@ App\Component\MyNewComponent: - { name: ezplatform.admin_ui.component, group: content-edit-form-before } ``` -`group` indicates where the widget will be displayed. The available groups are: +`group` indicates where the widget is displayed. The available groups are: - [`stylesheet-head`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/ui/layout.html.twig#L98) - [`script-head`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/ui/layout.html.twig#L99) @@ -27,10 +31,12 @@ App\Component\MyNewComponent: - [`dashboard-my-tab-groups`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/ui/dashboard/block/me.html.twig#L6) - [`content-type-tab-groups`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/content_type/index.html.twig#L10) - [`calendar-widget-before`](https://github.com/ezsystems/ezplatform-calendar/blob/master/src/bundle/Resources/views/themes/admin/calendar/view.html.twig#L24) +- [`login-form-before`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/account/login/index.html.twig#L8) +- [`login-form-after`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/account/login/index.html.twig#L69) ## Base component classes -If you only need to inject a short element (e.g. a Twig template or a CSS link) without writing a class, +If you only need to inject a short element (for example, a Twig template or a CSS link) without writing a class, you can make use of the following base classes: - `TwigComponent` renders a Twig template. diff --git a/docs/extending/custom_fieldtype_comparison.md b/docs/extending/custom_fieldtype_comparison.md deleted file mode 100644 index 130f520db0..0000000000 --- a/docs/extending/custom_fieldtype_comparison.md +++ /dev/null @@ -1,101 +0,0 @@ -# Creating custom version comparison of Field Types - -In the Back Office, you can compare the contents of Fields. -Comparing is possible only between two versions of the same Field that are in the same language. - -You can add the possibility to compare custom and other unsupported Field Types. -You can base the configuration on the comparison mechanism created for the `ezstring` Field Type. - -## Field Type configuration - -First, ensure that the Field Type to compare implements the `EzSystems\EzPlatformVersionComparison\FieldType\Comparable` interface with the following method: - -``` php -use eZ\Publish\SPI\FieldType\Value; -use EzSystems\EzPlatformVersionComparison\FieldType\FieldTypeComparisonValue; - -interface Comparable -{ - public function getDataToCompare(Value $value): FieldTypeComparisonValue; -} -``` - -This method fetches the data to compare and determines which [comparison engines](#comparison-engine) should be used. -The `ComparisonData` object is specific to the Field Type you want to compare. - -In case of `ezstring`, the implementation is: - -``` php -public function getDataToCompare(SPIValue $value): FieldTypeComparisonValue -{ - return new Value([ - 'textLine' => new StringComparisonValue([ - 'value' => $value->text, - ]), - ]); -} -``` - -Also, the Field Type must be registered as a service and tagged as comparable (e.g. `config/comparable_fieldtypes.yaml`). -An example configuration, in this case for `ezstring`, looks the following way: - -``` yaml -EzSystems\EzPlatformVersionComparison\FieldType\TextLine\Comparable: - tags: - - { name: ezplatform.field_type.comparable, alias: ezstring } -``` - -## Comparison engine - -The comparison engine handles the operations required for comparing the contents of Fields. -Note that each Field Type requires a separate comparison engine. - -When creating a custom engine, ensure that it implements the `EzSystems\EzPlatformVersionComparison\Engine\FieldTypeComparisonEngine` interface: - -``` php -namespace EzSystems\EzPlatformVersionComparison\Engine; - -use EzSystems\EzPlatformVersionComparison\FieldType\FieldTypeComparisonValue; -use EzSystems\EzPlatformVersionComparison\Result\ComparisonResult; - -interface FieldTypeComparisonEngine -{ - public function compareFieldsTypeValues(FieldTypeComparisonValue $comparisonDataA, FieldTypeComparisonValue $comparisonDataB): ComparisonResult; - - public function shouldRunComparison(FieldTypeComparisonValue $comparisonDataA, FieldTypeComparisonValue $comparisonDataB): bool; -} -``` - -The engine must also be registered as a service: - -``` yaml - -EzSystems\EzPlatformVersionComparison\Engine\FieldType\TextLineComparisonEngine: - tags: - - { name: ezplatform.field_type.comparable.engine, supported_type: EzSystems\EzPlatformVersionComparison\FieldType\TextLine\Value } -``` - -When configuring the engines, ensure to tag them with both the `ezplatform.field_type.comparable.engine` and `supported_type` tags. - -### VersionDiff - -`VersionDiff` is built by `VersionComparisonService::compare`. -It consists of an array of `EzSystems\EzPlatformVersionComparison\FieldValueDiff`. -It is an object that holds `EzSystems\EzPlatformVersionComparison\Result\ComparisonResult`. -It is specific to a Field Type because different Field Types have distinct way of showing the difference between their versions. - -``` php -namespace EzSystems\EzPlatformVersionComparison\Service; - -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use EzSystems\EzPlatformVersionComparison\VersionDiff; - -interface VersionComparisonService -{ - public function compare( - VersionInfo $versionA, - VersionInfo $versionB, - ?string $languageCode = null - ): VersionDiff; -} -``` diff --git a/docs/extending/custom_icons.md b/docs/extending/custom_icons.md index f1a29aa28a..811d25d594 100644 --- a/docs/extending/custom_icons.md +++ b/docs/extending/custom_icons.md @@ -1,11 +1,12 @@ -# Creating custom icons +--- +description: Configure custom icons to use for Content Types. +--- -## Custom Content Type icons +# Custom icons -To add custom icons for existing Content Types or custom Content Types in [[= product_name =]], follow the instructions below. -For more information on icons used in [[= product_name =]], see [the Icons section](../guidelines/resources/icons.md). +## Customize Content Type icons -### Configuration +To add custom icons for existing Content Types or custom Content Types in [[= product_name =]], follow the instructions below. To configure a custom icon for a Content Type, use the following configuration in `config/packages/ezplatform.yaml`, for example: @@ -18,13 +19,13 @@ ezplatform: thumbnail: /assets/images/custom_icon.svg#custom ``` -Place the icon in `public/assets/images` and remember to run `yarn encore ` after adding it. +Place the icon in `public/assets/images` and run `yarn encore ` after adding it. !!! note "Icons format" All icons should be in SVG format with `symbol` so they can display properly in the Back Office. -### Custom icons in Twig templates +### Access icons in Twig templates Content Type icons are accessible in Twig templates via the `ez_content_type_icon` function. It requires Content Type identifier as an argument. The function returns the path to a Content Type icon. @@ -35,15 +36,15 @@ It requires Content Type identifier as an argument. The function returns the pat ``` -### Custom icons in JavaScript +### Access icons in JavaScript Content Types icons configuration is stored in a global object: `eZ.adminUiConfig.contentTypes`. You can easily retrieve the icon URL with the `getContentTypeIcon` helper function that is set on the global `eZ.helpers.contentType` object. It takes Content Type identifier as an argument and returns one of the following items: - - URL of a given Content Type's icon - - `null` if there is no Content Type with given identifier +- URL of a specified Content Type's icon +- `null` if there is no Content Type with specified identifier Example with `getContentTypeIcon`: @@ -55,3 +56,18 @@ return ( ) ``` + +## Icon sets + +You can configure icon sets to be used per SiteAccess: + +``` yaml +ezplatform: + system: + : + assets: + icon_sets: + my_icons: /assets/images/icons/my_icons.svg + additional_icons: /assets/images/icons/additional_icons.svg + default_icon_set: my_icons +``` diff --git a/docs/extending/customize_calendar.md b/docs/extending/customize_calendar.md new file mode 100644 index 0000000000..547be831ae --- /dev/null +++ b/docs/extending/customize_calendar.md @@ -0,0 +1,113 @@ +--- +description: Add custom events to the calendar and customize its looks. +--- + +# Customize calendar + +By default, the Calendar displays scheduled events of the following types: + +- Content publication (`future_publication`) +- Content hide (`future_hide`) +- Block reveal (`page_block_reveal`) +- Block hide (`page_block_hide`) + +You can perform basic actions on these events. + +You can also configure the calendar to display custom event types. + +## Customize colors and icons + +You can change the color of a calendar event or change the icon of an action. +The setting is SiteAccess-aware. + +To customize the appearance settings, add the following configuration: + +``` yaml hl_lines="6" +[[= include_file('code_samples/back_office/calendar/config/packages/calendar.yaml') =]] +``` + +Line 6 contains the name of the event type, either a built-in custom one. + +`color` defines the color in which events of this type are displayed in the calendar. +`icon` is the icon used for a button with the relevant event action. + +![Bank holiday with custom color](img/extending_calendar_view.png) + +## Configure custom events + +The following example shows how to create custom events which add different holidays to the calendar. + +First, create a new event in `src/Calendar/Holidays/Event.php`: + +``` php +[[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/Event.php') =]] +``` + +Here, you define a new class for your event based on `EzSystems\EzPlatformCalendar\Calendar\Event`. + +Next, create `src/Calendar/Holidays/EventType.php`: + +```php hl_lines="20-23" +[[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/EventType.php') =]] +``` + +You can use the identifier defined in lines 20-23 to configure [event colors](#customize-colors-and-icons). + +Complete the procedure by registering the new event type as a service: + +``` yaml +[[= include_file('code_samples/back_office/calendar/config/custom_services.yaml', 0, 6) =]] +``` + +## Configure event sources + +To add specific events to your calendar, you need to create an event source. + +An event source must implement `EzSystems\EzPlatformCalendar\Calendar\EventSource\EventSourceInterface`. + +One such built-in implementation is `InMemoryEventSource`. +To add an in-memory collection as an event source, create `src/Calendar/Holidays/EventSourceFactory.php`: + +```php +[[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/EventSourceFactory.php', 0, 23) =]][[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/EventSourceFactory.php', 29, 40) =]] +``` + +!!! note + + When creating the list of events, you must list all the `createEvent()` entities chronologically. + + For example: + + ``` php + $collection = new EventCollection([ + $this->createEvent("Event 1", new DateTime("2020-01-01")), + $this->createEvent("Event 2", new DateTime("2020-01-02")), + // ... + ``` + +Next, register the event source as a service: + +``` yaml +[[= include_file('code_samples/back_office/calendar/config/custom_services.yaml', 0, 1) =]][[= include_file('code_samples/back_office/calendar/config/custom_services.yaml', 7, 16) =]] +``` + +Now you can go to the **Calendar** tab and see the configured holiday. + +![Custom events list view](img/extending_calendar_list_view.png) + +### Import events from external sources + +You can also import events from external sources, for example, a JSON file. +To do this, place the following `holidays.json` file in `src/Calendar/Holidays`: + +``` json +[[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/holidays.json') =]] +``` + +Next, import this file in `src/Calendar/Holidays/EventSourceFactory.php`: + +``` php hl_lines="6-9" +[[= include_file('code_samples/back_office/calendar/src/Calendar/Holidays/EventSourceFactory.php', 19, 33) =]] +``` + +The calendar now displays the events listed in the JSON file. diff --git a/docs/extending/customizing_calendar.md b/docs/extending/customizing_calendar.md deleted file mode 100644 index 1dd6882a14..0000000000 --- a/docs/extending/customizing_calendar.md +++ /dev/null @@ -1,260 +0,0 @@ -# Customizing Calendar widget - -By default, the Calendar widget enables you to display all scheduled events and perform basic actions on them. -You can also configure it to display your custom event types or display them from [custom sources](#configuring-event-sources). - -Optionally, you can [change the colors and icons](#customizing-colors-and-icons) and make the widget look differently depending on the [SiteAccess configuration](../guide/siteaccess.md#configuring-siteaccesses). - -## Configuring custom events - -This example shows you how to create custom events which add bank holidays to the calendar. - -First, create a new event in `src/Calendar/Holidays/Event.php`: - -``` php -actions = new EventActionCollection($actions); - $this->translator = $translator; - } - - public function getTypeIdentifier(): string - { - return self::EVENT_TYPE_IDENTIFIER; - } - - public function getTypeLabel(): string - { - return $this->translator->trans( - /** @Desc("Bank holidays") */ - 'bank_holiday.label', - [], - 'app_calendar_events' - ); - } - - public function getEventName(Event $event): string - { - return $event->getId(); - } - - public function getActions(): EventActionCollection - { - return $this->actions; - } -} -``` - -The identifier defined in lines 23-26 can be used later in [configuration](#customizing-colors-and-icons). - -Complete the procedure by registering the new event type in `config/services.yaml`: - -``` yaml -services: - // ... - App\Calendar\Holidays\EventType: - arguments: - $actions: [] - tags: - - { name: ezplatform.calendar.event_type } -``` - -## Configuring event sources - -To add specific events to your calendar, you need to create an event source. - -An event source must implement `EzSystems\EzPlatformCalendar\Calendar\EventSource\EventSourceInterface`. - -One such built-in implementation is `InMemoryEventSource`. -To add an in-memory collection as an event source, create `src/Calendar/Holidays/EventSourceFactory.php`: - -```php -eventType = $eventType; - } - - public function createEventSource(): EventSourceInterface - { - $eventCollectionArray = []; - $eventCollectionArray[] = $this->createEvent('New Year', new DateTime("2020-01-01")); - $eventCollectionArray[] = $this->createEvent('Christmas Day', new DateTime("2020-12-25")); - - $collection = new EventCollection($eventCollectionArray); - - return new InMemoryEventSource($collection); - } - - private function createEvent(string $id, DateTimeInterface $dateTime): Event - { - return new Event($id, $dateTime, $this->eventType); - } -} -``` - -!!! note - - When creating the list of events for `$collection_example = new EventCollection()`, you must put all the `createEvent()` entities chronologically. - - For example: - - ``` php - $collection_example = new EventCollection([ - $this->createEvent("Event 1", new DateTime("2020-01-01")), - $this->createEvent("Event 2", new DateTime("2020-01-02")), - // ... - ``` - -The event source must be registered in `config/services.yaml`: - -``` yaml -App\Calendar\Holidays\EventSourceFactory: - arguments: - $eventType: '@App\Calendar\Holidays\EventType' - -App\Calendar\Holidays\EventSource: - class: EzSystems\EzPlatformCalendar\Calendar\EventSource\InMemoryEventSource - factory: ['@App\Calendar\Holidays\EventSourceFactory','createEventSource'] - tags: - - { name: ezplatform.calendar.event_source } -``` - -Now you will be able to see the bank holidays in the **Calendar** tab. - -![Custom events list view](img/extending_calendar_list_view.png) - -### Importing events from external sources - -You can also import events from external sources, e.g. a JSON file: - -``` json -[ - { - "date": "2020-01-01", - "name": "New Year Day" - }, - { - "date": "2020-12-25", - "name": "Christmas Day" - }, -] -``` - -To do this, import this file in `src/Calendar/Holidays/EventSourceFactory.php`: - -``` php -eventType = $eventType; - } - - public function createEventSource(): EventSourceInterface - { - $items = json_decode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'data.json'), true); - $eventCollectionArray = []; - foreach ($items as $item) { - $eventCollectionArray[] = $this->createEvent($item['name'], new DateTime($item['date'])); - } - $collection = new EventCollection($eventCollectionArray); - - return new InMemoryEventSource($collection); - } - - private function createEvent(string $id, DateTimeInterface $dateTime): Event - { - return new Event($id, $dateTime, $this->eventType); - } -} -``` - -## Customizing colors and icons - -You can change the foreground and background color of a custom event or change the icon of an event or action. -The setting is SiteAccess-aware. - -To customize the appearance settings, add the following configuration to `config/packages/ezplatform.yaml`: - -``` yaml hl_lines="6" -ezplatform: - system: - : - calendar: - event_types: - holiday: - icon: /assets/images/event_icon.svg - color: '#FF0000' - actions: - action_name: - icon: /assets/images/action_icon.svg -``` - -Note that line 6 contains the name of the event type defined in `EventType.php`. - -After modifying the assets, for the new configuration to take effect, run: `yarn encore `. - -![Bank holiday with custom color](img/extending_calendar_view.png) diff --git a/docs/extending/drag_and_drop.md b/docs/extending/drag_and_drop.md index d287edb7ad..12549ee540 100644 --- a/docs/extending/drag_and_drop.md +++ b/docs/extending/drag_and_drop.md @@ -1,14 +1,18 @@ -# Creating drag and drop interface +--- +description: Add custom drag-and-drop interactions to Back Office interface. +--- -In [[= product_name =]], you can create a generic interface for drag and drop interactions reusable in many places. +# Add drag and drop -First, prepare the HTML code structure that will be placed in a Twig template in the following way: +You can create a generic interface for drag and drop interactions that you can reuse in many places across the Back Office. + +First, prepare the HTML code structure and place it in a Twig template. See the example: ```html
    ``` -Next, add options in the same Twig template or in a JavaScript code that comes with the template following the convention: +To initialize a drag and drop interface, add a JavaScript Code that comes with the template following the convention: ```javascript const draggable = new window.eZ.core.Draggable({ @@ -18,19 +22,19 @@ const draggable = new window.eZ.core.Draggable({ }); ``` -For more information on creating Twig templates, see [Templating basics](../guide/templates.md). +For more information on creating Twig templates, see [Templating basics](../guide/content_rendering/templates/templates.md). -## Options +## Configuration options Full list of options: |Option|Description|Required| |------|-----------|--------| -|`itemsContainer`|Reference to DOM node containing a draggable item|required| -|`selectorItem`|CSS selector of a draggable item|required| -|`selectorPlaceholder`|CSS selector of a placeholder|required| -|`afterInit`|Callback function invoked after interface initialization|optional| -|`afterDragStart`|Callback function invoked after starting to drag|optional| -|`afterDragOver`|Callback function invoked after moving onto a droppable element|optional| -|`afterDrop`|Callback function invoked after dropping an element|optional| -|`attachCustomEventHandlersToItem`|Function to be invoked while attaching event handlers to every item in the item's container. Item of `HTMLElement` type is passed to the function as the first param|optional| +|`itemsContainer`|Reference to DOM node containing a draggable item.|required| +|`selectorItem`|CSS selector of a draggable item.|required| +|`selectorPlaceholder`|CSS selector of a placeholder.|required| +|`afterInit`|Callback function invoked after interface initialization.|optional| +|`afterDragStart`|Callback function invoked after starting to drag.|optional| +|`afterDragOver`|Callback function invoked after moving onto a droppable element.|optional| +|`afterDrop`|Callback function invoked after dropping an element.|optional| +|`attachCustomEventHandlersToItem`|Function to be invoked while attaching event handlers to every item in the item's container. Item of `HTMLElement` type is passed to the function as the first param.|optional| diff --git a/docs/extending/extending_back_office.md b/docs/extending/extending_back_office.md index 18c12fe594..c1ead7d879 100644 --- a/docs/extending/extending_back_office.md +++ b/docs/extending/extending_back_office.md @@ -1,4 +1,8 @@ -# Extending Back Office +--- +description: Back Office holds the administrator and editor interface and allows creating, publishing and managing content, users, settings and so on. +--- + +# Back Office The Back Office interface is produced by the [`ezplatform-admin-ui` bundle](https://github.com/ezsystems/ezplatform-admin-ui). Additionally, it uses React-based modules that make each part of the UI extensible, and Bootstrap for styling. @@ -6,15 +10,12 @@ React modules that handle specific parts of the application can be found in [`ezplatform-admin-ui-modules`](https://github.com/ezsystems/ezplatform-admin-ui-modules) The interface is accessible in your browser at `http:///admin`. -To extend the Back Office with PHP code, you can use [events](https://symfony.com/doc/5.0/event_dispatcher.html), +To extend the Back Office with PHP code, you can use [events]([[= symfony_doc =]]/event_dispatcher.html), either built-in Symfony events or events dispatched by the application. -Some extensibility, such as [adding custom tags](extending_online_editor.md#custom-tags), +Some extensibility, such as [adding custom tags](extending_online_editor.md#configure-custom-tags), is possible without writing your own code, with configuration and templating only. -To learn more, take a look at the [Extending Admin UI tutorial](../tutorials/extending_admin_ui/extending_admin_ui.md) -or at the more specific cases in Extending Back Office section. - !!! note "String translations" Refer to [Custom string translations](../guide/back_office_translations.md#custom-string-translations) diff --git a/docs/extending/extending_dashboard.md b/docs/extending/extending_dashboard.md deleted file mode 100644 index 739f32d268..0000000000 --- a/docs/extending/extending_dashboard.md +++ /dev/null @@ -1,37 +0,0 @@ -# Extending the dashboard - -To extend the **My dashboard** page, make use of an event subscriber. - -In the following example, the `DashboardEventSubscriber.php` removes the **Common content** section of the **My dashboard** page, -identified by the `ezplatform.adminui.dashboard.all` key: - -``` php - ['onRenderGroupEvent', 20], - ]; - } - - public function onRenderGroupEvent(RenderGroupEvent $event) - { - if ($event->getGroupName() !== 'dashboard-blocks') { - return; - } - - $components = $event->getComponents(); - - unset($components['ezplatform.adminui.dashboard.all']); - $event->setComponents($components); - } -} -``` diff --git a/docs/extending/extending_date_and_time.md b/docs/extending/extending_date_and_time.md index e14967e72d..0d0704d5b5 100644 --- a/docs/extending/extending_date_and_time.md +++ b/docs/extending/extending_date_and_time.md @@ -1,36 +1,12 @@ -# Format date and time +--- +description: Use different formats to render dates and times in the Back Office and website front. +--- -## Twig filters +# Formatting date and time -Apart from changing the [date and time formats](../guide/config_back_office.md#date-and-time-formats), you can use Twig filters: +## Using Twig filters and PHP services -- `ez_short_datetime` -- `ez_short_date` -- `ez_short_time` -- `ez_full_datetime` -- `ez_full_date` -- `ez_full_time` - -The following are examples of using the filters: - -``` php hl_lines="3 6" -
    - // Date formatted in the preferred time zone and short datetime format: - {{ content.versionInfo.creationDate|ez_short_datetime }} - - // Date formatted in UTC and preferred short datetime format: - {{ content.versionInfo.creationDate|ez_short_datetime('UTC') }} -
    -``` - -The filters accept an optional `timezone` parameter for displaying date and time in a chosen time zone. -The default time zone is set in the User settings menu. - -For details, see reference materials on the [full format filters](../guide/twig_functions_reference.md#ez_full_datetime-ez_full_date-ez_full_time) and [short format filters](../guide/twig_functions_reference.md#ez_short_datetime-ez_short_date-ez_short_time). - -## Services - -You can also format date and time by using the following services: +You can format date and time by using the following services: - `@ezplatform.user.settings.short_datetime_format.formatter` - `@ezplatform.user.settings.short_datet_format.formatter` @@ -83,3 +59,63 @@ services: arguments: $shortDateTimeFormatter: '@ezplatform.user.settings.short_datetime_format.formatter' ``` + +## Using User settings menu + +Users can set their preferred date and time formats in the User settings menu. +This format is used throughout the Back Office. + +You can set the list of available formats with the following configuration: + +``` yaml +ibexa: + system: + : + user_preferences: + allowed_short_date_formats: + 'label for dd/MM/yyyy': 'dd/MM/yyyy' + 'label for MM/dd/yyyy': 'MM/dd/yyyy' + allowed_short_time_formats: + 'label for HH:mm' : 'HH:mm' + 'label for hh:mm a' : 'hh:mm a' + allowed_full_date_formats: + 'label for dd/MM/yyyy': 'dd/MM/yyyy' + 'label for MM/dd/yyyy': 'MM/dd/yyyy' + allowed_full_time_formats: + 'label for HH:mm': 'HH:mm' + 'label for hh:mm a': 'hh:mm a' +``` + +The default date and time format is set using: + +``` yaml +ibexa: + system: + : + user_preferences: + short_datetime_format: + date_format: 'dd/MM/yyyy' + time_format: 'hh:mm' + full_datetime_format: + date_format: 'dd/MM/yyyy' + time_format: 'hh:mm' +``` + +## Allowed formats + +The following subset of the [ICU date and time formats](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classSimpleDateFormat.html#details) is allowed: + +|Symbol|Meaning| +|---|---| +|y, yy, yyyy, Y, YY, YYYY|year| +|q, Q|quarter| +|M, MM, MMM, MMMM, L, LL, LLL, LLLL|month| +|w, WW|week| +|d, dd|day of the month| +|D, DDD|day of the year| +|E, EE, EEE, EEEE, EEEEEE, e, ee, eee, eeee, eeeeee, c, cc, ccc, cccc, cccccc|weekday| +|a|AM or PM| +|h, hh, H, HH, k, kk|hour| +|m, mm|minute| +|s, ss, S...|second| +|Z, ZZ, ZZZ, ZZZZZ|timezone| diff --git a/docs/extending/extending_field_type.md b/docs/extending/extending_field_type.md deleted file mode 100644 index c82f083c70..0000000000 --- a/docs/extending/extending_field_type.md +++ /dev/null @@ -1,205 +0,0 @@ -# Creating custom Field Type - -The Generic Field Type is an abstract implementation of Field Types holding structured data e.g. Address. -It can be used as a base for custom Field Types. -The Generic Field Type comes with the implementation of basic methods, reduces the number of classes, which must be created and simplifies the tagging process. - -!!! tip - - You should not use the Generic Field Type when you need a very specific implementation or complete control over the way data is stored. - -The simplified process of creating a custom Field Type based on a Generic Field Type requires the following steps: - -- Define Value Object -- Define fields and configuration -- Define form for Value Object -- Render field -- Final results - -## Define Value Object - -Create `Value.php` in `src/FieldType/HelloWorld` directory. -The Value class of a Field Type contains only the basic logic of a Field Type, the rest of it is handled by the `Type` class. -For more information about Field Type Value see [Value handling](../../api/field_type_type_and_value/#value-handling). - -The `HelloWorld` Value class should contain: - -- public properties that retrieve `name` -- an implementation of the `__toString()` method - -```php -name; - } - public function setName(?string $name): void - { - $this->name = $name; - } - - public function __toString() - { - return "Hello {$this->name}!"; - } -} -``` - -## Define fields and configuration - -In this step you will implement a definition of a Field Type extending the Generic Field Type in the `src/FieldType/HelloWorld/Type.php` class. -It provides settings for the Field Type and an implementation of the `eZ\Publish\SPI\FieldType\FieldType` abstract class. - -```php -add('name', TextType::class); - } - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'data_class' => Value::class - ]); - } -} -``` - -Now you will map Field definitions into Symfony forms with FormMapper. -Add `FieldValueFormMapperInterface` interface (`EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface`) -to the Field Type definition in `src/FieldType/HellowWorld/Type.php`. - -```php -fieldDefinition; - - $fieldForm->add('value', HelloWorldType::class, [ - 'required' => $definition->isRequired, - 'label' => $definition->getName() - ]); - } -} -``` - -For more information about the FormMappers see [Field Type form and template](../api/field_type_form_and_template.md). - -Next, add the `ezplatform.field_type.form_mapper.value` class to `config/services.yml`: - -```yaml -App\FieldType\HelloWorld\Type: - public: true - tags: - - { name: ezplatform.field_type, alias: hello_world } - - { name: ezplatform.field_type.form_mapper.value, fieldType: hello_world } -``` - -## Render fields - -### Create a template - -Create a template for the new Field Type. It will define the default display of the `HelloWorld` field. -In the `templates` directory create a `field_type.html.twig` file: - -```html+twig -{% block hello_world_field %} - Hello {{ field.value.getName() }}! -{% endblock %} -``` - -### Template mapping - -Provide the template mapping in `config/packages/ezplatform.yaml`: - -```yaml -ezplatform: - system: - default: - # ... - field_templates: - - { template: 'field_type.html.twig', priority: 0 } -``` - -## Final results - -Finally, you should be able to add a new Content Type in the Back Office interface. -Navigate to **Content Types** tab and under **Content** category create a new Content Type: - -![Creating new Content Type](img/extending_field_type_create.png) - -Next, define a **Hello World** field: - -![Defining Hello World](img/extending_field_type_definition.png) - -After saving, your **Hello World** Content Type should be available under **Content** in the sidebar menu. - -![Creating Hello World](img/extending_field_type_hello_world.png) - -For more detailed tutorial on Generic Field Type follow [Creating a Point 2D Field Type ](../tutorials/generic_field_type/creating_a_point2d_field_type.md). diff --git a/docs/extending/extending_form_builder.md b/docs/extending/extending_form_builder.md deleted file mode 100644 index 4048b9b02a..0000000000 --- a/docs/extending/extending_form_builder.md +++ /dev/null @@ -1,220 +0,0 @@ -# Extending Form Builder [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -## Existing Form fields - -### Captcha field - -The Captcha Form field is based on [Gregwar/CaptchaBundle](https://github.com/Gregwar/CaptchaBundle). - -![Captcha field](img/extending_form_builder_captcha_default.png) - -You can customize the field by adding configuration to `config/packages/gregwar_captcha.yaml` under `gregwar_captcha`: - -``` yaml -gregwar_captcha: - as_url: true - width: 150 - invalid_message: Code does not match, please retry. - reload: true -``` - -The configuration resizes the CAPTCHA image (line 3), changes the error message (line 4), enables the user to reload the code (line 5). - -![Custom captcha field](img/extending_form_builder_captcha_result.png) - -For information about available options, see [the bundle's documentation.](https://github.com/Gregwar/CaptchaBundle#options) - -!!! note - - If your installation uses Varnish to manage content cache, you must modify the configuration to avoid issues with the Captcha field. For more information, see [Ensure proper captcha behavior](../guide/http_cache.md#ensure-proper-captcha-behavior). - -## Extending Form fields - -You can extend the Form Builder by adding new Form fields or modifying existing ones. -Form fields are defined in YAML configuration. - -For example, to create a Country Form field in the "Custom form fields" category, -provide the block configuration in `config/packages/ez_platform_form_builder.yaml`: - -``` yaml -ez_platform_form_builder: - fields: - country: - name: Country - category: Custom form fields - thumbnail: `/bundles/ezplatformadminui/img/ez-icons.svg#input-line` - attributes: - label: - name: Display label - type: string - validators: - not_blank: - message: You must provide label of the field - help: - name: Help text - type: string - validators: - required: ~ -``` - -Available attribute types are: - -|Type|Description| -|----|----| -|`string`|String| -|`text`|Text block| -|`integer`|Integer number| -|`url`|URL| -|`multiple`|Multiple choice| -|`select`|Dropdown| -|`checkbox`|Checkbox| -|`location`|Content Location| -|`radio`|Radio button| -|`action`|Button| -|`choices`|List of available options| - -Each type of Form field can have validators of the following types: - -- `required` -- `min_length` -- `max_length` -- `min_choices` -- `max_choices` -- `min_value` -- `max_value` -- `regex` -- `upload_size` -- `extensions` - -New types of fields require a mapper which implements the `EzSystems\EzPlatformFormBuilder\FieldType\Field\FieldMapperInterface` interface. -Implement the `FieldMapperInterface` interface in `src/FormBuilder/Field/Mapper/CountryFieldMapper.php`: - -``` php -namespace App\FormBuilder\Field\Mapper; - -use EzSystems\EzPlatformFormBuilder\FieldType\Field\Mapper\GenericFieldMapper; -use EzSystems\EzPlatformFormBuilder\FieldType\Model\Field; - -class CountryFieldMapper extends GenericFieldMapper - -{ - /** - * {@inheritdoc} - */ - protected function mapFormOptions(Field $field, array $constraints): array - { - $options = parent::mapFormOptions($field, $constraints); - $options['label'] = $field->getAttributeValue('label'); - $options['help'] = $field->getAttributeValue('help'); - return $options; - } -} -``` - -The mapper must be registered as a service in `config/services.yaml`: - -``` yaml -services: - # ... - App\FormBuilder\Field\Mapper\CountryFieldMapper: - arguments: - $fieldIdentifier: country - $formType: Symfony\Component\Form\Extension\Core\Type\CountryType - tags: - - { name: ezplatform.form_builder.field_mapper } -``` - -Now you can go to Back Office and build a new form. -You should be able to see the new section in the list of available fields: - -![Custom form fields](img/extending_form_builder_custom_form_fields.png) - -And a new Country Form field: - -![Country field](img/extending_form_builder_country_field.png) - -## Changing field and field attribute definitions dynamically - -Field or field attribute definition can be modified by subscribing to one of the following events: - -``` -ezplatform.form_builder.field. -ezplatform.form_builder.field.. -``` - -The following example adds the `readonly` attribute to `single_line` field definition. - -``` php -namespace App\EventSubscriber; - -use EzSystems\EzPlatformFormBuilder\Event\FieldDefinitionEvent; -use EzSystems\EzPlatformFormBuilder\Event\FieldDefinitionEvents; -use EzSystems\EzPlatformFormBuilder\Definition\FieldAttributeDefinitionBuilder; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class FieldDefinitionSubscriber implements EventSubscriberInterface -{ - public function onSingleLineFieldDefinition(FieldDefinitionEvent $event): void - { - $isReadOnlyAttribute = new FieldAttributeDefinitionBuilder(); - $isReadOnlyAttribute->setIdentifier('readonly'); - $isReadOnlyAttribute->setName('Field is read only'); - $isReadOnlyAttribute->setType('string'); - - $definitionBuilder = $event->getDefinitionBuilder(); - $definitionBuilder->addAttribute($isReadOnlyAttribute->buildDefinition()); - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents(): array - { - return [ - FormEvents::getFieldDefinitionEventName('single_line') => 'onSingleLineFieldDefinition' - ]; - } -} -``` - -``` yaml -services: - App\EventSubscriber\FieldDefinitionSubscriber: - public: true - tags: - - kernel.event_subscriber -``` - -## Accessing Form field definitions - -Field definitions are accessible through: - -- `\EzSystems\EzPlatformFormBuilder\Definition\FieldDefinitionFactory` in the back end -- global variable `eZ.formBuilder.config.fieldsConfig` in the front end - -## Customizing email notifications - -Email is one of the Submit button options you can add to a form using the Form Builder. -It allows you to list email addresses to which notifications about newly filled forms should be sent. - -![Email notification](img/email_notification.png) - -### Override email template - -To customize the Form Builder submission notification, you need to override the `form_builder/form_submit_notification_email.html.twig` template. -It contains two blocks: subject and body. -Each of them is rendered independently and consists of three sets of parameters. - -|Parameter|Type|Description| -|---------|----|-----------| -|`content`|`eZ\Publish\API\Repository\Values\Content\Content`|Name of the form, its Content Type| -|`form`|`EzSystems\EzPlatformFormBuilder\FieldType\Model\Form`|Definition of the form| -|`data`|`EzSystems\EzPlatformFormBuilder\FieldType\Model\FormSubmission`|Sent data| - -By adjusting them to your needs, you will change your email template. - -### Configure sender details - -To send emails, you also need to configure `sender_address` in `config/packages/swiftmailer.yaml`. -It acts as a sender and return address for all bounced messages. -For details, see [Symfony Mailer Configuration Reference.](https://symfony.com/doc/5.0/reference/configuration/swiftmailer.html#sender-address) diff --git a/docs/extending/extending_menus.md b/docs/extending/extending_menus.md deleted file mode 100644 index e3858f496c..0000000000 --- a/docs/extending/extending_menus.md +++ /dev/null @@ -1,220 +0,0 @@ -# Extending menus - -Back Office menus are based on the [KnpMenuBundle](https://github.com/KnpLabs/KnpMenuBundle) and are easily extensible. - -!!! tip - - For general information on how to use `MenuBuilder`, - see [the official KnpMenuBundle documentation](https://symfony.com/doc/master/bundles/KnpMenuBundle/index.html). - -Menus are extensible using event subscribers/listeners. You can hook into the following events: - -- `ConfigureMenuEvent::MAIN_MENU` -- `ConfigureMenuEvent::USER_MENU` -- `ConfigureMenuEvent::CONTENT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_SIDEBAR_LEFT` -- `ConfigureMenuEvent::TRASH_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::SECTION_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::SECTION_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::POLICY_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::POLICY_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::ROLE_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::ROLE_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::USER_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::USER_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::ROLE_ASSIGNMENT_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::LANGUAGE_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::LANGUAGE_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_TYPE_GROUP_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_TYPE_GROUP_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_TYPE_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::CONTENT_TYPE_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::URL_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::USER_PASSWORD_CHANGE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::OBJECT_STATE_GROUP_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::OBJECT_STATE_GROUP_EDIT_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::OBJECT_STATE_CREATE_SIDEBAR_RIGHT` -- `ConfigureMenuEvent::OBJECT_STATE_EDIT_SIDEBAR_RIGHT` - -An event subscriber can be implemented as follows: - -``` php - ['onMenuConfigure', 0], - ]; - } - - public function onMenuConfigure(ConfigureMenuEvent $event) - { - $menu = $event->getMenu(); - $factory = $event->getFactory(); - // options passed from the context (i.e. Content item in Content View) - $options = $event->getOptions(); - - // your customizations - } -} -``` - -If [the autoconfigure option](https://symfony.com/doc/5.0/service_container.html#the-autoconfigure-option) is disabled, -you need to register the service with the `kernel.event.subscriber` tag in `config/services.yaml`: - -``` yaml -services: - App\EventListener\MenuListener: - tags: - - { name: kernel.event.subscriber } - -``` - -## Adding menu items - -Add a new menu item under "Content" with custom attributes - -``` php -$menu[MainMenuBuilder::ITEM_CONTENT]->addChild( - 'form_manager', - [ - 'route' => '_ezpublishLocation', - 'routeParameters' => ['locationId' => 2], - // attributes directly on element - 'linkAttributes' => [ - 'class' => 'test_class another_class', - 'data-property' => 'value', - ], - // attributes on container
  • element - 'attributes' => [ - 'data-property' => 'value', - ], - ] -); -``` - -Add a top-level menu item with a child: - -``` php -$menu->addChild( - 'menu_item_1', - ['label' => 'Menu Item 1', 'extras' => ['icon' => 'file']] -); -$menu['menu_item_1']->addChild( - '2nd_level_menu_item', - ['label' => '2nd level menu item', 'uri' => 'http://example.com'] -); -``` - -Add an item depending on a condition: - -``` php -$condition = true; -if ($condition) { - $menu->addChild( - 'menu_item_2', - ['label' => 'Menu Item 2', 'extras' => ['icon' => 'article']] - ); -} -``` - -Add a top-level menu item with URL redirection: - -``` php -$menu->addChild( - 'menu_item_3', - [ - 'label' => 'Menu Item 3', - 'uri' => 'http://example.com', - 'extras' => ['icon' => 'article'], - ] -); -``` - -## Modifying menu items - -Remove the *Media* menu item from the Content tab: - -``` php -$menu[MainMenuBuilder::ITEM_CONTENT]->removeChild( - MainMenuBuilder::ITEM_CONTENT__MEDIA -); -``` - -Reorder menu items, i.e. reverse the order: - -``` php -$menu->reorderChildren( - array_reverse(array_keys($menu->getChildren())) -); -``` - -## Other menu operations - -### Pass a parameter to a menu item - -You can pass parameters to menu items with `template_parameters`: - -``` php -$menu->addChild( - 'menu_item_with_params', - [ - 'extras' => [ - 'template' => 'admin_ui/menu_item_template.html.twig', - 'template_parameters' => [ - 'custom_parameter' => 'value', - ], - ], - ] -); -``` - -You can then use the variable `custom_parameter` in `templates/admin_ui/menu_item_template.html.twig`. - -### Translatable labels - -To have translatable labels, use `translation.key` from the `messages` domain: - -``` php -$menu->addChild( - 'menu_item_3', - [ - 'label' => 'translation.key', - 'uri' => 'http://example.com', - 'extras' => [ - 'icon' => 'article', - 'translation_domain' => 'messages', - ], - ] -); -``` - -### Custom icons - -You can use the `extras.icon` parameter to select an icon from the built-in set. - -To use your custom icon, use the `extras.icon_path` parameter: - -``` php -$menu->addChild( - 'menu_item_with_icon', - [ - 'extras' => [ - 'icon_path' => '/assets/images/icons/custom.svg', - 'icon_class' => 'my-custom-class', - ], - ] -); -``` - -The `extras.icon_class` parameter adds a custom CSS class to the `` element. diff --git a/docs/extending/extending_multifile_upload.md b/docs/extending/extending_multifile_upload.md index 83c173b1c2..c72d194e1c 100644 --- a/docs/extending/extending_multifile_upload.md +++ b/docs/extending/extending_multifile_upload.md @@ -1,16 +1,20 @@ -# Extending Multi-file Upload +--- +description: Configure multi-file upload functionality which allows uploading files in bulk. +--- -The Multi-file Upload module is meant to be used as a part of editorial interface of [[= product_name =]]. +# Multi-file upload + +You can use the multi-file upload module in the editorial interface of [[= product_name =]]. It provides an interface to publish content based on dropped files while uploading them in the interface. !!! caution - If you want to load the Multi-file Upload module, you need to load the JS code for it in your view, + If you want to load the multi-file upload module, you need to load the JS code for it in your view, as it is not available by default. -## How to use it? +## Use multi-file upload -With vanilla JS: +With JS only: ``` js React.createElement(eZ.modules.MultiFileUpload, { @@ -81,13 +85,13 @@ const attrs = { ## Properties list The `` module can handle additional properties. -There are 2 types of properties: **required** and **optional**. All of them are listed below. +There are two types of properties: **required** and **optional**. ### Required properties -Without all the following properties the Multi-file Upload will not work. +All of the following properties must be used, otherwise the multi-file upload does not work. -- **onAfterUpload** _{Function}_ - a callback to be invoked just after a file has been uploaded +- **onAfterUpload** _{Function}_ - a callback to be invoked immediately after a file has been uploaded - **adminUiConfig** _{Object}_ - UI config object. It should keep the following structure: - **multiFileUpload** _{Object}_ - multi file upload module config: - **defaultMappings** _{Array}_ - a list of file type to Content Type mappings @@ -112,31 +116,31 @@ Without all the following properties the Multi-file Upload will not work. ### Optional properties -Optionally, the Multi-file Upload module can take a following list of prop: - -- **checkCanUpload** _{Function}_ - checks whether am uploaded file can be uploaded. The callback takes 4 params: - - **file** _{File}_ - file object, - - **parentInfo** _{Object}_ - parent Location meta information, - - **config** _{Object}_ - Multi-file Upload module config, - - **callbacks** _{Object}_ - error callbacks list: **fileTypeNotAllowedCallback** and **fileSizeNotAllowedCallback**. -- **createFileStruct** _{Function}_ - a function that creates a _ContentCreate_ struct. The function takes 2 params: - - **file** _{File}_ - file object, - - **params** _{Object}_ - params hash containing: **parentInfo** and **adminUiConfig** stored under the **config** key. -- **deleteFile** _{Function}_ - a function deleting content created from a given file. It takes 3 params: - - **systemInfo** _{Object}_ - hash containing information about CSRF token and SiteAccess: **token** and **siteaccess**, - - **struct** _{Object}_ - Content struct, - - **callback** _{Function}_ - content deleted callback. -- **onPopupClose** _{Function}_ - function invoked when closing a Multi-file Upload popup. It takes one param: **itemsUploaded** - the list of uploaded items. -- **publishFile** _{Function}_ - publishes an uploaded file-based Content item. Takes 3 params: +Optionally, the multi-file upload module can take a following list of properties: + +- **checkCanUpload** _{Function}_ - checks whether am uploaded file can be uploaded. The callback takes four params: + - **file** _{File}_ - file object + - **parentInfo** _{Object}_ - parent Location meta information + - **config** _{Object}_ - Multi-file Upload module config + - **callbacks** _{Object}_ - error callbacks list: **fileTypeNotAllowedCallback** and **fileSizeNotAllowedCallback** +- **createFileStruct** _{Function}_ - a function that creates a _ContentCreate_ struct. The function takes two params: + - **file** _{File}_ - file object + - **params** _{Object}_ - params hash containing: **parentInfo** and **adminUiConfig** stored under the **config** key +- **deleteFile** _{Function}_ - a function deleting content created from a given file. It takes three params: + - **systemInfo** _{Object}_ - hash containing information about CSRF token and SiteAccess: **token** and **siteaccess** + - **struct** _{Object}_ - Content struct + - **callback** _{Function}_ - content deleted callback +- **onPopupClose** _{Function}_ - function invoked when closing a Multi-file Upload popup. It takes one param: **itemsUploaded** - the list of uploaded items +- **publishFile** _{Function}_ - publishes an uploaded file-based Content item. Takes three params: - **data** _{Object}_ - an object containing information about: - - **struct** _{Object}_ - the ContentCreate struct (), - - **token** _{String}_ - CSRF token, - - **siteaccess** _{String}_ - SiteAccess identifier, + - **struct** _{Object}_ - the ContentCreate struct () + - **token** _{String}_ - CSRF token + - **siteaccess** _{String}_ - SiteAccess identifier - **requestEventHandlers** _{Object}_ - a list of upload event handlers: - - **onloadstart** _{Function}_ - on load start callback, + - **onloadstart** _{Function}_ - on load start callback - **upload** _{Object}_ - file upload events: - - **onabort** _{Function}_ - on abort callback, - - **onload** _{Function}_ - on load callback, - - **onprogress** _{Function}_ - on progress callback, - - **ontimeout** _{Function}_ - on timeout callback. - - **callback** _{Function}_ - a callback invoked when an uploaded file-based content has been published. + - **onabort** _{Function}_ - on abort callback + - **onload** _{Function}_ - on load callback + - **onprogress** _{Function}_ - on progress callback + - **ontimeout** _{Function}_ - on timeout callback + - **callback** _{Function}_ - a callback invoked when an uploaded file-based content has been published diff --git a/docs/extending/extending_online_editor.md b/docs/extending/extending_online_editor.md index 7823046558..d026e04c3f 100644 --- a/docs/extending/extending_online_editor.md +++ b/docs/extending/extending_online_editor.md @@ -1,85 +1,67 @@ -# Extending Online Editor +--- +description: Add custom tags and styles to enrich the functionality of the Online Editor. +--- -The Online Editor is based on [Alloy Editor](https://alloyeditor.com/). -Refer to [Alloy Editor documentation](https://alloyeditor.com/docs/develop/) to learn how to extend the Online Editor with new elements. -To learn how to extend the [[= product_name =]] Back Office follow [Extending Admin UI tutorial](../../tutorials/extending_admin_ui/extending_admin_ui). +# Extend Online Editor + +[[= product_name =]] users edit the contents of RichText Fields, for example, +in the Content box of a Page, by using the Online Editor. + +You can extend the Online Editor by adding custom tags and styles, defining custom +data attributes, re-arranging existing buttons, grouping buttons into custom toolbars, +and creating [custom buttons](online_editor_button.md) and +[custom plugins](online_editor_plugin.md). + +Online Editor is based on the Alloy Editor. +Refer to [Alloy Editor documentation](https://alloyeditor.com/docs/develop/) to learn +how you can extend the Online Editor with even more elements. +For more information about extending the Back Office UI, see [Extend Back Office](extending_back_office.md). !!! note - Online Editor configuration works out of the box only if you have the Rich Text bundle enabled. - If you do not, for example due to an upgrade from an earlier version, - enable it according to the [installation guide](https://github.com/ezsystems/ezplatform-richtext#installation). + Online Editor configuration works out of the box only if you have the Rich + Text bundle enabled. + If the bundle is not enabled, for example, when you upgrade from an earlier + version of [[= product_name =]], enable the bundle: + + `composer require ezsystems/ezplatform-richtext` + + Add the following line to the `config/bundles.php` file: -## Custom tags + `EzSystems\EzPlatformRichTextBundle\EzPlatformRichTextBundle::class => ['all' => true],` + + And clear the cache: -Custom tags enable you to add more features to the Rich Text editor beyond the built-in ones. -They are configured under the `ezrichtext` key. + `php bin/console cache:clear` -If you want to learn how to apply them to your installation follow [Creating a custom tag tutorial](../../tutorials/extending_admin_ui/4_adding_a_custom_tag). +## Configure custom tags -Preparation of the tag always starts with the configuration file that should be added to the `config` folder. This is sample configuration for the Factbox tag, in `custom_tags.yaml`: +With custom tags, you can enhance the Online Editor with features that go beyond the built-in ones. +You configure custom tags under the `ezrichtext` key. + +Start preparing the tag by adding a configuration file: ```yaml -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_tags: [ezfactbox] - toolbars: - ezadd: - buttons: - ezfactbox: - priority: 5 - ezfactbox: - buttons: - ezmoveup: - priority: 40 - ezmovedown: - priority: 30 - ezcustomtagedit: - priority: 20 - ezblockremove: - priority: 10 -ezrichtext: - custom_tags: - ezfactbox: - template: field_type/ezrichtext/custom_tag/ezfactbox.html.twig - icon: '/assets/field_type/ezrichtext/custom_tag/icon/factbox.svg#factbox' - attributes: - name: - type: string - required: true - style: - type: choice - required: true - default_value: light - choices: [light, dark] +[[= include_file('code_samples/back_office/online_editor/custom_tags/factbox/config/packages/custom_tags.yaml') =]] ``` -Remember to provide your own files for the template and the icon. -Each custom tag can have any number of attributes. +Custom tags can have as many attributes as needed. Supported attribute types are: -`string`, `number`, `boolean`, `link`, and `choice` (which requires a list of choices provided by the `choices` key). +`string`, `number`, `boolean`, `link`, and `choice`. +`choice` requires that you provide a list of options in the `choices` key. -The configuration requires an `ezfactbox.html.twig` template for the custom tag that will be placed in `templates/field_type/ezrichtext/custom_tag`: +You must provide your own files for the Twig template and the icon. +Place the `factbox.html.twig` template in the +`templates/themes//field_type/ezrichtext/custom_tags` directory: ```html+twig -
    -

    {{ params.name }}

    -
    - {{ content|raw }} -
    -
    +[[= include_file('code_samples/back_office/online_editor/custom_tags/factbox/templates/themes/standard/field_type/ezrichtext/custom_tags/factbox.html.twig') =]] ``` -FactBox tag is a good example for showcasing possibilities of `ezcontent` property. -Each custom tag has an `ezcontent` property that contains the tag's main content. -This property is editable by a tab in a custom tag. - !!! tip - Remember that if an attribute is not required, you need to check if it is defined in the template, for example: + If an attribute is not required, check if it is defined by adding a check + in the template, for example: ```html+twig {% if params.your_attribute is defined %} @@ -87,15 +69,10 @@ This property is editable by a tab in a custom tag. {% endif %} ``` -To ensure the new tag has labels, provide translations in `translations/custom_tags.en.yaml` file: +Add labels for the new tag by providing translations in `translations/custom_tags.en.yaml`: ```yaml -ezrichtext.custom_tags.ezfactbox.label: FactBox -ezrichtext.custom_tags.ezfactbox.description: '' -ezrichtext.custom_tags.ezfactbox.attributes.name.label: Name -ezrichtext.custom_tags.ezfactbox.attributes.style.label: Style -ezrichtext.custom_tags.ezfactbox.attributes.style.choices.light.label: Light style -ezrichtext.custom_tags.ezfactbox.attributes.style.choices.dark.label: Dark style +[[= include_file('code_samples/back_office/online_editor/custom_tags/factbox/translations/custom_tags.en.yaml') =]] ``` Now you can use the tag. @@ -106,102 +83,48 @@ In the Online Editor, click **Add**, and from the list of available tags select ### Inline custom tags -Custom tags can also be placed inline with the following configuration: +You can also place custom tags inline with the following configuration: ``` yaml hl_lines="6" -ezrichtext: - custom_tags: - badge: - template: field_type/ezrichtext/custom_tag/badge.html.twig - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#bookmark' - is_inline: true - attributes: - # ... +[[= include_file('code_samples/back_office/online_editor/custom_tags/acronym/config/packages/custom_tags.yaml', 21, 28) =]] # ... ``` `is_inline` is an optional key. -The default value is `false`, so if it is not set, the custom tag will be treated as a block tag. - -You can only use inline custom tags in the `text` toolbar. - -!!! caution "Incorrect configuration" - - Newer configuration options, such as `is_inline`, only work with the configuration provided above. - If your project uses [configuration from version prior to 2.4](../updating/4_update_2.4.md#changes-to-custom-tags), - these options will not work. - You need to update your configuration to be placed under the `ezrichtext` key. +The default value is `false`, therefore, if it is not set, the custom tag is +treated as a block tag. ### Use cases #### Link tag -You can also configure a custom tag with a `link` attribute that offers a basic UI with text input. +You can configure a custom tag with a `link` attribute that offers a basic UI with text input. It is useful when migrating from eZ Publish to [[= product_name =]]. -The configuration in `app/config/custom_tags.yml` is: - -```yaml hl_lines="24 25" -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_tags: [linktag] +The configuration is: -ezrichtext: - custom_tags: - linktag: - template: '@ezdesign/custom_tags/vcustom.html.twig' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#link' - attributes: - attrTitle: - type: string - required: false - attrDesc: - type: string - required: false - attrColor: - type: choice - required: false - choices: [Red, Blue, Green] - attrUrl: - type: link - required: false +```yaml hl_lines="30-31" +[[= include_file('code_samples/back_office/online_editor/custom_tags/linktag/config/packages/custom_tags.yaml') =]] ``` -Remember to provide your own files for the template and the icon. -In this example, the tag has the `attrUrl` attribute with the `type` parameter set as `link`. (lines 24-25). - -Before proceeding, ensure that the `custom_tags.yml` file is added to `app/config/config.yml` under the `imports` key: +Provide your own files for the Twig template and the icon. -``` yaml -imports: -# ... - - { resource: custom_tags.yml } -``` +The tag has the `url` attribute with the `type` parameter set as `link` (lines 30-31). -Next, create a `app/Resources/views/field_type/ezrichtext/linktag.html.twig` template: +Then create the `templates/themes//field_type/ezrichtext/custom_tags/linktag.html.twig` template: ``` html+twig -

    vcustom

    -{% for attr_name, attr_value in params %} -
    {{ attr_name }}: {{ attr_value }}
    -{% endfor %} +[[= include_file('code_samples/back_office/online_editor/custom_tags/linktag/templates/themes/standard/field_type/ezrichtext/custom_tags/linktag.html.twig') =]] ``` -Lastly, provide the translations in a `app/Resources/translations/linktag.en.yaml` file: +Add labels for the tag by providing translations in `translations/custom_tags.en.yaml`: ``` yaml -ezrichtext.custom_tags.linktag.label: 'Link Tag' -ezrichtext.custom_tags.linktag.attributes.attrTitle.label: 'Title' -ezrichtext.custom_tags.linktag.attributes.attrDesc.label: 'Description' -ezrichtext.custom_tags.linktag.attributes.attrColor.label: 'Color' -ezrichtext.custom_tags.linktag.attributes.attrUrl.label: 'URL' +[[= include_file('code_samples/back_office/online_editor/custom_tags/linktag/translations/custom_tags.en.yaml') =]] ``` Now you can use the tag. In the Back Office, create or edit a Content item that has a RichText Field Type. -In the Online Editor, click **Add**, and from the list of available tags select the Link tag icon. +In the Online Editor's toolbar, click **Show more items**, and from the list of available tags select the Link tag icon. ![Link Tag](img/custom_tag_link.png "Link Tag in the Online Editor") @@ -210,37 +133,21 @@ In the Online Editor, click **Add**, and from the list of available tags select You can create an inline custom tag that will display a hovering tooltip with an explanation of an acronym. ``` yaml -ezpublish: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_tags: [acronym] - -ezrichtext: - custom_tags: - acronym: - template: field_type/ezrichtext/custom_tag/acronym.html.twig - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#information' - is_inline: true - attributes: - explanation: - type: 'string' +[[= include_file('code_samples/back_office/online_editor/custom_tags/acronym/config/packages/custom_tags.yaml') =]] ``` -The `explanation` attribute will contain the meaning of the acronym that will be provided +The `explanation` attribute contains the meaning of the acronym that will be provided while editing in the Online Editor. -Label translations can be provided in `translations/custom_tags.en.yaml`: +Add labels for the tag by providing translations in `translations/custom_tags.en.yaml`: ``` yaml -ezrichtext.custom_tags.acronym.label: 'Acronym' -ezrichtext.custom_tags.acronym.attributes.meaning.label: 'Explanation' +[[= include_file('code_samples/back_office/online_editor/custom_tags/acronym/translations/custom_tags.en.yaml') =]] ``` ![Adding an explanation to an Acronym custom tag](img/oe_custom_tag_add_acronym.png) -In the template file `acronym.html.twig` provide the explanation as `attr_value` +In the template file `acronym.html.twig` provide the explanation as attribute value to the title of the `abbr` tag: ``` html+twig @@ -249,61 +156,45 @@ to the title of the `abbr` tag: ![Acronym custom tag](img/oe_custom_tag_acronym.png) -## Custom styles +## Configure custom styles You can extend the Online Editor with custom text styles. -The feature depends on [Alloy Editor styles](https://alloyeditor.com/docs/features/styles.html). The styles are available in the text toolbar when a section of text is selected. There are two kinds of custom styles: block and inline. Inline styles apply to the selected portion of text only, while block styles apply to the whole paragraph. -### Back Office configuration +!!! note -The sample configuration is as follows: + The feature depends on [Alloy Editor styles](https://alloyeditor.com/docs/features/styles.html). -``` yaml -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_styles: [highlighted_block, highlighted_word] +Start creating a custom style by providing configuration: -ezrichtext: - custom_styles: - highlighted_word: - template: '@ezdesign/field_type/ezrichtext/custom_style/highlighted_word.html.twig' - inline: true - highlighted_block: - template: '@ezdesign/field_type/ezrichtext/custom_style/highlighted_block.html.twig' - inline: false -``` +- a global list of custom styles, defined under the node `ezrichtext.custom_styles`, +- a list of enabled custom styles for a given `admin` SiteAccess or `admin_group` SiteAccess group, located under the node `ezplatform.system..fieldtypes.ezrichtext.custom_styles` -The system expects two kinds of configuration: +A sample configuration could look as follows: -- a global list of custom styles, defined under the node `ezrichtext.custom_styles`, -- a list of enabled custom styles for a given Admin SiteAccess or Admin SiteAccess group, located under the node `ezplatform.system..fieldtypes.ezrichtext.custom_styles` +``` yaml +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_styles.yaml') =]] +``` !!! note - Defining this list for a front site SiteAccess currently has no effect. - -### Translations + Currently, if you define these lists for a front site SiteAccess, it has no effect. -Labels that appear for each custom style in the Online Editor need to be translated using Symfony translation system. -The translation domain is called `custom_styles`. For the code example above, you can do it in a `translations/custom_styles.en.yaml` file: +Add labels for the new styles by providing translations in `translations/custom_styles.en.yaml`: ```yaml -ezrichtext.custom_styles.highlighted_block.label: Highlighted block -ezrichtext.custom_styles.highlighted_word.label: Highlighted word +[[= include_file('code_samples/back_office/online_editor/translations/custom_styles.en.yaml') =]] ``` ### Rendering -The `template` key points to the template used to render the custom style. It is recommended to use the [design engine](../guide/design_engine.md). +The `template` key points to the template that is used to render the custom style. +It is recommended that you use the [design engine](../guide/content_rendering/design_engine/design_engine.md). -In the example above, the template files for the front end could be: +The template files for the front end could look as follows: - `templates/themes/standard/field_type/ezrichtext/custom_style/highlighted_word.html.twig`: @@ -317,32 +208,22 @@ In the example above, the template files for the front end could be:
    {% apply spaceless %}{{ content|raw }}{% endapply %}
    ``` -Templates for Content View in the Back Office would be `templates/themes/admin/field_type/ezrichtext/custom_style/highlighted_word.html.twig` and `templates/themes/admin/field_type/ezrichtext/custom_style/highlighted_block.html.twig` respectively (assuming Admin SiteAccess uses the `admin` theme). +Templates for Content View in the Back Office would be `templates/themes/admin/field_type/ezrichtext/custom_style/highlighted_word.html.twig` and `templates/themes/admin/field_type/ezrichtext/custom_style/highlighted_block.html.twig` respectively (assuming that the Back Office SiteAccess uses the default `admin` theme). ### Use cases #### Note box -You can create a custom style that will place a paragraph in a note box: +You can create a custom style that places a paragraph in a note box: ![Example of a note box custom style](img/oe_custom_style_note_box.png) ``` yaml -ezpublish: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_styles: [note_box] - -ezrichtext: - custom_styles: - note_box: - template: field_type/ezrichtext/custom_style/note_box.html.twig +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_styles_note_box.yaml') =]] ``` -The indicated `note_box.html.twig` template wraps the content of the selected text (`{{ content }}`) -in a custom CSS class: +The `note_box.html.twig` template wraps the content of the selected text +(`{{ content }}`) in a custom CSS class: ``` html+twig
    {{ content }}
    @@ -360,7 +241,7 @@ in a custom CSS class: } ``` -Label translation can be provided in `translations/custom_styles.en.yaml`: +Add label for the new style by providing a translation in `translations/custom_styles.en.yaml`: ``` yaml ezrichtext.custom_styles.note_box.label: 'Note box' @@ -370,7 +251,7 @@ ezrichtext.custom_styles.note_box.label: 'Note box' !!! tip - You can also create a similar note box using [custom classes](#note-box_1). + You can also create a similar note box with [custom classes](#note-box_1). #### Text highlight @@ -379,22 +260,11 @@ You can create an inline custom style that highlights a part of a text: ![Example of a custom style highlighting a portion of text](img/oe_custom_style_highlight.png) ``` yaml -ezpublish: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_styles: [highlight] - -ezrichtext: - custom_styles: - highlight: - template: field_type/ezrichtext/custom_style/highlight.html.twig - inline: true +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_styles_highlight.yaml') =]] ``` -The indicated `highlight.html.twig` template wraps the content of the selected text (`{{ content }}`) -in a custom CSS class: +The `highlight.html.twig` template wraps the content of the selected text +(`{{ content }}`) in a custom CSS class: ``` html+twig {{ content }} @@ -407,7 +277,7 @@ in a custom CSS class: } ``` -Label translation can be provided in `translations/custom_styles.en.yaml`: +Add label for the new style by providing a translation in `translations/custom_styles.en.yaml`: ``` yaml ezrichtext.custom_styles.highlight.label: 'Highlight' @@ -415,11 +285,10 @@ ezrichtext.custom_styles.highlight.label: 'Highlight' ![Adding a Highlight custom style](img/oe_custom_style_highlight_select.png) -## Custom data attributes and classes +## Configure custom data attributes and classes -You can add custom data attributes and CSS classes to elements in the Online Editor. - -The available elements are: +You can add custom data attributes and CSS classes to the following elements +in the Online Editor: - `embedinline` - `embed` @@ -436,158 +305,123 @@ The available elements are: !!! caution "Overriding embed templates" - If you override the default templates for `embedinline`, `embed` or `embedimage` elements, - (e.g. `@EzPublishCore/default/content/embed.html.twig`), + If you override the default templates for `embedinline`, `embed` or `embedimage` + elements, for example, `@EzPublishCore/default/content/embed.html.twig`, the data attributes and classes will not be rendered automatically. - Instead, you can make use of the `data_attributes` and `class` properties in your templates. - The `ez_data_attributes_serialize` helper enables you to serialize the data attribute array. + Instead, you can make use of the `data_attributes` and `class` properties + in your templates. + With the `ez_data_attributes_serialize` helper you can serialize the data + attribute array. ### Custom data attributes -Custom data attributes are configured under the `fieldtypes.ezrichtext.attributes` key. +You configure custom data attributes under the `fieldtypes.ezrichtext.attributes` key. The configuration is SiteAccess-aware. -A custom data attribute can belong to one of the following types: `choice`, `boolean`, `string`, or `number`. +A custom data attribute can belong to one of the following types: `choice`, +`boolean`, `string`, or `number`. You can also set each attribute to be `required` and set its `default_value`. For the `choice` type, you must provide an array of available `choices`. -Adding `multiple` enables you to choose whether more than one option can be selected. +By adding `multiple`, you can decide whether more than one option can be selected. It is set to `false` by default. -The example below adds two data attributes, `custom_attribute` and `another_attribute` -to the Heading element: +Use the example below to add two data attributes, `custom_attribute` and +`another_attribute` to the Heading element in the `admin_group` SiteAccess: ``` yaml -ezplatform: - system: - # The configuration only works with an admin (Back Office) SiteAccess - : - fieldtypes: - ezrichtext: - attributes: - heading: - custom-attribute: - type: boolean - default_value: false - another-attribute: - type: choice - choices: [attr1, attr2] - default_value: attr2 - required: false - multiple: true +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_data_attributes.yaml') =]] ``` -This configuration will output `data-ezattribute-=""` in the corresponding HTML element, -in this example as `data-ezattribute-custom-attribute="false"` and `data-ezattribute-another-attribute="attr1,attr2"`. +The configuration outputs `data-ezattribute-=""` in the +corresponding HTML element. +Here, the resulting values are `data-ezattribute-custom-attribute="false"` and +`data-ezattribute-another-attribute="attr1,attr2"`. ### Custom CSS classes -Custom CSS classes are configured under the `fieldtypes.ezrichtext.classes` key. +You configure custom CSS classes under the `fieldtypes.ezrichtext.classes` key. The configuration is SiteAccess-aware. You must provide the available `choices`. You can also set the values for `required`, `default_value` and `multiple`. `multiple` is set to true by default. -The example below adds a class choice to the Paragraph element: +Use the example below to add a class choice to the Paragraph element in the `admin_group` SiteAccess: ``` yaml -ezplatform: - system: - # The configuration only works with an admin (Back Office) SiteAccess - : - fieldtypes: - ezrichtext: - classes: - paragraph: - choices: [regular, special] - default_value: regular - required: false - multiple: false +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_classes.yaml') =]] ``` + +!!! note "Label translations" -### Label translations - -You can provide label translations for custom attributes with the translation extractor `ez_online_editor_attributes`. -It gets a full list of custom attributes for all elements in all scopes. + If there are many custom attributes, to provide label translations for these + attributes, you can use the `ez_online_editor_attributes` translation extractor + to get a full list of all custom attributes for all elements in all scopes. -For example: + For example: -``` bash -php ./bin/console translation:extract --enable-extractor=ez_online_editor_attributes - --dir=./templates --output-dir=./translations/ --output-format=yaml -``` + ``` bash + php ./bin/console translation:extract --enable-extractor=ez_online_editor_attributes + --dir=./templates --output-dir=./translations/ --output-format=yaml + ``` ### Use cases #### Note box -You can create a custom class that will enable you to place a paragraph element in a note box: +You can create a custom class that enables you to place a paragraph element in +a note box: ![Example of a note box custom style](img/oe_custom_style_note_box.png) ``` yaml -ezpublish: - system: - admin_group: - fieldtypes: - ezrichtext: - classes: - paragraph: - choices: [regular, tip_box, warning_box] - default_value: regular - required: false - multiple: false +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_classes.yaml', 0, 8) =]] [[= include_file('code_samples/back_office/online_editor/config/packages/custom_classes.yaml', 14, 18) =]] + ``` -This enables you to choose one of the following classes for each paragraph element: `regular`, `tip_box`, or `warning_box` -that you can then style individually using CSS. +With this class you can choose one of the following classes for each paragraph +element: `regular`, `tip_box`, or `warning_box`. +You can then style the class by using CSS. ![Selecting a custom style for a paragraph](img/oe_custom_class_note_box_select.png) !!! tip - You can also create a similar note box using [custom styles](#note-box). + You can also create a similar note box with [custom styles](#note-box). -## Customizing buttons +## Rearrange buttons -You can modify the buttons available in Online Editor toolbars through configuration: +You can modify the order and visibility of buttons that are available in the +Online Editor toolbar through configuration: ``` yaml -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - toolbars: - heading: - buttons: - ezanchor: - priority: 100 - ezembedinline: - visible: false +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_buttons.yaml', 0, 12) =]] ``` -For each button you can set `priority`, which defines the order of buttons in the toolbar, -and `visible`, which can turn off the button when set to `false`. +For each button you can set `priority`, which defines the order of buttons in +the toolbar, and `visible`, which can turn off the button when set to `false`. -For a full list of buttons, see [the configuration file.](https://github.com/ezsystems/ezplatform-richtext/blob/v2.0.0/src/bundle/Resources/config/prepend/ezpublish.yaml) +For a full list of standard buttons, see the RichText module's [configuration file](https://github.com/ezsystems/ezplatform-richtext/blob/v2.0.0/src/bundle/Resources/config/prepend/ezpublish.yaml) !!! tip - You can also [create your own custom buttons](online_editor_button.md). + You can also add your own buttons to the Online Editor. + For more information, see [Create Online Editor button](online_editor_button.md). -## Custom toolbars +## Create custom toolbars -You can extend the Online Editor with the custom toolbars. -The feature depends on [Alloy Editor](https://alloyeditor.com/). +You can extend the Online Editor by creating custom toolbars. -Preparation of the custom toolbar starts with creating a new toolbar config. -If you want to learn how to do it, see [Creating a Toolbar.](https://alloyeditor.com/docs/develop/create_toolbars.html) +To do this, you must first create a config object. +For more information, see [Alloy Editor documentation](https://alloyeditor.com/docs/develop/create_toolbars.html). -Next, add the toolbar config to the `ezplatform-admin-ui-alloyeditor-js` entry using encore. -Finally, add the toolbar JavaScript class to `ezAlloyEditor.customSelections.` eZ config. +Then, add the toolbar config to the `ezplatform-admin-ui-alloyeditor-js` entry +with Webpack Encore. + +Finally, add the toolbar JavaScript class to the +`ezAlloyEditor.customSelections.` config object. You can do it at the bottom of the toolbar config file: @@ -595,24 +429,36 @@ You can do it at the bottom of the toolbar config file: eZ.addConfig('ezAlloyEditor.customSelections.ContentVariableEdit', ContentVariableEditConfig); ``` -With this step, the `ContentVariableEditConfig` toolbar is injected and ready to be used. - -## Custom plugins +At this point, the `ContentVariableEditConfig` toolbar is injected and ready +to be used. -You can add your own plugins to the Online Editor. +## Add CKEditor plugins -For more information, follow [Creating Online Editor plugin](online_editor_plugin.md). +You can download an existing CKEditor plugin and make it part of the Online Editor. -You can also download an existing plugin from the CKEditor. -In this case, include it in a page after AlloyEditor is loaded: +To do this, modify the configuration file by adding keys similar to the following +example: ``` yaml +# ... ezrichtext: alloy_editor: extra_plugins: [plugin1, plugin2] ``` -The name of a plugin needs to be the same as the one passed to `CKEDITOR.plugins.add` in the plugin source code. +The name of a plugin must be the same as the one that is passed to the +`CKEDITOR.plugins.add` method in the [plugin's source code](https://ckeditor.com/docs/ckeditor4/latest/guide/plugin_sdk_sample.html#plugin-source-code). + +!!! caution "Supported changes to xhtml5" + + When a plugin changes the RichText input (in xhtml5 edit DocBook format), the + changes must be [supported by the RichText Field Type](../api/field_types_reference/richtextfield.md#input-formats). + For example, if the plugin adds a class to some element, you must ensure that this + class is stored when saving or publishing content. + Otherwise, it could result in either XML validation error or could be omitted + by the RichText processor. + +!!! tip -Please keep in mind that if a plugin changes RichText input (in xhtml5/edit format for DocBook), the changes need to be supported by RichText Field Type. -For example, if a plugin adds some class to some element, you need to confirm that this class is stored when saving or publishing content (it could result in either XML validation error or could be omitted by RichText processor). + You can also add your own plugins to the Online Editor. + For more information, see [Creating Online Editor plugin](online_editor_plugin.md). diff --git a/docs/extending/extending_page.md b/docs/extending/extending_page.md deleted file mode 100644 index 062544c6db..0000000000 --- a/docs/extending/extending_page.md +++ /dev/null @@ -1,749 +0,0 @@ -# Creating custom Page blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -To create an Example Page block, use the following YAML configuration in an application or a bundle, -under the `ezplatform_page_fieldtype` key, e.g. in `config/packages/ezplatform_page_fieldtype.yaml`: - -!!! caution - - Page block configuration is not SiteAccess-aware. - -``` yaml -ezplatform_page_fieldtype: - blocks: - example_block: - name: Example Block - # The group that contains the block in the Elements menu - category: Example - thumbnail: assets/images/ez-icons.svg#block-visible-recurring - configuration_template: blocks/config.html.twig - views: - default: - template: blocks/template.html.twig - name: Default view - priority: -255 - special: - template: blocks/special_template.html.twig - name: Special view - priority: 50 - attributes: - name: - type: text - validators: - not_blank: - message: Please provide a name - email: - type: string - name: E-mail address - validators: - regexp: - options: - pattern: '/^\S+@\S+\.\S+$/' - message: Provide a valid e-mail address - topics: - type: select - name: Select topics - value: value2 - options: - multiple: true - choices: - 'Sports': value1 - 'Culture': value2 - 'Politics': value3 -``` - -You can define multiple views for a block, with separate templates. - -`priority` defines the order of block views on the block configuration screen. -The highest number will show first on the list. - -!!! tip - - Default views have a `priority` of -255. - It's good practice to keep the value between -255 and 255. - -Now, you can add your example block in the Site tab. - -![Example Block](img/extending_example_page_block.png) - -## Block attributes - -A block has a number of attributes, each with the following properties: - -||| -|----|----| -|`type`|Type of attribute.| -|`name`|The displayed name for the attribute. You can omit it, block identifier will then be used as the name.| -|`value`|The default value for the attribute.| -|`category`|The tab where the attribute is displayed in the block edit modal.| -|`validators`|Available validators are `not_blank` and `regexp`.| -|`options`|Additional options, dependent on the attribute type.| - -#### Available attribute types - -|Type|Description|Options| -|----|----|----| -|`integer`|Integer value|-| -|`string`|String|-| -|`url`|URL|-| -|`text`|Text block|-| -|`richtext`|Rich text block (see [creating richtext block](richtext_block.md)|-| -|`embed`|Embedded Content item|-| -|`select`|Drop-down with options to select|`choices` lists the available options
    `multiple`, when set to true allows selecting more than one option. -|`multiple`|Checkbox(es)|`choices` lists the available options.| -|`radio`|Radio buttons|`choices` lists the available options.| -|`locationlist`|Location selection|-| -|`contenttypelist`|List of Content Types|-| -|`schedule_events`,
    `schedule_snapshots`,
    `schedule_initial_items`,
    `schedule_slots`,
    `schedule_loaded_snapshot`|Used in the Content Scheduler block|-| - -When defining attributes you can omit most keys as long as you use simple types that do not require additional options: - -``` yaml -attributes: - first_field: text - second_field: string - third_field: integer -``` - -!!! tip "Hiding blocks" - - To hide a block from the block menu in Page Builder, set its `visible` property to `false`: - - ``` yaml - example_block: - # ... - visible: false - ``` - -#### Custom block attributes - -You can create Page blocks with custom attributes. - -First, define the attribute type. -You can use one of the types available in `ezplatform-page-fieldtype/src/lib/Form/Type/BlockAttribute/*`. - -You can also use one of the [built-in Symfony types](https://symfony.com/doc/5.0/reference/forms/types.html), e.g. `AbstractType` for any custom type or `IntegerType` for numeric types. - -To define the type, create a file `src/Block/Attribute/MyStringAttributeType.php` that contains: - -``` php hl_lines="5 6 15" - -

    My String

    - {{ form_widget(form) }} - -{% endblock %} - -{# more templates here #} -``` - -Add the template to your configuration: - -``` yaml -system: - default: - page_builder_forms: - block_edit_form_templates: - - { template: custom_form_templates.html.twig, priority: 0 } -``` - -At this point, the attribute type configuration is complete, but it requires a mapper. -Depending on the complexity of the type, you can use a `GenericFormTypeMapper` or create your own. - -For a generic mapper, add a new service definition to `config/services.yaml`: - -``` yaml - my_application.block.attribute.my_string: - class: EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Attribute\FormTypeMapper\GenericFormTypeMapper - arguments: - $formTypeClass: App\Block\Attribute\MyStringAttributeType - tags: - - { name: ezplatform.page_builder.attribute_form_type_mapper, alias: my_string } -``` - -For creating your own mapper, create a class that inherits from `EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Attribute\FormTypeMapper\AttributeFormTypeMapperInterface`. -Then, register the class along with a tag by creating a `src/Block/Attribute/MyStringAttributeMapper.php` file: - -``` php -create( - 'value', - MyStringAttributeType::class, - [ - 'constraints' => $constraints, - ] - ); - } -} -``` - -The final step is to add a new service definition for your mapper to `config/services.yaml`: - -``` yaml -App\Block\Attribute\MyStringAttributeMapper: - tags: - - { name: ezplatform.page_builder.attribute_form_type_mapper, alias: my_string } -``` - -Now, create a block containing your custom attribute: - -``` yaml hl_lines="9 10 11 12" -ezplatform_page_fieldtype: - blocks: - my_block: - name: MyBlock - category: default - thumbnail: images/thumbnails/my_block.svg - views: - default: { name: Default block layout, template: my_block.html.twig, priority: -255 } - attributes: - my_string_attribute: - type: my_string - name: MyString - -``` - -## Overwriting existing blocks - -You can overwrite the following properties in the existing blocks: - -- `thumbnail` -- `category` -- `name` -- `views` - -## Block configuration modal - -The block configuration modal by default contains two tabs, Basic and Design. - -In Design you can choose the view that will be used for the block and its styling. - -- **Class** indicates the CSS class used for this block. -- **Style** defines the CSS rules. - -You can disable the Design tab by setting `ezsettings.default.page_builder.block_styling_enabled` to `false`. -It is set to `true` by default. - -#### Block modal template - -The template for the configuration modal of built-in Page blocks is contained in -`vendor/ezsystems/ezplatform-page-builder/src/bundle/Resources/views/page_builder/block/config.html.twig`. - -You can override it using the `configuration_template` setting: - -``` yaml -ezplatform_page_fieldtype: - blocks: - example_block: - name: Example Block - configuration_template: blocks/config/template.html.twig - # ... -``` - -The template can extend the default `config.html.twig` and modify its blocks. -Blocks `basic_tab_content` and `design_tab_content` correspond to the Basic and Design tabs in the modal. - -The following example wraps all form fields for block attributes in an ordered list: - -``` html+twig -{% extends '@EzPlatformPageBuilder/page_builder/block/config.html.twig' %} - -{% block basic_tab_content %} -
    - {{ form_row(form.name) }} - {% if attributes_per_category['default'] is defined %} -
      - {% for identifier in attributes_per_category['default'] %} - {% block config_entry %} -
    1. - {{ form_row(form.attributes[identifier]) }} -
    2. - {% endblock %} - {% endfor %} -
    - {% endif %} -
    -{% endblock %} -``` - -#### Exposing content relations from blocks - -Page blocks, for example Embed block or Collection block, can embed other Content items. -Publishing a Page with such blocks creates Relations to those Content items. - -When creating a custom block with embeds, you can ensure such Relations are created using the block Relation collection event. - -The event is dispatched on content publication. You can hook your event listener to one of the events: - -- `\EzSystems\EzPlatformPageFieldType\Event\BlockRelationEvents::COLLECT_BLOCK_RELATIONS` (`ezplatform.ezlandingpage.block.relation`) -- `ezplatform.ezlandingpage.block.relation.{blockTypeIdentifier}` - -To expose relations, pass an array containing Content IDs to the `\EzSystems\EzPlatformPageFieldType\Event\CollectBlockRelationsEvent::setRelations()` method. - -You don't have to keep track of Relations. If embedded Content changes, old Relations will be removed automatically. - -Providing Relations will also invalidate HTTP cache for your block response in one of the related Content items changes. - -#### Block render response - -Block responses dispatch their response events which enables you to modify the Response object. -You can use them for example to change cache headers. - -You can hook into `BlockResponseEvents` events: - -- `BlockResponseEvents::BLOCK_RESPONSE` (`ezplatform.ezlandingpage.block.response`) -- `ezplatform.ezlandingpage.block.response.{blockTypeIdentifier}` - -Aside from `Request` and `Response` objects it also includes `BlockContext` and `BlockValue` data. - -## Block templates - -All Page blocks, both those that come out of the box and custom ones, can have multiple templates. This allows you to create different styles for each block and let the editor choose them when adding the block from the UI. The templates are defined in your configuration files like in the following example, with `simplelist` and `special` being the template names: - -``` yaml -blocks: - contentlist: - views: - simplelist: - template: blocks/contentlist_simple.html.twig - name: Simple Content List - special: - template: blocks/contentlist_special.html.twig - name: Special Content List -``` - -Some blocks can have slightly more complex configuration. An example is the Collection block, which requires an `options` key. -This key defines which Content Types can be added to it. -See [this example from the Demo](https://github.com/ezsystems/ezplatform-ee-demo/blob/master/config/packages/default_layouts.yml#L186): - -``` yaml -blocks: - collection: - thumbnail: '/bundles/ezplatformadminui/img/ez-icons.svg#collection' - views: - cards: - template: '@ezdesign/blocks/collection/cards.html.twig' - name: 'Cards' - options: - match: [article, blog_post, image, product, place] - - list: - template: '@ezdesign/blocks/collection/list.html.twig' - name: 'List' - options: - match: [article, blog_post, image, product, place] -``` - -## Block definition events - -The following events are available to influence Page block definition: - -- `ezplatform.ezlandingpage.block.definition.{block_identifier}` is called when retrieving block definition. You can use it to influence any definition parameters. This includes the block's attributes. You can, e.g. add or remove some attributes. - -!!! caution - - You need to be careful when removing block attributes. If you modify the attributes of a block used on an existing Page, an error may occur. - -This event serves only to manipulate the block configuration, it does not add any logic to it. - -- `ezplatform.ezlandingpage.block.definition.{block_identifier}.attribute.{block_attribute_identifier}` works like the previous event, but is called for a specific block attribute, not the whole block. You can use it to manipulate attributes, validators, etc. - -The following example shows how the built-in Collection block uses events -to assign different options to an attribute of a block depending on the selected view: - -``` php - 'onLocationListAttributeDefinition', - ]; - } - - // ... - - /** - * @param BlockAttributeDefinitionEvent $event - */ - public function onLocationListAttributeDefinition(BlockAttributeDefinitionEvent $event) - { - $definition = $event->getDefinition(); - $configuration = $event->getConfiguration(); - - $options = $definition->getOptions(); - $options['match'] = $this->getViewMatchConfiguration($configuration); - - $definition->setOptions($options); - } - - /** - * @param array $configuration - * - * @return array - */ - private function getViewMatchConfiguration(array $configuration): array - { - $list = []; - - foreach ($configuration['views'] as $viewName => $viewConfig) { - if (!isset($viewConfig['options']) - || !isset($viewConfig['options']['match']) - || empty($viewConfig['options']['match']) - ) { - $list[$viewName] = []; - continue; - } - $list[$viewName] = $viewConfig['options']['match']; - } - - return $list; - } -} -``` - -### Block name translation - -A practical example of how you can use the `BlockDefinitionEvents` API is translating the block name. -You can modify the `name` attribute of the Page block so that it displays a translation -in one of the defined languages. - -The example uses a [Symfony Translator](https://github.com/symfony/symfony/blob/5.1/src/Symfony/Component/Translation/Translator.php) module and its `trans()` method. -The method takes three arguments: an identifier of the block name, an array of parameters, -and the domain of the translation. - -Start with adding a new language package to your project. -For example, to translate your application into French, run the following command: - - `composer require ezplatform-i18n/ezplatform-i18n-fr_fr` - -Then, create a translatable Page block by adding the following YAML configuration -under the `ezplatform_page_fieldtype` key: - -``` yaml -ezplatform_page_fieldtype: - blocks: - translate_block: - name: Translatable Block - # The group that contains the block in the Elements menu - category: Example - thumbnail: assets/images/ez-icons.svg#block-visible-recurring - views: - default: - template: "@ezdesign/blocks/translate_block.html.twig" - name: Default view - priority: -255 - attributes: - name: - type: text - validators: - not_blank: - message: Please provide a name - email: - type: string - name: E-mail address - validators: - regexp: - options: - pattern: '/^\S+@\S+\.\S+$/' - message: Provide a valid e-mail address -``` - -Create the `templates/themes/standard/blocks` folder. -In that folder, add a Twig template called `translate_block.html.twig`: - -``` html+twig -

    {{ name|default('Hello stranger') }}!

    -``` - -Next, implement the logic that is responsible for label translation. -Begin with implementing an event subscriber that listens to the block definition event. -For example, create an `src/Event/Subscriber/TranslateBlockNameSubscriber.php` file that contains the following code: - -``` php -translator = $translator; - } - - public static function getSubscribedEvents(): array - { - return [ - BlockDefinitionEvents::getBlockDefinitionEventName('translate_block') => 'onBlockDefinition', - BlockDefinitionEvents::getBlockAttributeDefinitionEventName('translate_block', 'name') => 'onNameAttributeDefinition' - ]; - } - - public function onBlockDefinition(BlockDefinitionEvent $event): void - { - $event->getDefinition()->setName( - $this->translator->trans('translate_block.name', [], 'translate_block') - ); - } - - public function onNameAttributeDefinition(BlockAttributeDefinitionEvent $event): void - { - $event->getDefinition()->setName( - $this->translator->trans('translate_block.attribute.name.name', [], 'translate_block') - ); - } -} -``` - -Then, register a new service in the `config/services.yaml` file: - -``` yaml -App\Event\Subscriber\TranslateBlockNameSubscriber: - tags: - - { name: event_subscriber } -``` - -You provide the translations of custom block labels in XLIFF files, one for the original language of the site, and one for each intended translation language. -You create the XLIFF files with an editor of your choice, for example [JMSTranslationBundle](https://github.com/schmittjoh/JMSTranslationBundle). -The XLIFF files are stored in the `translations` directory at the root of your project. -A name of the translation file corresponds to the domain that you defined above, and the language, for example `translate_block.en.xlf` for English and `translate_block.fr.xlf` for French. - -A file that contains English translations might look as follows: - -``` xml - - - -
    - -
    - - - My translatable block - My translatable block - key: translate_block.name - - - Hello stranger - Hello stranger - key: translate_block.attribute.name.name - - -
    -
    -``` - -To provide strings in French, add them in the tags of the `translate_block.fr.xlf` file. For example: - -``` xml - - - -
    - -
    - - - My translatable block - Mon bloc traduisible - key: translate_block.name - - - Hello stranger - Bonjour étranger - key: translate_block.attribute.name.name - - -
    -
    -``` - -After you add new files with translations, run `php bin/console cache:clear` to clear the cache. - -A language to be displayed is selected automatically based on [user preferences or browser setup](../../guide/back_office_translations/#selecting-back-office-language). - -!!! note "Additional information" - - For more information, see the following articles: - - - [Back office translations](../guide/back_office_translations.md) - - [Symfony translations](https://symfony.com/doc/current/translation.html) - - [Setting language preferences in a browser](https://www.w3.org/International/questions/qa-lang-priorities) - -## Block rendering events - -The following events are available to influence Page block rendering: - - - `ezplatform.ezlandingpage.block.render.pre` is called before rendering any block. - - `ezplatform.ezlandingpage.block.render.post` is called after rendering any block. - - `ezplatform.ezlandingpage.block.render.{block_identifier}.pre` is also called before rendering any block. This event contains the block logic (when needed). - -Example of the built-in Banner block: - -``` php -contentService = $contentService; - } - - /** - * @return array The event names to listen to - */ - public static function getSubscribedEvents() - { - return [ - BlockRenderEvents::getBlockPreRenderEventName('banner') => 'onBlockPreRender', - ]; - } - - /** - * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function onBlockPreRender(PreRenderEvent $event) - { - $blockValue = $event->getBlockValue(); - $renderRequest = $event->getRenderRequest(); - - $parameters = $renderRequest->getParameters(); - - $contentIdAttribute = $blockValue->getAttribute('contentId'); - $parameters['content'] = $this->contentService->loadContent($contentIdAttribute->getValue()); - - $renderRequest->setParameters($parameters); - } -} -``` - -The `onBlockPreRender` method adds `content` to `View` parameters. - -- `ezplatform.ezlandingpage.block.render.{block_identifier}.post` is called after rendering a specific block. It can be used to wrap HTML output. - -## Extensibility on block creation - -The `customizeNewBlockNode` extension point enables you to manipulate the block preview wrapper node. -You can use it e.g. to add a custom CSS class or a custom event listener when a new block is created. - -``` js -/** - * Extension point to customize the new block HTML attributes - * - * @function customizeNewBlockNode - * @param {HTMLElement} block - * @param {Object} meta - * @param {String} meta.blockType - * @param {String} meta.pageLayoutIdentifier - * @param {String} meta.zoneId - * @returns {HTMLElement} - */ -window.eZ.pageBuilder.callbacks.customizeNewBlockNode = function (blockNode, meta) {}; -``` - -## Extensibility on block preview update - -When block preview is updated, JavaScript event `ez-post-update-blocks-preview` is fired. -You can use it to run your own JS scripts, such as reinitializing the work of a slider -or any other JS-based feature implemented inside your block preview. - -``` js -(function () { - window.document.body.addEventListener('ez-post-update-blocks-preview', () => console.log('block updated'), false); -})(); -``` - -!!! caution "Extending Page Builder configuration" - - If your bundle overrides Page Builder configuration, the bundle must be registered *before* - `EzPlatformPageFieldTypeBundle()` in `AppKernel.php`. - - Also, `EzPlatformPageFieldTypeBundle()` must be registered *after* `EzPlatformPageBuilderBundle()`. - Watch out for this if you update bundles selectively: - - ```php - new Acme\PageBuilderCustomizationBundle\AcmePageBuilderCustomizationBundle(), - new EzSystems\EzPlatformPageBuilderBundle\EzPlatformPageBuilderBundle(), - new EzSystems\EzPlatformPageFieldTypeBundle\EzPlatformPageFieldTypeBundle(), - ``` diff --git a/docs/extending/extending_settings.md b/docs/extending/extending_settings.md deleted file mode 100644 index 0e7a46e997..0000000000 --- a/docs/extending/extending_settings.md +++ /dev/null @@ -1,130 +0,0 @@ -# Extending settings - -## Default settings - -Back Office provides a default set of `ezsettings` for `admin_group` SiteAccess. -Keep in mind that those settings might be easily overridden by global or SiteAccess `ezsettings`, as well as all bundles, including third party and yours. -For this reason, `ezsettings` values mentioned in the documentation might not work in your project. - -## User settings - -You can add new preferences to the User Settings menu in the Back Office. - -To do so, create a setting class implementing two interfaces: -`ValueDefinitionInterface` and `FormMapperInterface`. - -In this example the class is located in `src/Setting/Unit.php` -and enables the user to select their preference for metric or imperial unit systems. - -``` php - self::METRIC_OPTION, - 'Imperial' => self::IMPERIAL_OPTION, - ]; - - return $formBuilder->create( - 'value', - ChoiceType::class, - [ - 'multiple' => false, - 'required' => true, - 'label' => $this->getDescription(), - 'choices' => $choices, - ] - ); - } -} -``` - -Add new service definitions in `config/services.yaml`: - -``` yaml -services: - // - App\Setting\Unit: - tags: - - { name: ezplatform.admin_ui.user_setting.value, identifier: unit, priority: 50 } - - { name: ezplatform.admin_ui.user_setting.form_mapper, identifier: unit } -``` - -You can order the settings in the User menu by setting their `priority`. - -The value of the setting is accessible with `ez_user_settings['unit']`. - -## Templates settings - -You can define a template to be used when editing the given setting in `config/packages/ezplatform.yaml`: - -``` yaml -ezplatform: - system: - admin_group: - user_settings_update_view: - full: - unit: - template: User/Settings/update_unit.html.twig - match: - Identifier: [ unit ] -``` - -The `templates/User/Settings/update_unit.html.twig` template must extend the `@ezdesign/account/settings/update.html.twig` template: - -``` html+twig -{% extends '@ezdesign/account/settings/update.html.twig' %} - -{% block form %} - {{ parent() }} - -{% endblock %} -``` diff --git a/docs/extending/extending_subitems_list.md b/docs/extending/extending_subitems_list.md index 9bcf1842fc..ec26d69c56 100644 --- a/docs/extending/extending_subitems_list.md +++ b/docs/extending/extending_subitems_list.md @@ -1,4 +1,8 @@ -# Extending Sub-items List +--- +description: Inject a sub-items list into your Back Office customizations. +--- + +# Sub-items list The Sub-items List module is meant to be used as a part of the editorial interface of [[= product_name =]]. It provides an interface for listing the sub-items of any Location. @@ -8,9 +12,9 @@ It provides an interface for listing the sub-items of any Location. If you want to load the Sub-items module, you need to load the JS code for it in your view, as it is not available by default. -## How to use it? +## Use sub-items list -With vanilla JS: +With plain JS: ``` js const containerNode = document.querySelector('#sub-items-container'); @@ -43,7 +47,7 @@ const attrs = { ## Properties list -The `` module can handle additional properties. There are 2 types of properties: **required** and **optional**. All of them are listed below. +The `` module can handle additional properties. There are two types of properties: **required** and **optional**. All of them are listed below. ### Required props @@ -60,12 +64,12 @@ Without all the following properties the Sub-items module will not work. Optionally, Sub-items module can take a following list of props: -- **loadContentInfo** _{Function}_ - loads Content item info. Takes 2 params: +- **loadContentInfo** _{Function}_ - loads Content item info. Takes two params: - **contentIds** _{Array}_ - list of content IDs - **callback** _{Function}_ - a callback invoked when content info is loaded - **loadContentTypes** _{Function}_ - loads Content Types. Takes one param: - **callback** _{Function}_ - callback invoked when Content Types are loaded -- **loadLocation** _{Function}_ - loads Location. Takes 4 params: +- **loadLocation** _{Function}_ - loads Location. Takes four params: - **restInfo** _{Object}_ - REST info params: - **token** _{String}_ - the user token - **siteaccess** _{String}_ - the current SiteAccess @@ -73,9 +77,9 @@ Optionally, Sub-items module can take a following list of props: - **locationId** _{Number}_ - Location ID - **limit** _{Number}_ - Content item limit - **offset** _{Number}_ - items offset - - **sortClauses** _{Object}_ - the Sort Clauses, e.g. {LocationPriority: 'ascending'} + - **sortClauses** _{Object}_ - the Sort Clauses, for example, {LocationPriority: 'ascending'} - **callback** _{Function}_ - callback invoked when Location is loaded -- **updateLocationPriority** - updates item Location priority. Takes 2 params: +- **updateLocationPriority** - updates item Location priority. Takes two params: - **params** _{Object}_ - parameters hash containing: - **priority** _{Number}_ - priority value - **location** _{String}_ - REST Location ID @@ -97,7 +101,7 @@ Optionally, Sub-items module can take a following list of props: - **gridViewItem** _{Object}_ - list of grid item view component labels - **languageContainerSelector** _{String}_ - selector where the language selector should be rendered -## Reusing Sub-items list +## Reuse Sub-items list To add a Sub-items list on a page that does not have the (right) action sidebar, you need to do one of the following things: diff --git a/docs/extending/extending_tabs.md b/docs/extending/extending_tabs.md deleted file mode 100644 index 9f63768219..0000000000 --- a/docs/extending/extending_tabs.md +++ /dev/null @@ -1,254 +0,0 @@ -# Extending tabs - -Many elements of the Back Office interface, such as System Information or Location View, are built using tabs. - -![Tabs in System Information](img/tabs_system_info.png) - -You can extend existing tab groups with new tabs, or create your own tab groups. - -## Adding a new tab group - -New tab groups are created using the [`TabsComponent`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/lib/Component/TabsComponent.php). - -Register your tab group as a service in `config/services.yaml`: - -``` yaml -services: - # ... - app.my_tabs.custom_group: - parent: EzSystems\EzPlatformAdminUi\Component\TabsComponent - autowire: true - autoconfigure: false - arguments: - $groupIdentifier: 'custom_group' - tags: - - { name: ezplatform.admin_ui.component, group: 'dashboard-blocks' } -``` - -The tab group must be tagged with `ezplatform.admin_ui.component`. -The `group` tag indicates where the group will be rendered. -For the list of possible rendering places, see [Injecting custom components](custom_components.md). - -`$groupIdentifier` is the name that you point to when assigning a tab to this group. -You can also provide the `$template` argument to use a custom template for rendering the group. - -### Adding a tab group with custom logic - -To create a custom tab group with additional logic, you need to create it at the level of compiling the Symfony container, -using a [CompilerPass.](https://symfony.com/doc/5.0/service_container/compiler_passes.html) - -For example, in `src/DependencyInjection/Compiler/CustomTabGroupPass.php`: - -``` php -namespace App\DependencyInjection\Compiler; - -use EzSystems\EzPlatformAdminUi\Tab\TabGroup; -use EzSystems\EzPlatformAdminUi\Tab\TabRegistry; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; - -class CustomTabGroupPass implements CompilerPassInterface -{ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition(TabRegistry::class)) { - return; - } - - $tabRegistry = $container->getDefinition(TabRegistry::class); - $tabGroupDefinition = new Definition( - TabGroup::class, // or any class that extends TabGroup - ['custom-tab-group'] - ); - $tabRegistry->addMethodCall('addTabGroup', [$tabGroupDefinition]); - } -} -``` - -You also need to add the compiler pass to `src/Kernel.php`: - -``` php -use App\DependencyInjection\Compiler\CustomTabGroupPass; - -// - -protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void -{ - // - $container->addCompilerPass(new CustomTabGroupPass()); -} -``` - -## Adding a new tab - -Before you add a tab to a group you must create the tab's PHP class and define it as a Symfony service with the `ezplatform.tab` tag in `config/services.yaml`: - -``` yaml -services: - # ... - App\Custom\Tab: - parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab - autowire: true - autoconfigure: false - tags: - - { name: ezplatform.tab, group: dashboard-everyone } -``` - -This configuration also assigns the new tab to `dashboard-everyone`. -You can also use here the name of your own [custom tab group](#adding-a-new-tab-group). - -The tab class can look like this (in `src/Custom/Tab.php`): - -``` php -namespace App\Custom; - -use EzSystems\EzPlatformAdminUi\Tab\AbstractTab; - -class Tab extends AbstractTab -{ - public function getIdentifier(): string - { - return 'custom-tab'; - } - - public function getName(): string - { - return /** @Desc("Custom Tab") */ - $this->translator->trans('custom.tab.name', [], 'some_translation_domain'); - } - - public function renderView(array $parameters): string - { - // Do rendering here - - return $this->twig->render('my_tabs/custom.html.twig', [ - 'foo' => 'Bar!', - ]); - } -} -``` - -Beyond the `AbstractTab` used in the example above you can use two specialized tab types: - -- `AbstractControllerBasedTab` enables you to embed the results of a controller action in the tab. -- `AbstractRouteBasedTab` embeds the results of the selected routing, passing applicable parameters. - -![Custom Tab](img/extending_custom_tab.png) - -## Modifying tab display - -You can order the tabs by making the tab implement `OrderedTabInterface`. -The order will then depend on the numerical value returned by the `getOrder` method. - -``` php -class Tab extends AbstractTab implements OrderedTabInterface -{ - public function getOrder(): int - { - return 100; - } -} -``` - -The tabs will be displayed according to this value in ascending order. - -!!! tip - - It is good practice to reserve some distance between these values, for example to stagger them by step of 10. - It may come useful if you later need to place something between the existing tabs. - -You can also influence tab display (e.g. order tabs, remove or modify them, etc.) by using Event Listeners: - -``` php -class TabEvents -{ - /** - * Happens just before rendering a tab group. - */ - const TAB_GROUP_PRE_RENDER = 'ezplatform.tab.group.pre_render'; - - /** - * Happens just before rendering a tab. - */ - const TAB_PRE_RENDER = 'ezplatform.tab.pre_render'; -} -``` - -As an example, see how `OrderedTabInterface` is implemented: - -```php - ['onTabGroupPreRender'], - ]; - } - - /** - * @param TabGroupEvent $tabGroupEvent - */ - public function onTabGroupPreRender(TabGroupEvent $tabGroupEvent) - { - $tabGroup = $tabGroupEvent->getData(); - $tabs = $tabGroup->getTabs(); - - $tabs = $this->reorderTabs($tabs); - - $tabGroup->setTabs($tabs); - $tabGroupEvent->setData($tabGroup); - } - - /** - * @param TabInterface[] $tabs - * - * @return array - */ - private function reorderTabs($tabs): array - { - $orderedTabs = []; - foreach ($tabs as $tab) { - if ($tab instanceof OrderedTabInterface) { - $orderedTabs[$tab->getIdentifier()] = $tab; - unset($tabs[$tab->getIdentifier()]); - } - } - - uasort($orderedTabs, [$this, 'sortTabs']); - - return array_merge($orderedTabs, $tabs); - } - - /** - * @param OrderedTabInterface $tab1 - * @param OrderedTabInterface $tab2 - * - * @return int - */ - private function sortTabs(OrderedTabInterface $tab1, OrderedTabInterface $tab2): int - { - return $tab1->getOrder() <=> $tab2->getOrder(); - } -} -``` diff --git a/docs/extending/extending_thumbnails.md b/docs/extending/extending_thumbnails.md index 0f97b803a4..2a536b76a5 100644 --- a/docs/extending/extending_thumbnails.md +++ b/docs/extending/extending_thumbnails.md @@ -1,7 +1,11 @@ +--- +description: Customize thumbnails use for Content items in the Back Office. +--- + # Extending thumbnails The thumbnails API enable you to easily choose an image for a specific content. -If you do not want to use custom thumbnails [`ContentType` icons](../guidelines/resources/icons.md) will be used instead. +If you do not want to use custom thumbnails, `ContentType` will be used instead. ## Thumbnail mechanism @@ -32,7 +36,7 @@ If there is more than one Field in the Content Type that can be used as a thumbn This mechanism can be modified to fit your site needs, so you can decide from where and how the thumbnails will be downloaded. -### Add a thumbnail mechanism +### Create a thumbnail mechanism First, create base strategy for returning custom thumbnails from a static file. Create `StaticStrategy.php` in `src/Strategy`. diff --git a/docs/extending/extending_udw.md b/docs/extending/extending_udw.md index 9a9a66dd47..8c680cc4bf 100644 --- a/docs/extending/extending_udw.md +++ b/docs/extending/extending_udw.md @@ -1,9 +1,13 @@ -# Extending Universal Discovery Widget +--- +description: Customize the configuration of the content browser. +--- -Universal Discovery Widget (UDW) allows you to browse the content structure and search for content -using an interactive interface: browse, search, create, and bookmarks view. +# Browser -## How to use UDW? +Browsing the content structure and selecting content from the Repository uses the module Universal Discovery Widget (UDW). UDW has an interactive interface +which allows you to create, move or copy Content items. + +## Using UDW UDW requires that you provide configuration by using the `ez_udw_config` Twig helper. This configuration must be spread to the props of the component itself. @@ -13,9 +17,9 @@ UDW requires that you provide configuration by using the `ez_udw_config` Twig he ``` -> `single` configuration is one of the default configuration provided. You can also do your [own configuration](#Adding-new-configuration). +`single` configuration is one of the default configuration provided. You can also do your [own configuration](#add-new-configuration). -With vanilla JS: +With plain JS: ``` js const container = document.querySelector('#react-udw'); @@ -42,13 +46,13 @@ const config = /* fetch the config somewhere */; ``` -## Adding new tabs to the UDW +## Add new tabs to UDW -The Universal Discovery Widget enables you to add new tabs to the module. To learn more, see [Creating a UDW tab tutorial](adding_tab_to_udw.md). +The Universal Discovery Widget enables you to add new tabs to the module. To learn how to add new tabs, see [Creating a UDW tab tutorial](adding_tab_to_udw.md). -## Configuration +## UDW configuration -You can configure Universal Discovery Widget in the [`universal_discovery_widget.yaml`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/config/universal_discovery_widget.yaml) file. +You can configure UDW in the [`universal_discovery_widget.yaml`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/config/universal_discovery_widget.yaml) file. There you can set the following properties: @@ -64,11 +68,11 @@ There you can set the following properties: |active_sort_order
    `activeSortOrder`|ascending
    descending|no|Sorting order of the children in the Content Tree.| |active_tab
    `activeTab`|browse
    search
    bookmarks|no|Starting tab in the UDW.| |active_view
    `activeView`|finder
    grid|no|Starting view in the UDW.| -|allow_redirects
    `allowRedirects`|true
    false|yes|Allows to redirect content from the UDW tab to another page, e.g. to Content Edit page.| +|allow_redirects
    `allowRedirects`|true
    false|yes|Allows to redirect content from the UDW tab to another page, for example, to Content Edit page.| |selected_locations
    `selectedLocations`|[]
    [locationId]|no|Location that will be selected automatically.| |allow_confirmation
    `allowConfirmation`|true
    false|yes|Shows confirmations buttons in the UDW. If set to false, it will not be possible to confirm selection.| -### Content on the Fly Group +### Content on the Fly group |YML
    React props|Values|Required|Definition| |-------------------|------|--------|----------| @@ -79,78 +83,34 @@ There you can set the following properties: |hidden
    `hidden`|true
    false|yes|Content on the Fly visibility.| |auto_confirm_after_publish
    `autoConfirmAfterPublish`|true
    false|yes|If set to `true` UDW will be automatically closed after publishing the content.| -### Tabs Config Group +### Tabs config group -General configuration for tabs e.g. browse, search, bookmarks etc. +General configuration for tabs, for example, browse, search, bookmarks. |YML
    React props|Values|Required|Definition| |-------------------|------|--------|----------| -|items_per_page
    `itemsPerPage`|number|yes|Number of items that will be shown on one page.| -|priority
    `priority`|number|yes|Priority of items shown in the tab list. Item with a highest value will be displayed as first.| +|items_per_page
    `itemsPerPage`|number|yes|Number of items shown on one page.| +|priority
    `priority`|number|yes|Priority of items shown in the tab list. Item with a highest value is displayed as first.| |hidden
    `hidden`|true
    false|yes|Hides or reveals specific tabs.| ### Configuration available only through JS |React props|Values|Required|Definition| |-----------|------|--------|----------| -|`onConfirm`|function|yes|A callback to be invoked when a user clicks on the confirm button in a Universal Discovery Widget.| -|`onCancel`|function|yes|A callback to be invoked when a user clicks on the cancel button in a Universal Discovery Widget.| +|`onConfirm`|function|yes|A callback to be invoked when a user clicks the confirm button in a Universal Discovery Widget.| +|`onCancel`|function|yes|A callback to be invoked when a user clicks the cancel button in a Universal Discovery Widget.| |`title`|string|yes|The title of Universal Discovery Widget.| -UDW configuration is SiteAccess-aware. For each defined SiteAccess, you need to be able to use the same configuration tree in order to define SiteAccess-specific config. -These settings need to be mapped to SiteAccess-aware internal parameters that you can retrieve via the ConfigResolver. -For more information on ConfigResolver, see [[[= product_name =]] dynamic configuration basics](../guide/config_dynamic.md#configresolver). - -### Example configuration - -Default configuration of [the Universal Discovery Widget:](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/config/universal_discovery_widget.yaml) - -```yaml -system: - default: - universal_discovery_widget_module: - configuration: - # Default UDW Configuration - _default: - multiple: false - multiple_items_limit: 0 - root_location_id: 1 - starting_location_id: 1 - containers_only: false - allowed_content_types: null - active_sort_clause: 'DatePublished' - active_sort_order: 'ascending' - active_tab: 'browse' - active_view: 'finder' - allow_redirects: false - allow_confirmation: true - content_on_the_fly: - allowed_languages: null - allowed_locations: null - preselected_language: null - preselected_content_type: null - hidden: false - auto_confirm_after_publish: false - tabs_config: - search: - items_per_page: 50 - priority: 10 - hidden: false - bookmarks: - items_per_page: 50 - priority: 20 - hidden: false - browse: - items_per_page: 50 - priority: 30 -``` +UDW configuration is SiteAccess-aware. For each defined SiteAccess, you need to be able to use the same configuration tree to define SiteAccess-specific config. +These settings need to be mapped to SiteAccess-aware internal parameters that +you can retrieve with the [ConfigResolver](../guide/configuration/config_dynamic.md#configresolver). -## Adding new configuration +## Add new configuration UDW configuration can change dynamically depending on occurring events. -It can be used e.g. for defining which content should be exposed to a user after logging in. +You can use it, for example, to define which content should be exposed to a user after logging in. -By default only one element from configuration file is applied to Universal Discovery Widget. +By default, only one element from configuration file is applied to Universal Discovery Widget. You can modify it dynamically by passing context to generate configuration based on a specific event. This context event is caught by event listener `ConfigResolveEvent::NAME` before the original configuration is used. Depending on what additional parameters are provided, original or event-specific configuration is applied. @@ -167,9 +127,9 @@ ezplatform: multiple: false ``` -#### Adding new configuration to a button +### Add new configuration to button -In the `ez_udw_config` Twig helper define a specific part of YAML configuration that will be used to render the **Content Browser**. +In the `ez_udw_config` Twig helper, define a specific part of YAML configuration that will be used to render the **Content Browser**. You can find Twig helper in your button template. In the example below, a key is pointing to `my_custom_udw` configuration and has additional parameter `johndoe`. @@ -183,11 +143,11 @@ In the example below, a key is pointing to `my_custom_udw` configuration and has ``` -#### Additional parameters +### Additional parameters If an event listener catches additional parameters passed with context, it will use a configuration specified for it in the event subscriber. -In the example below the `johndoe` parameter enables the user to choose multiple items from a **Browser window** by changing `multiple: false` from `my_custom_udw` configuration to `multiple: true`. +In the example below, the `johndoe` parameter enables the user to choose multiple items from a **Browser window** by changing `multiple: false` from `my_custom_udw` configuration to `multiple: true`. ```php hl_lines="29 30 31" class JohnDoeCanSelectMore implements EventSubscriberInterface @@ -229,4 +189,4 @@ class JohnDoeCanSelectMore implements EventSubscriberInterface } ``` -For more information follow [Symfony Doctrine Event Listeners and Subscribers tutorial.](https://symfony.com/doc/5.0/event_dispatcher.html#creating-an-event-subscriber) +For more information, see [Symfony Doctrine Event Listeners and Subscribers tutorial.]([[= symfony_doc =]]/event_dispatcher.html#creating-an-event-subscriber) diff --git a/docs/extending/extending_workflow.md b/docs/extending/extending_workflow.md deleted file mode 100644 index acae91550f..0000000000 --- a/docs/extending/extending_workflow.md +++ /dev/null @@ -1,248 +0,0 @@ -# Extending Workflow - -## Adding custom actions - -[Built-in actions in the Editorial Workflow](../guide/workflow.md#publishing-content-with-workflow) -enable you to automatically publish a Content item or to send a notification to reviewers. - -You can also create custom actions that will be called when content reaches a specific stage -or goes through a transition in a workflow. In `config/packages/ezplatform.yaml`: - -``` yaml -ezplatform: - system: - # Workflow configuration is SiteAccess-aware - default: - workflows: - custom_workflow: - # ... - transitions: - # ... - to_proofread: - from: draft - to: proofread - label: To proofreading - actions: - proofread_transition_action: - data: - message: "The article has gone to proofreading" -``` - -??? tip "Complete `custom_workflow` configuration" - ``` yaml - ezplatform: - system: - # Workflow configuration is SiteAccess-aware - default: - workflows: - # Identifier of the workflow - custom_workflow: - name: Custom Workflow - matchers: - # Which Content Types can use this workflow, optional - content_type: article - # Which status of the Content item can use this workflow, optional. Available statuses are draft and published. - content_status: draft - # All stages the content goes through - stages: - draft: - label: Draft - color: '#f15a10' - proofread: - label: Proofread - color: '#5a10f1' - done: - label: Done - color: '#301203' - # Content items in this stage don't appear on My dashboard and in Review Queue. - last_stage: true - initial_stage: draft - # Available transitions between stages - transitions: - to_proofread: - from: draft - to: proofread - label: To proofreading - color: '#8888ba' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' - reviewers: - required: true - back_to_draft: - reverse: to_proofread - label: Back to draft - color: '#cb8888' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' - done: - from: proofread - to: done - label: Done - color: '#88ad88' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' - ``` - -The configuration indicates the name of the custom action (`proofread_transition_action`) that will call the action. -`data` contains additional data that can be passed to the action. In this case, a message to display. - -To define what the action does, create an Event Listener `src/EventListener/ProofreadTransitionListener.php`: - -``` php hl_lines="27 36" -notificationHandler = $notificationHandler; - } - - public function getIdentifier(): string - { - return 'proofread_transition_action'; - } - - public function onWorkflowEvent(TransitionEvent $event): void - { - $metadata = $this->getActionMetadata($event->getWorkflow(), $event->getTransition()); - $message = $metadata['data']['message']; - - $this->notificationHandler->info( - $message, - [], - 'domain' - ); - - $this->setResult($event, true); - } -} -``` - -This Listener displays a notification bar at the bottom of the page when a Content item goes through the `to_proofread` transition. - -The content of the notification is the message configured in `actions.proofread_transition_action.data`. -To get it, access the metadata for this transition through `getActionMetadata()` (line 27). - -The listener must be registered as a service (in `config/services.yaml`): - -``` yaml -App\EventListener\ProofreadTransitionListener: - tags: - - { name: ezplatform.workflow.action_listener } -``` - -Line 36 in the listener above sets a custom result value for the transition. -You can use this value in other stages and transitions for this Content item, for example: - -``` yaml -done: - from: proofread - to: done - label: Done - actions: - done_transition_action: - condition: - - result.proofread_transition_action == true -``` - -The action indicated here will be performed only if the result from the `proofread_transition_action` is set to `true`. -Then, the following `src/EventListener/DoneTransitionListener` is called: - -``` php hl_lines="27" -notificationHandler = $notificationHandler; - } - - public function getIdentifier(): string - { - return 'done_transition_action'; - } - - public function onWorkflowEvent(TransitionEvent $event): void - { - $context = $event->getContext(); - $message = $context['message']; - - $this->notificationHandler->info( - $message, - [], - 'domain' - ); - } -} -``` - -This listener also displays a notification, but in this case its content is taken from the message -that the user types when choosing the `Done` transition. - -The message is contained in the context of the action. - -`$event->getContext()` gives you access to the context. -The context contains: - -- `$workflowId` - the ID of the current workflow -- `$message` - content of the user's message when sending the Content item through the transitions -- `$reviewerId`: ID of the User who was selected as a reviewer -- `$result`: an array of transition actions performed so far - -You can also modify the context using the `setContext()` method. -For example, you can override the message typed by the user: - -``` -$new_context = $context; -$new_context['message'] = "This article went through proofreading"; -$event->setContext($new_context); -``` -Now you can send your articles to the proofreading stage. - -![Proofreading stage](img/extending_workflow_proofreading.png) - -## Workflow event timeline - -[Workflow event timeline](../guide/workflow.md) is used out of the box to display workflow transitions. - -You can also use it to render custom entries in the timeline, for example system alerts on workflows. - -### Adding custom entry type - -To add a custom entry type, create a custom class extending `EzSystems\EzPlatformWorkflow\WorkflowTimeline\Value\AbstractEntry`. -Use an `EzSystems\EzPlatformWorkflow\Event\TimelineEvents::COLLECT_ENTRIES` event to add your entries to the timeline. - -### Providing custom templates - -To provide custom templates for new event timeline entries, use the following configuration in `config/packages/ezplatform.yaml`: - -``` yaml -ezplatform: - system: - default: - workflows_config: - # Workflow Timeline - timeline_entry_templates: - - { template: '@EzPlatformWorkflow/ezplatform_workflow/timeline/entries.html.twig', priority: 10 } -``` - -The template has to provide a block named `ez_workflow_timeline_entry_{ENTRY_IDENTIFIER}`. diff --git a/docs/extending/img/custom_tag_ezyt.png b/docs/extending/img/custom_tag_ezyt.png deleted file mode 100644 index 1f8840ac99..0000000000 Binary files a/docs/extending/img/custom_tag_ezyt.png and /dev/null differ diff --git a/docs/extending/img/email_notification.png b/docs/extending/img/email_notification.png deleted file mode 100644 index 3774564ae5..0000000000 Binary files a/docs/extending/img/email_notification.png and /dev/null differ diff --git a/docs/extending/img/extending_calendar_list_view.png b/docs/extending/img/extending_calendar_list_view.png index df2a232608..66923a2de3 100644 Binary files a/docs/extending/img/extending_calendar_list_view.png and b/docs/extending/img/extending_calendar_list_view.png differ diff --git a/docs/extending/img/extending_calendar_view.png b/docs/extending/img/extending_calendar_view.png index 55c02b86ee..6e23017b46 100644 Binary files a/docs/extending/img/extending_calendar_view.png and b/docs/extending/img/extending_calendar_view.png differ diff --git a/docs/extending/img/extending_custom_tab.png b/docs/extending/img/extending_custom_tab.png deleted file mode 100644 index 1e7e10c422..0000000000 Binary files a/docs/extending/img/extending_custom_tab.png and /dev/null differ diff --git a/docs/extending/img/extending_date_time_short.png b/docs/extending/img/extending_date_time_short.png deleted file mode 100644 index d31de3c773..0000000000 Binary files a/docs/extending/img/extending_date_time_short.png and /dev/null differ diff --git a/docs/extending/img/extending_example_page_block.png b/docs/extending/img/extending_example_page_block.png deleted file mode 100644 index 8ef484b8d8..0000000000 Binary files a/docs/extending/img/extending_example_page_block.png and /dev/null differ diff --git a/docs/extending/img/extending_form_builder_country_field.png b/docs/extending/img/extending_form_builder_country_field.png deleted file mode 100644 index 577a35d4c7..0000000000 Binary files a/docs/extending/img/extending_form_builder_country_field.png and /dev/null differ diff --git a/docs/extending/img/extending_form_builder_custom_form_fields.png b/docs/extending/img/extending_form_builder_custom_form_fields.png deleted file mode 100644 index 28596575ff..0000000000 Binary files a/docs/extending/img/extending_form_builder_custom_form_fields.png and /dev/null differ diff --git a/docs/extending/img/extending_tooltips.png b/docs/extending/img/extending_tooltips.png deleted file mode 100644 index fd6e956a54..0000000000 Binary files a/docs/extending/img/extending_tooltips.png and /dev/null differ diff --git a/docs/extending/img/extending_workflow_proofreading.png b/docs/extending/img/extending_workflow_proofreading.png deleted file mode 100644 index 949cd2d632..0000000000 Binary files a/docs/extending/img/extending_workflow_proofreading.png and /dev/null differ diff --git a/docs/extending/img/oe_custom_class_note_box.png b/docs/extending/img/oe_custom_class_note_box.png deleted file mode 100644 index c8fe78f072..0000000000 Binary files a/docs/extending/img/oe_custom_class_note_box.png and /dev/null differ diff --git a/docs/extending/img/udw_image_tab.png b/docs/extending/img/udw_image_tab.png index 1b0768861e..66bf78c881 100644 Binary files a/docs/extending/img/udw_image_tab.png and b/docs/extending/img/udw_image_tab.png differ diff --git a/docs/extending/import_assets_from_bundle.md b/docs/extending/import_assets_from_bundle.md new file mode 100644 index 0000000000..5eccfb79e6 --- /dev/null +++ b/docs/extending/import_assets_from_bundle.md @@ -0,0 +1,141 @@ +--- +description: Import assets, such as stylesheets or images, from a separate bundle with customizations. +--- + +# Importing assets from a bundle + +[[= product_name =]] uses [Webpack Encore]([[= symfony_doc =]]/frontend.html#webpack-encore) for asset management. + +## Configuration from a bundle + +To import assets from a bundle, you must configure them in the bundle's `Resources/encore/ez.config.js` file: + +``` js +const path = require('path'); + +module.exports = (Encore) => { + Encore.addEntry('', [ + path.resolve(__dirname, ''), + ]); +}; +``` + +Use `` to refer to this configuration entry from Twig templates: + +`{{ encore_entry_script_tags('', null, 'ezplatform') }}` + +To import CSS files only, use: + +`{{ encore_entry_link_tags('', null, 'ezplatform') }}` + +!!! tip + + After you add new files, run `php bin/console cache:clear`. + + For a full example of importing asset configuration, + see [`ez.config.js`](https://github.com/ezsystems/ezplatform-admin-ui/blob/v2.0.2/src/bundle/Resources/encore/ez.config.js) + +To edit existing configuration entries, create a `Resources/encore/ez.config.manager.js` file: + +``` js +const path = require('path'); + +module.exports = (eZConfig, eZConfigManager) => { + eZConfigManager.replace({ + eZConfig, + entryName: '', + itemToReplace: path.resolve(__dirname, ''), + newItem: path.resolve(__dirname, ''), + }); + eZConfigManager.remove({ + eZConfig, + entryName: '', + itemsToRemove: [ + path.resolve(__dirname, ''), + path.resolve(__dirname, ''), + ], + }); + eZConfigManager.add({ + eZConfig, + entryName: '', + newItems: [ + path.resolve(__dirname, ''), + path.resolve(__dirname, ''), + ], + }); +}; +``` + +!!! tip + + If you do not know what `entryName` to use, you can use the browser's developer tools to check what files are loaded on the given page. + Then, use the file name as `entryName`. + +!!! tip + + After you add new files, run `php bin/console cache:clear`. + + For a full example of overriding configuration, + see [`ez.config.manager.js`](https://github.com/ezsystems/ezplatform-matrix-fieldtype/blob/v2.0.0/src/bundle/Resources/encore/ez.config.manager.js). + +To add a new configuration under your own namespace and with its own dependencies, +add the `Resources/encore/ez.webpack.custom.config.js` file, for example: + +``` js + const Encore = require('@symfony/webpack-encore'); + + Encore.setOutputPath('') + .setPublicPath('') + .addExternals('') + // ... + .addEntry('', ['']); + + const customConfig = Encore.getWebpackConfig(); + + customConfig.name = 'customConfigName'; + + // Config or array of configs: [customConfig1, customConfig2]; + module.exports = customConfig; +``` + +!!! tip + + If you don't plan to add multiple entry files on the same page in your custom configuration, + use the `disableSingleRuntimeChunk()` function to avoid adding a separate `runtime.js` file. + Otherwise, your JS code may be run multiple times. + By default, the `enableSingleRuntimeChunk()` function is used. + +## Configuration from main project files + +If you prefer to include the asset configuration in the main project files, +add it in [`webpack.config.js`](https://github.com/ezsystems/ezplatform/blob/v3.0.3/webpack.config.js#L15). + +To overwrite the built-in assets, use the following configuration to replace, remove or add asset files +in `webpack.config.js`: + +``` js +eZConfigManager.replace({ + eZConfig, + entryName: '', + itemToReplace: path.resolve(__dirname, ''), + newItem: path.resolve(__dirname, ''), +}); + +eZConfigManager.remove({ + eZConfig, + entryName: '', + itemsToRemove: [ + path.resolve(__dirname, ''), + path.resolve(__dirname, ''), + ], +}); + +eZConfigManager.add({ + eZConfig, + entryName: '', + newItems: [ + path.resolve(__dirname, ''), + path.resolve(__dirname, ''), + ], +}); +``` diff --git a/docs/extending/menus/add_menu_item.md b/docs/extending/menus/add_menu_item.md new file mode 100644 index 0000000000..02dfa99f08 --- /dev/null +++ b/docs/extending/menus/add_menu_item.md @@ -0,0 +1,49 @@ +--- +description: Create a custom menu in the Back Office. +--- + +# Add menu item + +To add a new menu entry in the Back Office, you need to use an event subscriber +and subscribe to [one of the events](back_office_menus.md#menu-events) dispatched when building menus. + +The following example shows how to add a "Content list" item to the main top menu +and list all Content items there, with a shortcut button to edit them. + +## Create event subscriber + +First, create an event subscriber in `src/EventSubscriber/MyMenuSubscriber.php`: + +``` php hl_lines="14 22-23" +[[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 0, 14) =]][[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 15, 36) =]] +} +``` + +This subscriber subscribes to the `ConfigureMenuEvent::MAIN_MENU` event (see line 14) +and creates an `all_content_list` menu item (lines 22-23). + +## Add route + +Next, configure the route that the menu item leads to: + +``` yaml +[[= include_file('code_samples/back_office/menu/menu_item/config/custom_routes.yaml') =]] +``` + +## Create controller + +The route indicates a controller that fetches all visible Content items and renders the view. + +Create the following controller file in `src/Controller/AllContentListController.php`: + +``` php hl_lines="56" +[[= include_file('code_samples/back_office/menu/menu_item/src/Controller/AllContentListController.php') =]] +``` + +## Add template + +Finally, create the `templates/list/all_content_list.html.twig` file indicated in line 56 in the controller: + +``` html+twig +[[= include_file('code_samples/back_office/menu/menu_item/templates/list/all_content_list.html.twig') =]] +``` diff --git a/docs/extending/menus/back_office_menus.md b/docs/extending/menus/back_office_menus.md new file mode 100644 index 0000000000..a761f5fc13 --- /dev/null +++ b/docs/extending/menus/back_office_menus.md @@ -0,0 +1,139 @@ +--- +description: All menus in the Back Office are based on KnpMenuBundle and you can easily extend them with new items. +--- + +# Back Office menus + +Back Office menus are based on the [KnpMenuBundle](https://github.com/KnpLabs/KnpMenuBundle) and are easily extensible. + +!!! tip + + For general information on how to use `MenuBuilder`, + see [the official KnpMenuBundle documentation](https://symfony.com/doc/current/bundles/KnpMenuBundle/index.html). + +Menus are extensible using event subscribers, for example: + +``` php +[[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 0, 14) =]][[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 15, 36) =]] +} +``` + +!!! tip + + The event subscriber is registered as a service by default, if `autoconfigure` is enabled. + If not, register it as a service and tag with `kernel.event.subscriber`. + +## Menu events + +You can listen to the following events: + +||| +|---|---| +| Main menu | `ConfigureMenuEvent::MAIN_MENU` | +|| `ConfigureMenuEvent::USER_MENU` | +| Content view | `ConfigureMenuEvent::CONTENT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_SIDEBAR_LEFT` | +| Trash | `ConfigureMenuEvent::TRASH_SIDEBAR_RIGHT` | +| Section | `ConfigureMenuEvent::SECTION_EDIT_SIDEBAR_RIGHT` +|| `ConfigureMenuEvent::SECTION_CREATE_SIDEBAR_RIGHT` | +| Policies and permissions | `ConfigureMenuEvent::POLICY_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::POLICY_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::ROLE_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::ROLE_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::ROLE_COPY_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::USER_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::USER_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::ROLE_ASSIGNMENT_CREATE_SIDEBAR_RIGHT` | +| Languages | `ConfigureMenuEvent::LANGUAGE_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::LANGUAGE_EDIT_SIDEBAR_RIGHT` | +| Object states | `ConfigureMenuEvent::OBJECT_STATE_GROUP_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::OBJECT_STATE_GROUP_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::OBJECT_STATE_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::OBJECT_STATE_EDIT_SIDEBAR_RIGHT` | +| Content Types | `ConfigureMenuEvent::CONTENT_TYPE_GROUP_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_TYPE_GROUP_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_TYPE_CREATE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_TYPE_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::CONTENT_TYPE_SIDEBAR_RIGHT` | +| URLs and wildcards | `ConfigureMenuEvent::URL_EDIT_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::URL_WILDCARD_EDIT_SIDEBAR_RIGHT` | +| User settings | `ConfigureMenuEvent::USER_PASSWORD_CHANGE_SIDEBAR_RIGHT` | +|| `ConfigureMenuEvent::USER_SETTING_UPDATE_SIDEBAR_RIGHT` | + +## Adding menu items + +To add a menu item, use the `addChild()` method. Provide the method with the new menu item's identifier +and, optionally, with parameters. + +The following method adds a new menu item under **Content** with custom attributes: + +``` php +[[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 22, 35) =]] +``` + +`label` is used for the new menu item in the interface. +`route` is the name of the route that the menu item leads to. + +`attributes` adds attributes (such as CSS classes) to the container `
  • ` element of the new menu item. +`linkAttributes` adds attributes to the `` element. + +### Passing a parameter to a menu item + +You can also pass parameters to templates used to render menu items with `template_parameters`: + +``` php +$menu->addChild( + 'all_content_list', + [ + 'extras' => [ + 'template' => 'list/all_content_list.html.twig', + 'template_parameters' => [ + 'custom_parameter' => 'value', + ], + ], + ] +); +``` + +You can then use the variable `custom_parameter` in `templates/list/all_content_list.html.twig`. + +### Translatable labels + +To have translatable labels, use `translation.key` from the `messages` domain: + +``` php +$menu->addChild( + 'all_content_list', + [ + 'label' => 'translation.key', + 'extras' => [ + 'translation_domain' => 'messages', + ], + ] +); +``` + +## Modifying menu items + +To modify the parameters of an existing menu item, use the `setExtra()` method. + +### Custom icons + +You can use the `extras.icon` parameter to define an icon for a menu item. +For example, the following code changes the default icon for the "Create" button in content view: + +``` php +[[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 43, 45) =]] +``` + +## Removing menu items + +To remove a menu item, for example, to remove the **Copy subtree** item from the right menu in content view, +use the following event listener: + +``` php +[[= include_file('code_samples/back_office/menu/menu_item/src/EventSubscriber/MyMenuSubscriber.php', 41, 42) =]] +``` + diff --git a/docs/extending/online_editor_button.md b/docs/extending/online_editor_button.md index ecb7dca2de..db75bf799b 100644 --- a/docs/extending/online_editor_button.md +++ b/docs/extending/online_editor_button.md @@ -1,98 +1,41 @@ -# Creating Online Editor button +# Create custom Online Editor button -You can add your own buttons to the Online Editor. +There are different ways to [extend the Online Editor](extending_online_editor.md). +Here you can learn how to add your own buttons. -In this example you will create a button which inserts an `
    ` element into a RichText Field. +Follow the procedure below to create a button that inserts an `
    ` element into +a RichText Field. -## Creating the button - -First, create the button file in `assets/js/alloyeditor/plugins/hr.js`: +First, create the button file in `assets/js/online_editor/buttons/hr.js`: ``` js -import PropTypes from 'prop-types'; -import AlloyEditor from 'alloyeditor'; -import EzButton - from '../../../../vendor/ezsystems/ezplatform-richtext/src/bundle/Resources/public/js/OnlineEditor/buttons/base/ez-button.js'; - -export default class EzBtnHr extends EzButton { - static get key() { - return 'hr'; - } - - addHr() { - this.execCommand({ - tagName: 'hr', - }); - } - - render() { - const title = "Hr"; - return ( - - ); - } -} - -AlloyEditor.Buttons[EzBtnHr.key] = AlloyEditor.EzBtnHr = EzBtnHr; - -const eZ = (window.eZ = window.eZ || {}); - -eZ.ezAlloyEditor = eZ.ezAlloyEditor || {}; -eZ.ezAlloyEditor.ezBtnHr = EzBtnHr; - -EzBtnHr.propTypes = { - command: PropTypes.string, - modifiesSelection: PropTypes.bool, -}; - -EzBtnHr.defaultProps = { - command: 'eZAddContent', - modifiesSelection: true, -}; +[[= include_file('code_samples/back_office/online_editor/assets/js/online_editor/buttons/hr.js') =]] ``` -## Enabling the button - -Now you need to enable the button. - -Add the following code to `webpack.config.js` under `// Put your config here`: +Then, enable the button. +Add the following code in the `webpack.config.js` file, under +`// Put your config here`: ``` js eZConfigManager.add({ eZConfig, entryName: 'ezplatform-richtext-onlineeditor-js', newItems: [ - path.resolve(__dirname, 'assets/js/alloyeditor/buttons/hr.js'), + path.resolve(__dirname, 'assets/js/online_editor/buttons/hr.js'), ] }); ``` -## Adding the button to configuration - Finally, add the button to your [[= product_name =]] configuration: ``` yaml -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - toolbars: - ezadd: - buttons: - hr: - priority: 50 +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_buttons.yaml', 0, 6) =]] [[= include_file('code_samples/back_office/online_editor/config/packages/custom_buttons.yaml', 12, 16) =]] ``` -At this point you can run `yarn encore dev` and create a Content item with a RichText Field. +You have successfully created a custom Online Editor button. + +You can now run the `yarn encore dev` command to regenerate the assets, and create +a Content item with a RichText Field. The new button appears in the Element toolbar and inserts an `
    ` element when clicked: ![Custom button inserting an `
    ` into RichText](img/oe_custom_button.png) diff --git a/docs/extending/online_editor_plugin.md b/docs/extending/online_editor_plugin.md index e83fe43441..fc43e5ad2e 100644 --- a/docs/extending/online_editor_plugin.md +++ b/docs/extending/online_editor_plugin.md @@ -1,127 +1,59 @@ -# Creating Online Editor plugin +# Create custom Online Editor plugin -You can add your own plugins to the Online Editor. +There are different ways to [extend the Online Editor](extending_online_editor.md). +Here you can learn how to add your own plugin to the Online Editor. -Online Editor is based on AlloyEditor, which in turn uses CKEditor, -so creating a custom plugin is similar to [the way you do it in CKEditor](https://ckeditor.com/docs/ckeditor4/latest/guide/plugin_sdk_sample.html). +Follow the procedure below to create a plugin which inserts the current date into +a RichText Field. -In this example you will create a plugin which inserts the current date into a RichText Field. +!!! note -## Creating the plugin + Online Editor is based on AlloyEditor, which in turn uses CKEditor. + As a result, you create custom Online Editor plugins in a way similar to + [the way you do it in CKEditor](https://ckeditor.com/docs/ckeditor4/latest/guide/plugin_sdk_sample.html). -First, create the plugin file in `assets/js/alloyeditor/plugins/date.js`: +First, create the plugin file in `assets/js/online_editor/plugins/date.js`. +The following code implements an `InsertDate` method and attaches it to the editor: ``` js -(function (global) { - if (CKEDITOR.plugins.get('date')) { - return; - } - - const InsertDate = { - exec: function (editor) { - const now = new Date(); - editor.insertHtml( now.toString() ); - }, - }; - - global.CKEDITOR.plugins.add('date', { - init: (editor) => editor.addCommand('InsertDate', InsertDate), - }); -})(window); +[[= include_file('code_samples/back_office/online_editor/assets/js/online_editor/plugins/date.js') =]] ``` -This file implements an `InsertDate` command and attaches it to the editor. - -## Creating a button - -Next, you need to add a button for inserting the date to the Online Editor toolbar. - -Create a file for the button in `assets/js/alloyeditor/buttons/date.js`: +Then, modify the Online Editor toolbar by adding a button that inserts the date. +Create a file for the button in `assets/js/online_editor/buttons/date.js`: ``` js -import PropTypes from 'prop-types'; -import AlloyEditor from 'alloyeditor'; -import EzButton - from '../../../../vendor/ezsystems/ezplatform-richtext/src/bundle/Resources/public/js/OnlineEditor/buttons/base/ez-button.js'; - -export default class BtnDate extends EzButton { - static get key() { - return 'date'; - } - - insertDate(data) { - this.execCommand(data); - } - - render() { - const title = 'Date'; - - return ( - - ); - } -} - -AlloyEditor.Buttons[BtnDate.key] = AlloyEditor.BtnDate = BtnDate; -eZ.addConfig('ezAlloyEditor.BtnDate', BtnDate); - -BtnDate.propTypes = { - command: PropTypes.string, -}; - -BtnDate.defaultProps = { - command: 'InsertDate', -}; +[[= include_file('code_samples/back_office/online_editor/assets/js/online_editor/buttons/date.js') =]] ``` -## Enabling the plugin - -Now you need to enable the plugin and the button. - -Add the following code to `webpack.config.js` under `// Put your config here`: +Next, enable the plugin and the button. +Add the following code in the `webpack.config.js` file, under +`// Put your config here`. +This way, [[= product_name =]] loads the plugin and button files when loading +the Online Editor: ``` js eZConfigManager.add({ eZConfig, entryName: 'ezplatform-richtext-onlineeditor-js', newItems: [ - path.resolve(__dirname, 'assets/js/alloyeditor/buttons/date.js'), - path.resolve(__dirname, 'assets/js/alloyeditor/plugins/date.js'), + path.resolve(__dirname, 'assets/js/online_editor/buttons/date.js'), + path.resolve(__dirname, 'assets/js/online_editor/plugins/date.js'), ] }); ``` -This file loads the plugin and button files when loading Online Editor. - -## Adding the plugin to configuration - Finally, add the plugin and its button to your [[= product_name =]] configuration: ``` yaml -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - toolbars: - paragraph: - buttons: - date: - priority: 0 -ezrichtext: - alloy_editor: - extra_plugins: [date] +[[= include_file('code_samples/back_office/online_editor/config/packages/custom_plugin.yaml') =]] ``` -At this point you can run `yarn encore dev` and create a Content item with a RichText Field. -The new button appears in the toolbar when editing a Paragraph element and inserts the current date when clicked: +You have successfully created a custom Online Editor plugin. + +You can now run the `yarn encore dev` command to regenerate the assets, and create +a Content item with a RichText Field. +The new button appears in the toolbar when editing a Paragraph element and inserts +the current date when clicked: ![Custom plugin inserting the current date into RichText](img/oe_custom_plugin.png) diff --git a/docs/extending/richtext_block.md b/docs/extending/richtext_block.md index 493bb6a7b8..ec1dac5321 100644 --- a/docs/extending/richtext_block.md +++ b/docs/extending/richtext_block.md @@ -1,108 +1,56 @@ -# Creating custom RichText blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Create a custom Page block containing rich text. +edition: experience +--- -To create a RichText [custom Page block](extending_page.md), you need to define its layout, provide templates, add a subscriber and register it as a service. +# Create custom RichText block -Start with providing the block configuration in `config/packages/ezplatform_page_fieldtype.yaml`: +A RichText block is a specific example of a [custom block](../guide/page/create_custom_page_block.md) that you can use when +you create a Page. +To create a custom block, you must define the block's layout, provide templates, add a subscriber +and register the subscriber as a service. -``` yaml hl_lines="3 15" -ezplatform_page_fieldtype: - blocks: - my_block: - name: My Richtext Block - thumbnail: assets/images/blocks/richtext_block_icon.svg - configuration_template: blocks/my_block/config.html.twig - views: - default: - template: blocks/my_block/default.html.twig - name: My block view - priority: -255 - attributes: - content: - name: Content - type: richtext +Follow the procedure below to create a RichText Page block. + +First, provide the block configuration in `config/packages/ezplatform_page_fieldtype.yaml`. +The following code defines a new block, its view and configuration +templates. +It also sets the attribute type to `richtext` (line 15): +``` yaml hl_lines="3 15" +[[= include_file('code_samples/back_office/online_editor/config/packages/ezplatform_page_fieldtype.yaml') =]] ``` -This configuration defines a new block, its view and configuration templates, and attribute type `richtext` (line 15). -Remember to provide an icon for the block in the `assets/images/blocks/` folder. +!!! note -Next, create a subscriber that converts a string of data into XML. -Create a `src/Event/Subscriber/RichTextBlockSubscriber.php` file containing: + Make sure that you provide an icon for the block in the `assets/images/blocks/` folder. -``` php hl_lines="32 41 42 43 44 45 46 47 48 49 50 51" -domDocumentFactory = $domDocumentFactory; - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents(): array - { - return [ - BlockRenderEvents::getBlockPreRenderEventName('my_block') => 'onBlockPreRender', - ]; - } - - /** - * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event - */ - public function onBlockPreRender(PreRenderEvent $event): void - { - $renderRequest = $event->getRenderRequest(); - if (!$renderRequest instanceof TwigRenderRequest) { - return; - } - $parameters = $renderRequest->getParameters(); - $parameters['document'] = null; - $xml = $event->getBlockValue()->getAttribute('content')->getValue(); - if (!empty($xml)) { - $parameters['document'] = $this->domDocumentFactory->loadXMLString($xml); - } - $renderRequest->setParameters($parameters); - } - } +Then, create a subscriber that converts a string of data into XML code. +Create a `src/Event/Subscriber/RichTextBlockSubscriber.php` file. -``` +In line 32, `my_block` is the same name of the block that you defined in line 3 +of the `ezplatform_page_fieldtype.yaml` file above. +Line 32 also implements the `PreRender` method. +Lines 41-51 handle the conversion of content into an XML string: -Note that in the line 32, `my_block` is the name of the block defined in the `ezplatform_page_fieldtype.yaml` file (line 3). -This line also implements the `PreRender` method. -Lines 41-51 handle the conversion of content into XML string. -At this point you need to create [templates](../guide/templates.md#templating-basics) for displaying and configuring your block. +``` php hl_lines="32 41 42 43 44 45 46 47 48 49 50 51" +[[= include_file('code_samples/back_office/online_editor/src/event/subscriber/RichTextBlockSubscriber.php') =]] +``` + +Now you can create [templates](../guide/content_rendering/templates/templates.md) that are used +for displaying and configuring your block. -Start with creating the view template in `templates/blocks/my_block/richtext.html.twig`: +Create the view template in `templates/blocks/my_block/richtext.html.twig`. +Line 2 is responsible for rendering the content from XML to HTML5: ``` html+twig hl_lines="2"
    {{ document | ez_richtext_to_html5 }}
    - ``` -Here, line 2 is responsible for rendering the content from XML to HTML5. -Proceed with creating a separate `templates/blocks/my_block/config.html.twig` template: +Then, create a separate `templates/blocks/my_block/config.html.twig` template: ``` html+twig {% extends '@EzPlatformPageBuilder/page_builder/block/config.html.twig' %} @@ -111,10 +59,9 @@ Proceed with creating a separate `templates/blocks/my_block/config.html.twig` te {{ parent() }} {% endblock %} - ``` -Complete the procedure with registering the subscriber as a service in `config/services.yaml`: +Finally, register the subscriber as a service in `config/services.yaml`: ``` yaml services: @@ -124,8 +71,10 @@ services: ``` -Now, you can add your custom RichText block in the Site tab. +You have successfully created a custom RichText block. +You can now add your block in the Site tab. ![RichText block](img/extending_richtext_block.png) -For details on customizing additional options of the block or creating custom blocks with other attribute types, see [Creating custom Page blocks](extending_page.md). +For more information about customizing additional options of the block or creating +custom blocks with other attribute types, see [Create custom Page block](../guide/page/create_custom_page_block.md). diff --git a/docs/extending/tabs/add_dashboard_tab.md b/docs/extending/tabs/add_dashboard_tab.md new file mode 100644 index 0000000000..a8d4619c07 --- /dev/null +++ b/docs/extending/tabs/add_dashboard_tab.md @@ -0,0 +1,26 @@ +--- +description: Add a new tab to the Back Office dashboard that welcomes every user after logging in. +--- + +# Create dashboard tab + +To create a new tab in the dashboard, create an `EveryoneArticleTab.php` file in `src/Tab/Dashboard/Everyone`. +This adds a tab to the **Common content** dashboard block that displays all articles in the repository. + +``` php hl_lines="17 45 57-60 70-72" +[[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php') =]] +``` + +This tab searches for content with Content Type "Article" (lines 57-60) +and renders the results using the built-in `all_content.html.twig` template, +which ensures the tab looks the same as the existing tabs (lines 70-72). + +The tab also implements OrderedTabInterface (line 17), +which enables you to define the order in which the tab is displayed on the dashboard page. +This is done using the `getOrder()` method (line 45). + +Register this tab as a service: + +``` yaml +[[= include_file('code_samples/back_office/dashboard/article_tab/config/custom_services.yaml') =]] +``` diff --git a/docs/extending/tabs/back_office_tabs.md b/docs/extending/tabs/back_office_tabs.md new file mode 100644 index 0000000000..654f52a5c0 --- /dev/null +++ b/docs/extending/tabs/back_office_tabs.md @@ -0,0 +1,82 @@ +--- +description: Tabs are used for content view, in dashboard, system information and other parts of the Back Office and are extensible. +--- + +# Back Office tabs + +Many elements of the Back Office interface, such as content view, dashboard or system information, are built using tabs. + +![Tabs in System Information](../img/tabs_system_info.png) + +You can extend existing tab groups with new tabs, or create your own tab groups. + +## Tabs + +A custom tab can extend one of the following classes: + +- `EzSystems\EzPlatformAdminUi\Tab\AbstractTab` - base tab. +- `EzSystems\EzPlatformAdminUi\Tab\AbstractControllerBasedTab` - embeds the results of a controller action in the tab. +- `EzSystems\EzPlatformAdminUi\Tab\AbstractRouteBasedTab` - embeds the results of the selected route, passing applicable parameters. + +``` php +//... +[[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php', 16, 17) =]] + //... +[[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php', 34, 43) =]][[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php', 49, 51) =]] + //... +[[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php', 69, 72) =]] +``` + +!!! tip + + For a full example of creating a custom tab, see [Add dashboard tab](add_dashboard_tab.md). + +You need to register the tab as a service. +Tag it with `ezplatform.tab` and indicate the group in which it should appear: + +``` yaml +[[= include_file('code_samples/back_office/dashboard/article_tab/config/custom_services.yaml') =]] +``` + +The group can be one of the existing components, or your own [custom tab group](#tab-groups). + +### Tab order + +You can order the tabs by making the tab implement `OrderedTabInterface`. +The order depends on the numerical value returned by the `getOrder` method: + +``` php +[[= include_file('code_samples/back_office/dashboard/article_tab/src/Tab/Dashboard/Everyone/EveryoneArticleTab.php', 44, 48) =]] +``` + +Tabs are displayed according to this value in ascending order. + +!!! tip + + It is good practice to reserve some distance between these values, for example to stagger them by step of 10. + It may come useful if you later need to place something between the existing tabs. + +You can also influence tab display (for example, order tabs, remove or modify them) by using the following event listeners: + +- `TabEvents::TAB_GROUP_PRE_RENDER` +- `TabEvents::TAB_PRE_RENDER` + +## Tab groups + +You can create new tab groups by using the [`TabsComponent`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/lib/Component/TabsComponent.php). + +To create a tab group, register it as a service: + +``` yaml +services: + app.my_tabs.custom_group: + parent: EzSystems\EzPlatformAdminUi\Component\TabsComponent + arguments: + $groupIdentifier: 'custom_group' + tags: + - { name: ezplatform.admin_ui.component, group: 'dashboard-blocks' } +``` + +Tag the group with `ezplatform.admin_ui.component`. +`group` indicates where the group is rendered. +For a list of possible rendering places, see [Injecting custom components](../custom_components.md). diff --git a/docs/fonts/Noto_Sans/NotoSans-SemiBold.ttf b/docs/fonts/Noto_Sans/NotoSans-SemiBold.ttf new file mode 100644 index 0000000000..8b7fd13026 Binary files /dev/null and b/docs/fonts/Noto_Sans/NotoSans-SemiBold.ttf differ diff --git a/docs/fonts/Noto_Sans/NotoSans-SemiBoldItalic.ttf b/docs/fonts/Noto_Sans/NotoSans-SemiBoldItalic.ttf new file mode 100644 index 0000000000..a7e904c38e Binary files /dev/null and b/docs/fonts/Noto_Sans/NotoSans-SemiBoldItalic.ttf differ diff --git a/docs/fonts/Work_Sans/WorkSans-SemiBold.ttf b/docs/fonts/Work_Sans/WorkSans-SemiBold.ttf new file mode 100644 index 0000000000..3ef9f833aa Binary files /dev/null and b/docs/fonts/Work_Sans/WorkSans-SemiBold.ttf differ diff --git a/docs/getting_started/bundles_starter_pack.md b/docs/getting_started/bundles_starter_pack.md deleted file mode 100644 index 46d1b02f58..0000000000 --- a/docs/getting_started/bundles_starter_pack.md +++ /dev/null @@ -1,20 +0,0 @@ -# Bundles starter pack - -[[= product_name =]] follows the [Symfony 5 bundle system](http://symfony.com/doc/5.0/book/bundles.html). -Clean installation comes with set of built-in packages which you can preview in [composer.json](https://github.com/ezsystems/ezplatform/blob/v3.0.0/composer.json). - -For more information about [[= product_name =]] bundle structure, see [Bundle section](../guide/bundles.md) in our Guide. - -## External packages - -If basic bundles do not give you enough flexibility or functionalities, you can extend your project with external packages. -They provide additional ways of customizing your installation, and can help you with content management, further development, integrations, security, social engagement, and system management. - -You can easily browse the external bundles and download them from [[[= product_name =]] Packages](https://ezplatform.com/packages). -Refer to their respective pages for instructions on how to install them. - -## Contributors - -|Bundle|Description| -|------|-----------| -|[RepositoryProfilerBundle](https://github.com/ezsystems/RepositoryProfilerBundle)| profiles Platform API/SPI and sets up scenarios to be able to continuously test to keep track of performance regressions of repository and underlying storage engines| diff --git a/docs/getting_started/first_steps.md b/docs/getting_started/first_steps.md index b17950dfe5..dc8d6e475c 100644 --- a/docs/getting_started/first_steps.md +++ b/docs/getting_started/first_steps.md @@ -1,3 +1,7 @@ +--- +description: Start off working with Ibexa DXP by doing initial configuration and testing system capabilities. +--- + # First steps This page lists first steps you can take after installing [[= product_name =]]. @@ -18,7 +22,7 @@ remove the following files and folders from your installation: - Delete the file `config/packages/ezplatform_welcome_page.yaml` - Delete the `templates/themes/standard` folder - Delete the `assets/scss` folder -- Delete all `translations/ezplatform_welcome_page.*` files +- Delete all `translations/ibexa_platform_welcome_page.*` files - In `webpack.config.js` remove the `Encore.addEntry` section and uncomment the last line, so that the end of the file looks like this: @@ -33,14 +37,16 @@ so that the end of the file looks like this: module.exports = [ eZConfig, ...customConfigs ]; ``` -## Create a Content Type +## Add a Content Type 1\. In your browser, go to the Back Office: `/admin`, and use the default credentials to log in: `admin/publish`. -!!! note "Password change" +!!! caution "Password change" - You will be requested to change your password at first login. + Make sure that you change the default password before you switch your installation + from development to production. For more information about passwords, see [Passwords](../guide/user_management/user_management.md#passwords). + For more information about production security, see [Security checklist](../guide/security_checklist.md). 2\. Select Admin and go to Content Types. @@ -90,7 +96,7 @@ Content view templates use the [Twig templating engine](https://twig.symfony.com !!! tip "More information" - - [Templates](../guide/templates.md) + - [Templates](../guide/content_rendering/templates/templates.md) - [Twig documentation](https://twig.symfony.com/doc/2.x/) ## Create content and test view templates @@ -137,8 +143,8 @@ For now the new SiteAccess does not differ from the main site. !!! tip "More information" - - [SiteAccess](../guide/siteaccess.md) - - [SiteAccess matchers](../guide/siteaccess_matching.md#available-matchers) + - [Multisite](../guide/multisite/multisite.md) + - [SiteAccess matchers](../guide/multisite/siteaccess_matching.md#available-siteaccess-matchers) ## Add a language and translate Content @@ -178,7 +184,7 @@ Switch to the Translations tab and add a new translation. !!! tip "More information" - [Languages](../guide/internationalization.md) - - [Multi-language SiteAccesses and corresponding translations](../guide/multi_language_siteaccesses.md) + - [Set up translation SiteAccess](../guide/multisite/set_up_translation_siteaccess.md) ## Add a design diff --git a/docs/getting_started/img/Landing_Page.png b/docs/getting_started/img/Landing_Page.png deleted file mode 100644 index ebb84389c4..0000000000 Binary files a/docs/getting_started/img/Landing_Page.png and /dev/null differ diff --git a/docs/getting_started/img/Using_Composer_Auth_token.png b/docs/getting_started/img/Using_Composer_Auth_token.png index d3e1f18117..0b44afd1c4 100644 Binary files a/docs/getting_started/img/Using_Composer_Auth_token.png and b/docs/getting_started/img/Using_Composer_Auth_token.png differ diff --git a/docs/getting_started/img/edit_icon.png b/docs/getting_started/img/edit_icon.png deleted file mode 100644 index 628dac3a90..0000000000 Binary files a/docs/getting_started/img/edit_icon.png and /dev/null differ diff --git a/docs/getting_started/img/flex_icon.png b/docs/getting_started/img/flex_icon.png deleted file mode 100644 index 4c761ace1e..0000000000 Binary files a/docs/getting_started/img/flex_icon.png and /dev/null differ diff --git a/docs/getting_started/img/schedule.png b/docs/getting_started/img/schedule.png deleted file mode 100644 index bfdc32a726..0000000000 Binary files a/docs/getting_started/img/schedule.png and /dev/null differ diff --git a/docs/getting_started/install-on-mac-os-and-windows.md b/docs/getting_started/install-on-mac-os-and-windows.md new file mode 100644 index 0000000000..71ce69aedf --- /dev/null +++ b/docs/getting_started/install-on-mac-os-and-windows.md @@ -0,0 +1,98 @@ +--- +description: Install Ibexa DXP on a macOS or Windows system to use it for development. +--- + +# Install [[= product_name =]] on macOS or Windows + +This page explains how to install [[= product_name =]] on macOS or Windows. + +!!! caution + + This procedure is **for development purposes only**. + Installing [[= product_name =]] for production purposes is supported only on Linux. + + For information about installing the product on Linux, see [Install [[= product_name =]]](../getting_started/install_ez_platform.md). + +### Prepare work environment + +To install [[= product_name =]], you need a stack with MySQL and PHP. +Additionally, you need [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/lang/en/docs/install/) for asset management. +If you want to use a web server, you need to install it as well: + +- For Windows: Apache +- For macOS: Apache/nginx + +The instructions below assume that you are using Apache. + +??? note "Windows" + + Locate the `php.ini` file and open it in a text editor. + Provide the missing values to relevant parameters, for example, `date.timezone` and `memory_limit`: + + ``` bash + date.timezone = "Europe/Warsaw" + memory_limit = 4G + ``` + + Uncomment or add extensions relevant to your project, for example, `opcache` extension for PHP (recommended, not required): + + ``` bash + zend_extension=opcache.so + ``` + + You can install Apache as a Windows service by running the following command in CMD as administrator: + + ``` bash + httpd.exe -k -install + ``` + + You can then start it with: + + ``` bash + httpd.exe -k start + ``` + + Edit Apache configuration file `httpd.conf`. + Replace placeholder values with corresponding values from your project, for example, `ServerName localhost:80`. + Uncomment relevant modules, for example: + + ``` bash + LoadModule rewrite_module modules/mod_rewrite.so + LoadModule vhost_alias_module libexec/apache2/mod_vhost_alias.so + ``` + +## Get Composer + +=== "macOS" + + Install Composer using a package manager, for example, [Homebrew.](https://brew.sh/) + +=== "Windows" + + Download and run [Composer-Setup.exe](https://getcomposer.org/Composer-Setup.exe) - it will install the latest Composer version. + +## Install [[= product_name =]] + +At this point the installation procedure is the same as when installing on Linux. +Follow the steps from the main [Install [[= product_name =]]](../getting_started/install_ez_platform.md#install-ibexa-dxp) page. + +## Set up virtual host + +Prepare a [virtual host configuration](https://httpd.apache.org/docs/2.4/vhosts/) for your site in your Apache directory: + +- For Windows: `\conf\vhosts` +- For macOS: `/private/etc/apache2/users/` + +Then restart the Apache server. + +## Set up permissions + +Directories `var` and `web/var` need to be writable by CLI and web server user. +Future files and directories created by these two users will need to inherit those permissions. + +For more information, see [Setting up or Fixing File Permissions.]([[= symfony_doc =]]/setup/file_permissions.html) + +!!! note "Security checklist" + + See the [Security checklist](../guide/security_checklist.md) for a list of security-related issues + that you should take care of before going live with a project. diff --git a/docs/getting_started/install_ez_platform.md b/docs/getting_started/install_ez_platform.md index a4a3a8c357..4ca4c4d609 100644 --- a/docs/getting_started/install_ez_platform.md +++ b/docs/getting_started/install_ez_platform.md @@ -1,3 +1,7 @@ +--- +description: Install Ibexa DXP on a Linux system and prepare your installation for production. +--- + # Install [[= product_name =]] !!! note @@ -5,9 +9,15 @@ Installation for production is only supported on Linux. To install [[= product_name =]] for development on macOS or Windows, - see [Install on macOS or Windows](../community_resources/installing-on-mac-os-and-windows.md). + see the [installation guide for macOS and Windows](install-on-mac-os-and-windows.md). + +!!! note "Installing Ibexa OSS" + + This installation guide details the steps to install Ibexa DXP for users who have a subscription agreement with Ibexa. + If you want to install Ibexa OSS, you do not need authentication tokens or an account on updates.ibexa.co, + but must adapt the steps shown here to the product edition and the `ibexa/oss-skeleton` repository. -## Prepare the work environment +## Prepare work environment To install [[= product_name =]] you need a stack with your operating system, MySQL and PHP. @@ -23,7 +33,7 @@ Additional requirements: Before getting started, make sure you review other [requirements](requirements.md) to see the systems we support and use for testing. -## Get Composer +### Get Composer Install a recent stable version of Composer, the PHP command line dependency manager. Use the package manager for your Linux distribution. @@ -50,15 +60,15 @@ composer -V If you do so, you must replace `composer` with `php -d memory_limit=-1 composer.phar` in all commands below. -## Get [[= product_name =]] +## Install [[= product_name =]] -### Set up authentication tokens [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +### Set up authentication tokens -[[= product_name_exp =]] and [[= product_name_com =]] subscribers have access to commercial packages at [updates.ez.no/](https://updates.ez.no/). +[[= product_name =]] subscribers have access to commercial packages at [updates.ibexa.co](https://updates.ibexa.co/). The site is password-protected. You must set up authentication tokens to access the site. -Log in to your service portal on [support.ez.no](https://support.ez.no), go to your **Service Portal**, and look for the following on the **Maintenance and Support agreement details** screen: +Log in to your service portal on [support.ibexa.co](https://support.ibexa.co), go to your **Service Portal**, and look for the following on the **Maintenance and Support agreement details** screen: ![Authentication token](img/Using_Composer_Auth_token.png) @@ -69,33 +79,30 @@ Log in to your service portal on [support.ez.no](https://support.ez.no), go to y !!! tip "Save the authentication token in `auth.json` to avoid re-typing it" Composer will ask whether you want to save the token every time you perform an update. - If you prefer, you can decline and create an `auth.json` file manually in one of the following ways: - - - A: Store your credentials in the project directory (for security reasons, do not check them in to git): + If you prefer, you can decline and create an `auth.json` file globally + in [`COMPOSER_HOME`](https://getcomposer.org/doc/03-cli.md#composer-home) directory for machine-wide use: ``` bash - composer config http-basic.updates.ez.no + composer config --global http-basic.updates.ibexa.co ``` - - B: If you only have one project on the machine/server/vm, and want to install globally in [`COMPOSER_HOME`](https://getcomposer.org/doc/03-cli.md#composer-home) directory for machine-wide use: + To store your credentials per project, add the credentials to the `COMPOSER_AUTH` variable: ``` bash - composer config --global http-basic.updates.ez.no + export COMPOSER_AUTH='{"http-basic":{"updates.ibexa.co": {"username": "", "password": ""}}}' ``` + + You then need to [add the contents of this variable to `auth.json`](#authentication-token). -After this, when running Composer to get updates, you will be asked for a username and password. Use: - -- as username - your Installation key found on the **Maintenance and Support agreement details** page in the service portal -- as password - the token password you retrieved in step 3. - -!!! note +!!! tip "Different tokens for different projects on a single host" - If you are using Platform.sh, you can set the token as an environment variable. + If you configure several projects on one machine, make sure that + you set different tokens for each of the projects in their respective `auth.json` files. - When you do, make sure the **Visible during runtime** box in Platform.sh configuration is unchecked. - This will ensure that the token is not exposed. +After this, when running Composer to get updates, you will be asked for a username and password. Use: - ![Setting token to be invisible during runtime](img/psh_addvariable.png) +- as username - your Installation key found on the **Maintenance and Support agreement details** page in the service portal +- as password - the token password you retrieved in step 3 above. !!! note "Authentication token validation delay" @@ -107,129 +114,116 @@ After this, when running Composer to get updates, you will be asked for a userna They will become active again if the agreement is renewed, but this process may take up to 24 hours. _(If the agreement is renewed before the expiry date, there will be no disruption of service.)_ -## Create project - -There are two ways to get an instance of [[= product_name =]]. -The result is the same, so you can use the way you prefer: - -- [Download or clone](#a-download-or-clone) -- [Create a project with Composer](#b-create-project-with-composer) +### Create project -### A. Download or clone +To use Composer to instantly create a project in the current folder with all the dependencies, +run the following command: -=== "[[= product_name =]]" - - You can either: - - - download an archive from [ezplatform.com](https://ezplatform.com/#download-option) - and extract the archive into the location where you want your project root directory to be, or - - clone the [`ezplatform` GitHub repository](https://github.com/ezsystems/ezplatform). +=== "[[= product_name_content =]]" ``` bash - git clone https://github.com/ezsystems/ezplatform . + composer create-project ibexa/content-skeleton:^3.3 . ``` - Check out a tag (e.g. `git checkout v1.13.4`) that you want to use in a project. - Use branches (e.g. `master` or `1.13`) only when contributing. +=== "[[= product_name_exp =]]" -=== "[[= product_name_exp =]] and [[= product_name_com =]]" + ``` bash + composer create-project ibexa/experience-skeleton:^3.3 . + ``` - Download an archive from the [Support portal](https://support.ez.no/Downloads). +=== "[[= product_name_com =]]" - Extract the archive into the location where you want your project root directory to be. + ``` bash + composer create-project ibexa/commerce-skeleton:^3.3 . + ``` -Next, install dependencies with Composer. From the folder into which you downloaded the files, run: +??? note "Using PHP 8.0, 7.4 or 7.3" -``` bash -composer install -``` + If you're using version lower than PHP 8.1, use a different set of commands: -Composer looks inside the `composer.json` file and installs all packages required to run the product. + === "[[= product_name_content =]]" -### B. Create a project with Composer + ``` bash + composer create-project ibexa/content-skeleton:^3.3 --no-install . + composer update + ``` -=== "[[= product_name =]]" + === "[[= product_name_exp =]]" - To use Composer to instantly create a project in the current folder with all the dependencies, run the following command: + ``` bash + composer create-project ibexa/experience-skeleton:^3.3 --no-install . + composer update + ``` - ``` bash - composer create-project --keep-vcs ezsystems/ezplatform . - ``` + === "[[= product_name_com =]]" -=== "[[= product_name_exp =]]" + ``` bash + composer create-project ibexa/commerce-skeleton:^3.3 --no-install . + composer update + ``` - To install a new project with the `composer create-project` command to get the latest version of [[= product_name_exp =]], - you must first inform the Composer, which token to use before the project folder is created. +!!! tip "Authentication token" - To do this, select the correct updates.ez.no channel. The following channels are available: +
    If you added credentials to the `COMPOSER_AUTH` variable, + at this point add this variable to `auth.json` (for example, by running `echo $COMPOSER_AUTH > auth.json`). - - Trial (limited access to try for up to 120 days): [ttl](https://updates.ez.no/ttl/) - - Enterprise Business User License (requires valid subscription): [bul](https://updates.ez.no/bul/) +!!! tip - For example, you select the `bul` channel in the following way: + You can set [different version constraints](https://getcomposer.org/doc/articles/versions.md): + specific tag (`3.3.2`), version range (`~3.3.2`), stability (`^3.3@rc`), etc.: ``` bash - COMPOSER_AUTH='{"http-basic":{"updates.ez.no":{"username":"","password":""}}}' composer create-project --keep-vcs --repository=https://updates.ez.no/bul/ ezsystems/ezplatform-ee my-new-ee-project + composer create-project ibexa/content-skeleton:3.3.2 . ``` - Edit `composer.json` in your project root and change the URL defined in the `repositories` section to `https://updates.ez.no/bul/`. - Once that is done, you can execute `composer update` to get packages with the correct license. +!!! note "Platform.sh" - !!! note "Moving from trial" + If you are deploying your installation on [Platform.sh](https://docs.platform.sh/frameworks/ibexa.html), + run the following command: + + ``` bash + composer ibexa:setup --platformsh + ``` + + This command provides the necessary configuration for using Platform.sh. - If you started with a trial installation and want to use the software under the [BUL license instead of a TTL license](https://ibexa.co/About-our-Software/Licenses-and-agreements/), you must change the channel setting that you have just made. +#### Add project to version control -=== "[[= product_name_com =]]" +It is recommended to add your project to version control. - To install a new project with the `composer create-project` command to get the latest version of [[= product_name_com =]], - you must first inform the Composer, which token to use before the project folder is created. +First, create a `.gitignore` file based on the `.gitignore.dist` provided in the project. - To do this, select the correct updates.ez.no channel. The following channels are available: +Then, initiate your project repository: - - Trial (limited access to try for up to 120 days): [ttl_com](https://updates.ez.no/ttl_com/) - - Enterprise Business User License (requires valid subscription): [bul_com](https://updates.ez.no/bul_com/) +``` bash +git init; git add . > /dev/null; git commit -m "init" > /dev/null +``` - For example, you select the `bul_com` channel in the following way: +### Change installation parameters - ``` bash - COMPOSER_AUTH='{"http-basic":{"updates.ez.no":{"username":"","password":""}}}' composer create-project --keep-vcs --repository=https://updates.ez.no/bul_com/ ezsystems/ezcommerce my-new-com-project - ``` +At this point configure your database via the `DATABASE_URL` in the `.env` file, +depending of the database you are using: - Edit `composer.json` in your project root and change the URL defined in the `repositories` section to `https://updates.ez.no/bul_com/`. - Once that is done, you can execute `composer update` to get packages with the correct license. +`DATABASE_URL=mysql://user:password@host:port/database_name`. - !!! note "Moving from trial" +or - If you started with a trial installation and want to use the software under the [BUL license instead of a TTL license](https://ibexa.co/About-our-Software/Licenses-and-agreements/), you must change the channel setting that you have just made. +`DATABASE_URL=postgresql://user:password@host:port/database_name`. -!!! tip +!!! tip "Encoding database password" - You can set [different version constraints](https://getcomposer.org/doc/articles/versions.md): - specific tag (`v2.2.0`), version range (`~1.13.0`), stability (`^2.3@rc`), etc. - For example if you want to get the latest stable 2.x release, with a minimum of v2.3.1, use: + The password entered in `DATABASE_URL` must either be URL encoded, or not contain any special characters that would require URL encoding. - ``` bash - composer create-project --keep-vcs ezsystems/ezplatform . ^2.3.1 - ``` + For more information, see [Encoding database password](troubleshooting.md#encoding-database-password). -!!! tip "Different tokens for different projects on a single host" +### Add entropy to improve cryptographic randomness - If you configure several projects on one machine, make sure that - you set different tokens for each of the projects in their respective `auth.json` files. - -## Change installation parameters - -At this point you can configure your database via the `DATABASE_URL` in the `.env` file: -`DATABASE_URL=mysql://user:password@host:port/name`. - -Choose a [secret](http://symfony.com/doc/5.0/reference/configuration/framework.html#secret) +Choose a [secret]([[= symfony_doc =]]/reference/configuration/framework.html#secret) and provide it in the `APP_SECRET` parameter in `.env`. It should be a random string, made up of at least 32 characters, numbers, and symbols. -This is used by Symfony when generating [CSRF tokens](https://symfony.com/doc/5.0/security/csrf.html), -[encrypting cookies](http://symfony.com/doc/5.0/cookbook/security/remember_me.html), -and for creating signed URIs when using [ESI (Edge Side Includes)](https://symfony.com/doc/5.0/http_cache/esi.html). - -Instead of setting `DATABASE_URL`, you can change individual installation parameters in `.env`. +This is used by Symfony when generating [CSRF tokens]([[= symfony_doc =]]/security/csrf.html), +[encrypting cookies]([[= symfony_doc =]]/cookbook/security/remember_me.html), +and for creating signed URIs when using [ESI (Edge Side Includes)]([[= symfony_doc =]]/http_cache/esi.html). !!! caution @@ -245,56 +239,27 @@ Instead of setting `DATABASE_URL`, you can change individual installation parame The same goes for other secrets, like database password, Varnish invalidate token, JWT passphrase, etc. After changing the app secret, make sure that you clear the application cache and log out all the users. - For more information, see [Symfony documentation](https://symfony.com/doc/5.0/reference/configuration/framework.html#secret). + For more information, see [Symfony documentation]([[= symfony_doc =]]/reference/configuration/framework.html#secret). It is recommended to store the database credentials in your `.env.local` file and not commit it to the Version Control System. -The configuration requires providing the following parameters: - -- `DATABASE_USER` -- `DATABASE_PASSWORD` -- `DATABASE_NAME` -- `DATABASE_HOST` -- `DATABASE_PORT` -- `DATABASE_PLATFORM` - prefix for distinguishing the database you are connecting to (e.g. `mysql` or `pgsql`) -- `DATABASE_DRIVER` - driver used by Doctrine to connect to the database (e.g. `pdo_mysql` or `pdo_pgsql`) -- `DATABASE_VERSION` - database server version (for a MariaDB database, prefix the value with `mariadb-`) - -!!! caution - - When you use the `.env.local` file with the `DATABASE_*` parameters mentioned above, you must re-define the `DATABASE_URL` parameter for interpolation after overriding those parameters: - - ``` - DATABASE_URL=${DATABASE_PLATFORM}://${DATABASE_USER}:${DATABASE_PASSWORD}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME} - ``` +In `DATABASE_VERSION` you can also configure the database server version (for a MariaDB database, prefix the value with `mariadb-`). !!! tip "Using PostgreSQL" If you want an installation with PostgreSQL instead of MySQL, refer to [Using PostgreSQL](../guide/databases.md#using-postgresql). -## Install and configure a search engine [[% include 'snippets/commerce_badge.md' %]] - -Search in the shop front end requires that you have either Solr or Elasticsearch installed as a search engine. - -=== "Solr" +#### Install and configure a search engine - Run the included script to install Solr: +You may choose to replace the [default search engine](../guide/search/search.md#legacy-search-engine) with either Solr or Elasticsearch. - ``` bash - bash ./install-solr.sh - ``` +!!! note "Shop front end requirement [[% include 'snippets/commerce_badge.md' %]]" - Configure the following parameters in the `.env` file: + Search in the shop front end requires that you have either Solr or Elasticsearch as a search engine. - - `SISO_SEARCH_SOLR_HOST` - - `SISO_SEARCH_SOLR_PORT` - - `SISO_SEARCH_SOLR_CORE` +=== "Solr" - Also in the `.env` file, set Solr as the search engine: - - ``` - SEARCH_ENGINE=solr - ``` + Follow [How to set up Solr search engine](../guide/search/solr.md#how-to-set-up-solr-search-engine) to install Solr. === "Elasticsearch" @@ -312,12 +277,13 @@ Search in the shop front end requires that you have either Solr or Elasticsearch ELASTICSEARCH_DSN=http://localhost:9200 ``` -## Install [[= product_name =]] +### Create a database Install [[= product_name =]] and create a database with: ``` bash -composer ezplatform-install +php bin/console ibexa:install +php bin/console ibexa:graphql:generate-schema ``` Before executing the command make sure that the database user has sufficient permissions. @@ -326,6 +292,14 @@ If Composer asks for your token, you must log in to your GitHub account and gene (edit your profile and go to **Developer settings** > **Personal access tokens** > **Generate new token** with default settings). This operation is performed only once, when you install [[= product_name =]] for the first time. +### Run post-installation script + +Run the post-installation script with the following command: + +``` bash +composer run post-install-cmd +``` + ## Use PHPs built-in server For development you can use the built-in PHP server. @@ -342,7 +316,7 @@ You can also use [Symfony CLI](https://symfony.com/download): symfony serve ``` -## Prepare the installation for production +## Prepare installation for production To use [[= product_name =]] with an HTTP server, you need to [set up directory permissions](#set-up-permissions) and [prepare a virtual host](#set-up-virtual-host). @@ -366,55 +340,57 @@ Future files and directories created by these two users will need to inherit tho To do so, follow the instructions on [setting up a virtual host below](#set-up-virtual-host). To set up permissions for production, it is recommended to use an ACL (Access Control List). -See [Setting up or Fixing File Permissions](http://symfony.com/doc/5.0/setup/file_permissions.html) in Symfony documentation +See [Setting up or Fixing File Permissions]([[= symfony_doc =]]/setup/file_permissions.html) in Symfony documentation for information on how to do it on different systems. -### Set up a virtual host +### Set up virtual host -#### Option A: Scripted configuration +Prepare a [virtual host configuration](https://en.wikipedia.org/wiki/Virtual_hosting) for your site. -Use the included shell script: `//bin/vhost.sh` to generate a ready to use `.conf` file. -Check out the source of `vhost.sh` to see the options provided. +=== "Apache" -#### Option B: Manual configuration + You can copy [the example vhost file](https://raw.githubusercontent.com/ibexa/post-install/main/resources/templates/apache2/vhost.template) + to `/etc/apache2/sites-available` as a `.conf` file and modify it to fit your project. -Copy `//doc/apache2/vhost.template` to `/etc/apache2/sites-available` as a `.conf` file. + Specify `//public` as the `DocumentRoot` and `Directory`, or ensure `BASEDIR` is set in the environment. + Uncomment the line that starts with `#if [APP_ENV]` and set the value to `prod` or `dev`, depending on the environment that you are configuring, + or ensure `APP_ENV` is set in the environment. -Modify the file to fit your project. + ``` + SetEnvIf Request_URI ".*" APP_ENV=prod + ``` -Specify `//public` as the `DocumentRoot` and `Directory`. -Uncomment the line that starts with `#if [SYMFONY_ENV]` and set the value to `prod` or `dev`, -depending on the environment that you are configuring: + When the virtual host file is ready, enable the virtual host and disable the default: -``` -SetEnvIf Request_URI ".*" SYMFONY_ENV=prod -``` + ``` bash + a2ensite ibexa + a2dissite 000-default.conf + ``` -#### Enable the virtual host + Finally, restart the Apache server. + The command may vary depending on your Linux distribution. + For example, on Ubuntu use: -When the virtual host file is ready, enable the virtual host and disable the default: + ``` bash + service apache2 restart + ``` -``` bash -a2ensite ezplatform -a2dissite 000-default.conf -``` +=== "nginx" -Finally, restart the Apache server. -The command may vary depending on your Linux distribution. -For example, on Ubuntu use: + You can use [this example vhost file](https://raw.githubusercontent.com/ibexa/post-install/main/resources/templates/nginx/vhost.template) + and modify it to fit your project. You will also need the `ibexa_params.d` files that should reside in a subdirectory below where the main file is, + [as is shown here](https://github.com/ibexa/post-install/tree/main/resources/templates/nginx). -``` bash -service apache2 restart -``` -Open your project in the browser by visiting the domain address, for example `http://localhost:8080`. -You should see the welcome page. + Specify `//public` as the `root`, or ensure `BASEDIR` is set in the environment. + Ensure `APP_ENV` is set to `prod` or `dev` in the environment, depending on the environment that you are configuring, and uncomment the line that starts with `#if[APP_ENV`. -!!! tip "eZ Launchpad for quick deployment" + When the virtual host file is ready, enable the virtual host and disable the default. + Finally, restart the nginx server. + The command may vary depending on your Linux distribution. - To get your [[= product_name =]] installation up and running quickly, - use the Docker-based [eZ Launchpad](https://ezsystems.github.io/launchpad/), which takes care of the whole setup for you. - eZ Launchpad is supported by the Ibexa Community. +Open your project in the browser by visiting the domain address, for example `http://localhost:8080`. +You should see the welcome page. ## Post-installation steps @@ -425,15 +401,15 @@ You should see the welcome page. ### Enable Date-based Publisher -To enable delayed publishing of Content using the Date-based Publisher, you must set up cron to run the `bin/console ezplatform:scheduled:run` command periodically. +To enable delayed publishing of Content using the Date-based Publisher, you must set up cron to run the `bin/console ibexa:scheduled:run` command periodically. For example, to check for publishing every minute, add the following script: -`echo '* * * * * cd [path-to-ezplatform]; php bin/console ezplatform:cron:run --quiet --env=prod' > ezp_cron.txt` +`echo '* * * * * cd [path-to-ibexa-dxp]; php bin/console ibexa:cron:run --quiet --env=prod' > ezp_cron.txt` For 5-minute intervals: -`echo '*/5 * * * * cd [path-to-ezplatform]; php bin/console ezplatform:cron:run --quiet --env=prod' > ezp_cron.txt` +`echo '*/5 * * * * cd [path-to-ibexa-dxp]; php bin/console ibexa:cron:run --quiet --env=prod' > ezp_cron.txt` Next, append the new cron to user's crontab without destroying existing crons. Assuming the web server user data is `www-data`: @@ -444,7 +420,7 @@ Finally, remove the temporary file: `rm ezp_cron.txt` -### Enable the Link manager [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +### Enable the Link manager To make use of the [Link Manager](../guide/url_management.md), you must [set up cron](../guide/url_management.md#enable-automatic-url-validation). @@ -452,3 +428,7 @@ To make use of the [Link Manager](../guide/url_management.md), you must [set up To provide the `JMS_PAYMENT_SECRET` secret for the [[= product_name_com =]] payment system, run `./vendor/defuse/php-encryption/bin/generate-defuse-key` and use the generated secret. + +## Ibexa Cloud + +If you want to host your application on Ibexa Cloud, follow the [Install on Ibexa Cloud](install_on_ibexa_cloud.md) procedure. diff --git a/docs/getting_started/install_on_ibexa_cloud.md b/docs/getting_started/install_on_ibexa_cloud.md new file mode 100644 index 0000000000..08f3cccbc2 --- /dev/null +++ b/docs/getting_started/install_on_ibexa_cloud.md @@ -0,0 +1,100 @@ +--- +description: Install and configure Ibexa DXP to run in cloud using Ibexa Cloud. +--- + +# Install on Ibexa Cloud + +Ibexa Cloud enables you to host your application in the cloud by using the [Platform.sh](https://platform.sh/) service. + +## 1. Prepare configuration files + +If you didn't run the `composer ibexa:setup` command during installation, run it now: + +``` bash +composer ibexa:setup --platformsh +``` + +This command adds to your project configuration files required for using Ibexa Cloud. + +You can adapt the configuration in the following places: + +- `.platform.app.yaml` - main configuration +- `.platform/services.yml` - additional services such as search engines or cache +- `.platform/routes.yml` - routes to additional services, for example Fastly + +For details about available configuration settings, +refer to [Platform.sh documentation.](https://docs.platform.sh/configuration/app.html) + +### Disk space + +The total disk space depends on your Ibexa Cloud subscription level. +You can assign disk space to the main app container under the `disk` key. +You can distribute the remaining space between other containers (for example, the database) or search engine +in `.platform/services.yaml`, under the individual service definitions. + +### Build and deploy process + +Configuration under `hooks` defines the process of building and deploying your project. + +!!! note + + During the build phase (defined in the `hooks.build` configuration), files in the project have read/write permissions (can be modified). + + During deployment (defined in the `hooks.deploy` configuration), all files in the project are read-only. + +### Additional services + +`.platform/services.yaml` contains preconfigured setting blocks that you can uncomment +to enable services such as Solr or Elasticsearch, or persistent Redis session storage. + +For information about available services, +see [Platform.sh documentation.](https://docs.platform.sh/configuration/services.html) + +If you enable any of the services, you must uncomment the relevant relationship +under the `relationship` key in `.platform.app.yaml` as well. + +## 2. Create an account + +Log in to https://cloud.ibexa.co or create an account if you do not have one yet. + +Create a project and select its region. + +!!! caution + + Don't use https://console.platform.sh/ which doesn't list Ibexa Cloud projects. + Use https://cloud.ibexa.co to manage your Ibexa Cloud projects. + +## 3. Prepare for hosting + +After the project is created, the website walks you through preparing your project for hosting. +This includes adding an SSH key, and adding Platform.sh as a git remote. + +Add your Composer authentication token to the project before pushing it to Platform.sh. +You can set this token as an environment variable. + +When you do, make sure the **Visible during runtime** box in Platform.sh configuration is unchecked. +This ensures that the token is not exposed. + +![Setting token to be invisible during runtime](img/psh_addvariable.png) + +## 4. Push the project + +When you are done with configuration, push your project to the Platform.sh remote: + +``` bash +git push -u master +``` + +!!! note + + `master` is the Platform.sh name for the production branch. + +!!! caution + + If you want to use the [Platform.sh CLI](https://docs.platform.sh/development/cli.html), you have to set it up for Ibexa Cloud. + Consider using the following alias `ibexa_cloud` with a dedicated setup instead of `platform`: + ```bash + alias ibexa_cloud="PLATFORMSH_CLI_SESSION_ID=ibexa_cloud \ + PLATFORMSH_CLI_API_URL=https://api.cloud.ibexa.co \ + platform" + ``` diff --git a/docs/getting_started/quickstart.md b/docs/getting_started/quickstart.md deleted file mode 100644 index 963603e0af..0000000000 --- a/docs/getting_started/quickstart.md +++ /dev/null @@ -1,41 +0,0 @@ -# Quickstart - -This page explains how to quickly install and set up eZ Platform for development. - -## Prerequisites - -To quickly install eZ Platform, you need a Linux machine with PHP and MySQL installed, as well as Node.js and Yarn. - -For more details see [Full requirements](requirements.md). - -## Installation - -To install eZ Platform you need [Composer](https://getcomposer.org/) and git on your system. - -``` bash -composer create-project --keep-vcs ezsystems/ezplatform . -composer ezplatform-install -``` - -For more details see [Install eZ Platform](install_ez_platform.md). - -## Run eZ Platform - -To run eZ Platform on the built-in PHP server: - -``` bash -php bin/console server:run -``` - -The command will output the address of the development server. -Add `/admin` to access the Back Office. The default Administrator login is `admin` with password `publish`. - -## Project structure - -eZ Platform is a Symfony app and follows the project structure used by Symfony. - -For more details see [Structuring a bundle](../guide/project_organization.md#structuring-a-bundle). - -## First steps - -See [First steps](first_steps.md) for common first tasks on a clean eZ Platform installation. diff --git a/docs/getting_started/requirements.md b/docs/getting_started/requirements.md index 793251e242..e74a6662c9 100644 --- a/docs/getting_started/requirements.md +++ b/docs/getting_started/requirements.md @@ -1,3 +1,7 @@ +--- +description: System, component and package requirements for running Ibexa DXP. +--- + # Requirements The following server requirements cover both running the software on-premise and on third-party PaaS providers. @@ -6,119 +10,103 @@ The following server requirements cover both running the software on-premise and For running on [Ibexa Cloud](https://www.ibexa.co/products/ibexa-cloud), where recommended configuration and support is provided out of the box, see separate [Ibexa Cloud section](#ibexa-cloud-requirements-and-setup) for further reading on its requirements. -## Server +The minimal setup requires PHP, MySQL/MariaDB, Apache/Nginx, Node.js and `yarn`. +Recommendation for production setups is to use Varnish/Fastly, Redis/Memcached, NFS/EFS/S3 and Solr/Elasticsearch in a [clustered setup](../guide/clustering.md). + +Using the latest listed version of each product or component is always recommended. + +## Operating system + +- Debian 10.x "Buster" +- Ubuntu 20.04 "Focal Fossa" or Ubuntu 22.04 "Jammy Jellyfish" +- RHEL / CentOS 8.1+ + +## Web server + +- Nginx 1.18 +- Apache 2.4 (with required modules `mod_rewrite`, `mod_env` and recommended: `mod_setenvif`, `mod_expires`; +event MPM is recommended, if you need to use prefork you also need the `mod_php` module) -Ibexa software is built to rely on existing technologies and standards. The minimal setup is `PHP`,  `MySQL/MariaDB`, `Apache/Nginx`, `Node.js` and `yarn`. Recommendation for production setups is to use `Varnish`/`Fastly`, `Redis`/`Memcached`, `NFS`/`EFS`/`S3` and `Solr`/`Elasticsearch` in a [clustered setup](../guide/clustering.md). +## DBMS -For supported versions of these technologies see Recommended and Supported setups below. +- MariaDB 10.3, 10.4 (optionally 10.2 - deprecated) +- MySQL 8.0 (optionally 5.7 - deprecated) +- PostgreSQL 10+ -### Recommended setups +## PHP -These setups are tested by QA and are generally recommended setups. For security and performance we furthermore recommend use of the newer versions of components below unless otherwise noted. +- 8.1 +- 8.0 +- 7.4 (PHP 7.4 has reached its End of Life. Unless you have extended support from vendors like Debian or Zend, you should use PHP 8.1) +- 7.3 (PHP 7.3 has reached its End of Life. Unless you have extended support from vendors like Debian or Zend, you should use PHP 8.1) -||Debian|Ubuntu|RHEL / CentOS| -|------|------|------|------| -|Operating system|10.x "Buster"|20.04 "Focal Fossa"|8.1+| -|Web Server|Nginx 1.14
    Apache 2.4|Nginx 1.18
    Apache 2.4|Nginx 1.14
    Apache 2.4| -|DBMS|MariaDB 10.3|MariaDB 10.3
    MySQL 8.0|MariaDB 10.3
    MySQL 8.0| -|PHP|PHP 7.3|PHP 7.4|PHP 7.3| -|PHP packages|php-cli
    php-fpm
    php-mysql or php-pgsql
    php-xml
    php-json
    php-intl
    php-curl
    php-gd *or* php-imagick|php-cli
    php-fpm
    php-mysql or php-pgsql
    php-xml
    php-mbstring
    php-json
    php-intl
    php-curl
    php-gd *or* php-imagick|php-cli
    php-fpm
    php-mysqlnd or php-pgsql
    php-xml
    php-mbstring
    php-json
    php-process
    php-intl
    php-pear *(optional, provides pecl)*
    php-gd *or* php-imagick *(via [pecl](https://pecl.php.net/package/imagick))*| -|Cluster PHP packages|[php-redis](https://pecl.php.net/package/redis) *or* [php-memcached](https://pecl.php.net/package/memcached)|[php-redis](https://pecl.php.net/package/redis) *or* [php-memcached](https://pecl.php.net/package/memcached)|[php-redis](https://pecl.php.net/package/redis) *or* [php-memcached](https://pecl.php.net/package/memcached)| +### PHP packages -||| -|------|------| -|Search|Solr 7.7LTS *or* Elasticsearch 7.7, using Oracle Java/Open JDK 8 or higher | -|Graphic Handler|GraphicsMagick or ImageMagick or GD| -|[Clustering](../guide/clustering.md)|Linux NFS *or* S3/EFS *(for IO, aka binary files stored in content repository, not supported with legacy)*
    Redis 5.0 or higher *(separate instances for session & cache, both using a `volatile-*` [eviction policy](https://redis.io/topics/lru-cache), session instance configured for persistance)* *or* [Memcached](https://memcached.org/) 1.5 or higher
    [Varnish](http://varnish-cache.org/) 6.0LTS with [varnish-modules](https://github.com/varnish/varnish-modules/blob/master/README.rst) *or* [Fastly](https://www.fastly.com/) using [the bundle provided with [[= product_name_exp =]]](../guide/http_cache.md#serving-varnish-through-fastly) *(for HttpCache)*| -|Filesystem|Linux ext4 / XFS| -|Package manager|Composer (recent stable version)| -|Asset manager|`Node.js` 10.15.3 LTS
    `yarn` 1.15.2 or higher| +- `php-cli` +- `php-fpm` +- `php-mysql` (`php-mysqlnd`) or `php-pgsql` +- `php-xml` +- `php-mbstring` +- `php-json` +- `php-process` (on RHEL/CentOS) +- `php-intl` +- `php-curl` +- `php-pear` (optional, provides pecl) +- `php-gd` or `php-imagick` (via pecl on RHEL/CentOS) +- `php-sodium` -### Other supported setups +### Cluster PHP packages -For security and performance we generally recommend (unless otherwise noted) using the newer versions of components below. +- `php-redis` or `php-memcached` -- OS: Linux -- Web Servers: - - Apache 2.4, with required modules `mod_rewrite`, `mod_env` and recommended: `mod_setenvif`, `mod_expires` - - event MPM is recommended, if you need to use _prefork_ you'll also need the `mod_php` module - - Nginx 1.12, 1.14, 1.16 -- DBMS - - MySQL 5.7 or 8.0 - - MariaDB 10.2, 10.3, 10.4 - - PostgreSQL 10+ -- PHP - - 7.3 - - 7.4 +## Search -- Cluster - - Cache: - - Redis 4.0+ (5.0 recommended, using `volatile-*` [eviction policy](https://redis.io/topics/lru-cache) is required with default [Redis adapter](../guide/persistence_cache.md#redis)) - - Memcached 1.5 or higher (See [Memcached adapter](../guide/persistence_cache.md##memcached) for comparison with Redis) - - Session: either own Redis instance with persistence turned on, or Database. - - Search: Solr 7, Solr 8, or Elasticsearch 7.7 (recommended over SQL-based Search engine, especially on cluster, as SQL does not provide the same feature set or performance as the other two). - - IO: NFS or S3 - - HttpCache, using one of: - - [Varnish](http://varnish-cache.org/) 6.0LTS with [varnish-modules](https://github.com/varnish/varnish-modules/blob/master/README.rst) - - [Fastly](https://www.fastly.com/) using [the bundle provided with [[= product_name_exp =]]](../guide/http_cache.md#serving-varnish-through-fastly) +- For content search, Solr 7.7 LTS or Solr 8, recommended 8.11.1 or higher. Alternatively, Elasticsearch 7.16.2 or higher 7.x version. +- For BinaryFile Field indexing, Apache Tika 1.20 or higher 1.x version, recommended 1.28.1 or higher. +- The above solutions require Oracle Java/Open JDK. The minimum requirement is 8 LTS, recommended 11 LTS. Newer versions are not supported. -- PHP extensions/modules - - curl - - ctype - - dom (usually bundled with `xml` extension package) - - fileinfo - - iconv - - intl - - mbstring - - json - - opcache - - pdo - - pdo mysql *(with mysqlnd)* - - pdo pgsql - - posix - - reflection - - xml - - xsl - - zip - - [php-redis](https://pecl.php.net/package/redis) *or* [php-memcached](https://pecl.php.net/package/memcached) +## Graphic Handler +- GraphicsMagick +- ImageMagick +- GD -### Development and Experimental setups +Optionally if you intend to edit [PNG, SVG, GIF or WEBP files in the Image Editor](../guide/images/images.md#image-optimization): -[[= product_name =]] can theoretically run and execute on many more setups than the ones listed as recommended and supported, including any [operating system supported by PHP](https://wiki.php.net/platforms), on a PHP 7.3 version or higher that pass the [Symfony requirements](http://symfony.com/doc/5.0/reference/requirements.html), using cache solutions technically supported by [Symfony Cache component](https://symfony.com/doc/5.0/components/cache/cache_pools.html), using databases supported by [Doctrine DBAL](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/configuration.html#driver), and using a binary file storage solution supported by [FlySystem](https://github.com/thephpleague/flysystem#adapters). +- JpegOptim +- Optipng +- Pngquant 2 +- SVGO 1 +- Gifsicle +- cwebp -Examples of Development setups: +## [Clustering](../guide/clustering.md) -- OS: Windows, macOS X, Linux -- Filesystem: NTFS, HFS+/APFS, ... +- Linux NFS or S3/EFS (for IO, aka binary files stored in content repository, not supported with legacy) +- Redis 4.0+, 5.0 or higher (separate instances for session and cache, both using a `volatile-*` [eviction policy](https://redis.io/topics/lru-cache), session instance configured for persistence) or [Memcached](https://memcached.org/) 1.5 or higher +- [Varnish](http://varnish-cache.org/) 6.0LTS with [varnish-modules](https://github.com/varnish/varnish-modules/blob/master/README.md) or [Fastly](https://www.fastly.com/) using [the bundle provided with [[= product_name_exp =]]](../guide/cache/http_cache.md#serving-varnish-through-fastly) (for HttpCache) -Examples of Experimental setups: +## Filesystem -- OS: Any system supported by PHP -- Filesystem: BTRFS, AUFS, ... -- IO: Azure, (S)FTP, GridFS, [etc.](https://flysystem.thephpleague.com/docs/adapter/local/) -- Databases: MSSQL, Oracle (databases technically supported by Doctrine DBAL which we use, but not supported by our installer at the moment, and not covered by automated testing) +- Linux ext4 / XFS +## Package manager -**While all these options are not actively supported by Ibexa**, they are community supported. Meaning you can use them with both open source edition and enterprise edition, however if you encounter issues best way to handle them is via contribution, and any such efforts made to improve support for these technologies can contribute to the technology being supported by Ibexa in the near future. +- Composer: recent 2.1 version -## Client +## Asset manager -[[= product_name =]] is developed to work with *any* web browser that support modern standards, on *any* screen resolution suitable for web, running on *any* device. However for the Editorial and Administration User Interfaces you'll need; a minimum of 1366-by-768 screen resolution, a desktop or tablet device, and a recommended/supported browsers found below. +- `Node.js` 12, 14+, 16+, 18+ (`Node.js` 12 and 14+ has reached its End of Life. We strongly recommend using a newer version to ensure you receive security updates.) +- `yarn` 1.15.2+ -### Recommended browsers +## Browser -These setups have been undergone some additional manual testing and is known to work. +[[= product_name =]] is developed to work with *any* web browser that supports modern standards, on *any* screen resolution suitable for web, running on *any* device. However for the Editorial and Administration User Interfaces you'll need; a minimum of 1366-by-768 screen resolution, a desktop or tablet device, and a recommended/supported browser among the ones found below. -- Mozilla® Firefox® most recent stable version -- Google Chrome™ most recent stable version - -### Supported browsers - -- Chromium™ based browsers such as Microsoft® Edge® and Opera®, most recent stable version, desktop *and* tablet -- Apple® Safari® most recent stable version, desktop *and* tablet - -Please note that the user interface might not look or behave exactly the same across all browsers as it will gracefully degrade if browser does not support certain features. +- Mozilla® Firefox® most recent stable version (recommended) +- Google Chrome™ most recent stable version (recommended) +- Chromium™ based browsers such as Microsoft® Edge® and Opera®, most recent stable version, desktop *and* tablet +- Apple® Safari® most recent stable version, desktop *and* tablet ## Ibexa Cloud requirements and setup diff --git a/docs/getting_started/troubleshooting.md b/docs/getting_started/troubleshooting.md index 4651189d91..f707c6ff81 100644 --- a/docs/getting_started/troubleshooting.md +++ b/docs/getting_started/troubleshooting.md @@ -1,18 +1,38 @@ +--- +description: See what issues you can encounter when installing Ibexa DXP and how to resolve them. +--- + # Troubleshooting This page lists potential problems that you may encounter while installing, configuring, and running [[= product_name =]]. -## Initial installation options +## Encoding database password + +The password entered in `DATABASE_URL` during installation must either be URL encoded, +or not contain any special characters that would require URL encoding. + +### URL encoding + +Using URL encoding involves two steps. First, the password must be URL encoded. This can for instance be done with PHP's `urlencode()` function. +For example, this function converts a password like `(/!=#Ƥ*;%?[` to `%28%2F%21%3D%23%C3%86%C2%A4%2A%3B%25%3F%5B`. -If you accepted default options during `composer install`, but need to change some of them later, -you can do it in the `.env` file. +Second, you must remove `resolve:` from `doctrine.dbal.url` in `config/packages/doctrine.yaml`. +That means changing `%env(resolve:DATABASE_URL)%` to `%env(DATABASE_URL)%`. -## Enable swap on systems with limited RAM +### Avoid special characters + +If your password only contains letters a-z, A-Z, and numbers 0-9, you don't need to do any encoding. +You can either create your password that way, in which case it is a good idea to make it longer to maintain entropy, +keeping the password hard to guess for an attacker. +Or, you can for instance convert your password with `bin2hex()`, so that e.g. `(/!=#Ƥ*;%?[` becomes `282f213d23c386c2a42a3b253f5b`. +The output from `bin2hex` is limited to 0-9 and a-f. This more than doubles the length of the password, keeping entropy similar. + +## Enabling swap with limited RAM If you have problems installing [[= product_name =]] on a system with limited RAM (for example 1GB or 2GB), enable swap. It allows your operating system to use the hard disk to supplement RAM when it runs out. -With swap enables you will be able to successfully run `php -d memory_limit=-1 bin/console ezplatform:install --env prod ezplatform-clean`. +With swap enables you will be able to successfully run `php -d memory_limit=-1 bin/console ibexa:install --env prod ezplatform-clean`. When a system runs out of RAM, you may see `Killed` when trying to clear the cache (e.g. `php bin/console --env=prod cache:clear` from your project's root directory). @@ -28,20 +48,24 @@ You also need to define settings for uploading files in `php.ini`: `upload_max_f ## Cloning failed using an ssh key -When dealing with Composer packages from [updates.ez.no](http://updates.ez.no), you may get a "Cloning failed using an ssh key" error +When dealing with Composer packages from [updates.ibexa.co](http://updates.ibexa.co), you may get a "Cloning failed using an ssh key" error if you tell Composer to download dev packages or to download from source. -[updates.ez.no](http://updates.ez.no) currently supports only distribution packages in alpha stability or higher. +[updates.ibexa.co](http://updates.ibexa.co) currently supports only distribution packages in alpha stability or higher. To avoid the error, check the stability of packages and avoid using `--prefer-source`. -## Redis: Cache / Session data inconsistent across web servers +## Redis sessions issues + +### Inconsistent cache/session data -See [Redis Cluster info in persistence cache doc](../guide/persistence_cache.md#redis-clustering), and make sure you only read/write to +If cache or session data inconsistent across web servers in Redis, +see [Redis clustering](../guide/persistence_cache.md#redis-clustering), and make sure you only read/write to one active master instance at a time. -## Redis: Sessions are removed or new sessions are refused +### Removed or refused sessions -See info on [Redis in session doc](../guide/sessions.md#cluster-setup). +If Redis sessions are removed or new sessions are refused. +see info on [Cluster setup](../guide/sessions.md#cluster-setup). Ideally, use a separated instance of Redis for sessions, that either never runs out of memory or uses an eviction policy that suits your needs. @@ -88,7 +112,7 @@ ln -s ../../src//Bundle/ProjectBundle/Resources/xsl - Make sure that you have the correct version of `vendor/moyarada`. -## Images in shop are not converted. +## Unconverted images in shop Make sure that you have set up correct rights for the image folder: @@ -96,7 +120,7 @@ Make sure that you have set up correct rights for the image folder: sudo chmod -R g+w web/var/ecommerce/storage/ ``` -## `Defuse\Crypto\Exception\BadFormatException` after installation +## `BadFormatException` after installation If you see the following error after installation: diff --git a/docs/guide/admin_panel.md b/docs/guide/admin_panel.md index 1ccf1499fb..cf04707a55 100644 --- a/docs/guide/admin_panel.md +++ b/docs/guide/admin_panel.md @@ -1,4 +1,8 @@ -# Administration management +--- +description: Ibexa DXP Back Office contains managements options for permissions, users, languages, Content Types, as well as system information. +--- + +# Admin panel Once you set up your environment you can start your work as an administrator. Your most useful tools can be found in **Admin Panel**. @@ -42,7 +46,7 @@ Section ID numbers are not recycled. If a Section is removed, its ID number will ## Users -Users in [[= product_name =]] are treated the same way as [Content Types](#content-types). +Users in [[= product_name =]] are treated the same way as Content items. They are organized in groups such as *Guests*, *Editors*, *Anonymous*, which makes it easier to manage them and their permissions. All User Groups and Users can be accessed in the Admin panel by selecting Users. @@ -56,7 +60,8 @@ All User Groups and Users can be accessed in the Admin panel by selecting Users. Registration form for your website is placed under this address: /register. By default, new Users created in this way are placed in the Guest accounts group. -If you want to give your users possibility to register themselves follow a tutorial on [enabling account registration](user_generated_content/#registering-new-users). +To give your users a possibility to register themselves, follow the instructions +on [enabling account registration](../tutorials/platform_beginner/8_enable_account_registration.md). ## Roles @@ -74,7 +79,7 @@ Rules that give users access to different function in a module. You can restrict what user can do with Limitations. The available Limitations depend on the chosen Policy. When Policy has more than one Limitation, all of them have to apply. -See [example below](#restrict-editing-to-part-of-the-tree). +See [example use case](permission_use_cases.md#restrict-editing-to-part-of-the-tree). !!! note @@ -94,118 +99,8 @@ Model your content (Content Types, Sections, Locations etc.) in a way that can b That way system will be more secure and easier to manage. This approach also improves performance. Role assignments and Policies are taken into account during search/load queries. -See [Permissions overview](permissions.md) for further information. - -### Policies examples - -Here are a few examples of sets of Policies you can use to get some common permission configurations. - -#### Enter back end interface - -To allow the User to enter the Back Office interface and view all content, you need to set the following Policies: - -- `user/login` -- `content/read` -- `content/versionread` -- `section/view` -- `content/reverserelatedlist` - -These Policies will be necessary for all other cases below that require access to the content structure. - -#### Create content without publishing [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -This option can be used together with [[= product_name_exp =]]'s content review options. -Using the following Policies, the User is able to create content, but can't publish it; instead, they must send it for review to another User with proper permissions (for example, senior editor, proofreader, etc.). - -- `content/create` -- `content/edit` - -Note that without [[= product_name_exp =]] this setup should not be used, as it will not allow the User to continue working with their content. - -#### Create and publish content - -To create and publish content, the user must additionally have the following Policies: - -- `content/create` -- `content/edit` -- `content/publish` - -This also lets the user copy and move content, as well as add new Locations to a Content item (but not remove them!). - -#### Removing content - -To send content to Trash, the User needs to have the `content/remove` Policy. - -To remove an archived version of content, the User must have the `content/versionremove` Policy. - -Further manipulation of Trash requires the `content/restore` Policy to restore items from Trash, and `content/cleantrash` to completely delete all content from Trash. - - -#### Restrict editing to part of the tree - -If you want to let the User create or edit content, but only in one part of the content tree, you need to use Limitations. -Three Limitations that could be used here are `Section` Limitation, `Location` Limitation and `Subtree of Location` Limitation. - -##### Section Limitation - -Let's assume you have two Folders under your Home: Blog and Articles. -You can let a User create content for the blogs, but not in Articles by adding a `Section` Limitation the Blog Content item. -This will allow the User to publish content anywhere under this Location in the structure. -Section does not have to belong to the same Subtree of Location in the content structure, any Locations can be assigned to it. - -##### Location Limitation - -If you add a `Location` Limitation and point to the same Location, the User will be able to publish content directly under the selected Location, but not anywhere deeper in its Subtree of Location. - -##### Subtree of Location Limitation - -If you want to limit User's access to a subtree you need to use the `Subtree of Location` Limitation. -To do so, you need to create two new Roles for a User Group: - - 1. Role with a `Subtree` Limitation for the User - 1. Role with a `Location` Limitation for the Subtree - -Follow the example below to learn how to do that. - -**Cookbook**, **Dinner recipes** and **Dessert recipes** containers are not accessible in the frontend, so you need to edit access to them in the **Admin Panel**. - -![Subtree file structure](img/subtree_usability_notes_1.png) - -To give the vegetarian editors access only to the **Vegetarian** dinner recipes section create a new Role e.g. *EditorVeg*. -Next, add to it a `content/read` Policy with the `Subtree` Limitation for `Cookbook/Dinner recipes/Vegetarian`. -Assign the Role to the vegetarian editors User Group. -It will allow users from that group to access the **Vegetarian** container but not **Cookbook** and **Dinner recipes**. - -To give users access to **Cookbook** and **Dinner recipes** containers you need to -create a new Role e.g. *EditorVegAccess*. -Next, add to it a `content/read` Policy with the `Location` Limitations **Cookbook** and **Dinner recipes**. -Assign the new Role to the vegetarian editors User Group as well. -Only then the limitations are combined with `AND` resulting in an empty set. - -The vegetarian editors should now see the following Content Tree: - -![Limited subtree file structure](img/subtree_usability_notes_2.png) - -Note that when a Policy has more than one Limitation, all of them have to apply, or the Policy will not work. -For example, a `Location` Limitation on Location `1/2` and `Subtree of Location` Limitation on `1/2/55` cannot work together, because no Location can satisfy both those requirements at the same time. -If you want to combine more than one Limitation with the *or* relation, not *and*, you can split your Policy in two, each with one of these Limitations. - -#### Editorial workflows [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -You can control which stages in an editorial workflow the user can work with. - -Do this by adding the `WorkflowStageLimitation` to `content` Policies such as `content/edit` or `content/publish`. - -You can also control which transitions the user can pass content through. -Do this by using the `workflow/change_stage` Policy together with the `WorkflowTransitionLimitation`. - -For example, to enable the user to edit only content in the "Design" stage -and to pass it after creating design to the "Proofread stage", use following permissions: - -- `content/edit` with `WorkflowStageLimitation` set to "Design". -- `workflow/change_stage` with `WorkflowTransitionLimitation` set to `to_proofreading` - -For more examples, see [Permissions use cases](permissions/#use-cases). +See [Permissions overview](permissions.md) for further information +and [Permission use cases](permission_use_cases.md) for details on how to customize access to different parts of the Back Office. ## Languages @@ -260,7 +155,7 @@ By default, [[= product_name =]] contains one Object state group: **Lock**, with Object states can be used in conjunction with permissions, in particular with the [State Limitation](limitation_reference.md#state-limitation). Their specific use cases depend on your needs and the setup of your permission system. -## Segments +## Segments [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] You can use Segments to display specific content to specific Users. They are used out of the box in the Targeting block in the Page. diff --git a/docs/guide/architecture.md b/docs/guide/architecture.md index df7f28de6d..49199643bf 100644 --- a/docs/guide/architecture.md +++ b/docs/guide/architecture.md @@ -1,3 +1,7 @@ +--- +description: Ibexa DXP architecture is structured in multiple layers connected by APIs. +--- + # Architecture [[= product_name =]] architecture is based on the philosophy to **use APIs** that will be maintained in the long term. This **makes upgrades easier and provides lossless couplings** between all parts of the architecture, at the same time improving the migration capabilities of the system. @@ -6,26 +10,26 @@ The structure of an [[= product_name =]] app is based on the Symfony framework but content management functions rely on the Public API. Other applications integrate with [[= product_name =]] via REST API, which also relies on the Public API. -![Architecture](img/ez_platform_architecture.png "Architecture") +![Architecture](img/architecture.png "Architecture") The architecture of [[= product_name =]] is layered and uses clearly defined APIs between the layers. |Layer|Description| |-----|-----------| -|[Admin UI](config_back_office.md)|Admin UI contains all the necessary parts to run the [[= product_name =]] Back Office interface.| -|[HTTP Cache](http_cache.md)|Symfony HTTP cache is used to manage content "view" cache with an expiration model. In addition it is extended by using FOSHttpCache to add several advanced features.| -|[Controllers](controllers.md)|Controllers created by you to read information from a Request object, create and return a Response objects.| -|[Twig templates](twig_functions_reference.md)|Set of custom and built-in Twig templates. User interfaces are developed using the Twig template engine and query the Public API directly.| -|[REST API v2](../api/rest_api_guide.md)|The REST API v2 enables you to interact with an [[= product_name =]] installation using the HTTP protocol, following a REST interaction model.| +|[Back Office](../extending/config_back_office.md)|Back Office contains all the necessary parts to run the [[= product_name =]] Back Office interface.| +|[HTTP Cache](cache/http_cache.md))|Symfony HTTP cache is used to manage content "view" cache with an expiration model. In addition it is extended by using FOSHttpCache to add several advanced features.| +|[Controllers](content_rendering/queries_and_controllers/controllers.md)|Controllers created by you to read information from a Request object, create and return a Response objects.| +|[Twig templates](content_rendering/twig_function_reference/twig_functions_reference.md)|Set of custom and built-in Twig templates. User interfaces are developed with the Twig template engine and query the Public API directly.| +|[REST API v2](../api/rest_api_usage.md)|The REST API v2 enables you to interact with an [[= product_name =]] installation using the HTTP protocol, following a REST interaction model.| |[GraphQL](../api/graphql.md)|GraphQL for [[= product_name =]] exposes the domain model using the Repository, based on Content Type groups, Content Types and Field definitions.| |[Public API](../api/public_php_api.md)|Public API exposes the Repository which enables you to create, read, update, manage and delete all objects available in [[= product_name =]].| |Business Logic|The business logic is defined in the kernel. This business logic is exposed to applications via an API. It is used to organize development of the user interface layer.| |[SPI](repository.md#spi)|Service Provider Interface which defines contracts for implementing various parts of the system, including persistence layer (`SPI\Persistence`), custom Field Types, custom Limitations, etc.| |[Persistence cache](persistence_cache.md)|The implementation of SPI\Persistence that decorates the main backend implementation.| |[Search](search/search.md)|Search API that allows both full-text search and querying the content.| -|[SQL Storage Engine](search/search_engines.md#legacy-search-engine-bundle)|Legacy search engine is SQL-based and uses Doctrine's database connection.| +|[SQL Storage Engine](search/search.md#legacy-search-engine)|Legacy search engine is SQL-based and uses Doctrine's database connection.| |[Solr Storage Engine](search/solr.md)|Transparent drop-in replacement for the SQL-based Legacy search engine.| -|[IO](file_management.md#native-io-handler)|The IO API is organized around two types of handlers, both used by the IOService.| +|[IO](file_management/file_management.md#native-io-handler)|The IO API is organized around two types of handlers, both used by the IOService.| |[IO Handler](clustering.md#dfs-io-handler)|The IO Handler manipulates metadata, making up for the potential inconsistency of network-based filesystems.| |[Recommendation](personalization/recommendation_client.md#enabling-recommendations)|Recommendation API.| |[Recommendation Engine](personalization/recommendation_client.md#enabling-recommendations)|Recommendation Engine allows displaying recommendations on your website.| diff --git a/docs/guide/back_office_translations.md b/docs/guide/back_office_translations.md index e107ab399e..588bf471f8 100644 --- a/docs/guide/back_office_translations.md +++ b/docs/guide/back_office_translations.md @@ -1,3 +1,7 @@ +--- +description: The language of the Back Office is selected automatically based on browser language, or you can choose it manually in user settings. +--- + # Back Office translations ## Changing Back Office languages @@ -28,7 +32,7 @@ If your browser language is set to French, the Back Office will be displayed in Then, run `composer run post-update-cmd` and clear the cache. -##### Contributing Back Office translations +### Contributing Back Office translations To learn how to contribute to a translation, see [Contributing translations](../community_resources/translations.md). diff --git a/docs/guide/backup.md b/docs/guide/backup.md index 88c9563553..37ac2ecf92 100644 --- a/docs/guide/backup.md +++ b/docs/guide/backup.md @@ -1,6 +1,10 @@ +--- +description: Periodically back up your Repository information by making a database backup. +--- + # Backup -You should always make sure that your solution is properly backed up. The following example shows you how to do this on a Linux-UNIX-based system where [[= product_name =]] is using a MySQL database called "example". You should shut down Platform if it's running before making a backup or an upgrade. +You should always make sure that your solution is properly backed up. The following example shows you how to do this on a Linux-UNIX-based system. You should shut down the DXP if it's running before making a backup. !!! note "Externally stored assets" @@ -29,10 +33,10 @@ mysqldump -u --add-drop-table > db_backup.sql pg_dump -c --if-exists > db_backup.sql ``` -4\. In parent directory create a tar archive of the files (including the DB dump) using the "tar" command: +4\. In parent directory create a tar archive of the files (including the database dump) using the "tar" command: ``` tar cfz backup_of_ezplatform.tar.gz ezplatform ``` -At this point, the file `backup_of_ezplatform.tar.gz` should contain a backup of DB and files. +At this point, the file `backup_of_ezplatform.tar.gz` should contain a backup of database and files. diff --git a/docs/guide/basket/basket.md b/docs/guide/basket/basket.md index f202677f05..c4b0b8b189 100644 --- a/docs/guide/basket/basket.md +++ b/docs/guide/basket/basket.md @@ -1,4 +1,11 @@ -# Basket [[% include 'snippets/commerce_badge.md' %]] +--- +description: The basket functionality covers the shopping basket, as well as wishlist and stored, named baskets. +edition: commerce +--- + +# Basket + +## Overview ![](../img/basket_1.png) @@ -11,8 +18,8 @@ A basket can be of one of the following types: - `basket` - [`quickOrder`](../quick_order/quick_order.md) -- [`storedBasket`](wishlist_and_stored_baskets.md#basket-type) -- [`wishList`](wishlist_and_stored_baskets.md#basket-type) +- [`storedBasket`](wishlist_and_stored_baskets.md) +- [`wishList`](wishlist_and_stored_baskets.md) - [`comparison`](../product_comparison/product_comparison.md) A standard basket (type `basket`) can have different states during the checkout process. After the order is sent, the basket is assigned the state `ordered`. @@ -25,3 +32,53 @@ Apart from the identifying information, the [basket data model](basket_api/baske - basket lines with information for each row of the basket - information about basket parties (buyer, invoice and delivery) - additional costs (such as shipping, packaging, discounts etc.) + +## Basket configuration + +### Basket storage time + +The time for which a basket is stored depends on whether the basket belongs to an anonymous user or a logged-in user. + +A basket for a logged-in customer is stored forever. + +A basket for an anonymous user is stored for 120 hours by default. +You can configure a different value: + +``` yaml +ibexa.commerce.site_access.config.basket.default.validHours: 120 +``` + +You can use the `ibexa:commerce:clear-baskets` command to delete expired baskets: + +``` bash +php bin/console ibexa:commerce:clear-baskets +``` + +It deletes all baskets from the database that are older than `validHours`. + +For example: + +``` bash +php bin/console ibexa:commerce:clear-baskets 720 +``` + +### Product quantity validation + +You can configure the minimum and maximum quantity that can be ordered per basket line: + +``` yaml +ibexa.commerce.basket.basketline_quantity_max: 1000000 +ibexa.commerce.basket.basketline_quantity_min: 1 +``` + +If the quantity is more than the maximum or less than the minimum, it is set to either max or min. + +### Shared baskets + +A basket can be shared if a user logs in from a different browser (default), or it can be bound to the session. + +If you do not want the basket to be shared between different sessions, change the following setting to `true`: + +``` yaml +ibexa.commerce.site_access.config.basket.default.basketBySessionOnly: true +``` \ No newline at end of file diff --git a/docs/guide/basket/basket_api/basket_data_model.md b/docs/guide/basket/basket_api/basket_data_model.md index 36bbf32e46..25ef7d04f3 100644 --- a/docs/guide/basket/basket_api/basket_data_model.md +++ b/docs/guide/basket/basket_api/basket_data_model.md @@ -1,4 +1,8 @@ -# Basket data model [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Basket data model The basket consists of two elements: diff --git a/docs/guide/basket/basket_api/basket_events.md b/docs/guide/basket/basket_api/basket_events.md index fdd6219da1..431fc1a090 100644 --- a/docs/guide/basket/basket_api/basket_events.md +++ b/docs/guide/basket/basket_api/basket_events.md @@ -1,4 +1,8 @@ -# Basket events [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Basket events ## Basket event listeners diff --git a/docs/guide/basket/basket_api/basket_routing.md b/docs/guide/basket/basket_api/basket_routing.md index 939a57e795..66296388a6 100644 --- a/docs/guide/basket/basket_api/basket_routing.md +++ b/docs/guide/basket/basket_api/basket_routing.md @@ -1,4 +1,8 @@ -# Basket routing [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Basket routing The basket provides the following routes: diff --git a/docs/guide/basket/basket_api/basketservice.md b/docs/guide/basket/basket_api/basketservice.md index 580b03a12f..9517fb8762 100644 --- a/docs/guide/basket/basket_api/basketservice.md +++ b/docs/guide/basket/basket_api/basketservice.md @@ -1,4 +1,8 @@ -# BasketService [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# BasketService The ID of the service is `silver_basket.basket_service`. diff --git a/docs/guide/basket/basket_configuration.md b/docs/guide/basket/basket_configuration.md index b349f2c2e7..0345de2c04 100644 --- a/docs/guide/basket/basket_configuration.md +++ b/docs/guide/basket/basket_configuration.md @@ -1,4 +1,8 @@ -# Basket configuration [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Basket configuration ## Additional data in the basket line @@ -30,10 +34,10 @@ You can configure a different value: ses_basket.default.validHours: 120 ``` -You can use the `silversolutions:baskets:clear` command to delete anonymous expired baskets: +You can use the `ibexa:commerce:clear-baskets` command to delete anonymous expired baskets: ``` bash -php bin/console silversolutions:baskets:clear +php bin/console ibexa:commerce:clear-baskets ``` It deletes all anonymous baskets from the database that are older than `validHours`. @@ -41,7 +45,7 @@ It deletes all anonymous baskets from the database that are older than `validHou For example: ``` bash -php bin/console silversolutions:baskets:clear 720 +php bin/console ibexa:commerce:clear-baskets 720 ``` ## Discontinued products diff --git a/docs/guide/basket/basket_templates.md b/docs/guide/basket/basket_templates.md index afe98aa884..065925bd82 100644 --- a/docs/guide/basket/basket_templates.md +++ b/docs/guide/basket/basket_templates.md @@ -1,4 +1,8 @@ -# Basket templates [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Basket templates ## Template list diff --git a/docs/guide/basket/calculating_prices.md b/docs/guide/basket/calculating_prices.md index 8e47457ab7..7e5c1dbd7d 100644 --- a/docs/guide/basket/calculating_prices.md +++ b/docs/guide/basket/calculating_prices.md @@ -1,7 +1,11 @@ -# Calculating prices [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Calculating prices The basket recalculates the prices after each change of its content. -The request to the price engine is sent when the basket is stored. +The request to the [price engine](../pricing/price_engine.md) is sent when the basket is stored. This avoids the price engine being triggered several times (for example, if more than one product has been updated or added). The prices provided by the price engine (and ERP) are stored in the basket for each line. diff --git a/docs/guide/basket/wishlist_and_stored_baskets.md b/docs/guide/basket/wishlist_and_stored_baskets.md index c0a1bd175c..f45ed3966e 100644 --- a/docs/guide/basket/wishlist_and_stored_baskets.md +++ b/docs/guide/basket/wishlist_and_stored_baskets.md @@ -1,4 +1,9 @@ -# Wishlist and stored baskets [[% include 'snippets/commerce_badge.md' %]] +--- +description: The customer can store products in a wishlist, or in a number of stored, named baskets, for future purchasing. +edition: commerce +--- + +# Wishlist and stored baskets The logged-in customer can save selected products in a list and easily access them or add them to the shopping basket. diff --git a/docs/guide/bestsellers.md b/docs/guide/bestsellers.md index e6eef476df..32729b99e3 100644 --- a/docs/guide/bestsellers.md +++ b/docs/guide/bestsellers.md @@ -1,4 +1,8 @@ -# Bestsellers [[% include 'snippets/commerce_badge.md' %]] +--- +description: The bestseller functionality calculates the best-selling products in the catalog. +--- + +# Bestsellers Bestsellers are determined based on all confirmed orders. The information how often a product was purchased is stored in Solr by a Solr plugin. The shop owner can specify from which point a product counts as a bestseller. diff --git a/docs/guide/breadcrumbs/breadcrumbs.md b/docs/guide/breadcrumbs/breadcrumbs.md index e0c3c34d88..1a5f16d456 100644 --- a/docs/guide/breadcrumbs/breadcrumbs.md +++ b/docs/guide/breadcrumbs/breadcrumbs.md @@ -1,6 +1,6 @@ -# Breadcrumbs [[% include 'snippets/commerce_badge.md' %]] +# Breadcrumbs -[[= product_name_com =]] automatically generates breadcrumbs for every part of the shop, including the catalog, +[[= product_name =]] automatically generates breadcrumbs for every part of the shop, including the catalog, internal shop routes (e.g. my profile, search), Content items and forms. ![](../img/breadcrumbs_1.png) diff --git a/docs/guide/breadcrumbs/breadcrumbs_api.md b/docs/guide/breadcrumbs/breadcrumbs_api.md index 38d78f48c2..3489fa253a 100644 --- a/docs/guide/breadcrumbs/breadcrumbs_api.md +++ b/docs/guide/breadcrumbs/breadcrumbs_api.md @@ -1,4 +1,4 @@ -# Breadcrumb API [[% include 'snippets/commerce_badge.md' %]] +# Breadcrumb API The controller method `BreadcrumbsController::renderBreadcrumbsAction()` uses the `BreadcrumbsAggregateGenerator` to render the breadcrumbs from the controller. diff --git a/docs/guide/breadcrumbs/breadcrumbs_templates.md b/docs/guide/breadcrumbs/breadcrumbs_templates.md index fbd2b04bb2..cf026bfe7c 100644 --- a/docs/guide/breadcrumbs/breadcrumbs_templates.md +++ b/docs/guide/breadcrumbs/breadcrumbs_templates.md @@ -1,4 +1,4 @@ -# Breadcrumb templates [[% include 'snippets/commerce_badge.md' %]] +# Breadcrumb templates ## Template list diff --git a/docs/guide/breadcrumbs/custom_breadcrumbs.md b/docs/guide/breadcrumbs/custom_breadcrumbs.md index 04d9c4dd02..0c500b666d 100644 --- a/docs/guide/breadcrumbs/custom_breadcrumbs.md +++ b/docs/guide/breadcrumbs/custom_breadcrumbs.md @@ -1,4 +1,8 @@ -# Custom breadcrumbs [[% include 'snippets/commerce_badge.md' %]] +--- +description: Customize breadcrumb rendering in shop front page. +--- + +# Custom breadcrumbs ## Breadcrumbs for custom routes diff --git a/docs/guide/bundles.md b/docs/guide/bundles.md index a7df44a310..319df5383a 100644 --- a/docs/guide/bundles.md +++ b/docs/guide/bundles.md @@ -1,3 +1,7 @@ +--- +description: Ibexa DXP is composed of bundles containing different parts of the application. +--- + # Bundles A bundle in Symfony (and [[= product_name =]]) is a separate part of your application that implements a feature. @@ -5,93 +9,97 @@ You can create bundles yourself or make use of available open-source bundles. You can also reuse the bundles you create in other projects or share them with the community. Many [[= product_name =]] functionalities are provided through separate bundles included in the installation. -You can see the bundles that are automatically installed with [[= product_name =]] in [composer.json](https://github.com/ezsystems/ezplatform/blob/3.0/composer.json). +You can see the bundles that are automatically installed with [[= product_name =]] +in the respective `composer.json` files. +For example, for Ibexa Content, see the [JSON file on GitHub](https://github.com/ibexa/content/blob/master/composer.json). ## Working with bundles All bundles containing built-in [[= product_name =]] functionalities are installed automatically. -Additionally, you can install community-developed bundles from [[[= product_name =]] Packages.](https://ezplatform.com/packages) +Additionally, you can install community-developed bundles from [[[= product_name =]] Packages.](https://developers.ibexa.co/packages) -To learn how to create your own bundles, see [Symfony documentation on bundles.](https://symfony.com/doc/5.0/bundles.html) +To learn how to create your own bundles, see [Symfony documentation on bundles.]([[= symfony_doc =]]/bundles.html) ### Overriding third-party bundles When you use an external bundle, you can override its parts, such as templates, controllers, etc. -To do so, make use of [Symfony's bundle override mechanism](https://symfony.com/doc/5.0/bundles/override.html). +To do so, make use of [Symfony's bundle override mechanism]([[= symfony_doc =]]/bundles/override.html). Note that when overriding files, the path inside your application has to correspond to the path inside the bundle. ### Removing bundles To remove a bundle (either one you created yourself, or an out-of-the-box one that you do not need), -see the [How to Remove a Bundle](http://symfony.com/doc/5.0/bundles/remove.html) instruction in Symfony doc. +see the [How to Remove a Bundle]([[= symfony_doc =]]/bundles/remove.html) instruction in Symfony doc. -## Built-in bundles +## Core packages -The following tables give an overview of the main [[= product_name =]] bundles. +!!! tip -### Core bundles + Ibexa Open Source is composed of the core packages. |Bundle|Description| |---------|-----------| -|[ezplatform-kernel](https://github.com/ezsystems/ezplatform-kernel)|contains the core of the whole [[= product_name =]] application e.g. EzPublishCoreBundle| -|[ezplatform-content-forms](https://github.com/ezsystems/ezplatform-content-forms)|provides form-based integration for the Symfony Forms into Content and User objects in kernel| -|[ezplatform-solr-search-engine](https://github.com/ezsystems/ezplatform-solr-search-engine)|[Solr-powered](http://lucene.apache.org/solr/) search handler for [[= product_name =]]| -|[ez-support-tools](https://github.com/ezsystems/ez-support-tools)|provides functionality for system information| -|[ezplatform-http-cache](https://github.com/ezsystems/ezplatform-http-cache)|HTTP cache handling for [[= product_name =]], using multi tagging (incl Varnish xkey)| -|[ezplatform-admin-ui](https://github.com/ezsystems/ezplatform-admin-ui)|contains Back Office interface for [[= product_name =]] v2+| -|[ezplatform-admin-ui-assets](https://github.com/ezsystems/ezplatform-admin-ui-assets)|contains assets for AdminUI| -|[ezplatform-design-engine](https://github.com/ezsystems/ezplatform-design-engine)|design fallback system for [[= product_name =]] similar to legacy design fallback system| -|[ezplatform-standard-design](https://github.com/ezsystems/ezplatform-standard-design)|defines standard Design and Theme to be handled by ezplatform-design-engine| -|[ezplatform-cron](https://github.com/ezsystems/ezplatform-cron)|exposes cron/cron package for use in [[= product_name =]] (or just plain Symfony) via a simple command `ezplatform:cron:run`| -|[ezplatform-graphql](https://github.com/ezsystems/ezplatform-graphql)|defines GraphQL server for [[= product_name =]]| -|[ezplatform-matrix-fieldtype](https://github.com/ezsystems/ezplatform-matrix-fieldtype)|dedicated to Matrix Field Type for [[= product_name =]], it replaces previous version found on `ezcommunity/EzMatrixFieldTypeBundle`| -|[ezplatform-query-fieldtype](https://github.com/ezsystems/ezplatform-query-fieldtype)|Field Type that lists Content items based by querying the Repository| -|[ezplatform-rest](https://github.com/ezsystems/ezplatform-rest)|contains REST API| -|[ezplatform-richtext](https://github.com/ezsystems/ezplatform-richtext)|Field Type for supporting rich formatted text stored in a structured XML format| -|[ezplatform-user](https://github.com/ezsystems/ezplatform-user)|dedicated to [[= product_name =]] User management.| -|[BehatBundle](https://github.com/ezsystems/BehatBundle)|common reusable sentence implementations and other common needs for Behat testing in bundles/projects| - -[[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +|[ezsystems/ez-support-tools](https://github.com/ezsystems/ez-support-tools)|System information| +|[ezsystems/ezplatform-admin-ui-assets](https://github.com/ezsystems/ezplatform-admin-ui-assets)|Assets for the Back Office| +|[ezsystems/ezplatform-admin-ui](https://github.com/ezsystems/ezplatform-admin-ui)|Back Office interface| +|[ezsystems/ezplatform-content-forms](https://github.com/ezsystems/ezplatform-content-forms)|Form-based integration for the Symfony Forms into Content and User objects in kernel| +|[ezsystems/ezplatform-core](https://github.com/ezsystems/ezplatform-core)|Core system functionalities| +|[ezsystems/ezplatform-cron](https://github.com/ezsystems/ezplatform-cron)|Cron package for use with the `ezplatform:cron:run` command| +|[ezsystems/ezplatform-design-engine](https://github.com/ezsystems/ezplatform-design-engine)|[Design fallback system](content_rendering/design_engine/design_engine.md)| +|[ezsystems/ezplatform-graphql](https://github.com/ezsystems/ezplatform-graphql)|GraphQL server for [[= product_name =]]| +|[ezsystems/ezplatform-http-cache](https://github.com/ezsystems/ezplatform-http-cache)|[HTTP cache handling](../guide/cache/http_cache.md), using multi tagging| +|[ezsystems/ezplatform-kernel](https://github.com/ezsystems/ezplatform-kernel)|Core of the [[= product_name =]] application| +|[ezsystems/ezplatform-matrix-fieldtype](https://github.com/ezsystems/ezplatform-matrix-fieldtype)|[Matrix Field Type](../api/field_types_reference/matrixfield.md)| +|[ezsystems/ezplatform-query-fieldtype](https://github.com/ezsystems/ezplatform-query-fieldtype)|[Query Field Type](../api/field_types_reference/contentqueryfield.md)| +|[ezsystems/ezplatform-rest](https://github.com/ezsystems/ezplatform-rest)|REST API| +|[ezsystems/ezplatform-richtext](https://github.com/ezsystems/ezplatform-richtext)|Field Type for supporting rich-formatted text stored in a structured XML format| +|[ezsystems/ezplatform-search](https://github.com/ezsystems/ezplatform-search)|Common search functionalities| +|[ezsystems/ezplatform-solr-search-engine](https://github.com/ezsystems/ezplatform-solr-search-engine)|[Solr-powered](http://lucene.apache.org/solr/) search handler| +|[ezsystems/ezplatform-standard-design](https://github.com/ezsystems/ezplatform-standard-design)|Standard design and theme to be handled by `ezplatform-design-engine`| +|[ezsystems/ezplatform-user](https://github.com/ezsystems/ezplatform-user)|User management| + +## Ibexa Content packages |Bundle|Description| |---------|-----------| -|date-based-publisher|provides the date based publishing functionality for [[= product_name_exp =]]| -|ezplatform-workflow|implementation of a collaboration feature that lets you send content draft to any user for a review or rewriting| -|ezplatform-page-fieldtype|Page handling Field Type| -|ezplatform-page-builder|contains [[= product_name_exp =]] Page editor| -|ezplatform-ee-installer|provides `ezplatform:install` Symfony console command which is the installer for [[= product_name_exp =]] v2| -|ezplatform-http-cache-fastly|extends ezplatform-http-cache to support Fastly, for use on Platform.sh PE or standalone| -|ezplatform-calendar|extends the Back Office by adding the calendar tab with the calendar widget| -|ezplatform-version-comparison|allows comparing between two versions of the same Field| -|ezplatform-form-builder|enables creating Form Content items with multiple form fields| -|ezplatform-site-factory|enables configuration of sites from UI| -|ezplatform-elastic-search-engine|provides integration with Elasticsearch search engine | - -### Optional bundles +|ezsystems/date-based-publisher|Date-based publishing functionality| +|ezsystems/ezcommerce-base-design|Standard design and theme for the shop| +|ezsystems/ezcommerce-checkout|Shop checkout functionality| +|ezsystems/ezcommerce-fieldtypes|Shop-specific Field Types| +|ezsystems/ezcommerce-price-engine|Engine for handling prices| +|ezsystems/ezcommerce-shop-ui|UI for the shop front page| +|ezsystems/ezcommerce-shop|Main shop functionalities| +|ezsystems/ezplatform-calendar|Calendar tab with a calendar widget| +|ezsystems/ezplatform-connector-dam|Connector for DAM (Digital Asset Management) systems| +|ezsystems/ezplatform-elastic-search-engine|Integration with Elasticsearch search engine| +|ezsystems/ezplatform-http-cache-fastly|Fastly support for `ezplatform-http-cache`, for use on Platform.sh or standalone| +|ezsystems/ezplatform-icons|Icon set for the Back Office| +|ezsystems/ezplatform-personalization|Functionality for personalized recommendations| +|ezsystems/ezplatform-version-comparison|Enables comparing between two versions of the same Field| +|ezsystems/ezplatform-workflow|Collaboration feature that enables you to send content draft to any user for a review or rewriting| +|ezsystems/ezrecommendation-client|Client for connecting with the personalization engine| +|ibexa/image-editor|[Image Editor](images/image_editor.md)| +|ibexa/installer|Provides the `ibexa:install` command| +|ibexa/migrations|[Migration of Repository data](data_migration/data_migration.md)| +|ibexa/oauth2-client|Integration with [`knpuniversity/oauth2-client-bundle`](https://github.com/knpuniversity/oauth2-client-bundle)| + +## Ibexa Experience packages |Bundle|Description| |---------|-----------| -|[ezplatform-i18n](https://github.com/ezsystems/ezplatform-i18n)|centralized internationalization| -|[ezplatform-multi-file-upload](https://github.com/ezsystems/ezplatform-multi-file-upload)|allows uploading multiple files as new content items at once| -|[ezplatform-demo-assets](https://github.com/ezsystems/ezplatform-demo-assets)|contains binary install data for ezsystems/ezplatform-demo| - -[[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -|Bundle|Description| -|---------|-----------| -|ezstudio-personalized-block|Ibexa Personalized Block Bundle| +|ezsystems/ezplatform-form-builder|Enables creating Form Content items with multiple form fields| +|ezsystems/ezplatform-page-builder|Page editor| +|ezsystems/ezplatform-page-fieldtype|Page handling Field Type| +|ezsystems/ezplatform-permissions|Additional permission functionalities| +|ezsystems/ezplatform-segmentation|Segment functionality for profiling the content displayed to specific users| +|ezsystems/ezplatform-site-factory|Enables configuration of sites from UI| -### Education +## Ibexa Commerce packages |Bundle|Description| -|------|-----------| -|[ezplatform-com](https://github.com/ezsystems/ezplatform-com)|the Ibexa Developer Hub for [[= product_name =]] (example site)| -|[ezplatform-ee-beginner-tutorial](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial)|resources used in the [[= product_name_exp =]] Beginner Tutorial| -|[docker-php](https://github.com/ezsystems/docker-php)|contains PHP docker image example| - -### Documentation - additional resources - -|Repository|Description| -|------|-----------| -|[developer-documentation](https://github.com/ezsystems/developer-documentation)|source for the developer documentation for [[= product_name =]], an open source CMS based on the Symfony Full Stack Framework in PHP. https://doc.ezplatform.com| -|[user-documentation](https://github.com/ezsystems/user-documentation)|source for the user documentation for [[= product_name =]], an open source CMS based on the Symfony Full Stack Framework in PHP| +|---------|-----------| +|ezsystems/ezcommerce-admin-ui|Shop-related Back Office functionalities| +|ezsystems/ezcommerce-erp-admin|ERP connection for the shop| +|ezsystems/ezcommerce-order-history|[Order history](order_history/order_history.md) functionality| +|ezsystems/ezcommerce-page-builder|Shop-related Page blocks| +|ezsystems/ezcommerce-transaction|Transactional shop functionalities| diff --git a/docs/guide/cache/basketpreview_cache_and_user_specific_data.md b/docs/guide/cache/basketpreview_cache_and_user_specific_data.md deleted file mode 100644 index 341259e3b4..0000000000 --- a/docs/guide/cache/basketpreview_cache_and_user_specific_data.md +++ /dev/null @@ -1,156 +0,0 @@ -# Basketpreview cache and user-specific data [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] uses a lot of dynamic data which has to be displayed in the shop: - -- basket preview shows the number of products and a list of products -- the name of the customer -- the number of items in the wishlist - -To improve the caching and speed up page loading, a special route is used to provide the dynamic data using a REST call. - -This enables you to cache pages in a more efficient way and place dynamic attributes with JavaScript. - -## The REST route - -The route `silversolutions_session_data` is used to send a call to the server to get the updated data for the dynamic attributes. - -By default the following attributes are returned: - -![](../img/basketpreview_1.png) - -The response is cached via HTTP cache. It is purged whenever a basket is updated or a customer logs in. -After page load JavaScript fetches the current data from the server and updates: - -- the logged-in user -- the basket preview - -![](../img/basketpreview_2.png) - -### JS Event - -An event is trigged after the data has been received via REST: - -``` -new CustomEvent("ses:dynamic-data", { "detail": response.data }); -``` - -You can subscribe to the event inside a vue app using `this.$on`: - -``` -document.addEventListener("ses:dynamic-data", function(e) { - console.log("ses:dynamic-data"); - console.log(e.detail); // Prints "meta and modules data" -}); -``` - -## Extending the REST call - -The modules section can be used for the project-specific data. It uses a tagged service that has to implement a service via service tag: - -``` - -``` - -The alias `mydata` is used as a key in the modules section. - -### Working with HTML fragments - -Each service implementing `SessionDataInterface` can return HTML fragments. -The keys should use the ID of a container inside your site. [[= product_name_com =]] automatically replaces this container with the indicated HTML. - -Example for providing the data in a service: - -``` php -/** - * Get header login session data - * - * @return \Symfony\Component\HttpFoundation\Response - */ -public function getSessionData() -{ - return array( - 'loginHeader' => array( - 'html' => array( - 'headerLoginMobile' => $this->getLoginData(true), - 'headerLoginDesktop' => $this->getLoginData(false) - ) - ) - ); -} -``` - -The corresponding div in the website needs to use the key `headerLoginDesktop`, e.g. as an ID: - -``` -
      -... -
    -``` - -## Updating existing projects - -1\. Allow accessing cookies via JS: - -``` -ezpublish: - system: - my_siteaccess: - session: - cookie_httponly: false -``` - -2\. Add `vue.js` in `pagelayout.html.twig` within the header: - -``` - -``` - -3\. Add the `routeconfig` to the `pagelayout.html.twig` at the beginning of the `javascripts` block: - -``` html+twig -{% block javascripts %} - - -``` - -4\. Add three JavaScript files to `pagelayout.html.twig`: - -``` html+twig -// If you plan to support the old IE as well: - - -{% javascripts -... - 'bundles/silversolutionseshop/js/vue/md5.js' - 'bundles/silversolutionseshop/js/vue/axios.min.js' - 'bundles/silversolutionseshop/js/dynamic-header.js' -.... -``` - -5.\ Change the Twig block `basket_preview` in `pagelayout.html.twig` using a static HTML markup which is replaced by JavaScript after loading the session data: - -``` html+twig -{% block basket_preview %} - {# This block is rendered via JS #} -
    - -
    -
    -
    {{ 'Shopping basket'|st_translate }}
    - -
    - - - - - - -{% endblock %} -``` diff --git a/docs/guide/cache/cache.md b/docs/guide/cache/cache.md deleted file mode 100644 index 967f9acefb..0000000000 --- a/docs/guide/cache/cache.md +++ /dev/null @@ -1,34 +0,0 @@ -# Cache [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] uses different caches, including HTTP cache, which can greatly increase shop performance. -Dynamic parts of the shop such as basket preview or prices are displayed using dynamic caching features such as ESI or JavaScript. - -This ensures that only small parts of a page have to be generated in real time. - -## Usaging ESI-rendered blocks - -|Controller|Purpose|Cache settings| -|--- |--- |--- | -|`SilversolutionsEshopBundle:CustomerProfileData:showHeaderLogin`|Displays information about the logged-in user in the top part of the page|Purged after login/logout and delegate process| -|`SilversolutionsEshopBundle:Basket:showBasketPreview`|Displays a short version of the basket in the top part of the page|Purged when basket changes.
    Tags: `siso_basket_`
    `siso_user_`| -|`SilversolutionsEshopBundle:PageLayout:getFooter`|Footer information shared among all pages|caching strategy `service_menue`| -|`SilversolutionsEshopBundle:Bestsellers:getBestsellersEsi`|Bestseller Box for catalog pages|caching strategy `product_list`| -|`SilversolutionsEshopBundle:Bestsellers:getCategoryBestsellers`
    `SilversolutionsEshopBundle:EzFlow:showLastViewedProducts`|Shows last viewed products e.g. on Landing Page|caching strategy `product_list`| -|`SisoSearchBundle:Search:productList`|Shows the product list for the logged-in user|no caching| -|`SilversolutionsEshopBundle:ProductType:productList`|Product type list page|caching strategy `product_type_children`| -|`SilversolutionsEshopBundle:Basket:showStoredBasketPreview`|User menu: displays a badge with the number of products in stored comparison or the number of stored baskets|caching strategy `basket_preview`
    Purged when basket changes.
    Tags: `siso_basket_`| -|`SilversolutionsEshopBundle:Navigation:showMenu`|Left menu|Tag: `siso_menu`| -|`SilversolutionsEshopBundle:Navigation:showMenu`|Main menu|Tag: `siso_menu`| - -Caching strategies are defined in configuration, see: [HTTP caching](content_cache_refresh/http_caching.md). - -## Usage of cache tags - -[[= product_name_com =]] uses cache tags to tag and purge content. - -|Tag|Used for|Purged| -|--- |--- |--- | -|`siso_basket_`|Basket preview in the header|By event, on basket change| -|`siso_menu`|Main menu and left side menus (product catalog)|| -|`content-`|Textmodules|When content (text modules) are changed| -|`siso_user_`|User-specific data (shows name of the user)|When the user logs in or out| diff --git a/docs/guide/cache/caching_faq.md b/docs/guide/cache/caching_faq.md deleted file mode 100644 index 5d19e12d32..0000000000 --- a/docs/guide/cache/caching_faq.md +++ /dev/null @@ -1,16 +0,0 @@ -# Caching FAQ [[% include 'snippets/commerce_badge.md' %]] - -## Basket preview and the box showing the logged-in user are not up to date - -This can be caused by invalid cache purge configuration. - -Check if the `purge_type` setting in `ezplatform.yml` is set correctly: - -``` yaml -ezpublish: - http_cache: - purge_type: local -``` - -`local` means Symfony proxy is used. -If Varnish or a CDN is used, set `purge_type` to `http`. diff --git a/docs/guide/cache/content_aware_cache.md b/docs/guide/cache/content_aware_cache.md new file mode 100644 index 0000000000..ab69660fe6 --- /dev/null +++ b/docs/guide/cache/content_aware_cache.md @@ -0,0 +1,613 @@ +--- +description: Content-aware HTTP cache takes into account the content it is connected to. +--- + +# Content-aware HTTP cache + +HTTP cache in [[= product_name =]] is aware of which content or entity it is connected to. +This awareness is accomplished by means of cache tagging. All supported reverse proxies are content-aware. + +!!! note "Tag header is stripped in production for security reasons" + + For security reasons this header, and other internal cache headers, + are stripped from output in production by the reverse proxy (in VCL for Varnish and Fastly). + +## Cache tags + +Understanding tags is the key to making the most of `ezplatform-http-cache`. + +Tags form a secondary set of keys assigned to every cache item, on top of the "primary key" which is the URI. +Like an index in a database, a tag is typically used for anything relevant that represents the given cache item. +Tags are used for cache invalidation. + +For example, the system tags every article response, and when the article Content Type is updated, +it tells Varnish that all articles should be considered stale +and updated in the background when someone requests them. + +Current content tags (and when the system purges on them): + +- Content: `c` - Purged on all smaller or larger changes to content (including its metadata, Fields and Locations). +- Content Version: `cv` - Purged when any version of Content is changed (for example, a draft is created or removed). +- Content Type: `ct` - Used when the Content Type changes, affecting content of its type. +- Location: `l` - Used for clearing all cache relevant for a given Location. +- Parent Location: `pl<[parent-]location-id>` - Used for clearing all children of a Location (`pl`), or all siblings (`pl`). +- Path: `p` - For operations that change the tree itself, like move, remove, etc. +- Relation: `r` - Only purged on when content updates are severe enough to also affect reverse relations. +- Relation location: `rl` - Same as relation, but by Location ID. + +!!! note "Automatic repository prefixing of cache tags" + + As [[= product_name =]] supports multi-repository (multi-database) setups that can have overlapping IDs, + the shared HTTP cache systems need to distinguish tags relevant to the different content repositories. + + This is why in multi-repository setup you can see cache tags such as `1p2`. + In this example `1` represents the index among configured repositories, meaning the second repository in the system. + + Tags are not prefixed for default repository (index "0"). + +The content tags are returned in a header in the responses from [[= product_name =]]. The header name is dependent on +which HTTP Cache [[= product_name =]] is configured with: + +- Symfony reverse proxy: `X-Cache-Tags` +- Varnish: `xkey` +- Fastly: `Surrogate-Key` + +Examples: + +- `X-Cache-Tags: ez-all,c52,ct42,l2,pl1,p1,p2,r56,r57` +- `xkey: ez-all c52 ct42 l2 pl1 p1 p2 r56 r57` +- `Surrogate-Key: ez-all c52 ct42 l2 pl1 p1 p2 r56 r57` + +### Troubleshooting - Cache header too long errors + +In case of complex content, for example, Pages with many blocks, or RichText with a lot of embeds/links, +you can encounter problems with too long cache header on responses. +It happens because necessary cache entries may not be tagged properly. +You may also see `502 Headers too long` errors, and webserver refusing to serve the page. + +You can solve this issue in one of the following ways: + +#### A. Allow larger headers + +Varnish configuration: + +- [http_resp_hdr_len](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-resp-hdr-len) (default 8k, change to for example, 32k) +- [http_max_hdr](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-max-hdr) (default 64, change to for example, 128) +- [http_resp_size](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-resp-size) (default 23k, change to for example, 96k) +- [workspace_backend](https://varnish-cache.org/docs/6.0/reference/varnishd.html#workspace-backend) (default 64k, change to for example, 128k) + +If you need to see these long headers in `varnishlog`, adapt the [vsl_reclen](https://varnish-cache.org/docs/6.0/reference/varnishd.html#vsl-reclen) setting. + +Nginx has a default limit of 4k/8k when buffering responses: + +- For [PHP-FPM](https://www.php.net/manual/en/install.fpm.php) setup using proxy module, configure [proxy_buffer_size](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) +- For FastCGI setup using fastcgi module, configure [fastcgi_buffer_size](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffer_size) + +Fastly has a `Surrogate-Key` header limit of 16 kB, and this cannot be changed. + +Apache has a [hard](https://github.com/apache/httpd/blob/5f32ea94af5f1e7ea68d6fca58f0ac2478cc18c5/server/util_script.c#L495) [coded](https://github.com/apache/httpd/blob/7e2d26eac309b2d79e467ef586526c10e0f226f8/include/httpd.h#L299-L303) limit of 8 kB, so if you face this issue consider using Nginx instead. + +#### B. Limit tags header output by system + +1\. For inline rendering just displaying the content name, image attribute, and/or link, it would be enough to: + +- Look into how many inline (non ESI) render calls for content rendering you are doing, and see if you can organize it differently. +- Consider inlining the views not used elsewhere in the given template and [tagging the response in Twig](#response-tagging-in-twig) with "relation" tags. + - (Optional) You can set reduced cache TTL for the given view, to reduce the risk of stale cache on subtree operations affecting the inlined content. + +2\. You can opt in to set a max length parameter (in bytes) and corresponding ttl (in seconds) +for cases when the limit is reached. The system will log a warning where the limit is reached, and when needed, you can optimize +these cases as described above. + +```yaml +parameters: + # Warning, setting this means you risk losing tag information, risking stale cache. Here set below 8k: + ezplatform.http_cache.tags.header_max_length: 7900 + # In order to reduce risk of stale cache issues, you should set a lower TTL here then globally (here set as 2h) + ezplatform.http_cache.tags.header_reduced_ttl: 7200 +``` + +## Response tagging with content view + +For content views response tagging is done automatically, and cache system outputs headers as follows: + +``` +HTTP/1.1 200 OK +Cache-Control: public, max-age=86400 +xkey: ez-all c1 ct1 l2 pl1 p1 p2 +``` + +If the given content has several Locations, you can see several `l` and `p` tags in the response. + +!!! note "How response tagging for ContentView is done internally" + + In `ezplatform-http-cache` there is a dedicated response listener `HttpCacheResponseSubscriber` that checks if: + + - the response has attribute `view` + - the view implements `eZ\Publish\Core\MVC\Symfony\View\CachableView` + - cache is not disabled on the individual view + + If that checks out, the response is adapted with the following: + + - `ResponseCacheConfigurator` applies SiteAccess settings for enabled/disabled cache and default TTL. + - `DispatcherTagger` dispatches the built-in ResponseTaggers which generate the tags as described above. + +### ResponseConfigurator + +A `ReponseCacheConfigurator` configures an HTTP Response object, makes the response public, adds tags and sets the shared max age. +It is provided to `ReponseTaggers` that use it to add the tags to the response. + +The `ConfigurableResponseCacheConfigurator` (`ezplatform.view_cache.response_configurator`) follows the `view_cache` configuration and only enables cache if it is enabled in the configuration. + +### Delegator and Value taggers + +- Delegator taggers - extract another value or several from the given value and pass it on to another tagger. For example, a `ContentView` is covered both by the `ContentValueViewTagger` and `LocationValueViewTagger`, where the first extracts the Content from the `ContentView` and passes it to the `ContentInfoTagger`. +- Value taggers - extract the `Location` and pass it on to the `LocationViewTagger`. + +## DispatcherTagger + +Accepts any value and passes it on to every tagger registered with the service tag `ezplatform.http_response_tagger`. + +## Response tagging in controllers + +For tagging needs in controllers, there are several options, here presented in recommended order: + +1\. Reusing `DispatcherTagger` to pick correct tags. + +Examples for tagging everything needed for content using the autowirable `ResponseTagger` interface: + +``` php +/** @var \EzSystems\PlatformHttpCacheBundle\ResponseTagger\ResponseTagger $responseTagger */ + +// If you have a View object you can simply call: +$responseTagger->tag($view); + +// Or if you have content / Location object only, you can instead provide content info and Location: +$responseTagger->tag($contentInfo); +$responseTagger->tag($location); +``` + +2\. Use `ContentTagInterface` API for content related tags. + +Examples for adding specific content tags using the autowireable `ContentTagInterface`: + +``` php +/** @var \EzSystems\PlatformHttpCacheBundle\Handler\ContentTagInterface $tagHandler */ + +// Example for tagging everything needed for Content: +$tagHandler->addContentTags([$content->id]); +$tagHandler->addLocationTags([$location->id]); +$tagHandler->addParentLocationTags([$location->parentLocationId]); +$tagHandler->addPathTags($location->path); +$tagHandler->addContentTypeTags([$content->getContentType()->id]); + +// Example when using ESI as also shown below using FOS tag handler (there is also a method for relation locations): +$tagHandler->addRelationTags([33, 44]); +``` + +3\. Manually add tags yourself using low-level FOS `TagHandler`. + +In PHP, FOSHttpCache exposes the `fos_http_cache.http.symfony_response_tagger` service which enables you to add tags to a response. + +The following example adds minimal tags when ID 33 and 34 are rendered in ESI, +but parent response needs these tags to get refreshed if they are deleted: + +``` php +/** @var \FOS\HttpCacheBundle\Http\SymfonyResponseTagger $responseTagger */ +$responseTagger->addTags([ContentTagInterface::RELATION_PREFIX . '33', ContentTagInterface::RELATION_PREFIX . '34']); +``` + +See [Tagging from code](https://foshttpcachebundle.readthedocs.io/en/2.8.0/features/tagging.html#tagging-and-invalidating-from-php-code) in FOSHttpCacheBundle doc. + +4\. Use deprecated `X-Location-Id` header. + +For custom or built-in controllers (e.g. REST) still using `X-Location-Id`, `XLocationIdResponseSubscriber` handles translating +this header to tags. It supports singular and comma-separated Location ID value(s): + +```php +/** @var \Symfony\Component\HttpFoundation\Response $response */ +$response->headers->set('X-Location-Id', 123); + +// Alternatively using several Location ID values +$response->headers->set('X-Location-Id', '123,212,42'); +``` + +!!! caution "X-Location-Id use is deprecated" + + `X-Location-Id` is deprecated and will be removed in future. + For rendering content it is advised to refactor to use Content View, + if not applicable `ContentTagInterface` or lastly manually output tags. + +## Response tagging in templates + +1\. `ez_http_cache_tag_location()` + +For full content tagging when inline rendering, use the following: + +``` html+twig +{{ ez_http_cache_tag_location(location) }} +``` + +2\. `ez_http_cache_tag_relation_ids()` or `ez_http_cache_tag_relation_location_ids()` + +When you want to reduce the amount of tags, or the inline content is rendered using ESI, a minimum set of tags can be set: + +``` html+twig +{{ ez_http_cache_tag_relation_ids(content.id) }} + +{# Or using array for several values #} +{{ ez_http_cache_tag_relation_ids([field1.value.destinationContentId, field2.value.destinationContentId]) }} +``` + +3\. `{{ fos_httpcache_tag(['r33', 'r44']) }}` + +As a last resort you can also use the following function from FOS which lets you set low level tags directly: + +``` html+twig +{{ fos_httpcache_tag('r33') }} + +{# Or using array for several values #} +{{ fos_httpcache_tag(['r33', 'r44']) }} +``` + +See [Tagging from Twig Templates](https://foshttpcachebundle.readthedocs.io/en/latest/features/tagging.html#tagging-from-twig-templates) in FOSHttpCacheBundle documentation. + +## Tag purging + +### Default tag purging + +`ezplatform-http-cache` uses Repository API event subscribers to listen to events emitted on Repository operations, +and depending on the operation triggers expiry on a specific tag or set of tags. +All event subscribers can be found in `ezplatform-http-cache/src/EventSubscriber/CachePurge`. + +### Tags purged on publish event + +Below is an example of a Content structure. The tags which the content view controller adds to each location are +also listed + +``` + - [Home] (content-id=52, location-id=2) + ez-all c52 ct42 l2 pl1 p1 p2 + | + - [Parent1](content-id=53, location-id=20) + ez-all c53 ct1 l20 pl2 p1 p2 p20 + | + - [Child](content-id=55, location-id=22) + ez-all c55 ct1 l22 pl20 p1 p2 p20 p22 + - [Parent2](content-id=54, location-id=21) + ez-all c55 ct1 l22 pl2 p1 p2 p22 +``` + +In the event when a new version of `Child` is published, the following keys are purged: + +- `c55`, because Content `[Child]` was changed +- `r55`, because cache for any object that has a relation to Content `[Child]` should be purged +- `l22`, because Location `[Child]` has changed ( that would be location holding content-id=55) +- `pl22`, because cache for children of `[Child]` should be purged +- `rl22`, because cache for any object that has a relation to Location `[Child]` should be purged +- `l20`, because cache for parent of `[Child]` should be purged +- `pl20`, because cache for siblings of `[Child]` should be purged + +In summary, HTTP Cache for any location representing `[Child]`, any Content that relates to the Content `[Child]`, the +location for `[Child]`, any children of `[Child]`, any Location that relates to the Location `[Child]`, location for +`[Parent1]`, any children on `[Parent1]`. +Effectively, in this example HTTP cache for `[Parent1]` and `[Child]` will be cleared. + + +### Tags purged on move event + +With the same Content structure as above, the `[Child]` location is moved below `[Parent2]`. + +The new structure will then be: +``` + - [Home] (content-id=52, location-id=2) + ez-all c52 ct42 l2 pl1 p1 p2 + | + - [Parent1](content-id=53, location-id=20) + ez-all c53 ct1 l20 pl2 p1 p2 p20 + - [Parent2](content-id=54, location-id=21) + ez-all c55 ct1 l22 pl2 p1 p2 p22 + | + - [Child](content-id=55, location-id=22) + ez-all c55 ct1 l22 pl21 p1 p2 p21 p22 +``` + +The following keys will be purged during the move: +- `l20`, because cache for previous parent of `[Child]` should be purged (`[Parent1]`) +- `pl20`, because cache for children of `[Parent1]` should be purged +- `l21`, because cache for new parent of `[Child]` should be purged (`[Parent2]`) +- `pl21`, because cache for all children of new parent (`[Parent2]`) should be purged +- `p22`, because cache for any element below `[Child]` should be purged (because path has changed) + +In other words, HTTP Cache for `[Parent1]`, children of `[Parent1]` ( if any ), `[Parent2]`, children of `[Parent2]` ( if any ), +`[Child]` and any subtree below `[Child]`. + +### Custom purging from code + +While the system purges tags whenever API is used to change data, you may need to purge directly from code. +For that you can use the built-in purge client: + +```php +/** @var \EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface $purgeClient */ + +// Example for purging by Location ID: +$purgeClient->purge([ContentTagInterface::LOCATION_PREFIX . $location->id]); + +// Example for purging all cache for instance for full re-deploy cases, usually this triggers an expiry (soft purge): +$purgeClient->purgeAll(); +``` + +### Purging from command line + +Example for purging by Location and by Content ID: + +```bash +bin/console fos:httpcache:invalidate:tag l44 c33 +``` + +Example for purging by all cache: + +```bash +bin/console fos:httpcache:invalidate:tag ez-all +``` + +!!! tip "Purge is done on the current Repository" + + Similarly to purging from code, the tags you purge on, are prefixed to match the currently configured SiteAccess. + When you use this command in combination with multi-repository setup, make sure to specify SiteAccess argument. + +## Testing and debugging HTTP cache + +It is important to test your code in an environment which is as similar as your production environment as possible. That +means that if only are testing locally using the default Symfony Reverse proxy when your are going to use Varnish or +Fastly in production, you are likely ending up some (bad) surprises. Due to the symfony reverse proxy's lack of support for ESIs, it behaves +quite different from Varnish and Fastly in some aspects. +If you are going to use Varnish in production, make sure you also test your code with Varnish. +If you are going to use Fastly in production, testing with Fastly in your developer install is likely not feasible +(you're local development environment must then be accessible for Fastly). Testing with Varnish instead will in most +cases do the job. But if you need to change the varnish configuration to make your site work, be aware that Varnish and Fastly uses different dialects, and +that .vcl code for Varnish V6.x will likely not work as-is on Fastly. + +This section describes to how to debug problems related to HTTP cache. + In order to that, you must be able to look both at + responses and headers [[= product_name =]] sends to HTTP cache, and not so much at responses and headers + the HTTP cache sends to the client (web browser). + It means you must be able to send requests to your origin (web server) that do not go through Varnish or Fastly. + If you run Nginx and Varnish on premise, you should know what host and port number both Varnish and Nginx runs on. If you + perform tests on Fastly enabled environment on Ibexa Cloud provided by Platform.sh, you need to use the Platform.sh + Dashboard to obtain the endpoint for Nginx. + +The following example shows how to debug and check why Fastly does not cache the front page properly. +If you run the command multiple times: + +`curl -IXGET https://www.staging.foobar.com.us-2.platformsh.site` + +it always outputs: + +``` +HTTP/2 200 +(...) +x-cache: MISS +``` + +### Nginx endpoint on Platform.sh + +#### Finding Nginx endpoint for environments located on the grid + +To find the Nginx point, first, you need to know in which region your project is located. To do that, go to the Platform.sh dashboard. +To find a valid route, click an element in the **URLs** drop-down for the specified environment and select the route. +A route may look like this: +`https://www.staging.foobar.com.us-2.platformsh.site/` + +In this case the region is `us-2` and you can find the public IP list on [Platform.sh documentation page](https://docs.platform.sh/development/public-ips.html) +Typically, you can add a `gw` to the hostname and use nslookup to find it. + +```bash + $ nslookup + > gw.us-2.platformsh.site + (...) + Address: 1.2.3.4 +``` + +You can also use the [Platform.sh CLI command](https://docs.platform.sh/development/cli.html) to find [the endpoint](https://docs.platform.sh/domains/steps/dns.html?#where-should-the-cname-point-to): + +```bash + # Define ibexa_cloud alias if it not already exists: + alias ibexa_cloud='PLATFORMSH_CLI_API_URL=https://api.cloud.ibexa.co PLATFORMSH_CLI_SESSION_ID=ibexa_cloud platform' + # Get the endpoint: + ibexa_cloud environment:info edge_hostname +``` + +#### Finding Nginx endpoint on dedicated cloud + +If you have a dedicated 3-node cluster on Platform.sh, the procedure for getting the endpoint to environments that are +located on that cluster (`production` and sometimes also `staging`) is slightly different. +In the **URLs** drop-down in the Platform.sh dashboard, find the route that has the format +`somecontent.[clusterid].ent.platform.sh/`, for example, `myenvironment.abcdfg2323.ent.platform.sh/` + +The endpoint in case has the format `c.[clusterid].ent.platform.sh`, for example, `c.asddfs2323.ent.platform.sh/` +Next, use nslookup to find the IP: + +```bash + $ nslookup + > c.asddfs2323.ent.platform.sh + (...) + Address: 1.2.3.4 +``` + +### Fetching user context hash + +As explained in [User Context Hash caching](context_aware_cache.md#user-context-hash-caching), the HTTP cache indexes the cache based on the +user-context-hash. Users with the same user-context-hash here the same cache (as long as [[= product_name =]] +responds with `Vary: X-User-Hash`). + +In order to simulate the requests the HTTP cache sends to [[= product_name =]], you need this user-context-hash. +To obtain it, use `curl`. + +```bash + $ curl -IXGET --resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4 --header "Surrogate-Capability: abc=ESI/1.0" --header "accept: application/vnd.fos.user-context-hash" --header "x-fos-original-url: /" https://www.staging.foobar.com.us-2.platformsh.site/_fos_user_context_hash +``` + +Some notes about each of these parameters: +- `-IXGET`, one of many ways to tell curl that we want to send a GET request, but we are only interested in outputting the headers +- `--resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4` + - We tell curl not to do a DNS lookup for `www.staging.foobar.com.us-2.platformsh.site`. We do that because in our case + that will resolve to the Fastly endpoint, not our origin (nginx) + - We specify `443` because we are using `https` + - We provide the IP of the nginx endpoint at platform.sh (`1.2.3.4` in this example) +- `--header "Surrogate-Capability: abc=ESI/1.0"`, strictly speaking not needed when fetching the user-context-hash, but this tells [[= product_name =]] that client understands ESI tags. + It is good practice to always include this header when imitating the HTTP Cache. +- `--header "accept: application/vnd.fos.user-context-hash"` tells [[= product_name =]] that the client wants to receive the user-context-hash +- `--header "x-fos-original-url: /"` is required by the fos-http-cache bundle in order to deliver the user-context-hash +- `https://your-page-blah-blah.us-2.platformsh.site/_fos_user_context_hash` : here we use the hostname we earlier told + curl how to resolve using `---resolve`. `/_fos_user_context_hash` is the route to the controller that are able to + deliver the user-context-hash. +- You may also provide the session cookie (`--cookie ".....=....") for a logged-in-user if you are interested in + the x-user-context-hash for a different user but anonymous + +The output for this command should look similar to this: +``` + HTTP/1.1 200 OK + Server: nginx/1.20.0 + Content-Type: application/vnd.fos.user-context-hash + Transfer-Encoding: chunked + Connection: keep-alive + X-User-Hash: daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814 + Cache-Control: max-age=600, public + Date: Tue, 31 Aug 2021 13:35:00 GMT + Vary: Origin + Vary: cookie + Vary: authorization + X-Cache-Debug: 1 + Surrogate-Key: ez-user-context-hash ez-all fos_http_cache_hashlookup- +``` + +The header `X-User-Hash` is the one of the interest here, but you may also note the `Surrogate-Key` which +holds the [cache tags](#cache-tags). + +### Fetching HTML response + +Now you have the user-context-hash, and you can ask origin for the actual resource you are after: + +```bash + $ curl -IXGET --resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4 --header "Surrogate-Capability: abc=ESI/1.0" --header "x-user-hash: daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814" https://www.staging.foobar.com.us-2.platformsh.site/ +``` + +The output : +``` +HTTP/1.1 200 OK +Server: nginx/1.20.0 +Content-Type: text/html; charset=UTF-8 +Transfer-Encoding: chunked +Connection: keep-alive +Cache-Control: public, s-maxage=86400 +Date: Wed, 01 Sep 2021 07:18:27 GMT +X-Cache-Debug: 1 +Vary: X-User-Hash +Vary: X-Editorial-Mode +Surrogate-Control: content="ESI/1.0" +Surrogate-Key: ez-all c52 ct42 l2 pl1 p1 p2 r56 r57 +``` + +The `Cache-Control` header tells the HTTP cache to store the result in the cache for 1 day (86400 seconds) +The `Vary: X-User-Hash` header tells the HTTP cache that this cache element may be used for all users which has +the given `x-user-hash` (`daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814`). +The document might also be removed from the cache by purging any of the keys provided in the `Surrogate-Key` header. + +So back to the original problem here. This resource is for some reason not cached by Fastly ( remember the +`x-cache: MISS` we started with). But origin says this page can be cached for 1 day. How can that be? +The likely reason is that this page also contains some ESI fragments and that one or more of these are not cachable. + +So, first let's see if there are any ESIs here. We remove the `-IXGET` options (in order to see content of the response, +not only headers) to curl and search for esi: + +```bash + $ curl --resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4 --header "Surrogate-Capability: abc=ESI/1.0" --header "x-user-hash: daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814" https://www.staging.foobar.com.us-2.platformsh.site/ | grep esi +``` + +The output is : + +```HTML + + + +``` + +Now, investigate the response of each of these ESI fragments to understand what is going on. It is important to +put that URL in single quotes as the URLS to the ESIs include special characters that can be interpreted by the +shell. + +#### 1st ESI + +```bash + $ curl -IXGET --resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4 --header "Surrogate-Capability: abc=ESI/1.0" --header "x-user-hash: daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814" 'https://www.staging.foobar.com.us-2.platformsh.site/_fragment?_hash=B%2BLUWB2kxTCc6nc5aEEn0eEqBSFar%2Br6jNm8fvSKdWU%3D&_path=locationId%3D2%26contentId%3D52%26blockId%3D11%26versionNo%3D3%26languageCode%3Deng-GB%26serialized_siteaccess%3D%257B%2522name%2522%253A%2522site%2522%252C%2522matchingType%2522%253A%2522default%2522%252C%2522matcher%2522%253Anull%252C%2522provider%2522%253Anull%257D%26serialized_siteaccess_matcher%3Dnull%26_format%3Dhtml%26_locale%3Den_GB%26_controller%3DEzSystems%255CEzPlatformPageFieldTypeBundle%255CController%255CBlockController%253A%253ArenderAction' +``` + +You can also note that this ESI is handled by a controller in the `EzPlatformPageFieldTypeBundle` bundle provided by [[= product_name =]]. + +The output is: + +``` +HTTP/1.1 200 OK +Server: nginx/1.20.0 +Content-Type: text/html; charset=UTF-8 +Transfer-Encoding: chunked +Connection: keep-alive +Cache-Control: public, s-maxage=86400 +Date: Wed, 01 Sep 2021 07:51:40 GMT +Vary: Origin +Vary: X-User-Hash +Vary: X-Editorial-Mode +X-Cache-Debug: 1 +Surrogate-Key: ez-all c52 l2 +``` + +The headers here look correct and do not indicate that this ESI will not be cached by the HTTP cache +The second ESI has a similar response. + +#### 3rd ESI + +```bash + $ curl -IXGET --resolve www.staging.foobar.com.us-2.platformsh.site:443:1.2.3.4 --header "Surrogate-Capability: abc=ESI/1.0" --header "x-user-hash: daea248406c0043e62997b37292bf93a8c91434e8661484983408897acd93814" 'https://www.staging.foobar.com.us-2.platformsh.site//_fragment?_hash=lnKTnmv6bb1XpaMPWRjV3sNazbn9rDXskhjGae1BDw8%3D&_path=locationId%3D2%26contentId%3D52%26blockId%3D13%26versionNo%3D3%26languageCode%3Deng-GB%26serialized_siteaccess%3D%257B%2522name%2522%253A%2522site%2522%252C%2522matchingType%2522%253A%2522default%2522%252C%2522matcher%2522%253Anull%252C%2522provider%2522%253Anull%257D%26serialized_siteaccess_matcher%3Dnull%26_format%3Dhtml%26_locale%3Den_GB%26_controller%3DEzSystems%255CCustomBundle%255CController%255CFooController%253A%253AcustomAction' +``` + +This ESI is handled by a custom `FooController::customAction` and the output of the command is: + +Output: + +``` +HTTP/1.1 200 OK +Server: nginx/1.20.0 +Content-Type: text/html; charset=UTF-8 +Transfer-Encoding: chunked +Connection: keep-alive +Set-Cookie: eZSESSID21232f297a57a5a743894a0e4a801fc3=asrpqgmh5ll5ssseca3cov8er7; path=/; HttpOnly; SameSite=lax +Cache-Control: public, s-maxage=86400 +Date: Wed, 01 Sep 2021 07:51:40 GMT +Vary: Origin +Vary: X-User-Hash +Vary: X-Editorial-Mode +X-Cache-Debug: 1 +Surrogate-Key: ez-all +``` + +The `Cache-Control` and `Vary` headers look correct. The request is handled by a custom controller and the `Surrogate-Key` only contains the default `ez-all` value. +This is not a problem as long as the controller +does not return values from any Content in the [[= product_name =]] Repository. If it does, the controller should also add +the corresponding IDs to such objects in that header. + +The `Set-Cookie` here may cause the problem. A ESI fragment should never set a cookie because: +- Clients will only receive the headers set in the "mother" document (the headers in the "/" response in this case). + +- Only the content of ESIs responses will be returned to the client. **No headers set in the ESI response will ever reach the client**. ESI headers are only seen by the HTTP cache. + +- Symfony reverse proxy does not support ESIs at all, and any ESI calls (`render_esi()`) will implicitly be replaced by + sub-requests (`render()`). So any `Set-Cookie` **will** be sent to the client when using Symfony reverse proxy. + +- Fastly will flag it resource as "not cachable" because it set a cookie at least once. Even though that endpoint. + stops setting cookies, Fastly will still not cache that fragment. Any document referring to that ESI will be a `MISS`. + Fastly cache needs to be purged (`Purge-all` request) in order to remove this flag. + +- It means that it is not recommended to always initiate a session when loading the front page. + +You must ensure that you do not unintendedly start a session in a controller used by ESIs, for example, when trying to access as session variable before a session has been initiated yet. diff --git a/docs/guide/cache/content_cache_refresh/content_cache_refresh.md b/docs/guide/cache/content_cache_refresh/content_cache_refresh.md deleted file mode 100644 index 381cfee122..0000000000 --- a/docs/guide/cache/content_cache_refresh/content_cache_refresh.md +++ /dev/null @@ -1,203 +0,0 @@ -# Content cache refresh [[% include 'snippets/commerce_badge.md' %]] - -The content cache refreshing system enables displaying the current information when objects in the Back Office are modified. - -The content cache refresh service is used in the following situations: - -- Navigation cache is refreshed for example if one or many navigation nodes are changed -- If a category is changed, the cached names for categories used by the search are updated (`CategoryNameService`) - -!!! note - - You need to have a cron job set up to use this feature. - -## Set up cron job to refresh caches - -To set up a cron job to refresh caches, create a system cron job to run the following console command: - -``` bash -php bin/console silversolutions:cache:refresh -``` - -## Slot implementation - -!!! note - - See [SignalSlot documentation](https://doc.ezplatform.com/en/2.5/guide/signalslots/#signals-reference) for more information about the SignalSlot system. - -The `ContentModificationSlot` pushes content modification data into the queue by using the `ContentModificationQueueService`. - -## Definitions - -Content modification is an array of information about the modified content. - -|Attribute (key)|Value type|Description| -|--- |--- |--- | -|`content_id`|int|Content ID of the modified Content item.| -|`action`|string|Name of the action which resulted in content modification, e.g. `publish` or `delete`.| -|`trigger`|string|Legacy trigger name which caused content modification, e.g. `post_publish`, `pre_delete`. This attribute is created only to provide additional information for content modification processor developers.| - -!!! note - - You can find a list of actions and triggers in [supported content modification actions and triggers](#supported-content-modification-actions-and-triggers) - - Content modification processors can provide additional attributes. - -Content modification queue is a series of content modifications. -It is implemented with Doctrine. - -- `ContentModificationQueue` - Doctrine entity (queue item). -- `ContentModificationQueueRepository` - Doctrine entity repository for queue items. - -`ContentModificationQueueService` is a service that stores content modifications in the queue and runs processors to handle content modifications. - -Content modification processor is a service implemented to process information about content modification (e.g. refreshes related caches). - -## ContentModificationQueueService - -`ContentModificationQueueService` has several responsibilities: - -1. Gathers all services that are tagged with `siso_tools.content_modification_processor` (content modification processors). -1. Fetches all current content modifications from the queue. -1. Gets additional attributes from processors. -1. Pushes content modifications to the queue (table `ses_content_modification_queue`). -1. Passes every content modification object to every processor service. -1. Removes processed content modifications from the queue. - -![](../../img/content_cache_1.png) - -## Content modification processor - -Every content modification processor must implement two actions: - -1. Get content ID and expose additional attributes that the processor can process later. - For example, a navigation cache processor retrieves a Content item's language code from the given content ID. This information is added to content modification data along with initial data. -2. Process content modification data (e.g. clear cache). - -### Content modification processor actions - -Content modifications are stored in the content modification queue immediately after content is changed. -However, processing of the queue doesn't happen immediately. -Shop administrator can process the queue manually with a console script or configure a cron job task. - -It means that if a user removes a Content item, there is no data that the processor can use for clearing the cache. -For this reason, a content modification processor has to store information about the removed Content item before it is processed. - -Content modification processing is usually a resource-consuming operation. Therefore it should be done as a deferred task. - -### Implementing a new content modification processor - -There are two steps to add your own Processor Service to the chain for content cache refresh. - -#### Step 1. Prepare service definition - -Prepare service definition to add a service with the tag `siso_tools.content_modification_processor`. - -``` xml - - - ... - - -``` - -#### Step 2. Create processor service class - -Create a processor service class, which has to implement the `ContentModificationProcessorServiceInterface` interface. - -``` php -interface ContentModificationProcessorServiceInterface -{ - /** - * Processes modified eZ content. - * - * @param ContentModificationQueueItemInterface[] $queue - * @return void - */ - function process($queue); - - /** - * Get an array of attributes to use them later on the process stage. - * - * @param $contentId - * @return array|null - */ - function getContentModificationAttributes($contentId); -} -``` - -#### Example - -To understand how the `getContentModificationAttributes` and `process` methods cooperate, -you can refer to the example of `TransContentModificationProcessorService` -which is responsible for clearing translation caches. - -``` php -function getContentModificationAttributes($contentId) -{ - // 1. Get services - $contentService = $this->repository->getContentService(); - $content = $contentService->loadContent($contentId); - $contentTypeService = $this->repository->getContentTypeService(); - - // 2. Get data to store in attributes - $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); - $languageCode = $content->getVersionInfo()->initialLanguageCode; - - // 3. Define array of attributes - return array( - 'content_type' => $contentType->identifier, - 'language_code' => $languageCode, - ); -} -// ... -public function process($queue) -{ - ... - - foreach($queue as $queueItem) { - - $data = $queueItem->getData(); - - // Content type attribute is requested - if (!isset($data['content_type']) || $data['content_type'] !== self::TRANSLATION_IDENTIFIER) { - continue; - } - - if(in_array($data['action'], array('delete','removetranslation', 'hide'))) { - $this->transService->removeTranslationCache( - ... - // Language code attribute is requested - $data['language_code'] - ); - } - } -} -``` - -!!! note - - Please pay attention that process method uses attributes `language_code` and `content_type` - which were added to content modification array using the `getContentModificationAttributes` method. - -#### Supported content modification actions and triggers - -|Action|Trigger| -|--- |--- | -|`publish`|`post_publish`| -|`delete`|`pre_delete`| -|`hide`|`post_hide`| -|`move`|`post_move`| -|`swap`|`post_swap`| -|`updateobjectstate`|`post_updateobjectstate`| -|`updatepriority`|`post_updatepriority`| -|`addlocation`|`post_addlocation`| -|`removelocation`|`pre_removelocation`| -|`removetranslation`|`pre_removetranslation`| - -### Implemented content modification processor services - -|Service|Purpose| -|--- |--- | -|`NavigationContentModificationProcessorService`|Clears navigation cache| -|`TransContentModificationProcessorService`|Clears translation cache| diff --git a/docs/guide/cache/content_cache_refresh/http_caching.md b/docs/guide/cache/content_cache_refresh/http_caching.md deleted file mode 100644 index 686eceb2c6..0000000000 --- a/docs/guide/cache/content_cache_refresh/http_caching.md +++ /dev/null @@ -1,181 +0,0 @@ -# HTTP caching [[% include 'snippets/commerce_badge.md' %]] - -The central point of HTTP caching is a service called `HttpCachingStrategyService`. -It enables configuring each controller's response separately. - -Another important solution is `Identity\IdentityDefiner` -which has separate caches for different Users or User Groups. This service is optional. - -## Configuration - -First, enable HTTP caching and ESI support and configure services in Symfony. - -Be sure that HTTP caching is enabled in web server virtual host configuration: - -``` -SetEnv USE_HTTP_CACHE 1 -``` - -Next, enable ESI support in Symfony: - -``` yaml -framework: - esi: { enabled: true } - fragments: { path: /_fragment } -``` - -The service configuration is handled in `services.xml`: - -``` xml - - - -``` - -Optionally, configure identity definer service: - -``` xml - - - - -``` - -### Cache blocks configuration - -You need to configure sets of caching parameters (cache blocks) and use the name of cache blocks in controller actions: - -``` php -$response = new Response(); -// Get an instance of HTTP caching strategy -$cachingStrategy = $this->get('siso_core.http_caching.strategy'); - -// Cache response using preconfigured cache block name "product" -$cachingStrategy->defineResponse($response, 'product', $catalogElement->cacheIdentifier); -``` - -All cache blocks are described in the `silver_eshop.default.http_cache` parameter in `parameters.yml`: - -``` yaml -silver_eshop.default.http_cache: - # cache block name - any_cache_block_name: - # Response max age in seconds. Zero means that this response will not be cached. - max_age: 3600 - # Vary is a criteria that allows to have different caches for different users or user groups. - # Possible values: - # ~ : don't vary cache. Let's have one page representation for everybody. - # cookie : each user will have his own cache block for this response - # user-hash : each user group will have its own cache based on IdentityDefiner. - vary: ~ -``` - -The following example configures four different blocks: - -``` yaml -silver_eshop.default.http_cache: - product: - max_age: 3600 - vary: ~ - product_list: - max_age: 3600 - vary: ~ - price_block: - max_age: 0 - header_login: - max_age: 36000 - vary: cookie - basket_preview: - max_age: 36000 - vary: cookie -``` - -## Usage - -To cache responses you need to inject an instance of `HttpCachingStrategyService` -or get this instance from a container. - -Symfony HTTP cache or Varnish use response headers to store the response. - -`defineResponse()` reads parameters from cache block configuration and defines a unique cache -that gives a possibility to purge particular cache blocks matched by this ID. - -Parameters: - -- `Response $response` - response object which is modified -- `string $blockName` - cache block name -- `$id = null` - cache identifier (e.g, basket ID or catalog element ID) - -Example: - -``` php -$cachingStrategy->defineResponse($response, 'product', $catalogElement->cacheIdentifier); -``` - -## Purging caches - -### Console command - -Parameters: - -- IDs - List of space-separated cache identifiers, e.g. "111 222 333". By default the command clears all HTTP caches. - -Example: - -``` bash -php bin/console silversolutions:http-cache:purge 1056 222 --env="prod" -``` - -### Purging using [[= product_name =]] service - -If you programmatically updated product data, basket or some other cached content, -you need to update caches to show users current information. - -To do this you need to inject or get the purger service: - -Example: - -``` - - - -``` - -Next, call the purger service: - -Example: - -``` php -/** - * @param GatewayCachePurger $cachePurger - */ -public function (EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface $cachePurger) -{ - $this->cachePurger = $cachePurger; -} -/** - * @param $basketId - */ -protected function purgeBasketHttpCache($basketId) -{ - if($this->cachePurger){ - $this->cachePurger->purge(['siso_basket' . $basketId]); - } -} -``` - -## Caching in `CatalogController` - -Caching for different kinds of catalog elements is implemented in the `CatalogController`: - -``` php -if ($catalogElement instanceof ProductNode) { - $cachingStrategy->defineResponse($response, 'product', $catalogElement->cacheIdentifier); -} elseif ($catalogElement instanceof ProductType) { - $cachingStrategy->defineResponse($response, 'product_type', $catalogElement->cacheIdentifier); -} elseif ($catalogElement instanceof CatalogElement - && $this->getCustomerProfileDataService()->isUserAnonymous() -) { - $cachingStrategy->defineResponse($response, 'product_list', $catalogElement->cacheIdentifier); -} -``` diff --git a/docs/guide/cache/context_aware_cache.md b/docs/guide/cache/context_aware_cache.md new file mode 100644 index 0000000000..238cc5865c --- /dev/null +++ b/docs/guide/cache/context_aware_cache.md @@ -0,0 +1,208 @@ +--- +description: Context-aware HTTP cache caches requests depending on the logged-in user context. +--- + +# Context-aware HTTP cache + +[[= product_name =]] allows caching requests made by logged-in users. +This is called (user) context-aware cache. + +It means that HTTP cache is unique per set of user permissions (Roles and Limitations), +and there are variations of cache shared only among users that have the exact same permissions. +So if a user browses a list of children Locations, they will only see children Locations +they have access to, even if their rendering is served from HTTP cache. + +This is accomplished by varying on a header called `X-Context-User-Hash`, which the system populates on the request. +The [logic for this](#request-lifecycle) is accomplished in the provided VCL for Varnish and Fastly. +A similar but internal logic is done in the provided enhanced Symfony Proxy (AppCache). + +## Request lifecycle + +This expands steps covered in [FOSHttpCacheBundle documentation on user context feature](https://foshttpcachebundle.readthedocs.io/en/latest/features/user-context.html#how-it-works): + +1. A client (browser) requests URI `/foo`. +1. The caching proxy receives the request and holds it. It first sends a hash request to the application's context hash route: `/_fos_user_context_hash`. +1. The application receives the hash request. An event subscriber (`UserContextSubscriber`) aborts the request immediately after the Symfony firewall is applied. + The application calculates the hash (`HashGenerator`) and then sends a response with the hash in a custom header (`X-Context-User-Hash`). +1. The caching proxy receives the hash response, copies the hash header to the client's original request for `/foo` and restarts the modified original request. +1. If the response to `/foo` should differ per user context, the application sets a `Vary: X-Context-User-Hash` header, which will make Proxy store the variations of this cache varying on the hash value. + +The next time a request comes in from the same user, application lookup for the hash (step 3) does not take place, +as the hash lookup itself is cached by the cache proxy as described below. + +### User context hash caching + +Example of a response sent to reverse proxy from `/_fos_user_context_hash` with [[[= product_name =]]'s default config](#default-options-for-FOSHttpCacheBundle-defined-in-ibexa-dxp): + +``` +HTTP/1.1 200 OK +X-Context-User-Hash: +Content-Type: application/vnd.fos.user-context-hash +Cache-Control: public, max-age=600 +Vary: Cookie, Authorization +``` + +In the example above the response is set to be cached for 10 minutes. +It varies on the `Cookie` header in order to be able to cache it for the given user. +To optimize it, the default VCL strips any cookie other than session cookies to make this work. + +It also varies on `Authorization` to cover any possible basic authorization headers in case that is used over sessions for some requests. + +!!! note "Problems with stale user hash" + + If you notice issues with stale hash usage, before you disable this cache, make sure login or logout always + generates new session IDs and performs a full redirect to make sure no requests are being made using stale + user context hashes. + +!!! caution "Limitations of the user context hash" + + If you use URI-based SiteAccess matching on a multi-repository installation (multiple databases), + the default SiteAccess on the domain needs to point to the same repository (database), + because `/_fos_user_context_hash` is not SiteAccess-aware by default (see + `ezplatform.default_router.non_siteaccess_aware_routes` parameter). + This occurs because reverse proxy does not have knowledge about SiteAccesses + and it does not pass the whole URL to be able to cache the user context hash response. + + The only known workaround is to make it SiteAccess aware, and have custom VCL logic tied to your SiteAccess + matching with Varnish/Fastly, to send the SiteAccess prefix as URI. + +!!! caution "Default options for FOSHttpCacheBundle" + + The following configuration is defined by default for FOSHttpCacheBundle. + You should not override these settings unless you know what you are doing. + + ``` yaml + fos_http_cache: + proxy_client: + default: varnish + varnish: + http: + servers: ['$http_cache.purge_servers$'] + tag_mode: 'purgekeys' + + user_context: + enabled: true + hash_cache_ttl: 600 + # NOTE: These are also defined/used in AppCache, in Varnish VCL, and Fastly VCL + session_name_prefix: eZSESSID + ``` + +## Personalize responses + +Here are some generic recommendations on how to approach personalized content with [[= product_name =]] / Symfony: + +1\. ESI with vary by cookie: + +Default VCL strips everything except session cookie, so this is effectively "per user". +If you are on single-server setup without Varnish or Fastly, you can use the same cookie logic on the web server instead. + +This a low effort solution, and can be enough for one fragment that is reused across the whole site, +for example, in header to show user name: + +Example: + +```php + // Inside a custom controller action, or even a Content View controller + $response->setVary('Cookie'); +``` + +2\. Ajax/JS lookup to "uncached" custom Symfony controllers: + +This method does not consume memory in Varnish. +It can optionally be cached with custom logic: Symfony Cache on server side and/or with client side caching techniques. +This should be done as Ajax/JS lookup to avoid the uncached request that slows down the whole delivery of Vanish if it is done as ESI. + +This solution requires more effort depending on project requirements (traffic load, etc.). + +3\. Custom vary by logic, for example, `X-User-Preference-Hash` inspired by `X-Context-User-Hash`: + +This method allows for fine-grained caching as you can explicitly vary on this in only the places that need it. + +This solution requires more effort (controller, VCL logic and adapting your own code), see the examples below. + +!!! tip "Dealing with paywall use cases" + + If you need to handle a paywall on a per-item basis, or example, do a + lookup to backend for each URL where this is relevant. + + You can find an example for paywall authorization in [FOSHTTPCache documentation.](https://foshttpcache.readthedocs.io/en/latest/user-context.html#alternative-for-paywalls-authorization-request) + +### Best practices for custom vary by logic + +For information on how user context hashes are generated, see [FOSHttpCacheBundle documentation](https://foshttpcachebundle.readthedocs.io/en/latest/features/user-context.html#generating-hashes). + +[[= product_name =]] implements a custom context provider in order to make user context hash reflect the current User's Roles and Limitations. +This is needed given [[= product_name =]]'s more complex permission model compared to Symfony's. + +You can technically extend the user context hash by [implementing your own custom context provider(s)](https://foshttpcachebundle.readthedocs.io/en/latest/reference/configuration/user-context.html#custom-context-providers). +However, **this is strongly discouraged** as it means increasing the amount of cache variations +stored in proxy for every single cache item, lowering cache hit ratio and increasing memory use. + +Instead, you can create your own hash header for use cases where you need it. +This way only controllers and views that really vary by your custom logic will vary on it. + +You can do it using several methods, ranging from completely custom VCL logic and dedicated controller to respond with hash +to trusted proxy lookups, but this means additional lookups. + +### Example for custom vary by logic + +You can extend `/_fos_user_context_hash` lookup to add another HTTP header with custom hash for your +needs, and adapt the user context hash VCL logic to use the additional header. + +To avoid overloading any application code, take advantage of Symfony's event system: + +1\. Add a [Response event (`kernel.response`)](https://symfony.com/doc/5.4/reference/events.html#kernel-response) [listener or subscriber](https://symfony.com/doc/5.4/event_dispatcher.html) to add your own hash to `/_fos_user_context_hash`: + +```php +public function addPreferenceHash(FilterResponseEvent $event) +{ + $response = $event->getResponse(); + if ($response->headers->get('Content-Type') !== 'application/vnd.fos.user-context-hash') { + return; + } + + $response->headers->set('X-User-Preference-Hash', ''); +} +``` + +2\. Adapt VCL logic to pass the header to requests: + +```diff +@@ -174,6 +174,7 @@ sub ez_user_context_hash { + if (req.restarts == 0 + && (req.http.accept ~ "application/vnd.fos.user-context-hash" + || req.http.X-Context-User-Hash ++ || req.http.x-user-preference-hash + ) + ) { + return (synth(400, "Bad Request")); +@@ -263,12 +264,19 @@ sub vcl_deliver { + && resp.http.content-type ~ "application/vnd.fos.user-context-hash" + ) { + set req.http.X-Context-User-Hash = resp.http.X-Context-User-Hash; ++ set req.http.x-user-preference-hash = resp.http.x-user-preference-hash; + + return (restart); + } + + // If we get here, this is a real response that gets sent to the client. + ++ // Remove the vary on user preference hash, no need to expose this publicly. ++ if (resp.http.Vary ~ "X-User-Preference-Hash") { ++ set resp.http.Vary = regsub(resp.http.Vary, "(?i),? *X-User-Preference-Hash *", ""); ++ set resp.http.Vary = regsub(resp.http.Vary, "^, *", ""); ++ } ++ + // Remove the vary on user context hash, this is nothing public. Keep all + // other vary headers. + if (resp.http.Vary ~ "X-Context-User-Hash") { +``` + +3\. Add `Vary` in your custom controller or content view controller: + +```php +$response->setVary('X-User-Preference-Hash'); + +// If you _also_ need to vary on eZ permissions, instead use: +//$response->setVary(['X-Context-User-Hash', 'X-User-Preference-Hash']); +``` diff --git a/docs/guide/cache/fastly.md b/docs/guide/cache/fastly.md new file mode 100644 index 0000000000..c5555e4e42 --- /dev/null +++ b/docs/guide/cache/fastly.md @@ -0,0 +1,483 @@ +--- +description: Configure Fastly for use with Ibexa DXP. +--- + +# Configure and customize Fastly + +You can configure Fastly by using API calls or through the Fastly Web Interface. +Fastly provides a [Fastly CLI](https://developer.fastly.com/reference/cli/) for configuring Fastly through its API. + +Ibexa Cloud is delivered with Fastly preconfigured. +It means that you don't have to do any changes to the Fastly configuration to make your site work. +The information provided here is only applicable if you want to change the default Fastly configuration on Ibexa Cloud, +or if you are not using Ibexa Cloud and want to configure Fastly to work with [[= product_name =]] on premise. + +!!! note "The Fastly Web Interface is not available for Ibexa Cloud" + It's recommend for Ibexa Cloud customers to use the Fastly CLI instead of using the Fastly API directly with `curl`, and so on. + +!!! note "Disable Varnish when you use Fastly" + Varnish is automatically provisioned on Ibexa Cloud. Varnish needs to be disabled on all environments that use + Fastly. See [documentation on how to do that](https://docs.platform.sh/guides/ibexa/fastly.html). + +## Prepare for using Fastly locally + +These steps are not needed when you use Ibexa Cloud, because Fastly is preconfigured in it. + +### Get Fastly credentials from Ibexa Cloud installation + +To use Fastly CLI or Fastly API directly, you need to obtain the credentials for your site. +To obtain the credentials, connect to your Fastly-enabled environment (for example, production or staging) through SSH and run the following command: + +``` bash +declare|grep FASTLY +FASTLY_KEY=... +FASTLY_SERVICE_ID=... +``` + +These credentials are different for your production and staging environments. +When you configure the Fastly CLI, use the credentials for the environment that you want to change. + +!!! note "Different environment variable names between products" + When you configure Fastly CLI, you use the `FASTLY_API_TOKEN` variable to store the token, while with [[= product_name =]] you use `FASTLY_KEY` for the same purpose. + +### Quickly configure Fastly for use with [[= product_name =]] + +Use the commands below to install VCL configuration required for running Fastly with [[= product_name =]]. +You also need to set up domains, HTTPS and origin configuration (not covered here). +All commands are explained in detail [below](#viewing-and-modifying-the-vcl-configuration): + +``` bash +fastly vcl custom create --name=ez_main.vcl --version=active --autoclone --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ --version=latest --main +fastly vcl custom create --name=ez_user_hash.vcl --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_user_hash.vcl --version=latest +fastly vcl snippet create --name="Re-Enable shielding on restart" --version=latest --priority 100 --type recv --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl +fastly service-version activate --version=latest +``` + +## Quick introduction to Fastly CLI + +Fastly configuration is versioned, which means that when you alter the configuration, you create a new version +and activate it. +If needed, you can revert the configuration to one of previous versions at any point. + +### List configuration versions + +``` bash hl_lines="10" +fastly service-version list +NUMBER ACTIVE LAST EDITED (UTC) +1 false 2023-07-03 10:01 +2 false 2023-07-03 10:35 +3 false 2023-07-03 11:00 +4 false 2023-07-03 11:28 +5 false 2023-07-03 10:58 +6 false 2023-07-03 11:59 +7 false 2023-07-03 12:13 +8 true 2023-07-03 12:13 +``` + +In the example above, version 8 is used (ACTIVE=true). + +### Create new configuration version + +A version that is ACTIVE cannot be modified. To change the configuration, you need to create a new version: + +Clone the current active version: + +``` bash +fastly service-version clone --version=active +``` + +Clone a particular version: + +``` bash +fastly service-version clone --version=4 +``` + +Clone the newest version: + +``` bash +fastly service-version clone --version=latest +``` + +!!! note "Command parameters" + Most Fastly CLI commands have the `--version` parameter. + In addition to a specific version number, the `--version` parameter always supports aliases like `active` and `latest`. + + Most Fastly CLI commands that alter the config also support the `--autoclone` parameter. + With such commands, when you use the `--autoclone` parameter, calling `fastly service-version clone` is no longer needed. + +### Activate version + +Activate a version with this command: + +``` bash +fastly service-version activate --version=latest +``` + +## View and modify VCL configuration + +Fastly configuration is stored in Varnish Configuration Language (VCL) files. +You can change the behaviour of Fastly by [uploading custom VCL files](https://docs.fastly.com/en/guides/uploading-custom-vcl). +[[= product_name =]] ships with two VCL files that need to be enabled for Fastly to work correctly with the platform; `ez_main.vcl` and `ez_user_hash.vcl` (located in `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/`) + +### List custom `.vcl` files for specific version + +``` bash +fastly vcl custom list --version 77 +SERVICE ID VERSION NAME MAIN +4SEKDky8P3wdrctwZCi1C1 77 ez_main.vcl true +4SEKDky8P3wdrctwZCi1C1 77 ez_user_hash.vcl false +``` + +### Get `ez_main.vcl` for specific version + +``` bash +fastly vcl custom describe --name=ez_main.vcl --version=77 + +Service ID: 4SEKDky8P3wdrctwZCi1C1 +Service Version: 77 + +Name: ez_main.vcl +Main: true +Content: + +include "ez_user_hash.vcl" + +sub vcl_recv { +(....) +``` + +### Provide description for specific version + +For each version, you can provide a description that explains what changed in that version: + +``` bash +fastly service-version update --version=52 --comment="Added support for basic-auth on the staging domain" +``` + +#### List descriptions for all versions + +You can list the descriptions by adding the `--verbose` (`-v`) option to the `service-version list` command: + +``` bash +fastly service-version list -v + +Fastly API token provided via FASTLY_API_TOKEN +Fastly API endpoint: https://api.fastly.com +Service ID (via FASTLY_SERVICE_ID): KlUh0J1fnw1JY1aEQ0up + +Versions: 8 + Version 1/8 + Number: 1 + Comment: Initial config + Service ID: KlUh0J1fnw1JY1aEQ0up + Active: false + Locked: true + Deployed: false + Staging: false + Testing: false + Created (UTC): 2023-07-03 08:50 + Last edited (UTC): 2023-07-03 10:01 + Version 2/8 + Number: 2 + Comment: Fixed name of origin + Service ID: KlUh0J1fnw1JY1aEQ0up + Active: false + Locked: true + Deployed: false + Staging: false + Testing: false + Created (UTC): 2023-07-03 10:01 + Last edited (UTC): 2023-07-03 10:35 + (...) +``` + +### Modify Fastly configuration + +You can modify the existing Fastly configuration, for example, by uploading a modified `.vcl` file. + +Create a new version based on the one that is currently active, and upload the file: + +``` bash +fastly vcl custom update --name=ez_main.vcl --version=active --autoclone --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_main.vcl +``` + +Provide a description of the change in Fastly's version system: + +``` bash +fastly service-version update --version=latest --comment="Added feature X" +``` + +Activate the new version: + +``` bash +fastly service-version activate --version=latest +``` + +## Snippets + +You can also add VCL code to the Fastly configuration without modifying the custom `.vcl` files directly. +You do it by creating [snippets](https://docs.fastly.com/en/guides/about-vcl-snippets). +It is recommended that you use snippets instead of changing the VCL files provided by [[= product_name =]] as much as possible, which makes it easier to upgrade the [[= product_name =]] VCL configuration later. + +When you use snippets, the snippet code is injected into the VCL where the `#FASTLY ...` macros are placed. +For example, if you create a snippet for the `recv` subroutine, it is injected into the `ez_main.vcl` file, the +line where `#FASTLY recv` is found. + +### List available snippets for specific version + +``` bash +fastly vcl snippet list --version=active +SERVICE ID VERSION NAME DYNAMIC SNIPPET ID +KlUh0J1fnw1JY1aEQ0up 8 Re-Enable shielding on restart false 1iJWIfsPLNGxcphsjggq +``` + +!!! note + As of version 3.3.24, 4.1.6 and 4.2.0, [[= product_name =]] also requires one snippet to be installed, in addition to the custom VCLs `ez_main.vcl` and `ez_user_hash.vcl`. That snippet is by default named `Re-Enable shielding on restart`. + +### Get details of installed snippets + +Use the `vcl snippet list` command with the `--verbose` option to get information such as: priority, which subroutine it is attached to (`vcl_recv`, `vcl_fetch` etc.) and the code itself. + +``` bash +fastly vcl snippet list --version=active -v +Fastly API token provided via FASTLY_API_TOKEN +Fastly API endpoint: https://api.fastly.com +Service ID (via FASTLY_SERVICE_ID): [....] + +Service Version: 8 + +Name: Re-Enable shielding on restart +ID: 1iJWIfsPLNGxcphsjggq +Priority: 100 +Dynamic: false +Type: recv +Content: +// This code should be added as a snippet in your config: +// Name: Re-Enable shielding on restart +// Priority: 100 +// Type: recv +// +// Fastly CLI: +// - fastly vcl snippet create --name="Re-Enable shielding on restart" --version=active --autoclone --priority 100 --type recv --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl +// - fastly service-version activate --version=latest + + +set var.fastly_req_do_shield = (req.restarts <= 2); + +# set var.fastly_req_do_shield = (req.restarts > 0 && req.http.accept == "application/vnd.fos.user-context-hash"); +set req.http.X-Snippet-Loaded = "v1"; + + +Created at: 2022-06-23 10:55:34 +0000 UTC +Updated at: 2022-06-23 12:24:48 +0000 UTC +``` + +You can also get the same details for a particular snippet using the `vcl snippet describe` command. + +### Get specific snippet + +``` bash +fastly vcl snippet describe --name="Re-Enable shielding on restart" --version=latest +``` + +### Create snippet + +``` bash +fastly vcl snippet create --name="Re-Enable shielding on restart" --version=active --autoclone --priority 100 --type recv --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl +fastly service-version activate --version=latest +``` + +### Update existing snippet + +``` bash +fastly vcl snippet update --name="Re-Enable shielding on restart" --version=active --autoclone --priority 100 --type recv --content=vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl +fastly service-version activate --version=latest +``` + +### Delete snippet + +``` bash +fastly vcl snippet delete --name="Re-Enable shielding on restart" --version=active --autoclone +fastly service-version activate --version=latest +``` + +### Get diff between two versions + +You can easily view the diff between two different versions by using the Fastly web interface. +Unfortunately, Fastly CLI does not support this functionality. +However, Fastly API and GNU diff can help you get an identical result. + +Use the Fastly API to download the generated `.vcl` file. It includes the VCL configuration that Fastly generates +based on all the configuration settings (from all custom `.vcl` files, snippets, and origin configuration). + +The example below extracts the generated VCL for version no. 11 of some service: + +``` bash +curl -i "https://api.fastly.com/service/[FASTLY_SERVICE_ID]/version/11/generated_vcl" -H "Fastly-Key: [FASTLY_API_TOKEN]" -H "Accept: application/json" > generated_vcl_11_raw +cp generated_vcl_11_raw generated_vcl_11_json_only +``` + +Next, you need to edit `generated_vcl_11_json_only` in your favourite editor, remove anything before the json data and save. +Then, follow the same steps again for version no. 12 (or whatever version you want to diff version 11 against). + +Then replace `\n` in the files to get human-readable diffs: + +``` bash +cat generated_vcl_11_json_only |jq .content|perl -pe 's/\\n/\n/g' > generated_vcl_11_json_done +cat generated_vcl_12_json_only |jq .content|perl -pe 's/\\n/\n/g' > generated_vcl_12_json_done +``` + +Finally, you can use GNU diff to get a readable diff of the two versions: + +``` bash +diff -ruN generated_vcl_11_json_done generated_vcl_12_json_done +``` + +## Enable basic-auth on Fastly + +To enable basic-auth, use [Fastly documentation](https://developer.fastly.com/solutions/examples/http-basic-auth) as an example. + +Follow the steps below. + +Usernames and passwords can be stored inside the VCL file, but in this case credentials are stored in a [dictionary](https://docs.fastly.com/en/guides/working-with-dictionaries-using-the-web-interface#working-with-dictionaries-using-vcl-snippets). + +!!! note + To make this example work, you must run [[= product_name =]] in version 3.3.16 or later, or 4.5. + +### Create and activate dictionary + +Fastly configuration includes a dictionary named `basicauth`. +Using a dictionary instead of storing usernames directly in a `.vcl` file is beneficial, because you can add or remove records without having to create and activate new configuration versions. + +``` bash +fastly dictionary create --version=active --autoclone --name=basicauth +fastly service-version activate --version=latest +``` + +### Get dictionary ID + +To add users to the dictionary, first get the dictionary ID. + +``` bash hl_lines="5" +fastly dictionary list --version=active + +Service ID: KlUh0J1fnw1JY1aEQ0up +Version: 3 +ID: ltC6Rg4pqw4qaNKF5tEW +Name: basicauth +Write Only: false +Created (UTC): 2023-07-03 10:33 +Last edited (UTC): 2023-07-03 10:33 +``` + +In the example above, the ID is `ltC6Rg4pqw4qaNKF5tEW`. + + +### Create record in dictionary + +Add username and password to the dictionary: + +``` bash +fastly dictionary-entry create --dictionary-id=ltC6Rg4pqw4qaNKF5tEW --key=user1 --value=foobar1 +``` + +### List dictionary records + +You can list the records from a dictionary by using the following command: + +``` bash +fastly dictionary-entry list --dictionary-id=ltC6Rg4pqw4qaNKF5tEW33 +``` + +Now your dictionary stores new username and password. The next thing to do is to alter the Fastly VCL configuration +and add the basic-auth support. +This example uses [snippets](https://docs.fastly.com/en/guides/about-vcl-snippets), so that no changes are needed in the `.vcl` files that are shipped with [[= product_name =]]. +You need two snippets, store these as files in your system: + +In `snippet_basic_auth_error.vcl`: + +``` bash +// This code should be added as a snippet in your config: +// Name: BasicAuth error +// Priority: 100 +// Type: error + +// See snippet_basic_auth_recv.vcl for installation instructions +// + + +# If status code is a 401, a synthetic HTML page with this error is served to the user. +if (obj.status == 401) { + set obj.http.Content-Type = "text/html; charset=utf-8"; + set obj.http.WWW-Authenticate = "Basic realm=MYREALM"; + synthetic {" + + + + Error + + +

    401 Unauthorized (Fastly)

    + + "}; + return (deliver); +} +``` + +In `snippet_basic_auth_recv.vcl`: + +``` bash +// This code should be added as a snippet in your config: +// Name: BasicAuth recv +// Priority: 100 +// Type: recv +// +// Fastly CLI: +// - fastly vcl snippet create --name="BasicAuth recv" --version=active --autoclone --priority 100 --type recv --content=snippet_basic_auth_recv.vcl +// - fastly vcl snippet create --name="BasicAuth error" --version=latest --priority 100 --type error --content=snippet_basic_auth_error.vcl +// - fastly service-version activate --version=latest + + +declare local var.credential STRING; +declare local var.username STRING; +declare local var.password STRING; +declare local var.result STRING; + +# Basic auth is checked on edge nodes only. The logic below makes sure that it is only run at the edge. +if (fastly.ff.visits_this_service == 0 && req.restarts == 0) { + if (req.http.Authorization ~ "(?i)^Basic ([a-z0-9_=]+)$") { + set var.credential = digest.base64_decode(re.group.1); + set var.username = if(var.credential ~ "^(.+?):.+$", re.group.1, ""); + set var.password = if(var.credential ~ "^.+?:(.+)$", re.group.1, ""); + set var.result = table.lookup(basicauth, var.username, "NOTFOUND"); + + if (var.result == "NOTFOUND") { + error 401 "Restricted"; + } else if (var.result != var.password) { + error 401 "Restricted"; + } + # The Auth header is unset to avoid exposing it as a response header. + unset req.http.Authorization; + set req.http.Auth-User = var.username; + } else { + error 401 "Restricted"; + } +} +``` + +To enable basic-auth for one domain only, alter `snippet_basic_auth_recv.vcl`: + +``` diff +-if (fastly.ff.visits_this_service == 0 && req.restarts == 0 &&) { ++if (fastly.ff.visits_this_service == 0 && req.restarts == 0 && req.http.host == "example.com") { +``` + +Install the snippets with the following Fastly CLI command: + +``` bash +fastly vcl snippet create --name="BasicAuth recv" --version=active --autoclone --priority 100 --type recv --content=snippet_basic_auth_recv.vcl +fastly vcl snippet create --name="BasicAuth error" --version=latest --priority 100 --type error --content=snippet_basic_auth_error.vcl +fastly service-version activate --version=latest +``` + + diff --git a/docs/guide/cache/http_cache.md b/docs/guide/cache/http_cache.md new file mode 100644 index 0000000000..37d6305361 --- /dev/null +++ b/docs/guide/cache/http_cache.md @@ -0,0 +1,26 @@ +--- +description: Ibexa DXP's HTTP cache functionalities enable using reverse proxies: Symfony HttpCache Proxy, Varnish or Fastly. +--- + +# HTTP cache + +[[= product_name =]] provides advanced caching features needed for its own content views, +to make Varnish and Fastly act as the view cache for the system. +This and other features allow [[= product_name =]] to be scaled up to serve high traffic websites and applications. + +HTTP cache is handled by the [ezplatform-http-cache](https://github.com/ezsystems/ezplatform-http-cache) bundle, +which extends [friendsofsymfony/http-cache-bundle](https://foshttpcachebundle.readthedocs.io/en/2.8.0/), +a Symfony community bundle that in turn extends [Symfony HTTP cache](http://symfony.com/doc/5.1/http_cache.html). + +For content view responses coming from [[= product_name =]] itself, this means that: + +- Cache is **[content-aware](content_aware_cache.md)**, always kept up-to-date by invalidating using cache tags. +- Cache is **[context-aware](context_aware_cache.md)**, to cache request for logged-in users by varying on user permissions. + +All of this works across all the supported reverse proxies: + +- [Symfony HttpCache Proxy](symfony_reverse_proxy.md) - limited to a single server, and with limited performance/features +- [Varnish](https://varnish-cache.org/) - high performance reverse proxy +- [Fastly](https://www.fastly.com/) - Varnish-based CDN service + +You can use all these features in custom controllers as well. diff --git a/docs/guide/cache/http_cache_config.md b/docs/guide/cache/http_cache_config.md new file mode 100644 index 0000000000..bd90a79fd8 --- /dev/null +++ b/docs/guide/cache/http_cache_config.md @@ -0,0 +1,98 @@ +--- +description: Configure HTTP cache for Ibexa DXP, including cache header rules and time-to-live. HTTP cache configuration is SiteAccess-aware. +--- + +# HTTP cache configuration + +## Content view configuration + +You can configure cache globally for content views in `config/packages/ezplatform.yaml`: + +``` yaml +ezplatform: + system: + : + content: + # Activates HTTP cache for content + view_cache: true + # Activates expiration based HTTP cache for content (very fast) + ttl_cache: true + # Number of seconds an HTTP response cache is valid (if ttl_cache is true, and if no custom s-maxage is set) + default_ttl: 7200 +``` + +You may want to set a high default time to live (TTL) (`default_ttl`) to have a high cache hit ratio on your installation. +As the system takes care of purges, the cache should not become stale with the exception of grace handing in Varnish and Fastly. + +## Cache header rules + +A few redirect and error pages are served through the content view system. If you set a high `default_ttl`, they can also be served from cache. + +To avoid this, the installation ships with configuration to match these specific situations and set a much lower TTL. +[FOSHttpCacheBundle matching rules](http://foshttpcachebundle.readthedocs.io/en/2.8.0/reference/configuration/headers.html) enables you to specify a different TTL: + +``` yaml +fos_http_cache: + cache_control: + rules: + # Make sure cacheable (fresh) responses from Ibexa DXP which are errors/redirects get lower TTL than default_ttl + - + match: + match_response: 'response.isFresh() && ( response.isServerError() || response.isClientError() || response.isRedirect() )' + headers: + overwrite: true + cache_control: + max_age: 5 + s_maxage: 20 +``` + +Similarly, by default the performance tuning is applied to avoid crawlers affecting the setup too much, by caching of generic 404s and similar error pages in the following way: + +``` yaml +fos_http_cache: + cache_control: + rules: + # Example of performance tuning, force TTL on 404 pages to avoid crawlers, etc., taking too much load + # Should not be set too high, as cached 404s can cause issues for future routes, URL aliases, wildcards, etc. + - + match: + match_response: '!response.isFresh() && response.isNotFound()' + headers: + overwrite: true + cache_control: + public: true + max_age: 0 + s_maxage: 20 +``` + +## Time-to-live value for Page blocks + +For the Page Builder, block cache by default respects `$content.ttl_cache$` and `$content.default_ttl$` settings. +However, if the given block value has a since or till date, +it is taken into account for the TTL calculation for both the block and the whole page. + +To overload this behavior, listen to [`BlockResponseEvents::BLOCK_RESPONSE`](../repository/event_reference/page_events.md), +and set priority to `-200` to adapt what Page Field Type does by default. + +For example, to disable cache for the block, use `$event->getResponse()->setPrivate()`. + +## When to use ESI + +[Edge Side Includes](https://symfony.com/doc/current/http_cache/esi.html) (ESI) can be used to split out the different parts of a web page into separate fragments that can be freely reused as pieces by reverse proxy. + +In practice, with ESI, every sub-request is regenerated from application perspective. And while you can tune your system to reduce this, it always causes additional overhead in the following situations: + +- When cache is cold on all or some of the sub-requests +- With Symfony Proxy (AppCache) there is always some overhead, even on warm cache (hits) +- In development environment + +This may differ depending on your system, however, it is recommended to stay below 5 ESI +requests per page and only using them for parts that are the same across the whole site or larger parts of it. + +You should not use ESI for parts that are effectively uncached, +because your reverse proxy has to wait for the back end and cannot deliver cached pages directly. + +!!! note "ESI limitations with the URIElement SiteAccess matcher" + + Is is not possible to share ESIs across the SiteAccesses when using URI matching + as URI contains the SiteAccess name encoded in its path information. diff --git a/docs/guide/cache/navigation_cache.md b/docs/guide/cache/navigation_cache.md deleted file mode 100644 index 644780d6c1..0000000000 --- a/docs/guide/cache/navigation_cache.md +++ /dev/null @@ -1,49 +0,0 @@ -# Navigation cache [[% include 'snippets/commerce_badge.md' %]] - -Navigation uses HTTP cache to store navigation, and stash cache to store generated URLs. - -After an element is modified in the Back Office, it is added to the queue to be deleted from HTTP cache and stash cache. -See [Content cache refresh](../cache/content_cache_refresh/content_cache_refresh.md) for more information. - -The top navigation and the left menu are cached using HTTP cache, with one cache per SiteAccess. - -By default, navigation is cached by `user-hash` for 10 hours. - -``` yaml -silver_eshop.default.http_cache: - navigation: - max_age: 36000 - vary: user-hash -``` - -When a Content item which is included in the navigation is modified, a content modification handler is triggered. -Make sure that a cron job is activated to refresh the cache if required: - -`php bin/console silversolutions:cache:refresh --env=prod` - -## Caching time - -To change the navigation cache time, set the `router_cache_ttl` parameter (in seconds): - -``` yaml -siso_core.default.router_cache_ttl: 86400 -``` - -## Caching in debug mode - -In some environments (e.g. dev) the HTTP caching is not active, so navigation is stored in the stash cache to improve performance. - -To check if the debug mode is active, use the following parameter: - -``` php -$isDebugEnabled = (bool) $this->container->getParameter('kernel.debug') -``` - -In other environments (e.g. prod) stash cache is not used. - -Caching TTL can be configured in the following way: - -``` yaml -parameters: - siso_core.default.nav_debug_env_ttl: 86400 -``` diff --git a/docs/guide/cache/shop_caching.md b/docs/guide/cache/shop_caching.md new file mode 100644 index 0000000000..71b16ee9a9 --- /dev/null +++ b/docs/guide/cache/shop_caching.md @@ -0,0 +1,56 @@ +--- +description: Caching shop content by using HTTP cache helps increase page performance. +--- + +# Shop cache + +[[= product_name =]] uses different caches for the shop, including HTTP cache, which can greatly increase shop performance. +Dynamic parts of the shop, such as basket preview or prices, are displayed using dynamic caching features such as ESI or JavaScript. + +## ESI-rendered blocks in the shop + +|Controller|Purpose|Cache settings| +|--- |--- |--- | +|`SilversolutionsEshopBundle:CustomerProfileData:showHeaderLogin`|Displays information about the logged-in user in the top part of the page|Purged after login/logout and delegation process| +|`IbexaPlatformCommerceCheckoutBundle:Basket:showBasketPreview` [[% include 'snippets/commerce_badge.md' %]]|Displays a short version of the basket in the top part of the page|Purged when basket changes
    Tags: `siso_basket_`
    `siso_user_`| +|`SilversolutionsEshopBundle:PageLayout:getFooter`|Footer information shared among all pages|Caching strategy `service_menu`| +|`SilversolutionsEshopBundle:Bestsellers:getBestsellersEsi`
    `SilversolutionsEshopBundle:Bestsellers:getCategoryBestsellers`|Bestseller box for catalog pages|Caching strategy `product_list`| +|`SilversolutionsEshopBundle:ProductType:productList`|Product type list page|Caching strategy `product_type_children`| +|`IbexaPlatformCommerceCheckoutBundle:Basket:showStoredBasketPreview` [[% include 'snippets/commerce_badge.md' %]]|Displays a badge with the number of products in stored comparison or the number of stored baskets|Caching strategy `basket_preview`
    Purged when basket changes
    Tags: `siso_basket_`| +|`SilversolutionsEshopBundle:Navigation:showMenu`|Left menu|Tag: `siso_menu`| +|`SilversolutionsEshopBundle:Navigation:showMenu`|Main menu|Tag: `siso_menu`| + +All cache blocks are described in the `silver_eshop.default.http_cache` parameter in `parameters.yml`: + +``` yaml +silver_eshop.default.http_cache: + product: + # Response max age in seconds. Zero means that this response will not be cached. + max_age: 28800 + vary: ~ + product_list: + max_age: 28800 + vary: ~ +``` + +## Shop cache tags + +[[= product_name =]] uses cache tags to tag and purge content. + +|Tag|Used for|Purged| +|--- |--- |--- | +|`siso_basket_` [[% include 'snippets/commerce_badge.md' %]]|Basket preview in the header|By event, on basket change| +|`siso_menu`|Main menu and left side menus (product catalog)|| +|`content-` [[% include 'snippets/commerce_badge.md' %]]|Textmodules|When content (text modules) are changed| +|`siso_user_`|User-specific data (shows name of the user)|When the user logs in or out| + +## Purging caches + +You can purge the shop's HTTP cache with the `ibexa:commerce:purge-http-cache` command. + +By default, the command purges all shop-related HTTP caches. +You can specify cache identifiers to purge as a space-separated list, for example: + +``` bash +php bin/console ibexa:commerce:purge-http-cache 1056 222 --env="prod" +``` diff --git a/docs/guide/cache/symfony_reverse_proxy.md b/docs/guide/cache/symfony_reverse_proxy.md new file mode 100644 index 0000000000..c4cdf912b5 --- /dev/null +++ b/docs/guide/cache/symfony_reverse_proxy.md @@ -0,0 +1,319 @@ +--- +description: You can use Symfony HttpCache Proxy, Varnish or Fastly as reverse proxies with Ibexa DXP. +--- + +# Reverse proxy + +Before you start using Symfony reverse proxy, you must change your kernel to use `EzSystems\PlatformHttpCacheBundle\AppCache` instead of `Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache`. + +Next, to use Symfony reverse proxy, follow the [Symfony documentation](https://symfony.com/doc/current/http_cache.html#symfony-reverse-proxy). + +## Using Varnish or Fastly + +As [[= product_name =]] is built on top of Symfony, it uses standard HTTP cache headers. +By default, the Symfony reverse proxy is used to handle cache. +You can replace it with other reverse proxies, such as Varnish, or CDN like Fastly. + +Using a different proxy is highly recommended as they provide better performance and more advanced features such as grace handling, configurable logic through VCL and much more. + +!!! note + + Use of Varnish or Fastly is a requirement for a [Clustering](../clustering.md) setup, as Symfony Proxy does not support sharing cache between several application servers. + +## VCL base files + +For reverse proxies to work properly with your installation, you need to add the corresponding VCL files for your +HTTP Cache. + +- [Varnish VCL xkey example](https://github.com/ezsystems/ezplatform-http-cache/tree/2.3/docs/varnish/vcl) +- Fastly VCLs can be found in `vendor/ezsystems/ezplatform-http-cache-fastly/fastly`. You must install the following to use Fastly: + - `ez_main.vcl` as the **main** custom VCL + - `ez_user_hash.vcl` as another custom VCL + - `snippet_re_enable_shielding.vcl` as snippet + +The provided `.vcl` files will work both with [Fastly Shielding](https://docs.fastly.com/en/guides/shielding) enabled and without it. + +For more information on how to configure Fastly with [[= product_name =]], see [Configure and customize Fastly](fastly.md). + +!!! tip + + Support for Fastly Shielding was added in [[= product_name =]] v3.3.24 and v4.1.6. + + When you extend [FOSHttpCacheBundle](https://foshttpcachebundle.readthedocs.io/en/2.9.1/), + you can also adapt your VCL further with [FOSHttpCache documentation](http://foshttpcache.readthedocs.org/en/latest/varnish-configuration.html) + to use additional features. + +## Configure Varnish and Fastly + +The configuration of [[= product_name =]] for using Varnish or Fastly requires a few steps, starting with configuring proxy. + +Failing to configure reverse proxies correctly may introduce several problems, including, but not limited to: + +- [[= product_name =]] generating links with a wrong protocol schema (HTTP instead of HTTPS) if HTTPS termination is done before the web server due to the `X-Forward-Proto` headers being ignored +- [[= product_name =]] generating links with wrong port numbers due to the `X-Forward-Port` headers being ignored +- Back Office showing the login screen because JWT tokens are not accepted due to the `X-Forward-For` headers being ignored + +### Configure Symfony front controller + +You need to consider your `TrustedProxy` configuration when you use Symfony [behind a load balancer or a reverse proxy.](https://symfony.com/doc/5.1/deployment/proxies.html) + +Set the following environment variable: +- `TRUSTED_PROXIES`: String with trusted IP, multiple proxies can be configured with a comma, for example, `TRUSTED_PROXIES="192.0.0.1,10.0.0.0/8"` + +Add the trusted proxies to your configuration file: + +``` yaml +framework: + trusted_proxies: '%env(TRUSTED_PROXIES)%' +``` + +!!! caution "Careful when trusting dynamic IP using `REMOTE_ADDR` value or similar" + + On Platform.sh, Varnish does not have a static IP, like with [AWS LB](https://symfony.com/doc/5.1/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly). + For this reason, the `TRUSTED_PROXIES` env variable supports being set to value `REMOTE_ADDR`, which is equal to: + + ```php + Request::setTrustedProxies([$request->server->get('REMOTE_ADDR')], Request::HEADER_X_FORWARDED_ALL); + ``` + + When trusting remote IP like this, make sure your application is only accessible through Varnish. + If it is accessible in other ways, this may result in trusting, for example, the IP of client browser instead, which would be a serious security issue. + + Make sure that **all** traffic always comes from the trusted proxy/load balancer, + and that there is no other way to configure it. + +When using Fastly, you need to set `trusted_proxies` according to the [IP ranges used by Fastly](https://docs.fastly.com/en/guides/accessing-fastlys-ip-ranges). + +!!! tip + + You don't have to set `trusted_proxies` when using Fastly on Platform.sh. + The Platform.sh router automatically changes the source IP of requests coming from Fastly, + replacing the source IP with the actual client IP and removing any `X-FORWARD-...` header in the request before it reaches Ibexa DXP. + +For more information about setting these variables, see [Configuration examples](#configuration-examples). + +### Update YML configuration + +Next, you need to tell [[= product_name =]] to use an HTTP-based purge client (specifically the FosHttpCache Varnish purge client), +and specify the URL that Varnish can be reached on (in `config/packages/ezplatform.yaml`): + +| Configuration | Parameter| Environment variable| Possible values| +|---------|--------|--------|----------| +| `ezplatform.http_cache.purge_type` | `purge_type` | `HTTPCACHE_PURGE_TYPE` | local, varnish, fastly | +| `ezplatform.system..http_cache.purge_servers` | `purge_server` | `HTTPCACHE_PURGE_SERVER` | Array of URLs to proxies when using Varnish or Fastly (`https://api.fastly.com`). | +| `ezplatform.system..http_cache.varnish_invalidate_token` | `varnish_invalidate_token` | `HTTPCACHE_VARNISH_INVALIDATE_TOKEN` | (Optional) For token-based authentication. | +| `ezplatform.system..http_cache.fastly.service_id` | `fastly_service_id` | `FASTLY_SERVICE_ID` | Service ID to authenticate with Fastly. | +| `ezplatform.system..http_cache.fastly.key` | `fastly_key` | `FASTLY_KEY` | Service key/token to authenticate with Fastly. | + +If you need to set multiple purge servers, configure them in the YAML configuration, +instead of parameter or environment variable, as they only take single string value. + +Example configuration for Varnish as reverse proxy, providing that [front controller has been configured](#configure-symfony-front-controller): + +``` yaml +ezplatform: + http_cache: + purge_type: varnish + + system: + # Assuming that my_siteaccess_group contains both your front-end and back-end SiteAccesses + my_siteaccess_group: + http_cache: + # Fill in your Varnish server(s) address(es). + purge_servers: [http://my.varnish.server:8081] +``` + +!!! note "Invalidating Varnish cache using tokens" + + In setups where the Varnish server IP can change (for example, on Ibexa Cloud), + you can use token-based cache invalidation through [`ez_purge_acl`](https://github.com/ezsystems/ezplatform-http-cache/blob/v2.1.0/docs/varnish/vcl/varnish5.vcl#L174). + + In such situation, use strong, secure hash and make sure to keep the token secret. + +### Ensure proper Captcha behavior [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +If your installation uses Varnish and you want users to be able to configure and use Captcha in their forms, +you must enable sending Captcha data as a response to an Ajax request. +Otherwise, Varnish does not allow for the transfer of Captcha data to the form, and as a result, users see an empty image. + +To enable sending Captcha over Ajax, add the following configuration to `config/packages/ezplatform.yaml`: + +``` yaml +ezplatform: + system: + default: + form_builder: + captcha: + use_ajax: true +``` + +### Update custom Captcha block [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +If you created a custom Captcha block for your site by overriding the default file (`vendor/gregwar/captcha-bundle/Resources/views/captcha.html.twig`), +you must make the following changes to the custom block template file: + +- change the name of the block to `ajax_captcha_widget` +- include the JavaScript file: + +``` js +{{ encore_entry_script_tags('ezplatform-form-builder-ajax-captcha-js', null, 'ezplatform') }} +``` + +- add a data attribute with a `fieldId` value: + +``` js +data-field-id="{{ field.id }}" +``` + +As a result, your file should be similar to [this example.](https://github.com/ezsystems/ezplatform-form-builder/blob/master/src/bundle/Resources/views/themes/standard/fields/captcha.html.twig) + +For more information about configuring Captcha fields, see [Captcha field](../../guide/form_builder/forms.md#captcha-field). + +### Use Fastly as HttpCache proxy + +[Fastly](https://www.fastly.com/) delivers Varnish as a CDN service and is supported with [[= product_name =]]. +To learn how it works, see [Fastly documentation](https://docs.fastly.com/guides/basic-concepts/how-fastlys-cdn-service-works). + +#### Configure Fastly in YML + +``` yaml +ezplatform: + http_cache: + purge_type: fastly + + system: + # Assuming that my_siteaccess_group contains both your front-end and back-end SiteAccesses + my_siteaccess_group: + http_cache: + purge_servers: [https://api.fastly.com] + fastly: + # See below for obtaining these values + service_id: "ID" + key: "token" +``` + +#### Configure Fastly using environment variables + +See the example below to configure Fastly with the `.env` file: + +``` +HTTPCACHE_PURGE_TYPE="fastly" +# Optional +HTTPCACHE_PURGE_SERVER="https://api.fastly.com" + +# See below for obtaining service ID and application key/token +FASTLY_SERVICE_ID="ID" +FASTLY_KEY="token" +``` + +#### Configure Fastly on Platform.sh + +If you use Platform.sh, it is recommended to configure all environment variables through [Platform.sh variables](https://docs.platform.sh/frameworks/ibexa/fastly.html). +In [[= product_name =]], Varnish is enabled by default. To use Fastly, first you must +[disable Varnish](https://docs.platform.sh/frameworks/ibexa/fastly.html#remove-varnish-configuration) + +#### Get Fastly service ID and API token + +To get the service ID, log in to http://fastly.com. In the upper menu, click the **CONFIGURE** tab. +The service ID is displayed next to the name of your service on any page. + +For instructions on how to generate a Fastly API token, see [the Fastly guide](https://docs.fastly.com/guides/account-management-and-security/using-api-tokens). +The API token needs the `purge_all` an `purge_select` scopes. + +### Configuration examples + +See below the most common configuration examples for the system, using environment variables. + +Example for Varnish with the `.env` file: + +``` bash +TRUSTED_PROXIES="127.0.0.1" +HTTPCACHE_PURGE_TYPE="varnish" +HTTPCACHE_PURGE_SERVER="http://varnish:80" +``` + +Example for Apache with `mod_env`: + +```apacheconfig +SetEnv TRUSTED_PROXIES "127.0.0.1" +SetEnv HTTPCACHE_PURGE_TYPE varnish +SetEnv HTTPCACHE_PURGE_SERVER "http://varnish:80" +``` + +Example for Nginx: + +```nginx +fastcgi_param TRUSTED_PROXIES "127.0.0.1"; +fastcgi_param HTTPCACHE_PURGE_TYPE varnish; +fastcgi_param HTTPCACHE_PURGE_SERVER "http://varnish:80"; +``` + +Example for Platform.sh: + +You can configure environment variables through [Platform.sh variables](https://docs.platform.sh/frameworks/ibexa/fastly.html). + +!!! tip + + For HTTP cache, you will most likely only use this for configuring Fastly for production and optionally staging, + allowing `variables:env:` in `.platform.app.yaml` to, for example, specify Varnish or Symfony proxy as default for dev environment. + +#### Apache with Varnish + +```apacheconfig +# mysite_com.conf + +# Configure Varnish +SetEnv HTTPCACHE_PURGE_TYPE varnish +SetEnv HTTPCACHE_PURGE_SERVER "http://varnish:80" + +# Configure IP of your Varnish server to be trusted proxy +# !! Replace IP with the real one used by Varnish +SetEnv TRUSTED_PROXIES "193.22.44.22" +``` + +#### Nginx with Fastly + +```nginx +# mysite_com.conf + +# Configure Fastly +fastcgi_param HTTPCACHE_PURGE_TYPE fastly; +fastcgi_param HTTPCACHE_PURGE_SERVER "https://api.fastly.com"; + +# See above for obtaining service ID and application key/token +fastcgi_param FASTLY_SERVICE_ID "ID" +fastcgi_param FASTLY_KEY "token" +``` + +## Stale cache + +Stale cache, or grace mode in Varnish, occurs when: +- Cache is served some time after the TTL expired. +- When the back-end server does not respond. + +This has several benefits for high traffic installations to reduce load to the back end. +Instead of creating several concurrent requests for the same page to the back end, +the following happens when a page has been soft purged: + +- Next request hitting the cache triggers an asynchronous lookup to the back end. +- If cache is still within grace period, first and subsequent requests for the content are served from cache, +and do not wait for the asynchronous lookup to finish. +- The back-end lookup finishes and refreshes the cache so any subsequent requests get a fresh cache. + +By default, [[= product_name =]] always soft purges content on reverse proxies that support it (Varnish and Fastly), +with the following logic in the out-of-the-box VCL: + +- Cache is within grace period. +- Either the server is not responding, or the request comes without a session cookie (anonymous user). + +Serving grace is not always allowed by default because: + +- It is a safe default. Even if just for anonymous users, stale cache can easily be confusing during acceptance testing. +- It means REST API, which is used by the Back Office, would serve stale data, breaking the UI. + +!!! tip "Customizing stale cache handling" + + If you want to use grace handling for logged-in users as well, you can adapt the provided VCL to add a condition + for opting out if the request has a cookie and the path contains REST API prefix to make sure the Back Office is not negatively affected. + + If you want to disable grace mode, you can adapt the VCL to do hard instead of soft purges, or set grace/stale time to `0s`. diff --git a/docs/guide/cache/translations_cache_for_textmodules.md b/docs/guide/cache/translations_cache_for_textmodules.md deleted file mode 100644 index 144c6945db..0000000000 --- a/docs/guide/cache/translations_cache_for_textmodules.md +++ /dev/null @@ -1,19 +0,0 @@ -# Translation cache for Text modules [[% include 'snippets/commerce_badge.md' %]] - -!!! note - - See [SignalSlots documentation](https://doc.ezplatform.com/en/2.5/guide/signalslots/#signals-reference) for more information about the SignalSlot system. - -## RemoveTranslationCacheSlot - -The `RemoveTranslationCacheSlot` slot is used to remove translations of `st_textmodules` immediately after they have been updated, deleted, created, moved to or recovered from Trash. - -`RemoveTranslationCacheSlot` only accepts the following signals: - -``` php -Signal\ContentService\UpdateContentSignal -Signal\ContentService\DeleteContentSignal -Signal\ContentService\CreateContentSignal -Signal\TrashService\TrashSignal -Signal\TrashService\RecoverSignal -``` diff --git a/docs/guide/catalog/catalog.md b/docs/guide/catalog/catalog.md index acb7de9df7..fdce5f73fc 100644 --- a/docs/guide/catalog/catalog.md +++ b/docs/guide/catalog/catalog.md @@ -1,4 +1,8 @@ -# Catalog [[% include 'snippets/commerce_badge.md' %]] +--- +description: Product catalog offers PIM functionalities, with product, product type and attribute management capabilities to manage complex products. +--- + +# Catalog Products in the catalog can be stored in the content model, or in optimized eContent storage, which is able to store up to two million products diff --git a/docs/guide/catalog/catalog_api/catalog_element.md b/docs/guide/catalog/catalog_api/catalog_element.md index dadeff32bf..701e3155ec 100644 --- a/docs/guide/catalog/catalog_api/catalog_element.md +++ b/docs/guide/catalog/catalog_api/catalog_element.md @@ -1,4 +1,4 @@ -# Catalog element [[% include 'snippets/commerce_badge.md' %]] +# Catalog element The `CatalogElement` class defines the generic product and category model. diff --git a/docs/guide/catalog/catalog_api/customer_skus.md b/docs/guide/catalog/catalog_api/customer_skus.md index 13543b7c03..159cd8af4c 100644 --- a/docs/guide/catalog/catalog_api/customer_skus.md +++ b/docs/guide/catalog/catalog_api/customer_skus.md @@ -1,4 +1,4 @@ -# Customer SKUs [[% include 'snippets/commerce_badge.md' %]] +# Customer SKUs In some projects, (B2B) customers know products by their own article number (SKU) that comes from the ERP. When the customer e.g. searches for a product in the shop, they search for an article number that is only known to them. diff --git a/docs/guide/catalog/catalog_api/extending_a_catalogfactory.md b/docs/guide/catalog/catalog_api/extending_a_catalogfactory.md index de9b25c035..c15c0f6a4d 100644 --- a/docs/guide/catalog/catalog_api/extending_a_catalogfactory.md +++ b/docs/guide/catalog/catalog_api/extending_a_catalogfactory.md @@ -1,4 +1,4 @@ -# Extending a CatalogFactory [[% include 'snippets/commerce_badge.md' %]] +# Extending a CatalogFactory If you need to prepare special fields for products or categories, or you need to do a mapping, then the easiest way is to extend the catalog factory. @@ -8,7 +8,7 @@ It is called every time a product is provided by the data provider. Each provider has a separate catalog factory. The data provider is defined by the `silver_eshop.default.catalog_data_provider` parameter. -For more information about data providers, see [Content model data provider](../../data_providers/content_model_dataprovider.md). +For more information about data providers, see [Repository data provider](../../data_providers/repository_data_provider.md). The following example provides an additional mapping for the Keyword Field Type which enables you to display tags added to the product: diff --git a/docs/guide/catalog/catalog_api/productnode.md b/docs/guide/catalog/catalog_api/productnode.md index 03b5dbb37c..5484cef14a 100644 --- a/docs/guide/catalog/catalog_api/productnode.md +++ b/docs/guide/catalog/catalog_api/productnode.md @@ -1,4 +1,4 @@ -# ProductNode [[% include 'snippets/commerce_badge.md' %]] +# ProductNode `ProductNode` is an abstract base class for product elements. It inherits from product category `CatalogElement`. diff --git a/docs/guide/catalog/catalog_api/producttype.md b/docs/guide/catalog/catalog_api/producttype.md index bbc279b376..ff32acf156 100644 --- a/docs/guide/catalog/catalog_api/producttype.md +++ b/docs/guide/catalog/catalog_api/producttype.md @@ -1,4 +1,4 @@ -# ProductType [[% include 'snippets/commerce_badge.md' %]] +# ProductType `ProductType` extends [CatalogElement](catalog_element.md) and implements `ProductNodeContainerInterface`. diff --git a/docs/guide/catalog/catalog_api/routing_system.md b/docs/guide/catalog/catalog_api/routing_system.md index e2d45602e8..6614c32998 100644 --- a/docs/guide/catalog/catalog_api/routing_system.md +++ b/docs/guide/catalog/catalog_api/routing_system.md @@ -1,6 +1,6 @@ -# Routing system [[% include 'snippets/commerce_badge.md' %]] +# Routing system -[[= product_name_com =]] uses its own routing/URL matching solution to avoid conflicting with the [[= product_name =]] routing system +The shop uses its own routing/URL matching solution to avoid conflicting with the [[= product_name =]] routing system and to enable using Platform API in commerce routes. !!! note @@ -23,7 +23,7 @@ and to enable using Platform API in commerce routes. The default router (`Silversolutions\Bundle\EshopBundle\Routing\StandardRouter`) checks each requested URL. -If the URL belongs to a [catalog element](catalog_element.md) or a [silver module](../../../api/commerce_api/field_type_reference/silver.module.md), +If the URL belongs to a [catalog element](catalog_element.md) or a silver module, the router becomes active and redirects the user to the appropriate controller. This router is a derived class from a chained router implementation. It attempts to match the URL to its own specifications. @@ -66,7 +66,7 @@ The router is defined with the priority of 280, so you can still add your own ch ## Usage of the navigation service -In order to determine whether a URL belongs to the catalog or [silver module](../../../api/commerce_api/field_type_reference/silver.module.md), -the navigation service or the [catalog data provider](../../data_providers/access_dataprovider_via_php.md) is used. +To determine whether a URL belongs to the catalog or a silver module, +the navigation service or the [catalog data provider](../../data_providers/data_providers.md) is used. -Additionally, the router uses the navigation service in order to set the [URL Mapping](../../data_providers/access_dataprovider_via_php.md) and set the proper URL. +Additionally, the router uses the navigation service in order to set the URL mapping and set the proper URL. diff --git a/docs/guide/catalog/catalog_templates.md b/docs/guide/catalog/catalog_templates.md index 3dfaff3428..84dcbf05ec 100644 --- a/docs/guide/catalog/catalog_templates.md +++ b/docs/guide/catalog/catalog_templates.md @@ -1,4 +1,4 @@ -# Catalog templates [[% include 'snippets/commerce_badge.md' %]] +# Catalog templates ## Template list diff --git a/docs/guide/catalog/product.md b/docs/guide/catalog/product.md new file mode 100644 index 0000000000..35f063c7bc --- /dev/null +++ b/docs/guide/catalog/product.md @@ -0,0 +1,87 @@ +# Product + +The built-in Product Content Type contains the following Fields: + +|Name | Identifier | Type | Description | +|---|---|---|---| +|Productname | `ses_name` | `ezstring` | Main name of the product. Used to create the URL | +|Product type | `ses_type` | `ezselection` | | +|SKU | `ses_sku` | `ezstring` | Unique Stock keeping unit | +|Subtitle | `ses_subtitle` | `ezstring` | Additional product name | +|Short description | `ses_short_description` | `ezrichtext` | Short product description | +|Long description | `ses_long_description` | `ezrichtext` | Long product description | +|Specifications | `ses_specifications` | `sesspecificationstype` | A set of product specification values. They are indexed in the search engine and can be used for faceted search | +|EAN | `ses_ean` | `ezstring` | European Article Number | +|Variants | `ses_variants` | `uivarvarianttype` | [Product variants](#product-variants) | +|Manufacturer SKU | `ses_manufacturer_sku` | `ezstring` | SKU of the product as assigned by the manufacturer | +|Unit price | `ses_unit_price` | `ezstring` | Product price | +|Product image | `ses_image_main` | ezimage | Main product image | +|Manufacturer | `ses_manufacturer` | ezstring | Manufacturer name | +|Color | `ses_color` | `ezstring` | Product color | +|Technical specification | `ses_specification` | `eztext` | Technical product description | +|Video | `ses_video` | `ezstring` | Link to a product video | +|Add. Product image 1-4 | `ses_image_1` | `ezimage` | Up to four additional images | +|Currency | `ses_currency` | `ezstring` | Default product currency | +|VAT Code | `ses_vat_code` | `sesselection` | One of predefined VAT rates | +|Product Type | `ses_product_type` | `ezstring` | Product type used for grouping products in comparison | +|Packaging unit | `ses_packaging_unit` | `ezstring` | Product packaging unit | +|Min order quantity | `ses_min_order_quantity` | `ezstring` | Minimum quantity that can be ordered | +|Max order quantity | `ses_max_order_quantity` | `ezstring` | Maximum quantity that can be ordered | +|Unit | `ses_unit` | `ezstring` | Product unit | +|Stock numeric | `ses_stock_numeric` | `ezstring` | | +|Discontinued | `ses_discontinued` | `ezboolean` | Flag to indicate if the product is discontinued | +|Tags | `tags` | `ezkeyword` | Product keywords | + +## Custom product Content Type + +To create a custom Content Type that acts like a product, add its identifier to the following configuration: + +``` yaml +silversolutions_eshop: + product_content_type_identifiers: + default: + - my_custom_product_type +``` + +The custom Content Type must mirror the structure of the built-in `ses_product` Content Type, +by having Fields with the same identifiers, +but you can add other Fields to it. + +!!! tip + + To ensure that all the Fields are set up correctly, you can copy the `ses_product` Content Type + and add your custom Field to the copy. + +Additionally, add the following parameters for your custom Content Type's identifier: + +``` yaml +parameters: + # Enable buying the product in the shop + silver_eshop.default.catalog_factory.my_custom_product_type: createOrderableProductNode + # Enable price export in the Back Office + siso_price.default.price_export.product_type_filter: [ ses_product, my_custom_product_type ] +``` + +## Product specifications + +You can configure the available product specifications and default values by using the following configuration: + +``` yaml +siso_core.default.specification_groups: + - + code: "technic" + label: "Technical data" + default_values: + - + id: width + label: "Width" + value: "" + options: ['mm','cm', 'in'] + - + id: diameter + label: "Diameter" + value: "" + options: ['mm','cm'] +``` + +With the optional `option` attribute you can add a select field that offers, for example, a selection of units. diff --git a/docs/guide/catalog/product_rendering.md b/docs/guide/catalog/product_rendering.md index 5a74619a53..cc7e1b8f26 100644 --- a/docs/guide/catalog/product_rendering.md +++ b/docs/guide/catalog/product_rendering.md @@ -1,4 +1,4 @@ -# Product rendering [[% include 'snippets/commerce_badge.md' %]] +# Product rendering The `catalogElement` object represents the product in catalog templates. Use it with the `ses_render_field` Twig function to render product Fields: diff --git a/docs/guide/catalog/product_variants/product_variant_api.md b/docs/guide/catalog/product_variants/product_variant_api.md index 1cc901b6d1..70688cf643 100644 --- a/docs/guide/catalog/product_variants/product_variant_api.md +++ b/docs/guide/catalog/product_variants/product_variant_api.md @@ -1,4 +1,4 @@ -# Product variant API [[% include 'snippets/commerce_badge.md' %]] +# Product variant API ## VariantService diff --git a/docs/guide/catalog/product_variants/product_variants.md b/docs/guide/catalog/product_variants/product_variants.md index 0195c259f4..4d7f1a495c 100644 --- a/docs/guide/catalog/product_variants/product_variants.md +++ b/docs/guide/catalog/product_variants/product_variants.md @@ -1,4 +1,4 @@ -# Product variants [[% include 'snippets/commerce_badge.md' %]] +# Product variants Product variants define different versions of a product. Variants can, for example, define product color or size. @@ -160,7 +160,7 @@ In this case the product is identified by the SKU and the `variantCode`: `SKU_AN These "variants" can be grouped by a PIM system or a special field in the ERP system. In this case, the `variantCode` from the catalog is the actual SKU that is sent to the ERP: `SKU_ONLY` -[[= product_name_com =]] supports both methods. +[[= product_name =]] supports both methods. To select a method, use the following configuration: ``` yaml diff --git a/docs/guide/catalog/product_variants/setting_up_variants_from_external_source.md b/docs/guide/catalog/product_variants/setting_up_variants_from_external_source.md index d5740ab89b..bf19f61eba 100644 --- a/docs/guide/catalog/product_variants/setting_up_variants_from_external_source.md +++ b/docs/guide/catalog/product_variants/setting_up_variants_from_external_source.md @@ -1,6 +1,6 @@ -# Setting up variants from external source [[% include 'snippets/commerce_badge.md' %]] +# Setting up variants from external source -The default data provider and `CatalogFactory` ([content model data provider](../../data_providers/content_model_dataprovider.md)) +The default data provider and `CatalogFactory` ([Repository data provider](../../data_providers/repository_data_provider.md)) already support variants up to two levels. You can set the variants up by [using a variant Field Type](product_variants.md) in the Back Office. diff --git a/docs/guide/checkout/checkout.md b/docs/guide/checkout/checkout.md index 20c12a6bb5..c9d2e68b1a 100644 --- a/docs/guide/checkout/checkout.md +++ b/docs/guide/checkout/checkout.md @@ -1,6 +1,11 @@ -# Checkout [[% include 'snippets/commerce_badge.md' %]] +--- +description: One-page checkout form covers providing customer information, addresses, payment and shipping methods. +edition: commerce +--- -[[= product_name_com =]] provides a multi-step checkout process that takes place on one page. +# Checkout + +[[= product_name =]] provides a multi-step checkout process that takes place on one page. ![](../img/checkout_1.png) diff --git a/docs/guide/checkout/checkout_api/checkout_controllers.md b/docs/guide/checkout/checkout_api/checkout_controllers.md index b88c13fbe7..bdcda6c44c 100644 --- a/docs/guide/checkout/checkout_api/checkout_controllers.md +++ b/docs/guide/checkout/checkout_api/checkout_controllers.md @@ -1,4 +1,4 @@ -# Checkout controllers [[% include 'snippets/commerce_badge.md' %]] +# Checkout controllers ## CheckoutController diff --git a/docs/guide/checkout/checkout_api/checkout_events.md b/docs/guide/checkout/checkout_api/checkout_events.md index 460f48763e..20f808e77b 100644 --- a/docs/guide/checkout/checkout_api/checkout_events.md +++ b/docs/guide/checkout/checkout_api/checkout_events.md @@ -1,4 +1,4 @@ -# Checkout events [[% include 'snippets/commerce_badge.md' %]] +# Checkout events The following events are dispatched during the checkout process: diff --git a/docs/guide/checkout/checkout_api/forms/checkout_delivery_address_form.md b/docs/guide/checkout/checkout_api/forms/checkout_delivery_address_form.md index c59fb23330..e40db6dd88 100644 --- a/docs/guide/checkout/checkout_api/forms/checkout_delivery_address_form.md +++ b/docs/guide/checkout/checkout_api/forms/checkout_delivery_address_form.md @@ -1,4 +1,4 @@ -# Delivery address form [[% include 'snippets/commerce_badge.md' %]] +# Delivery address form ## Model class diff --git a/docs/guide/checkout/checkout_api/forms/checkout_invoice_address_form.md b/docs/guide/checkout/checkout_api/forms/checkout_invoice_address_form.md index 2117436226..5ab4a1573f 100644 --- a/docs/guide/checkout/checkout_api/forms/checkout_invoice_address_form.md +++ b/docs/guide/checkout/checkout_api/forms/checkout_invoice_address_form.md @@ -1,4 +1,4 @@ -# Invoice address form [[% include 'snippets/commerce_badge.md' %]] +# Invoice address form ## Model class diff --git a/docs/guide/checkout/checkout_api/forms/checkout_shipping_payment_form.md b/docs/guide/checkout/checkout_api/forms/checkout_shipping_payment_form.md index 51f7d6f325..516eb09a21 100644 --- a/docs/guide/checkout/checkout_api/forms/checkout_shipping_payment_form.md +++ b/docs/guide/checkout/checkout_api/forms/checkout_shipping_payment_form.md @@ -1,4 +1,4 @@ -# Shipping payment form [[% include 'snippets/commerce_badge.md' %]] +# Shipping payment form ## Model Class diff --git a/docs/guide/checkout/checkout_api/forms/checkout_summary_form.md b/docs/guide/checkout/checkout_api/forms/checkout_summary_form.md index 1b7b216166..5dd47c2944 100644 --- a/docs/guide/checkout/checkout_api/forms/checkout_summary_form.md +++ b/docs/guide/checkout/checkout_api/forms/checkout_summary_form.md @@ -1,4 +1,4 @@ -# Checkout summary form [[% include 'snippets/commerce_badge.md' %]] +# Checkout summary form `CheckoutSummary` (`Ibexa\Platform\Commerce\Checkout\Form\CheckoutSummary`) extends `AbstractFormEntity` and manages the HTML form for order summary in checkout process. diff --git a/docs/guide/checkout/checkout_api/forms/configuration_for_checkout_forms.md b/docs/guide/checkout/checkout_api/forms/configuration_for_checkout_forms.md index e584fd8928..998f015590 100644 --- a/docs/guide/checkout/checkout_api/forms/configuration_for_checkout_forms.md +++ b/docs/guide/checkout/checkout_api/forms/configuration_for_checkout_forms.md @@ -1,4 +1,4 @@ -# Configuration for checkout forms [[% include 'snippets/commerce_badge.md' %]] +# Configuration for checkout forms You can use generic configuration for checkout forms to override the forms and form logic for your project. diff --git a/docs/guide/checkout/checkout_api/forms/order_summary.md b/docs/guide/checkout/checkout_api/forms/order_summary.md index 4bbd1e256a..241bf7c63b 100644 --- a/docs/guide/checkout/checkout_api/forms/order_summary.md +++ b/docs/guide/checkout/checkout_api/forms/order_summary.md @@ -1,4 +1,4 @@ -# Order summary [[% include 'snippets/commerce_badge.md' %]] +# Order summary Summary is the last step in the checkout process. In order to finish the process, the customer needs to accept terms and conditions. diff --git a/docs/guide/checkout/checkout_api/forms/services_for_checkout_forms.md b/docs/guide/checkout/checkout_api/forms/services_for_checkout_forms.md index f12c6bdd69..3981a1272c 100644 --- a/docs/guide/checkout/checkout_api/forms/services_for_checkout_forms.md +++ b/docs/guide/checkout/checkout_api/forms/services_for_checkout_forms.md @@ -1,4 +1,4 @@ -# Services for checkout forms [[% include 'snippets/commerce_badge.md' %]] +# Services for checkout forms ## CheckoutFormServiceInterface diff --git a/docs/guide/checkout/checkout_faq.md b/docs/guide/checkout/checkout_faq.md deleted file mode 100644 index 3bc08e78ee..0000000000 --- a/docs/guide/checkout/checkout_faq.md +++ /dev/null @@ -1,11 +0,0 @@ -# Checkout FAQ [[% include 'snippets/commerce_badge.md' %]] - - -## How can I change the subject of the confirmation mail? - -This translations can be edited in order to change the subject of the order confirmation mails: - -``` -siso_core.default.shop_owner_mail_subject: "common.shop_owner_mail_subject" -siso_core.default.sales_contact_mail_subject: "common.sales_contact_mail_subject" -``` diff --git a/docs/guide/checkout/checkout_templates.md b/docs/guide/checkout/checkout_templates.md index 0cfabbbc37..8cc3207297 100644 --- a/docs/guide/checkout/checkout_templates.md +++ b/docs/guide/checkout/checkout_templates.md @@ -1,4 +1,4 @@ -# Checkout templates [[% include 'snippets/commerce_badge.md' %]] +# Checkout templates ## Template list diff --git a/docs/guide/checkout/local_orders.md b/docs/guide/checkout/local_orders.md index a6a86f8d64..d2e28338e1 100644 --- a/docs/guide/checkout/local_orders.md +++ b/docs/guide/checkout/local_orders.md @@ -1,4 +1,8 @@ -# Local orders [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Local orders If the shop is not connected to an ERP system, the customer can still follow the order process and create a local order that generates an invoice PDF and sends it by mail. diff --git a/docs/guide/checkout/order_confirmation.md b/docs/guide/checkout/order_confirmation.md index 97f47a3944..523b4571c2 100644 --- a/docs/guide/checkout/order_confirmation.md +++ b/docs/guide/checkout/order_confirmation.md @@ -1,4 +1,9 @@ -# Order confirmation [[% include 'snippets/commerce_badge.md' %]] +--- +description: A finished order sends out a configurable confirmation email. +edition: commerce +--- + +# Order confirmation After finishing the checkout (in case of an electronic payment transaction after redirection from the payment provider) the user is redirected to the confirmation page. diff --git a/docs/guide/checkout/return_process.md b/docs/guide/checkout/return_process.md index c982ec53fb..4a315ce152 100644 --- a/docs/guide/checkout/return_process.md +++ b/docs/guide/checkout/return_process.md @@ -1,6 +1,11 @@ -# Return process [[% include 'snippets/commerce_badge.md' %]] +--- +description: Ibexa DXP offers a built-in product return process in its shop. +edition: commerce +--- -[[= product_name_com =]] offers a simple RMA (return merchandise authorization) process. +# Return process + +[[= product_name =]] offers a simple RMA (return merchandise authorization) process. The goal is to inform the user about the cancellation policies and give them the possibility to return their goods, including online returns. The cancellation process validates the input and sends an email to the shop administrator. diff --git a/docs/guide/clustering.md b/docs/guide/clustering.md index 389a5f27a7..0ad5e498ea 100644 --- a/docs/guide/clustering.md +++ b/docs/guide/clustering.md @@ -1,3 +1,7 @@ +--- +description: Clustering enables you to host one installation of Ibexa DXP on multiple servers. +--- + # Clustering Clustering in [[= product_name =]] refers to setting up your installation with several web servers for handling more load and/or for failover. @@ -15,7 +19,7 @@ is up to you and your performance needs. The minimal requirements are: -- [Shared HTTP cache (using Varnish)](http_cache.md#using-varnish) +- [Shared HTTP cache (using Varnish)](cache/symfony_reverse_proxy/#using-varnish-or-fastly) - [Shared persistence cache](#shared-persistence-cache) and [sessions](#shared-sessions) (using Redis or Memcached) - Shared database (using MySQL/MariaDB) - [Shared binary files](#shared-binary-files) (using NFS, or S3) @@ -24,7 +28,7 @@ For further details on requirements, see [Requirements page](../getting_started/ It is also recommended to use: -- [Solr](search/solr.md) for better search and performance +- [Solr](search/solr.md) or [Elasticsearch](search/elastic.md) for better search and performance - a CDN for improved performance and faster ping time worldwide - you can use Fastly, which has native support as HTTP cache and CDN. - active/passive database for failover @@ -52,7 +56,7 @@ On Ibexa Cloud (and Platform.sh) Redis is preferred and supported. ### Shared binary files -[[= product_name =]] supports multi-server setups by means of [custom IO handlers](file_management.md#the-dfs-cluster-handler). +[[= product_name =]] supports multi-server setups by means of [custom IO handlers](file_management/file_management.md#the-dfs-cluster-handler). They make sure that files are correctly synchronized among the multiple clients using the data. ## DFS IO handler @@ -185,7 +189,7 @@ ezplatform: #### Customizing the storage directory Earlier versions required the NFS adapter directory to be set to `$var_dir$/$storage_dir$` part for the NFS path. -This is no longer required (unless you plan to use [Legacy Bridge](https://github.com/ezsystems/LegacyBridge)), +This is no longer required, but the default prefix used to serve binary files still matches this expectation. If you decide to change this setting, make sure you also set `io.url_prefix` to a matching value. @@ -211,7 +215,7 @@ ezplatform: url_prefix: 'http://static.example.com/' ``` -You can read more about that on [Binary files URL handling](file_management.md#url-handling). +You can read more about that on [Binary files URL handling](file_management/handling_file_url.md#file-url-handling). ### Web server rewrite rules @@ -239,13 +243,13 @@ Place this before the include of `ez_params.d`/`ez_rewrite_params` in your vhost ## Migrating to a cluster setup If you are migrating an existing single-server site to a cluster setup, and not setting up clustering from scratch, you need to migrate your files. -Once you have configured your binarydata and metadata handlers, you can run the `ezplatform:io:migrate-files` command. +Once you have configured your binarydata and metadata handlers, you can run the `ibexa:io:migrate-files` command. You can also use it when you are migrating from one data handler to another, e.g. from NFS to Amazon S3. This command shows which handlers are configured: ``` -> php bin/console ezplatform:io:migrate-files --list-io-handlers +> php bin/console ibexa:io:migrate-files --list-io-handlers Configured meta data handlers: default, dfs, aws_s3 Configured binary data handlers: default, nfs, aws_s3 ``` @@ -253,7 +257,7 @@ Configured binary data handlers: default, nfs, aws_s3 You can do the actual migration like this: ``` -> php bin/console ezplatform:io:migrate-files --from=default,default --to=dfs,nfs --env=prod +> php bin/console ibexa:io:migrate-files --from=default,default --to=dfs,nfs --env=prod ``` The `--from` and `--to` values must be specified as `,`. @@ -268,7 +272,3 @@ While the command is running, the files should not be modified. To avoid surprises you should create a [backup](backup.md) and/or execute a dry run before doing the actual update, using the `--dry-run` switch. Since this command can run for a very long time, to avoid memory exhaustion run it in the production environment using the `--env=prod` switch. - -## Clustering using Amazon AWS S3 - -See [AWS S3 clustering](clustering_aws_s3.md). diff --git a/docs/guide/clustering_aws_s3.md b/docs/guide/clustering_aws_s3.md index 6b68e8ed40..1602d851e3 100644 --- a/docs/guide/clustering_aws_s3.md +++ b/docs/guide/clustering_aws_s3.md @@ -1,3 +1,7 @@ +--- +description: When you are using a clustering configuration, you can store binary files on Amazon AWS S3. +--- + # Clustering with Amazon AWS S3 When setting up clustering, you can use Amazon AWS S3 as a binary handler, @@ -30,7 +34,7 @@ The secret key cannot be retrieved again after the key has been created, so don' ## Set up [[= product_name =]] for AWS S3 -In your [[= product_name =]] root directory, run `php composer.phar require league/flysystem-aws-s3-v3`. +In your [[= product_name =]] root directory, run `php composer.phar require league/flysystem-aws-s3-v3:^1.0`. In your [[= product_name =]] configuration, e.g. `config/packages/ezplatform.yaml`, set up the AWS S3 client: ``` yaml @@ -52,9 +56,9 @@ oneup_flysystem: adapters: aws_s3_adapter: awss3v3: - client: amazon.s3_client + client: Aws\S3\S3Client bucket: my-bucket # Your bucket name - prefix: '%database_name%' + prefix: 'my-prefix' # Your custom prefix, for example: 'my_site' ``` In the same place, set up the binary data handler for the S3 adapter: @@ -91,5 +95,5 @@ Clear all caches and reload, and that's it. ## Migrate your existing binary data to S3 -You can [migrate existing binary data](../guide/clustering.md#migrating-to-a-cluster-setup) to S3 using the `php bin/console ezplatform:io:migrate-files` command +You can [migrate existing binary data](../guide/clustering.md#migrating-to-a-cluster-setup) to S3 using the `php bin/console ibexa:io:migrate-files` command which was added in [EZP-25946](https://jira.ez.no/browse/EZP-25946). diff --git a/docs/guide/config_back_office.md b/docs/guide/config_back_office.md deleted file mode 100644 index 67f0010f47..0000000000 --- a/docs/guide/config_back_office.md +++ /dev/null @@ -1,198 +0,0 @@ -# Back Office configuration - -## Copy subtree limit - -Copying large subtrees can cause performance issues, so you can limit the number of Content items -that can be copied at once using `ezplatform.system..subtree_operations.copy_subtree.limit` -in `config/packages/ezplatform_admin_ui.yaml`. - -The default value is `100`. You can set it to `-1` for no limit, -or to `0` to completely disable copying subtrees. - -You can copy subtree from CLI using the command: `bin/console ezplatform:copy-subtree `. - -## Pagination limits - -Default pagination limits for different sections of the Back Office can be defined through respective settings in -[`ezplatform_default_settings.yaml`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/config/ezplatform_default_settings.yaml#L7) - -You can set the pagination limit for user settings with the following configuration: - -``` yaml -ezplatform: - system: - default: - pagination_user: - user_settings_limit: 6 -``` - -## Default Locations - -Default Location IDs for Content structure, Media and Users in the menu are configured using the following settings: - -``` yaml -ezplatform: - system: - default: - location_ids: - content_structure: 2 - media: 43 - users: 5 -``` - -## Notification timeout - -To define the timeout for hiding Back-Office notification bars, per notification type, -use the following configuration (times are provided in milliseconds): - -``` yaml -ezplatform: - system: - admin: - notifications: - error: - timeout: 0 - warning: - timeout: 0 - success: - timeout: 5000 - info: - timeout: 0 -``` - -The values shown above are the defaults. `0` means the notification does not hide automatically. - -## Location for Form-uploaded files - -You can use Forms to enable the user to upload files. -The default Location for files uploaded in this way is `/Media/Files/Form Uploads`. -You can change it with the following configuration: - -``` yaml -ezplatform: - system: - default: - form_builder: - upload_location_id: 54 -``` - -This applies only if no specific Location is defined in the Form itself. - -## Date and time formats - -Users can set their preferred date and time formats in the User settings menu. -This format is used throughout the Back Office. - -You can set the list of available formats with the following configuration: - -``` yaml -ezplatform: - system: - : - user_preferences: - allowed_short_date_formats: - 'label for dd/MM/yyyy': 'dd/MM/yyyy' - 'label for MM/dd/yyyy': 'MM/dd/yyyy' - allowed_short_time_formats: - 'label for HH:mm' : 'HH:mm' - 'label for hh:mm a' : 'hh:mm a' - allowed_full_date_formats: - 'label for dd/MM/yyyy': 'dd/MM/yyyy' - 'label for MM/dd/yyyy': 'MM/dd/yyyy' - allowed_full_time_formats: - 'label for HH:mm': 'HH:mm' - 'label for hh:mm a': 'hh:mm a' -``` - -The default date and time format is set using: - -``` yaml -ezplatform: - system: - : - user_preferences: - short_datetime_format: - date_format: 'dd/mm/yyyy' - time_format: 'hh:mm' - full_datetime_format: - date_format: 'dd/mm/yyyy' - time_format: 'hh:mm' -``` - -You can also [format date and time](../extending/extending_date_and_time.md) by using Twig filters and PHP services. - -### Allowed formats - -The following subset of the [ICU date and time formats](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classSimpleDateFormat.html#details) is allowed: - -|Symbol|Meaning| -|---|---| -|y, yy, yyyy, Y, YY, YYYY|year| -|q, Q|quarter| -|M, MM, MMM, MMMM, L, LL, LLL, LLLL|month| -|w, WW|week| -|d, dd|day of the month| -|D, DDD|day of the year| -|E, EE, EEE, EEEE, EEEEEE, e, ee, eee, eeee, eeeeee, c, cc, ccc, cccc, cccccc|weekday| -|a|AM or PM| -|h, hh, H, HH, k, kk|hour| -|m, mm|minute| -|s, ss, S...|second| -|Z, ZZ, ZZZ, ZZZZZ|timezone| - -## Content Tree - -With this configuration you can: - -- define configuration for a SiteAccess or a SiteAccess group -- decide how many Content items are displayed in the tree -- set maximum depth of expanded tree -- hide Content Types -- set a tree root Location -- override Content Tree's root for specific Locations - -```yaml -ezplatform: - system: - # any SiteAccess or SiteAccess group - admin_group: - content_tree_module: - # defines how many children will be shown after expanding parent - load_more_limit: 15 - # users won't be able to load more children than that - children_load_max_limit: 200 - # maximum depth of expanded tree - tree_max_depth: 10 - # Content Types to display in Content Tree, value of '*' allows all CTs to be displayed - allowed_content_types: '*' - # Content Tree won't display these Content Types, can be used only when 'allowed_content_types' is set to '*' - ignored_content_types: - - post - - article - # ID of Location to use as tree root. If omitted - content.tree_root.location_id setting is used. - tree_root_location_id: 2 - # list of Location IDs for which Content Tree's root Location will be changed - contextual_tree_root_location_ids: - - 2 # Home (Content structure) - - 5 # Users - - 43 # Media -``` - -## Universal Discovery Widget (UDW) configuration - -The Universal Discovery Widget (UDW) can be found in [Extending UDW.](../extending/extending_udw.md) - -## Icon sets - -You can configure icon sets to be used per SiteAccess: - -``` yaml -ezplatform: - system: - : - assets: - icon_sets: - my_icons: /assets/images/icons/my_icons.svg - additional_icons: /assets/images/icons/additional_icons.svg - default_icon_set: my_icons -``` diff --git a/docs/guide/config_connector.md b/docs/guide/config_connector.md deleted file mode 100644 index 480fcb9ec8..0000000000 --- a/docs/guide/config_connector.md +++ /dev/null @@ -1,19 +0,0 @@ -# Connector configuration - -## DAM configuration - -You can configure a connection with a Digital Asset Management (DAM) system. - -``` yaml -ezplatform: - system: - default: - content: - dam: [ dam_name ] -``` - -The configuration for each connector depends on the requirements of the specific DAM system. - -You can create your own connectors, or use the provided example DAM connector for [Unsplash](https://unsplash.com/). - -To add the Unsplash connector to your system add the `ezsystems/ezplatform-connector-unsplash` bundle to your installation. diff --git a/docs/guide/config_dynamic.md b/docs/guide/config_dynamic.md deleted file mode 100644 index a9e88b4ea4..0000000000 --- a/docs/guide/config_dynamic.md +++ /dev/null @@ -1,92 +0,0 @@ -# Dynamic configuration - -## ConfigResolver - -Dynamic configuration is handled by a **ConfigResolver**. - -It exposes the `hasParameter()` and `getParameter()` methods. -You can use them to check the different *scopes* available for a given *namespace* to find the appropriate parameter. - -In order to work with the ConfigResolver, your dynamic settings must have the following name format: `..parameter.name`. - -``` yaml -parameters: - # Internal configuration - ezsettings.default.content.default_ttl: 60 - ezsettings.site_group.content.default_ttl: 3600 -  - # Here "myapp" is the namespace, followed by the SiteAccess name as the parameter scope - # Parameter "my_param" will have a different value in site_group and admin_group - myapp.site_group.my_param: value - myapp.admin_group.my_param: another value - # Defining a default value, for other SiteAccesses - myapp.default.my_param: Default value -``` - -Inside a controller, in `site_group` SiteAccess, you can use the parameters in the following way -(note that the same applies for `hasParameter()`): - -``` php -$configResolver = $this->getConfigResolver(); -  -// ezsettings is the default namespace, so no need to specify it -// The following will resolve ezsettings..content.default_ttl -// In the case of site_group, it will return 3600. -// Otherwise it will return the value for ezsettings.default.content.default_ttl (60) -$locationViewSetting = $configResolver->getParameter( 'content.default_ttl' ); - -// For you own namespace, you need to specify it, here as "myapp" -$myParamSetting = $configResolver->getParameter( 'my_param', 'myapp' ); -// $myParamSetting's value will be 'value' -  -// You can also force the scope by naming it explicitly (here as "admin_group") -$myParamSettingAdmin = $configResolver->getParameter( 'my_param', 'myapp', 'admin_group' ); -// $myParamSetting's value will be 'another value' -``` - -!!! tip - - To learn more about scopes, see [SiteAccess documentation](siteaccess.md#scope). - -Both `getParameter()` and `hasParameter()` can take three arguments: - -1. `$paramName` - the name of the parameter -2. `$namespace` - your application namespace, `myapp` in the previous example. If null, the default namespace will be used, which is `ezsettings` by default. -3. `$scope` - a SiteAccess name. If null, the current SiteAccess will be used. - -## Inject the ConfigResolver in your services - -Instead of injecting the whole ConfigResolver service, you may directly [inject your SiteAccess-aware (dynamic) settings into your own services](#dynamic-settings-injection). - -You can use the ConfigResolver in your own services whenever needed. -To do this, inject the `ezpublish.config.resolver` service: - -``` yaml -parameters: - my_service.class: App\Service -  -services: - my_service: - class: '%my_service.class%' - arguments: ['@ezpublish.config.resolver'] -``` - -``` php -namespace App; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -  -class Service -{ -  /** - * @var \eZ\Publish\Core\MVC\ConfigResolverInterface - */ - private $configResolver; -  - public function __construct( ConfigResolverInterface $configResolver ) - { - $this->configResolver = $configResolver; - $myParam = $this->configResolver->getParameter( 'my_param', 'myapp' ); - } -} -``` diff --git a/docs/guide/config_repository.md b/docs/guide/config_repository.md deleted file mode 100644 index 4578fda82c..0000000000 --- a/docs/guide/config_repository.md +++ /dev/null @@ -1,198 +0,0 @@ -# Content Repository configuration - -You can define several Repositories within a single application. However, you can only use one per site. - -## Repository connection - -### Using default values - -To use the default Repository connection, you do not need to specify its details: - -``` yaml -ezplatform: - repositories: - # Defining Repository with alias "main" - # Default storage engine is used, with default connection - # Equals to: - # main: { storage: { engine: legacy, connection: } } - main: ~ -``` - -!!! note "Legacy storage engine" - - Legacy storage engine is the default storage engine for the Repository. - - It uses [Doctrine DBAL](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) (Database Abstraction Layer). - Database settings are supplied by [DoctrineBundle](https://github.com/doctrine/DoctrineBundle). - As such, you can refer to [DoctrineBundle's documentation](https://github.com/doctrine/DoctrineBundle/blob/master/Resources/doc/configuration.rst#doctrine-dbal-configuration). - -If no Repository is specified for a SiteAccess or SiteAccess group, -the first Repository defined under `ezplatform.repositories` will be used: - -``` yaml -ezplatform: - repositories: - main: ~ - system: - # All members of site_group will use "main" Repository - # No need to set "repository", it will take the first defined Repository by default - site_group: - # ... -``` - -### Defining custom connection - -You can also explicitly define a custom Repository connection: - -``` yaml -doctrine: - dbal: - default_connection: my_connection_name - connections: - my_connection_name: - driver: pdo_mysql - host: localhost - port: 3306 - dbname: my_database - user: my_user - password: my_password - charset: UTF8MB4 - - my_second_connection_name: - driver: pdo_mysql - url: '%env(resolve:SECOND_DATABASE_URL)%' - charset: UTF8MB4 - - another_connection_name: - # ... - -ezplatform: - repositories: - first_repository: - storage: - engine: legacy - connection: my_connection_name - config: {} - # Configuring search is required when using Legacy search engine - search: - connection: my_connection_name - second_repository: - storage: - engine: legacy - connection: my_second_connection_name - config: {} - search: - connection: my_second_connection_name - another_repository: - storage: - engine: legacy - connection: another_connection_name - config: {} - search: - connection: another_connection_name - - # ... - - system: - my_first_siteaccess: - repository: first_repository - - my_second_siteaccess: - repository: second_repository -``` - -``` -# .env.local - -SECOND_DATABASE_URL=otherdb://otheruser:otherpasswd@otherhost:otherport/otherdbname?otherdbserversion -``` - -## Field groups configuration - -Field groups, used in content and Content Type editing, can be configured under the `repositories` key. -Values entered there are Field group *identifiers*: - -``` yaml -repositories: - default: - fields_groups: - list: [content, features, metadata] - default: content -``` - -These identifiers can be given human-readable values and can be translated. Those values are used when editing Content Types. -The translation domain is `ezplatform_fields_groups`. -This example in `translations/ezplatform_fields_groups.en.yaml` defines English names for Field groups: - -``` yaml -content: Content -metadata: Metadata -user_data: User data -``` - -## Limit of archived Content item versions - -`default_version_archive_limit` controls the number of archived versions per Content item that are stored in the Repository. -By default it is set to 5. This setting is configured in the following way (typically in `ezplatform.yaml`): - -``` yaml -ezplatform: - repositories: - default: - options: - default_version_archive_limit: 10 -``` - -This limit is enforced on publishing a new version and only covers archived versions, not drafts. - -!!! tip - - Don't set `default_version_archive_limit` too high. - In Legacy storage engine you will see performance degradation if you store too many versions. - The default value of 5 is the recommended value, but the less content you have overall, - the more you can increase this to, for instance, 25 or even 50. - -### Removing old versions - -You can use the `ezplatform:content:cleanup-versions` command to remove old content versions. - -The command takes the following optional parameters: - -- `status` or `t` - status of versions to remove: `draft`, `archived` or `all` -- `keep` or `k` - number of versions to keep -- `user` or `u` - the User that the command will be performed as. The User must have the `content/remove`, `content/read` and `content/versionread` Policies. By default the `administrator` user is applied. -- `excluded-content-types` - exclude versions of one or multiple Content Types from the cleanup procedure; separate multiple Content Types identifiers with the comma. - -`ezplatform:content:cleanup-versions --status --keep --user --excluded-content-types article,blog_post` - -For example, the following command removes archived versions as user `admin`, but leaves the 5 most recent versions: - -`ezplatform:content:cleanup-versions --status archived --keep 5 --user administrator` - -## User identifiers - -`ezplatform_default_settings.yaml` contains two settings that indicate which Content Types are treated like users and user groups: - -``` yaml -ezplatform: - system: - default: - user_content_type_identifier: [user] - user_group_content_type_identifier: [user_group] -``` - -You can override these settings if you have other Content Types that should be treated as users/user groups in the Back Office. -When viewing such Content in the Back Office you will be able to see e.g. the assigned Policies. - -## Top-level Locations - -You can change the default path for top-level Locations such as Content or Media in the Back Office, e.g.: - -```yaml -ezplatform: - system: - : - subtree_paths: - content: '/1/18/' - media: '/1/57/' -``` diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md deleted file mode 100644 index b3706e2294..0000000000 --- a/docs/guide/configuration.md +++ /dev/null @@ -1,77 +0,0 @@ -# Configuration - -[[= product_name =]] configuration is delivered using a number of dedicated configuration files. -It contains everything from selecting the content Repository to SiteAccesses to language settings. - -### Configuration format - -The recommended configuration format is YAML. It is used by default in the kernel (and in examples throughout the documentation). -However, you can also use XML or PHP formats for configuration. - -### Configuration files - -Configuration files are located in the `config` folder. -Configuration is provided per package in the `config/packages` folder, -and routes are defined per package in `config/routes`. - -`config/packages/ezplatform.yaml` contains basic configuration (coming from [ezplatform-kernel](https://github.com/ezsystems/ezplatform-kernel)). -It stores, among others, [SiteAccess](siteaccess.md) information and content view config. - -Other configuration is provided in respective files, e.g. `config/packages/ezplatform_admin_ui.yaml`, -`config/packages/ezplatform_http_cache.yaml`. - -Configuration can be made environment-specific using separate folders for each environment. -These files contain additional settings and point to the general (not environment-specific) configuration that is applied in other cases. - -!!! tip - - Read more about [how configuration is handled in Symfony](https://symfony.com/doc/5.0/best_practices/configuration.html). - -### Configuration handling - -!!! note - - Configuration is tightly related to the service container. - To fully understand it, you need to be familiar with [Symfony's service container](service_container.md) and [its configuration](https://symfony.com/doc/5.0/service_container.html#service-parameters). - -Basic configuration handling in [[= product_name =]] is similar to what is commonly possible with Symfony. -You can define key/value pairs in your configuration files. - -Internally and by convention, keys follow a **dot syntax**, where the different segments follow your configuration hierarchy. -Keys are usually prefixed by a *namespace* corresponding to your application. All kinds of values are accepted, including arrays and deep hashes. - -For configuration that is meant to be exposed to an end-user (or end-developer), -it's usually a good idea to also [implement semantic configuration.](https://symfony.com/doc/5.0/components/config/definition.html) - -Note that you can also [implement SiteAccess-aware semantic configuration](siteaccess.md#exposing-siteaccess-aware-configuration-for-your-bundle). - -For example: - -``` yaml -parameters: - myapp.parameter.name: someValue - myapp.boolean.param: true - myapp.some.hash: - foo: bar - an_array: [apple, banana, pear] -``` - -``` php -// Usage inside a controller -$myParameter = $this->container->getParameter( 'myapp.parameter.name' ); -``` - -## Configuration settings - -For specific configuration settings, see: - -- [Back Office configuration](config_back_office.md) -- [Repository configuration](config_repository.md) -- [Content views](content_rendering.md#configuring-views-the-viewprovider) -- [Multisite](multisite.md#configuring-multisite) -- [SiteAccess](siteaccess.md#configuring-siteaccesses) -- [Image variations](images.md#configuring-image-variations) -- [Logging and debug](devops.md#logging-and-debug-configuration) -- [Authentication](security.md#symfony-authentication) -- [Sessions](sessions.md#configuration) -- [Persistence cache](persistence_cache.md#configuration) diff --git a/docs/guide/configuration/config_dynamic.md b/docs/guide/configuration/config_dynamic.md new file mode 100644 index 0000000000..7bab92f133 --- /dev/null +++ b/docs/guide/configuration/config_dynamic.md @@ -0,0 +1,105 @@ +--- +description: Use the ConfigResolver to inject dynamic configuration into your services. +--- + +# Dynamic configuration + +## ConfigResolver + +Dynamic configuration is handled by a ConfigResolver. + +It exposes the `hasParameter()` and `getParameter()` methods. +You can use them to check the different *scopes* available for a given *namespace* to find the appropriate parameter. + +In order to work with the ConfigResolver, your dynamic settings must have the following name format: `..parameter.name`. + +``` yaml +parameters: + # Internal configuration + ezsettings.default.content.default_ttl: 60 + ezsettings.site_group.content.default_ttl: 3600 +  + # Here "myapp" is the namespace, followed by the SiteAccess name as the parameter scope + # Parameter "my_param" will have a different value in site_group and admin_group + myapp.site_group.my_param: value + myapp.admin_group.my_param: another value + # Defining a default value, for other SiteAccesses + myapp.default.my_param: Default value +``` + +Inside a controller, in `site_group` SiteAccess, you can use the parameters in the following way +(note that the same applies for `hasParameter()`): + +``` php +$configResolver = $this->getConfigResolver(); +  +// ezsettings is the default namespace, so no need to specify it +// The following will resolve ezsettings..content.default_ttl +// In the case of site_group, it will return 3600. +// Otherwise it will return the value for ezsettings.default.content.default_ttl (60) +$locationViewSetting = $configResolver->getParameter( 'content.default_ttl' ); + +// For you own namespace, you need to specify it, here as "myapp" +$myParamSetting = $configResolver->getParameter( 'my_param', 'myapp' ); +// $myParamSetting's value will be 'value' +  +// You can also force the scope by naming it explicitly (here as "admin_group") +$myParamSettingAdmin = $configResolver->getParameter( 'my_param', 'myapp', 'admin_group' ); +// $myParamSetting's value will be 'another value' +``` + +!!! tip + + To learn more about scopes, see [SiteAccess documentation](../multisite/multisite_configuration.md#scope). + +Both `getParameter()` and `hasParameter()` can take three arguments: + +1. `$paramName` - the name of the parameter +2. `$namespace` - your application namespace, `myapp` in the previous example. If null, the default namespace will be used, which is `ezsettings` by default. +3. `$scope` - a SiteAccess name. If null, the current SiteAccess will be used. + +## Inject ConfigResolver into services + +You can use the ConfigResolver in your own services whenever needed. +To do this, inject the `ezpublish.config.resolver` service: + +``` yaml +services: + App\Service: + arguments: ['@ezpublish.config.resolver'] +``` + +You can also use the [autowire feature]([[= symfony_doc =]]/service_container/autowiring.html), by type hinting against ConfigResolverInterface. + +!!! tip + + For more information about dependency injection, see [Service container](../../api/public_php_api.md#service-container). + +!!! note + + Do not store the retrieved config value unless you know what you are doing. + SiteAccess can change during code execution, which means you might work on the wrong value. + +``` php +namespace App; + +use eZ\Publish\Core\MVC\ConfigResolverInterface; +  +class Service +{ +  /** + * @var \eZ\Publish\Core\MVC\ConfigResolverInterface + */ + private $configResolver; +  + public function __construct( ConfigResolverInterface $configResolver ) + { + $this->configResolver = $configResolver; + } + + public function someMethodThatNeedConfig() + { + $configValue = $this->configResolver->getParameter('my_param', 'myapp'); + } +} +``` diff --git a/docs/guide/configuration/config_repository.md b/docs/guide/configuration/config_repository.md new file mode 100644 index 0000000000..762a6be250 --- /dev/null +++ b/docs/guide/configuration/config_repository.md @@ -0,0 +1,280 @@ +--- +description: Configure Repository connections, archive limits, Field groups and other settings. +--- + +# Repository configuration + +You can define several Repositories within a single application. However, you can only use one per site. + +## Repository connection + +### Using default values + +To use the default Repository connection, you do not need to specify its details: + +``` yaml +ezplatform: + repositories: + # Defining Repository with alias "main" + # Default storage engine is used, with default connection + # Equals to: + # main: { storage: { engine: legacy, connection: } } + main: ~ +``` + +!!! note "Legacy storage engine" + + Legacy storage engine is the default storage engine for the Repository. + + It uses [Doctrine DBAL](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/) (Database Abstraction Layer). + Database settings are supplied by [DoctrineBundle](https://github.com/doctrine/DoctrineBundle). + As such, you can refer to [DoctrineBundle's documentation](https://github.com/doctrine/DoctrineBundle/blob/master/Resources/doc/configuration.rst#doctrine-dbal-configuration). + +If no Repository is specified for a SiteAccess or SiteAccess group, +the first Repository defined under `ezplatform.repositories` will be used: + +``` yaml +ezplatform: + repositories: + main: ~ + system: + # All members of site_group will use "main" Repository + # No need to set "repository", it will take the first defined Repository by default + site_group: + # ... +``` + +#### Multisite URI matching with multi-Repository setup + +You can use only one Repository (database) per domain. +This does not prohibit using [different Repositories](../persistence_cache.md#multi-repository-setup) on different subdomains. +However, when using URI matching for multisite setup, all SiteAccesses sharing domain also need to share Repository. +For example: + +- `ibexa.co` domain can use `ibexa_repo` +- `doc.ibexa.co` domain can use `doc_repo` + +But the following configuration would be invalid: + +- `ibexa.co` domain can use `ibexa_repo` +- `ibexa.co/doc` **cannot** use `doc_repo`, as it is under the same domain. + +Invalid configuration causes problems for different parts of the system, +for example back-end UI, REST interface and other non-SiteAccess-aware Symfony routes +such as `/_fos_user_context_hash` used by [HTTP cache](../cache/http_cache.md). + +### Entity manager + +If you use the [Doctrine entity manager](https://www.doctrine-project.org/projects/doctrine-orm/en/2.10/tutorials/getting-started.html#obtaining-the-entitymanager), +you are unable to connect different SiteAccesses to different databases. + +To have this possibility, you need to use the SiteAccess-aware entity manager: `ibexa.doctrine.orm.entity_manager`. + +To inject your entities into the SiteAccess-aware entity manager, use the following configuration: + +``` yaml +ezplatform: + orm: + entity_mappings: + EzPublishCoreBundle: + is_bundle: true + type: annotation + dir: Entity + prefix: eZ\Bundle\EzPublishCoreBundle\Entity +``` + +Refer to [DoctrineBundle documentation](https://symfony.com/doc/3.4/reference/configuration/doctrine.html) +for more information. + +!!! note + + In contrast with DoctrineBundle, when using the SiteAccess-aware entity manager you need to explicitly set all options: + `dir` (it still accepts relative path in case of bundles), `prefix`, `type`, and `is_bundle`. + +### Defining custom connection + +You can also explicitly define a custom Repository connection: + +``` yaml +doctrine: + dbal: + default_connection: my_connection_name + connections: + my_connection_name: + driver: pdo_mysql + host: localhost + port: 3306 + dbname: my_database + user: my_user + password: my_password + charset: UTF8MB4 + + my_second_connection_name: + driver: pdo_mysql + url: '%env(resolve:SECOND_DATABASE_URL)%' + charset: UTF8MB4 + + another_connection_name: + # ... + +ezplatform: + repositories: + first_repository: + storage: + engine: legacy + connection: my_connection_name + config: {} + # Configuring search is required when using Legacy search engine + search: + connection: my_connection_name + second_repository: + storage: + engine: legacy + connection: my_second_connection_name + config: {} + search: + connection: my_second_connection_name + another_repository: + storage: + engine: legacy + connection: another_connection_name + config: {} + search: + connection: another_connection_name + + # ... + + system: + my_first_siteaccess: + repository: first_repository + + my_second_siteaccess: + repository: second_repository +``` + +``` +# .env.local + +SECOND_DATABASE_URL=otherdb://otheruser:otherpasswd@otherhost:otherport/otherdbname?otherdbserversion +``` + +## Field groups configuration + +Field groups, used in content and Content Type editing, can be configured under the `repositories` key. +Values entered there are Field group *identifiers*: + +``` yaml +repositories: + default: + fields_groups: + list: [content, features, metadata] + default: content +``` + +These identifiers can be given human-readable values and can be translated. Those values are used when editing Content Types. +The translation domain is `ezplatform_fields_groups`. +This example in `translations/ezplatform_fields_groups.en.yaml` defines English names for Field groups: + +``` yaml +content: Content +metadata: Metadata +user_data: User data +``` + +To see your translations, you need to change your [Back Office language](../back_office_translations.md#changing-back-office-languages) accordingly. + +## Limit of archived content versions + +`default_version_archive_limit` controls the number of archived versions per Content item that are stored in the Repository. +By default it is set to 5. This setting is configured in the following way (typically in `ezplatform.yaml`): + +``` yaml +ezplatform: + repositories: + default: + options: + default_version_archive_limit: 10 +``` + +This limit is enforced on publishing a new version and only covers archived versions, not drafts. + +!!! tip + + Don't set `default_version_archive_limit` too high. + In Legacy storage engine you will see performance degradation if you store too many versions. + The default value of 5 is the recommended value, but the less content you have overall, + the more you can increase this to, for instance, 25 or even 50. + +### Removing versions on publication + +With `remove_archived_versions_on_publish` setting, you can control whether versions that exceed the limit are deleted when you publish a new version. + +``` yaml +ezplatform: + repositories: + default: + options: + remove_archived_versions_on_publish: true +``` + +`remove_archived_versions_on_publish` is set to `true` by default. +Set it to `false` if you have multiple older versions of content and need to avoid performance drops when publishing. + +When you set the value to `false`, run [`ibexa:content:cleanup-versions`](#removing-old-versions) periodically +to make sure that Content item versions that exceed the limit are removed. + +### Removing old versions + +You can use the `ibexa:content:cleanup-versions` command to remove old content versions. + +The command takes the following optional parameters: + +- `status` or `t` - status of versions to remove: `draft`, `archived` or `all` +- `keep` or `k` - number of versions to keep +- `user` or `u` - the User that the command will be performed as. The User must have the `content/remove`, `content/read` and `content/versionread` Policies. By default the `administrator` user is applied. +- `excluded-content-types` - exclude versions of one or multiple Content Types from the cleanup procedure; separate multiple Content Types identifiers with the comma. + +`ibexa:content:cleanup-versions --status --keep --user --excluded-content-types article,blog_post` + +For example, the following command removes archived versions as user `admin`, but leaves the 5 most recent versions: + +`ibexa:content:cleanup-versions --status archived --keep 5 --user administrator` + +## User identifiers + +`ezplatform_default_settings.yaml` contains two settings that indicate which Content Types are treated like users and user groups: + +``` yaml +ezplatform: + system: + default: + user_content_type_identifier: [user] + user_group_content_type_identifier: [user_group] +``` + +You can override these settings if you have other Content Types that should be treated as users/user groups in the Back Office. +When viewing such Content in the Back Office you will be able to see e.g. the assigned Policies. + +## Top-level Locations + +You can change the default path for top-level Locations such as Content or Media in the Back Office, e.g.: + +```yaml +ezplatform: + system: + : + subtree_paths: + content: '/1/18/' + media: '/1/57/' +``` + +## Content Scheduler snapshots + +Content Scheduler snapshots speed up the rendering of Content Scheduler blocks and reduce the space used in the database. +By default, five snapshots are stored, but you can modify this number with the following configuration, +depending on the complexity of the Content Scheduler blocks: + +``` yaml +parameters: + ezplatform.fieldtype.ezlandingpage.block.schedule.snapshots.amount: 10 +``` diff --git a/docs/guide/configuration/configuration.md b/docs/guide/configuration/configuration.md new file mode 100644 index 0000000000..1f2cef494e --- /dev/null +++ b/docs/guide/configuration/configuration.md @@ -0,0 +1,80 @@ +--- +description: In Ibexa DXP you store and manage configuration in project files, typically in YAML format. +--- + +# Configuration + +[[= product_name =]] configuration is delivered using a number of dedicated configuration files. +It contains everything from selecting the content Repository to SiteAccesses to language settings. + +### Configuration format + +The recommended configuration format is YAML. It is used by default in the kernel (and in examples throughout the documentation). +However, you can also use XML or PHP formats for configuration. + +### Configuration files + +Configuration files are located in the `config` folder. +Configuration is provided per package in the `config/packages` folder, +and routes are defined per package in `config/routes`. + +`config/packages/ezplatform.yaml` contains basic configuration (coming from [ezplatform-kernel](https://github.com/ezsystems/ezplatform-kernel)). +It stores, among others, [SiteAccess](../multisite/multisite.md) information and content view config. + +Other configuration is provided in respective files, e.g. `config/packages/ezplatform_admin_ui.yaml`, +`config/packages/ezplatform_http_cache.yaml`. + +Configuration can be made environment-specific using separate folders for each environment. +These files contain additional settings and point to the general (not environment-specific) configuration that is applied in other cases. + +!!! tip + + Read more about [how configuration is handled in Symfony]([[= symfony_doc =]]/best_practices/configuration.html). + +### Configuration handling + +!!! note + + Configuration is tightly related to the [service container](../../api/public_php_api.md#service-container). + To fully understand it, you must be familiar with the service container and [its configuration]([[= symfony_doc =]]/service_container.html#service-parameters). + +Basic configuration handling in [[= product_name =]] is similar to what is commonly possible with Symfony. +You can define key/value pairs in your configuration files. + +Internally and by convention, keys follow a *dot syntax*, where the different segments follow your configuration hierarchy. +Keys are usually prefixed by a *namespace* corresponding to your application. All kinds of values are accepted, including arrays and deep hashes. + +For configuration that is meant to be exposed to an end-user (or end-developer), +it's usually a good idea to also [implement semantic configuration.]([[= symfony_doc =]]/components/config/definition.html) + +Note that you can also [implement SiteAccess-aware semantic configuration](../multisite/siteaccess_aware_configuration.md). + +For example: + +``` yaml +parameters: + myapp.parameter.name: someValue + myapp.boolean.param: true + myapp.some.hash: + foo: bar + an_array: [apple, banana, pear] +``` + +``` php +// Usage inside a controller +$myParameter = $this->container->getParameter( 'myapp.parameter.name' ); +``` + +## Configuration settings + +For specific configuration settings, see: + +- [Back Office configuration](../../extending/config_back_office.md) +- [Repository configuration](config_repository.md) +- [Content views](../content_rendering/templates/template_configuration.md) +- [Multisite configuration](../multisite/multisite_configuration.md) +- [Image variations](../images/images.md#configuring-image-variations) +- [Logging and debug](../devops.md#logging-and-debug-configuration) +- [Authentication](../security.md#symfony-authentication) +- [Sessions](../sessions.md#configuration) +- [Persistence cache](../persistence_cache.md#persistence-cache-configuration) diff --git a/docs/guide/content_management.md b/docs/guide/content_management.md index f6e87bc680..58bdc2975f 100644 --- a/docs/guide/content_management.md +++ b/docs/guide/content_management.md @@ -1,3 +1,7 @@ +--- +description: Locations hold published Content items and can be used to control visibility. +--- + # Content management ## Locations @@ -45,7 +49,7 @@ can be viewed by selecting the **Content structure** tab in the Content mode int ![Content structure](img/content_management_tree.png "Content structure") This part of the tree is typically used for organizing folders, articles, information pages, etc. -The default ID number of this Location is 2, but it can be [modified via configuration](config_repository.md#top-level-locations). +The default ID number of this Location is 2, but it can be [modified via configuration](configuration/config_repository.md#top-level-locations). It contains a Folder Content item. #### Media @@ -56,7 +60,7 @@ that is frequently used by Content items located below the **Content** node. ![Media](img/content_management_media.png "Media") It usually contains images, animations, documents and other files. -The default ID number of the **Media** Location is 43, but it can be [modified via configuration](config_repository.md#top-level-locations). +The default ID number of the **Media** Location is 43, but it can be [modified via configuration](configuration/config_repository.md#top-level-locations). It contains a Folder Content item. #### Users @@ -70,12 +74,12 @@ The Users are organized within User Group Content items below this Location. In other words, the **Users** Location contains the actual Users and User Groups, which can be viewed by selecting the **Users** tab in the Admin Panel. -The default ID number of the **Users** Location is 5, but it can be [modified via configuration](config_repository.md#top-level-locations). +The default ID number of the **Users** Location is 5, but it can be [modified via configuration](configuration/config_repository.md#top-level-locations). It contains a User Group Content item. #### Forms [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] -**Forms** is the top level Location that is intended for Forms created using the [Form Builder](https://doc.ezplatform.com/projects/userguide/en/master/creating_content_advanced/#forms). +**Forms** is the top level Location that is intended for Forms created using the [Form Builder](https://doc.ibexa.co/projects/userguide/en/3.3/creating_forms/). ![Forms](img/content_management_forms.png "Forms") @@ -90,8 +94,17 @@ Location visibility allows you to control which parts of the content tree are av ![Location visibility](img/content_management_visibility.png "Location visibility") Once a Content item is published, it cannot be un-published. -Limiting visibility is the only way to withdraw content from the website without moving it to Trash. -When the Location of a Content item is hidden, any access to it will be denied, preventing the system from displaying it. +When the Location of a Content item is hidden, the system will not display it on the website. + +!!! caution "Visibility and permissions" + + The [visibility switcher](https://doc.ibexa.co/en/latest/content_management/locations/#location-visibility) is a convenient feature for withdrawing content from the frontend. + It acts as a filter in the frontend by default. You can choose to respect it or ignore it in your code. + It isn't permission-based, and **doesn't restrict access to content**. Hidden content can be read through other means, like the REST API. + + If you need to restrict access to a given Content item, you could create a role that grants read access for a given [**Section**](https://doc.ibexa.co/en/latest/administration/content_organization/sections/) or + [**Object State**](https://doc.ibexa.co/en/latest/administration/content_organization/object_states/), and set a different Section or Object State for the given Content. + Or use other permission-based [**Limitations**](limitations.md). If a Content item is hidden, it is invisible in all its Locations. If a Location is hidden, all of its descendants in the tree will be hidden as well. diff --git a/docs/guide/content_model.md b/docs/guide/content_model.md index 51a03d5a91..47006f0b35 100644 --- a/docs/guide/content_model.md +++ b/docs/guide/content_model.md @@ -1,4 +1,8 @@ -# Content Model +--- +description: Ibexa DXP's content model relies on Content items that are instances of Content Types and contain content Fields. +--- + +# Content model ## Content model overview @@ -25,7 +29,7 @@ General information about a Content item is stored in a [`ContentInfo`](https:// **`id`** - the unique ID of the Content object. These numbers are not recycled, so if an item is deleted, its ID will not be reused when a new one is created. -**`contentTypeId`** - the unique ID of the Content Type on which the Content item is based. +**`contentTypeId`** - the unique numerical ID of the Content Type, on which the Content item is based. **`name`** - the name is generated automatically based on a [pattern specified in the Content Type definition](#content-name-pattern). The name is in the main language of the Content item. @@ -116,6 +120,52 @@ Each Content Type is characterized by a set of metadata which define the general ![Creating a new Content Type](img/admin_panel_new_content_type.png) +### Removing Content Types + +System Content Types are by default used for the File Uploads and removing them will cause errors. + +If you decide to remove a `file` or `image` Content Type, or change their identifiers, +you will need to change the configuration, so it reflects the available Content Types. + +Example configuration: + +```yaml +parameters: + ezplatform.multifile_upload.location.default_mappings: + # Image + - mime_types: + - image/jpeg + - image/jpg + - image/pjpeg + - image/pjpg + - image/png + - image/bmp + - image/gif + - image/tiff + - image/x-icon + - image/webp + content_type_identifier: custom_image_contenttype + content_field_identifier: image + name_field_identifier: name + # File + - mime_types: + - image/svg+xml + - application/msword + - application/vnd.openxmlformats-officedocument.wordprocessingml.document + - application/vnd.ms-excel + - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + - application/vnd.ms-powerpoint + - application/vnd.openxmlformats-officedocument.presentationml.presentation + - application/pdf + content_type_identifier: custom_file_contenttype + content_field_identifier: file + name_field_identifier: name + ezplatform.multifile_upload.fallback_content_type: + content_type_identifier: custom_file_contenttype + content_field_identifier: file + name_field_identifier: name +``` + ### Field definitions Aside from the metadata, a Content Type may contain any number of Field definitions (but has to contain at least one). @@ -125,7 +175,7 @@ They determine what Fields of what Field Types will be included in all Content i !!! note - You can assign each Field defined in a Content Type to a group by selecting one of the groups in the Category drop-down. [Available groups can be configured in the content repository](configuration.md#content-repository-configuration). + You can assign each Field defined in a Content Type to a group by selecting one of the groups in the Category drop-down. [Available groups can be configured in the content repository](config_repository.md). !!! caution diff --git a/docs/guide/content_rendering.md b/docs/guide/content_rendering.md deleted file mode 100644 index fe67e07707..0000000000 --- a/docs/guide/content_rendering.md +++ /dev/null @@ -1,314 +0,0 @@ -# Content Rendering - -## The ViewController - -[[= product_name =]] comes with a native controller to display your content, known as the **`ViewController`**. It is called each time you try to reach a Content item from its **URL alias** (human-readable, translatable URI generated for any content based on URL patterns defined per Content Type). It is able to render any content created in the admin interface or via the [Public API Guide](../api/public_php_api.md). - -It can also be called straight by its direct URI:  - -`/view/content//full/true/` - -`/view/content/` - -A Content item can also have different **view types** (full page, abstract in a list, block in a Page, etc.). By default the view type is **full** (for full page), but it can be anything (*line*, *block, etc*.). - -## Configuring views: the ViewProvider - -The **ViewProvider** allows you to configure template selection when using the `ViewController`, either directly from a URL or via a sub-request. - -#### Principle - -The ViewProvider takes its configuration from your SiteAccess in the `content_view` section. This configuration is [necessary for views to be defined](templates.md#templating-basics) and is a hash built in the following way: - -``` yaml -ezplatform: - system: - # Defines the scope: a valid SiteAccess, SiteAccess group or even "global" - front_siteaccess: - # Configuring the ViewProvider - content_view: - # The view type (full/line are standard, but you can use custom ones) - full: -  # A simple unique key for your matching ruleset - folderRuleset: - # The template identifier to load - template: full/small_folder.html.twig - # Hash of matchers to use, with their corresponding values to match against - match: - # The key defines the matcher rule (class name or service identifier) - # The value will be passed to the matcher's setMatchingConfig() method. - Identifier\ContentType: [small_folder, folder] -``` - -!!! caution "Template matching and non-existent Field Types" - - **Template matching will NOT work if your content contains a Field Type that is not supported by the repository**. It can happen when you are in the process of migrating from eZ Publish 4.x, where custom datatypes have been developed. - - In this case the repository will throw an exception, which is caught in the `ViewController`, and *if* you are using Legacy Bridge it will end up doing a [fallback to legacy kernel](https://doc.ez.no/display/EZP/Legacy+template+fallback). - - The list of Field Types supported out of the box [is available here](../api/field_type_reference.md). - -!!! tip - - You can define your template selection rules, alongside other settings, in a different bundle. - For details, see [Importing configuration from a bundle](organization.md#importing-configuration-from-a-bundle). - - You can also [use your own custom controller to render a Content/Location](controllers.md#custom-rendering-logic). - -## View Matchers - -To be able to select the right templates for the right conditions, the view provider uses matcher objects which implement the `eZ\Publish\Core\MVC\Symfony\View\ContentViewProvider\Configured\Matcher` interface. - -##### Matcher identifier - -The matcher identifier can comply to 3 different formats: - -1. **Relative qualified class name** (e.g. `Identifier\ContentType`). This is the most common case, it is used for native matchers. It is relative to `eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased`. -1. **Full qualified class name** (e.g. `\Foo\Bar\MyMatcher`). This is a way to specify a **custom matcher** that doesn't need specific dependency injection. Note that it **must** start with a backslash (`\`). -1. **Service identifier**, as defined in Symfony service container. This is the way to specify a **more complex custom matcher** that has dependencies. - -!!! note "Injecting the Repository" - - If your matcher needs the repository, make it implement `eZ\Publish\Core\MVC\RepositoryAwareInterface` or extend the `eZ\Publish\Core\MVC\RepositoryAware` abstract class. The repository will then be correctly injected before matching. - -##### Matcher value - -The value associated with the matcher is passed to its `setMatchingConfig()` method. The value can be anything that is supported by the matcher. - -!!! note - - Native matchers support both **scalar values** or **arrays of scalar values**. Passing an array amounts to applying a logical OR. - -##### Combining matchers - -It is possible to combine multiple matchers: - -``` yaml -# ... -match: - Identifier\ContentType: [small_folder, folder] - Identifier\ParentContentType: frontpage -``` - -The example above can be translated as "Match any content whose **ContentType** identifier is `small_folder` OR `folder` , **AND** having `frontpage` as **ParentContentType** identifier". - -#### Available matchers - -The following table presents all native matchers. - -|Identifier|Description| -|------|------| -|`Id\Content`|Matches the ID number of the Content item.| -|`Id\ContentType`|Matches the ID number of the Content Type that the Content item is an instance of.| -|`Id\ContentTypeGroup`|Matches the ID number of the group containing the Content Type that the Content item is an instance of.| -|`Id\Location`|Matches the ID number of a Location. *In the case of a Content item, matched against the main location.*| -|`Id\LocationRemote`|Matches the Remote ID number of a Location. *In the case of a Content item, matches against the main Location.*| -|`Id\ParentContentType`|Matches the ID number of the parent Content Type. *In the case of a Content item, matched against the main location.*| -|`Id\ParentLocation`|Matches the ID number of the parent Location. *In the case of a Content item, matched against the main location.*| -|`Id\Remote`|Matches the remoteId of either Content or Location, depending on the object matched.| -|`Id\Section`| Matches the ID number of the Section that the Content item belongs to.| -|`Identifier\ContentType`|Matches the identifier of the Content Type that the Content item is an instance of.| -|`Identifier\ParentContentType`|Matches the identifier of the parent Content Type. *In the case of a Content item, matched against the main Location.*| -|`Identifier\Section`|Matches the identifier of the Section that the Content item belongs to.| -|`Depth`|Matches the depth of the Location. The depth of a top level Location is 1.| -|`UrlAlias`|Matches the virtual URL of the Location (i.e. `/My/Content-Uri`). **Important: Matches when the UrlAlias of the Location starts with the value passed.** *Not supported for Content (aka content_view).*| - -### Custom matchers - -Beside the built-in matchers, you can also use your own services to match views: - -``` yaml -ezplatform: - system: - site: - content_view: - full: - folder: - template: folder.html.twig - match: - '@App\Matcher\MyMatcher': ~ -``` - -The service must be tagged with `ezplatform.view.matcher` -and must implement `\eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\ViewMatcherInterface`. - -### Content view templates - -#### Template variables - -Every content view offers a set of variables you can use in templates and controllers. - -You can view the whole list of variables by using the `dump()` Twig function in your template. -You can also dump a specific variable, for example `dump(location.id)`. - -Main content-related variables include: - -|Variable name|Type|Description| -|------|------|------| -|`content`|[eZ\Publish\Core\Repository\Values\Content\Content](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Repository/Values/Content/Content.php)|The Content item, containing all Fields and version information (VersionInfo)| -|`location`|[eZ\Publish\Core\Repository\Values\Content\Location](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Repository/Values/Content/Location.php)|The Location object. Contains meta information on the Content (ContentInfo) (only when accessing a Location) | -|`no_layout`|Boolean|If true, indicates if the Content item/Location is to be displayed without any pagelayout (i.e. AJAX, sub-requests, etc.). It's generally `false` when displaying a Content item in view type **full**.| -|`view_base_layout`|String|The base layout template to use when the view is requested to be generated outside of the pagelayout (when `no_layout` is true).| - -The `dump()` function also displays other variables, such as specific configuration including the SiteAccess -under the `ezplatform` key. - -#### Template inheritance and sub-requests - -Like any template, a content view template can use [template inheritance](http://symfony.com/doc/5.0/book/templating.html#template-inheritance-and-layouts). However keep in mind that your Content may be also requested via [sub-requests](https://symfony.com/doc/5.0/templates.html#embedding-controllers) (see below how to render [embedded Content items](templates.md#embedding-content-items)), in which case you probably don't want the global layout to be used. - -If you use different templates for embedded content views, this should not be a problem. If you'd rather use the same template, you can use an extra `no_layout` view parameter for the sub-request, and conditionally extend an empty pagelayout: - -``` html+twig -{% extends no_layout ? view_base_layout : "pagelayout.html.twig" %} - -{% block content %} -... -{% endblock %} -``` - -#### Default view templates - -Content view uses default templates to render content unless custom view rules are used. - -Those templates can be customized by means of container- and SiteAccess-aware parameters. - -##### Overriding the default template for common view types - -Templates for the most common view types (content/full, line, embed, or block) can be customized by setting one the `ezplatform.default_view_templates` variables: - -| Controller | ViewType | Parameter | Default value | -|---------------------------------------------------------|----------|---------------------------------------------------|---------------------------------------------------------| -| `ez_content::viewAction` | `full` | `ezplatform.default_view_templates.content.full` | `'@@EzPublishCore/default/content/full.html.twig'` | -| `ez_content::viewAction` | `line` | `ezplatform.default_view_templates.content.line` | `'@@EzPublishCore/default/content/line.html.twig'` | -| `ez_content::viewAction` | `embed` | `ezplatform.default_view_templates.content.embed` | `'@@EzPublishCore/default/content/embed.html.twig'` | -| `ez_page::viewAction` | `n/a` | `ezplatform.default_view_templates.block` | `'@@EzPublishCore/default/block/block.html.twig'` | - -###### Example - -Add this configuration to `config/packages/ezplatform_admin_ui.yaml` to use `templates/content/view/full.html.twig` as the default template when viewing content with the `full` view type: - -``` yaml -parameters: - ezplatform.default_view_templates.content.full: "content/view/full.html.twig" -``` - -Alternatively, you can do it using the [design engine](design_engine.md). For example, if your theme is `site`, -create `themes/site/default/content/.html.twig` to override the core template. - - -##### Customizing the default controller - -The controller used to render content by default can also be changed. The `ezsettings.default.content_view_defaults` container parameter contains a hash that defines how content is rendered by default. It contains a set of [content view rules for the common view types](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/EzPublishCoreBundle/Resources/config/default_settings.yml#L45-L73). This hash can be redefined to whatever suits your requirements, including custom controllers, or even matchers. - -### View providers - -The `ViewProvider` selects the right template (view type, correct parameters, etc.) for displaying a given Content item based on the configuration. - -The ViewProviders are objects implementing the `eZ\Publish\Core\MVC\Symfony\View\ViewProvider` interface. - -By default, the [Configured ViewProvider](#configuring-views-the-viewprovider) is used. It selects templates using the view configuration. - -#### Custom ViewProvider - -##### When to develop a custom `ViewProvider` - -- You want a custom template selection based on a very specific state of your application -- You depend on external resources for view selection -- You want to override the default one view provider (based on configuration) - -`ViewProvider` objects need to be properly registered in the service container with the `ezpublish.view_provider` service tag. - -``` yaml -services: - App\Content\MyViewProvider: - tags: - - {name: ezpublish.view_provider, type: eZ\Publish\Core\MVC\Symfony\View\ContentView, priority: 30} -``` - -`type` should point to a class implementing the `View\View` interface. It determines which type of View will be handled by the `ViewProvider`. Out of the box it's `eZ\Publish\Core\MVC\Symfony\View\ContentView`. - -`priority` is an integer giving the priority to the `ViewProvider`. The priority range is from -255 to 255. - -##### Example - -``` php -// Custom ViewProvider -getViewType(); - $location = $view->getLocation(); - // If you wish, you can also easily access Content object - // $content = $view->getContent(); - - // Let's check location Id - if ($location->id === self::HOMEPAGE_ID) { - // Special template for home page, passing "foo" variable to the template - return new ContentView("$viewType/home.html.twig", ['foo' => 'bar']); - } - - // And let's also check ContentType Id - if ($location->contentInfo->contentTypeId === self::FOLDER_CONTENT_TYPE_ID) { - // For view full, it will load full/small_folder.html.twig - return new ContentView("$viewType/small_folder.html.twig"); - } - - return null; - } -} -``` - -## Events - -This section presents the events that are triggered by [[= product_name =]]. - -### Core - -|Event name|Triggered when...|Usage| -|-------|------|------| -|`ezpublish.siteaccess`|After the SiteAccess matching has occurred.|Gives further control on the matched SiteAccess. The event listener method receives an `eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent` object.| -|`ezpublish.pre_content_view`|Right before a view is rendered for a Content item, via the content view controller.|This event is triggered by the view manager and allows you to inject additional parameters to the content view template. The event listener method receives an `eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent` object.| -|`ezpublish.api.contentException`|The API throws an exception that could not be caught internally (missing field type, internal error...).|This event allows further programmatic handling (like rendering a custom view) for the exception thrown. The event listener method receives an `eZ\Publish\Core\MVC\Symfony\Event\APIContentExceptionEvent object`.| - -## Reference - -### Twig Helper - -[[= product_name =]] comes with a Twig helper as a [global variable](http://symfony.com/doc/5.0/cookbook/templating/global_variables.html) named `ezplatform`. - -This helper is accessible from all Twig templates and allows you to easily retrieve useful information. - -|Property|Description| -|------|------| -|`ezplatform.siteaccess`|Returns the current SiteAccess.| -|`ezplatform.rootLocation`|Returns the root Location object.| -|`ezplatform.requestedUriString`|Returns the requested URI string (also known as semanticPathInfo).| -|`ezplatform.systemUriString`| Returns the "system" URI string. System URI is the URI for internal content controller. If current route is not a URL alias, then the current Pathinfo is returned.| -|`ezplatform.viewParameters`|Returns the view parameters as a hash.| -|`ezplatform.viewParametersString`|Returns the view parameters as a string.| -|`ezplatform.translationSiteAccess`|Returns the translation SiteAccess for a given language, or null if it cannot be found.| -|`ezplatform.availableLanguages`|Returns the list of available languages.| -|`ezplatform.configResolver`|Returns the config resolver.| diff --git a/docs/guide/content_rendering/assets.md b/docs/guide/content_rendering/assets.md new file mode 100644 index 0000000000..636475054d --- /dev/null +++ b/docs/guide/content_rendering/assets.md @@ -0,0 +1,53 @@ +--- +description: Add assets (CSS, JS, image and other files) to your site and manage them using Webpack Encore. +--- + +# Assets + +Assets enable you to add CSS, JS, image or other files to your project, +to style and customize its look and behavior. + +## Asset files + +To add assets to the project, provide asset files (such as CSS or JS files) +in the `assets` folder, for example under `assets/css` and `assets/js`. + +## Configure assets + +All asset files must be added to `webpack.config.js` in the root folder, +so that Webpack Encore can use them. +To do it, use `Encore.addStyleEntry` for CSS files and `Encore.addEntry` for other files, such as JS: + +``` js +Encore.addStyleEntry('style', [ + path.resolve(__dirname, './assets/css/style.css'), +]); + +Encore.addEntry('script', [ + path.resolve(__dirname, './assets/js/script.js'), +]); +``` + +## Include assets in templates + +To include assets in your templates, add them to the template's `` tag, +and provide the name of the asset entry you configured in `webpack.config.js`, for example: + +``` html+twig +{{ encore_entry_link_tags('style') }} + +{{ encore_entry_script_tags('script') }} +``` + +!!! note + + After you add the asset files, clear the cache and run `yarn encore `. + +To include a single asset file in your template, for example an image, +use the [`asset()`]([[= symfony_doc =]]/reference/twig_reference.html#asset) Twig function: + +``` html+twig + +``` + +Place the image file in the `public/assets/images` folder. diff --git a/docs/guide/content_rendering/design_engine/add_new_design.md b/docs/guide/content_rendering/design_engine/add_new_design.md new file mode 100644 index 0000000000..95727ae8b6 --- /dev/null +++ b/docs/guide/content_rendering/design_engine/add_new_design.md @@ -0,0 +1,112 @@ +--- +description: Add a new design for a special marketing campaign site. +--- + +# Add new design + +To create different designs for different version of the website, +you configure different sites based on the [SiteAccess](../../multisite/multisite.md) content. + +This example shows how to prepare a site for a "Summer Sale" marketing campaign +and provide it with a distinct design. + +## Configure a new SiteAccess + +First, in the SiteAccess configuration in `config/packages/ezplatform.yaml`, +add the `campaign` SiteAccess to the list under `ezplatform.siteaccess`: + +``` yaml +ezplatform: + siteaccess: + list: [site, campaign] + groups: + site_group: [site, campaign] + default_siteaccess: site +``` + +Adding the `campaign` SiteAccess to [`site_group`](../../multisite/multisite_configuration.md#siteaccess-groups) enables you to add common configuration for both SiteAccesses at the same time. + +!!! tip + + For details about configuring different site roots and matching SiteAccesses, see [Set up campaign SiteAccess](../../multisite/set_up_campaign_siteaccess.md). + +## Add themes + +Next, configure a new `summersale` design for this theme, also named `summersale`: + +``` yaml +[[= include_file('code_samples/front/add_design/config/packages/views.yaml', 0, 3) =]] +``` + +Ensure that the `campaign` site uses this design (while the default `site` uses the default `standard` design) + +``` yaml +[[= include_file('code_samples/front/add_design/config/packages/views.yaml', 4, 6) =]][[= include_file('code_samples/front/add_design/config/packages/views.yaml', 13, 19) =]] +``` + +## Add templates + +Now, create templates for the two sites. +Templates for the main site should be placed in `templates/themes/standard`, +and templates for the campaign site in `templates/themes/summersale`. + +First, modify the built-in general [page layout](../templates/template_configuration.md#page-layout) template for the standard site in `templates/themes/standard/pagelayout.html.twig` +by including a header and a footer section: + +``` html+twig hl_lines="3 8" +[[= include_file('code_samples/front/add_design/templates/themes/standard/pagelayout.html.twig', 18, 28) =]] +``` + +`@ezdesign` in the template paths points to a template relevant for the current design. +In case of `site`, the template used for the header is `templates/themes/standard/parts/header.html.twig`. + +Create both the header and the footer template, for example: + +``` html+twig +[[= include_file('code_samples/front/add_design/templates/themes/standard/parts/header.html.twig') =]] +``` + +``` html+twig +[[= include_file('code_samples/front/add_design/templates/themes/standard/parts/footer.html.twig') =]] +``` + +Now, create templates for content, for example for an article, that [extend the page layout](../templates/templates.md#connecting-templates): + +```html+twig +[[= include_file('code_samples/front/add_design/templates/themes/standard/full/article.html.twig') =]] +``` + +Configure the content view so that both sites, the main one and the campaign, use this template. +To do it, use the `site_group` that both sites belong to: + +``` yaml hl_lines="3 7" +[[= include_file('code_samples/front/add_design/config/packages/views.yaml', 4, 13) =]] +``` + +Now, create an Article Content item and preview it on the front page. +You should see the article with a header and footer that you defined for the main site. + +## Override templates + +Now, you need to override the header of the site to fit the campaign. +Create a separate `templates/themes/summersale/parts/header.html.twig` file with different content, for example: + +``` html+twig +[[= include_file('code_samples/front/add_design/templates/themes/summersale/parts/header.html.twig') =]] +``` + +Preview the Article through the `campaign` SiteAccess: `/campaign/`. +You can see that the page uses the campaign header, while the rest of the layout, including the footer, +is the same as in the main site. +This is because you defined `standard` design as fallback for this SiteAccess: + +``` yaml +[[= include_file('code_samples/front/add_design/config/packages/views.yaml', 0, 3) =]] +``` + +In this case, if the design engine cannot find a template for the current design, +it uses the template from the next configured design. +In the case above, the engine does not find the footer template for the `campaign` SiteAccess, +so it uses the one from `standard`. + +This way you do not need to provide all templates for a new design, but only those that you want to be different than the fallback one. diff --git a/docs/guide/content_rendering/design_engine/design_engine.md b/docs/guide/content_rendering/design_engine/design_engine.md new file mode 100644 index 0000000000..d9bb5cc008 --- /dev/null +++ b/docs/guide/content_rendering/design_engine/design_engine.md @@ -0,0 +1,85 @@ +--- +description: Design engine allows you to use different SiteAccess-aware themes in your site. +--- + +# Design engine + +You can use multiple different designs (themes) in your installation. +You can set up different designs per SiteAccess or SiteAccess group. + +Designs are configured under the `ezdesign.design_list` key: + +``` yaml +ezdesign: + design_list: + my_design: [theme1, theme2] + another_design: [theme3] +``` + +To indicate when to use a design, configure it under `ezplatform.system.`: + +``` yaml +ezplatform: + system: + : + design: my_design +``` + +Each scope can use only one design. + +!!! caution + + After you create a new folder with a design in `templates/themes`, + you must clear the cache (`php bin/console cache:clear`), even if you work in the dev environment. + +## Order of themes + +The order of themes in a design is important. +The design engine attempts to apply the first theme in configuration (for example, `theme1`). +If it cannot find the required template or asset in this theme, it proceeds to the next theme in the list (for example, `theme2`). + +You can use this behavior to override only some templates from the main theme of your website. +Do this, for example, when you create a SiteAccess with a special design for a campaign. + +## Additional configuration + +### Additional theme paths + +You can add any Twig template folder to the theme configuration. + +You can use it if you want to define templates from third-party bundles as part of one of your themes, +or to [override built-in shop templates](../templates/overriding_shop_templates.md). + +To do it, set the `ezdesign.templates_theme_paths` parameter: + +``` yaml +ezdesign: + design_list: + my_design: [my_theme] + templates_theme_paths: + my_theme: + - '%kernel.project_dir%/vendor///Resources/views' +``` + +Theme folders that you define have priority over the ones defined in `templates_theme_paths`. +This ensures that it is always possible to override a template at the application level. + +You can also add a global override folder, by listing paths without assigning them to a theme: + +``` yaml +ezdesign: + templates_override_paths: + - '%kernel.project_dir%/src/' +``` + +### Asset resolution + +In production environments, to improve performance, asset resolution is done at compilation time. +In development environments, assets are resolved at runtime. + +You can change this behavior by setting `disable_assets_pre_resolution`: + +``` yaml +ezdesign: + disable_assets_pre_resolution: true +``` diff --git a/docs/guide/content_rendering/embed_and_list_content/embed_content.md b/docs/guide/content_rendering/embed_and_list_content/embed_content.md new file mode 100644 index 0000000000..e485a34134 --- /dev/null +++ b/docs/guide/content_rendering/embed_and_list_content/embed_content.md @@ -0,0 +1,55 @@ +--- +description: Embed a Content item in another using query types or controllers. +--- + +# Embed related content + +To embed content in another Content item, you query for it in the Repository. +There are two ways to query for a Content item: + +- by using a [Query type](#embed-siblings-with-query-type) +- by writing a [custom controller](#embed-relations-with-a-custom-controller) + +## Embed siblings with Query type + +To render the Siblings of a Content item (other content under the same parent Location), use the [Siblings Query type](../queries_and_controllers/built-in_query_types.md#siblings). + +To do it, use the built-in `ez_query` controller's `contentQueryAction`: + +``` yaml +[[= include_file('code_samples/front/embed_content/config/packages/views.yaml', 8, 23) =]] +``` + +The results of the Siblings query are placed in the `items` variable, which you can use in the template: + +``` html+twig +[[= include_file('code_samples/front/embed_content/templates/themes/my_theme/full/blog_post.html.twig') =]] +``` + +## Embed Relations with a custom controller + +You can use a custom controller for any situation where Query types are not sufficient. + +``` yaml +[[= include_file('code_samples/front/embed_content/config/packages/views.yaml', 23, 30) =]] +``` + +This configuration points to a custom `RelationController` that should render all Articles with the `showContentAction()` method. + +``` php hl_lines="23 27 28" +[[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php') =]] +``` + +This controller uses the Public PHP API to get [the Relations of a Content item](../../../api/public_php_api_browsing.md#relations) (lines 27-28). + +The controller takes the custom parameter called `accepted_content_types` (line 23), +which is an array of Content Type identifiers that are rendered. + +This way you can control which Content Types you want to show or exclude. + +Finally, the controller returns the view with the results that were provided in the `items` parameter. +You can use this parameter as a variable in the template: + +``` html+twig +[[= include_file('code_samples/front/embed_content/templates/themes/my_theme/full/article.html.twig') =]] +``` diff --git a/docs/guide/content_rendering/embed_and_list_content/embed_product.md b/docs/guide/content_rendering/embed_and_list_content/embed_product.md new file mode 100644 index 0000000000..ffd24536fa --- /dev/null +++ b/docs/guide/content_rendering/embed_and_list_content/embed_product.md @@ -0,0 +1,31 @@ +# Embed product + +To render a product embedded in an article in the form of a product card, +first, you need to [override the existing shop templates](../templates/overriding_shop_templates.md): + +``` yaml +[[= include_file('code_samples/front/shop/override_navigation/config/packages/design.yaml') =]] +``` + +Next, in the view configuration, override the default `embed` view for products: + +``` yaml +[[= include_file('code_samples/front/shop/embed_product/config/packages/views.yaml', 13, 23) =]] +``` + +Create a `templates/themes/my_theme/embed/product_card.html.twig` template that renders the product's name, image and price wrapped in a link: + +``` html+twig +[[= include_file('code_samples/front/shop/embed_product/templates/themes/my_theme/embed/product_card.html.twig') =]] +``` + +This template uses the Catalog controller to render the product price. +To modify the way the controller displays the price, +override the built-in `Catalog/Subrequests/product.html.twig` template to render the price only, +without additional information such as stock. + +To do it, create a `templates/themes/my_theme/Catalog/Subrequests/product.html.twig` file: + +``` html+twig +[[= include_file('code_samples/front/shop/embed_product/templates/themes/my_theme/Catalog/Subrequests/product.html.twig') =]] +``` diff --git a/docs/guide/content_rendering/embed_and_list_content/list_content.md b/docs/guide/content_rendering/embed_and_list_content/list_content.md new file mode 100644 index 0000000000..cdf99611e5 --- /dev/null +++ b/docs/guide/content_rendering/embed_and_list_content/list_content.md @@ -0,0 +1,73 @@ +--- +description: Create and render a list of Content items, for example, content in a folder or blog posts in a blog. +--- + +# List content + +To render a list of Content items, for example, content in a folder, blog posts in a blog, and so on, +you can use one of two methods: + +- use a [Query type](#list-children-with-query-type) +- create a Content Type with a [Content Query Field](#list-children-in-content-query-field) + +## List children with Query type + +The following example shows how to render the children of a Folder. + +First, in the [content view configuration](../templates/template_configuration.md), add the following view for the Folder Content Type: + +``` yaml +[[= include_file('code_samples/front/list_content/config/packages/views.yaml', 8, 22) =]] +``` + +`controller` defines which controller is used to render the view. +In this example, it is the default [Query controller](../queries_and_controllers/content_queries.md). + +``` yaml +[[= include_file('code_samples/front/list_content/config/packages/views.yaml', 11, 12) =]] +``` + +`params` define that you want to render the content by using the [`Children` Query type](../queries_and_controllers/built-in_query_types.md#children). +This Query type automatically finds the children of the current Content item. +The results of the query are placed in the `items` variable, which you can use in templates. + +Then, place the following template in `templates/themes//full/folder.html.twig`: + +``` html+twig +[[= include_file('code_samples/front/list_content/templates/themes/my_theme/full/folder.html.twig') =]] +``` + +This template uses the [`ez_render()` Twig function](../twig_function_reference/content_twig_functions.md#ez_render) +to render every child of the folder with the default template for the `line` view. + +## List children in Content query Field + +A [Content query Field](../../../api/field_types_reference/contentqueryfield.md) is a Field that defines a query. +The following example shows how to use a Content query Field to render a Blog with its Blog Post children. + +First, create a Blog Content Type that contains a Content query Field with the identifier `query`. + +In the Field definition, select "Children" as the Query type. +Provide the `content` parameter that the Query type requires: + +``` +content: '@=content' +``` + +You can paginate the query results by checking the **Enable pagination** box and selecting a limit of results per page. + +Select the Content Type you want to render (in this case, Blog Post) as **Returned type**. + +Then, in the content view configuration, add the configuration under `content_query_field`: + +``` yaml +[[= include_file('code_samples/front/list_content/config/packages/views.yaml', 8, 9) =]][[= include_file('code_samples/front/list_content/config/packages/views.yaml', 22, 28) =]] +``` + +The `match` configuration matches both the Content Type and the identifier of the Content query Field. + +Finally, in the template `templates/themes/
    +{% endblock %} +``` + +This template uses the focal point information contained in the image's additional data +to position the background so that the focussed part of the image is displayed. diff --git a/docs/guide/content_rendering/image_variations.md b/docs/guide/content_rendering/image_variations.md new file mode 100644 index 0000000000..5cb7f18a19 --- /dev/null +++ b/docs/guide/content_rendering/image_variations.md @@ -0,0 +1,75 @@ +--- +description: Configure image variations to scale, crop and otherwise modify rendered images. +--- + +# Image variations + +With image variations you can render different versions of one image by means +of scaling, cropping and other filters. + +Built-in image variations include four versions that provide the image at a specific scale: +`tiny`, `small`, `medium`, and `large`. + +You can also create custom image variations. + +## Custom image variations + +Image variation configuration is [SiteAccess](../multisite/multisite.md)-aware. +Place it under the `image_variations` key per [scope](../multisite/multisite_configuration.md#scope): + +``` yaml +ezplatform: + system: + : + image_variations: + : + reference: null + filters: + : +``` + +Variation name must be unique. +It may contain characters, numbers, underscores (`_`) or hyphens (`-`), but no spaces. + +Each variation takes the following parameters: + +- `reference` - (optional) name of a reference variation to base the variation on. +If set to `null` or `~`, the variation takes the original image for reference. +- `filters` - array of variation filters and their parameters. + +## Available variation filters + +| Filter name | Parameters | Description | +|--------|------|----------| +| `geometry/scaledownonly` | `[width, height]` | Scales image down to fit the provided width/height. Preserves aspect ratio. | +| `geometry/scalewidthdownonly` | `[width]` | Scales image down to fit the provided width. Preserves aspect ratio. | +| `geometry/scaleheightdownonly` | `[height]` | Scales image down to fit the provided height. Preserves aspect ratio. | +| `geometry/scalewidth` | `[width]` | Scales image width, both up and down. Preserves aspect ratio. | +| `geometry/scaleheight` | `[height]` | Scales image height, both up and down. Preserves aspect ratio. | +| `geometry/scale` | `[width, height]` | Scales image size to the provided width and height, both up and down. Preserves aspect ratio. | +| `geometry/scaleexact` | `[width, height]` | Scales image to exactly fit the provided width and height. Does not preserve aspect ratio. | +| `geometry/scalepercent` | `[widthPercent, heightPercent]` | Scales width and height by the provided percent values. Does not preserve aspect ratio. | +| `geometry/crop` | `[width, height, startX, startY]` | Crops the image. The result has the provided width/height, starting at the provided startX/startY | +| `border` | `[thickBorderX, thickBorderY, color=#000]` | Adds a border around the image. Thickness is defined in px. Color is `#000` by default. | +| `filter/noise` | `[radius=0]` | Smooths the contours of an image (`imagick`/`gmagick` only). `radius` is in px. | +| `filter/swirl` | `[degrees=60]` | Swirls the pixels of the center of the image (`imagick`/`gmagick` only). `degrees` defaults to 60.| +| `resize` | {size: `[width, height]`} | Simple resize filter (provided by LiipImagineBundle). | +| `colorspace/gray` | N/A | Converts the image to grayscale. | + +!!! note + + After you change the image variation configuration, remove the existing variations + with the `liip:imagine:cache:remove` command and provide the variation name: + + ``` bash + php bin/console liip:imagine:cache:remove --filter=large + ``` + + Next, clear the cache. + + You can also remove all generated image variations: + + ``` bash + php bin/console liip:imagine:cache:remove -v + ``` + diff --git a/docs/guide/content_rendering/layout/add_breadcrumbs.md b/docs/guide/content_rendering/layout/add_breadcrumbs.md new file mode 100644 index 0000000000..37752348b6 --- /dev/null +++ b/docs/guide/content_rendering/layout/add_breadcrumbs.md @@ -0,0 +1,33 @@ +--- +description: Add and render a breadcrumbs element on your site. +--- + +# Add breadcrumbs + +To add breadcrumbs to your website, first prepare a general layout template in a `templates/themes//pagelayout.html.twig` file. + +This template can contain things such as header, menu, footer, as well as [assets](../assets.md) for the whole site, +and all other templates [extend](../templates/templates.md#connecting-templates) it. + +Then, to render breadcrumbs, create a `BreadcrumbController.php` file in `src/Controller`: + +``` php hl_lines="26 34" +[[= include_file('code_samples/front/layouts/breadcrumbs/src/Controller/BreadcrumbController.php') =]] +``` + +The controller uses the [Ancestor Search Criterion](../../search/criteria_reference/ancestor_criterion.md) +to find all Ancestors of the current Location (line 26). +It then places the ancestors in the `breadcrumbs` variable that you can use in the template. + +Next, call this controller from the page layout template and pass the current Location ID as a parameter: + +``` html+twig +[[= include_file('code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/pagelayout.html.twig', 0, 8) =]] +``` + +Finally, create a breadcrumb template in `templates/themes//parts/breadcrumbs.html.twig`, as indicated in the controller (line 34). +In this template, iterate over all breadcrumbs and render links to them: + +``` html+twig +[[= include_file('code_samples/front/layouts/breadcrumbs/templates/themes/my_theme/parts/breadcrumbs.html.twig') =]] +``` diff --git a/docs/guide/content_rendering/layout/add_forgot_password.md b/docs/guide/content_rendering/layout/add_forgot_password.md new file mode 100644 index 0000000000..11b86ee46d --- /dev/null +++ b/docs/guide/content_rendering/layout/add_forgot_password.md @@ -0,0 +1,40 @@ +--- +description: Add a "forgot password" form and configure templates for it. +--- + +# Add "forgot password" option + +The "forgot password" option allows users of a specific SiteAccess, admin or front, to request a password change. +You can customize the template used in the `/user/forgot-password` route. + +Follow the instructions to create and configure a "forgot password" form. + +Edit `config/packages/ezplatform.yaml` and add the following configuration: + +```yaml +ezplatform: + system: + : + user_forgot_password: + templates: + form: + mail: +``` + +Under the `templates` key, provide the path to templates responsible for rendering the forgot password form (`form`) and email (`mail`), +which users receive after they request a password change. + +The [default templates](https://github.com/ezsystems/ezplatform-user/tree/master/src/bundle/Resources/views) for forgot password form and email are located in `ezplatform-user/src/bundle/Resources/views`. +The [templates](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/views/themes/admin/account/forgot_password/) specific for the Back Office are in `ezplatform-admin-ui/src/bundle/Resources/views/themes/admin/account`. + +You can also modify [other user management templates](../../user_management/user_management.md#other-user-management-templates). + +To add a link redirecting to the reset password form, in the page layout template, provide the following code: + +```html+twig +{{ 'authentication.forgot_password'|trans|desc('Forgot password?') }} +``` + +You can customize the layout of templates according to your needs. + +For more information, see [Template documentation](../templates/templates.md). diff --git a/docs/guide/content_rendering/layout/add_login_form.md b/docs/guide/content_rendering/layout/add_login_form.md new file mode 100644 index 0000000000..5a236978d6 --- /dev/null +++ b/docs/guide/content_rendering/layout/add_login_form.md @@ -0,0 +1,135 @@ +--- +description: Customize the login form for new users in your site front end. +--- + +# Create login form + +You can create a login form for your users. +Follow the instruction below to create a template with login form. If you want to configure more options for example, password expiration, see [other user management templates](../../user_management/user_management.md#other-user-management-templates). + +First, make sure you have configured [login methods](../../user_management/user_management.md#login-methods). + +If you only want to change a template, in the `config/packages/views.yaml` add the following configuration: + +```yaml +ezpublish: + system: + my_siteaccess: + user: + login_template: '@ezdesign/Security/login.html.twig' +``` + +To add a link redirecting to the login form, in the page layout template, provide the following code: + +```html+twig +Log in +``` + +Next, add the template defined in the event. + +In `templates/themes//login`, create an `expired_credentials.html.twig` file: + +```html+twig +{% extends '@ezdesign/Security/base.html.twig' %} + +{%- block content -%} + +

    + {{ 'authentication.credentials_expired.message'|trans|desc( + 'For security reasons, your password has expired and needs to be changed. An email has been sent to you with instructions.' + ) }} +

    +

    + +

    +{%- endblock -%} +``` + +## Customize login form + +You can use a custom template for example to display information about password expiration +or to customize [other user management templates](../../user_management/user_management.md#other-user-management-templates). + +In case of more advanced template customization, you can use a subscriber, +for example in `src/EventSubscriber/LoginFormViewSubscriber.php`: + +``` php hl_lines="23 35 40 42" + 'onPreContentView', + ]; + } + + public function onPreContentView(PreContentViewEvent $event): void + { + $view = $event->getContentView(); + + if (!($view instanceof LoginFormView)) { + return ; + } + + $view->addParameters([ + 'foo' => 'foo', + 'bar' => 'bar' + ]); + + if ($view->getLastAuthenticationException() instanceof CredentialsExpiredException) { + // View with instruction to unlock account + $view->setTemplateIdentifier('login/expired_credentials.html.twig'); + } + } +} +``` + +In the provided example, in line 23, the `PRE_CONTENT_VIEW` event is used. +You can also pass additional parameters to the view (line 35). +In this case, at the instance of exception (line 40), the subscriber displays the `expired_credentials.html.twig` template (line 42). + +Remember to provide a template and point to it in the subscriber +(in this case, in `templates/login/expired_credentials.html.twig`): + +```html+twig +{% extends '@ezdesign/Security/base.html.twig' %} + +{%- block content -%} + +

    + {{ 'authentication.credentials_expired.message'|trans|desc( + 'For security reasons, your password has expired and needs to be changed. An email has been sent to you with instructions.' + ) }} +

    +

    + +

    +{%- endblock -%} +``` + +For more information, see [Templates documentation](../templates/templates.md). diff --git a/docs/guide/content_rendering/layout/add_menu.md b/docs/guide/content_rendering/layout/add_menu.md new file mode 100644 index 0000000000..e60fe14b72 --- /dev/null +++ b/docs/guide/content_rendering/layout/add_menu.md @@ -0,0 +1,73 @@ +--- +description: Enrich you site front with a menu displaying selected Content items. +--- + +# Add navigation menu + +To add a navigation menu to your website, prepare a general layout template in a `templates/themes//pagelayout.html.twig` file. + +This template can contain things such as header, menu, footer, as well as [assets](../assets.md) for the whole site, +and all other templates [extend](../templates/templates.md#connecting-templates) it. + +To select items that should be rendered in the menu, you can use one of the following ways: + +- create a [query](#render-menu-using-a-query) +- create a [MenuBuilder](#create-a-menubuilder) + +## Render menu using a query + +To create a menu that contains a specific set of Content items, for example all content under the root Location, use a [Query Type](../queries_and_controllers/content_queries.md). + +First, in `src/QueryType`, create a custom `MenuQueryType.php` file that queries for all items that you want in the menu: + +``` php hl_lines="15 16 28" +[[= include_file('code_samples/front/layouts/menu/src/QueryType/MenuQueryType.php') =]] +``` + +In this case, it queries for all visible children of Location `2`, the root Location, (lines 15-16) +and renders them in order according to their Location priority. + +The Query Type has the name `Menu` (line 28). +You can use it in the template to render the menu. +Add the following [`ez_render_content_query` function](../twig_function_reference/content_twig_functions.md#ez_render_content_query) to the `pagelayout_html.twig` template: + +``` html+twig +[[= include_file('code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout.html.twig', 0, 7) =]] +``` + +Next, add the `templates/themes//pagelayout_menu.html.twig` template, +which renders the individual items of the menu: + +``` html+twig +[[= include_file('code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout_menu.html.twig') =]] +``` + +## Create a MenuBuilder + +To make a more configurable menu, where you select the specific items to render, +use the [KNPMenuBundle](https://github.com/KnpLabs/KnpMenuBundle) that is installed together with the product. + +To use it, first create a `MenuBuilder.php` file in `src/Menu`: + +``` php hl_lines="20 21 22 26" +[[= include_file('code_samples/front/layouts/menu/src/Menu/MenuBuilder.php') =]] +``` + +In the builder, you can define items that you want in the menu. +For example, lines 20-22 add a specific Location by using the [`ez_urlalias`](../twig_function_reference/url_twig_functions.md#ez_urlalias) route. +Line 26 adds a defined system route that leads to the search form. + +Next, register the menu builder as a service: + +``` yaml +App\Menu\MenuBuilder: + tags: + - {name: knp_menu.menu_builder, method: buildMenu, alias: root} +``` + +Finally, you can render the menu in `pagelayout.html.twig`. +Identify it by the name that you provided in the Menu Builder's `buildMenu()` method: + +``` html+twig +[[= include_file('code_samples/front/layouts/menu/templates/themes/my_theme/pagelayout.html.twig', 8, 9) =]] +``` diff --git a/docs/guide/content_rendering/layout/add_register_user_template.md b/docs/guide/content_rendering/layout/add_register_user_template.md new file mode 100644 index 0000000000..184fe3fb95 --- /dev/null +++ b/docs/guide/content_rendering/layout/add_register_user_template.md @@ -0,0 +1,75 @@ +--- +description: Customize the registration form for new users in your site front end. +--- + +# Create user registration form + +You can create a registration form for users to your website. +Follow the instructions below to create and customize templates for a registration form, and a registration confirmation page. + +First, make sure you [enabled user registration](../../permission_use_cases.md#register-users). + +Next, in the `config/packages/views.yaml` file add the following configuration: + +``` yaml +ezplatform: + system: + default: + user_registration: + templates: + form: '@ezdesign/user/registration_form.html.twig' + confirmation: '@ezdesign/user/registration_confirmation.html.twig' +``` +This defines which templates will be used for rendering the registration form and confirmation page. + +In the `templates/themes//user/registration_form.html.twig` create the template for registration form. + +Example registration form: + +``` html+twig +{% extends no_layout is defined and no_layout == true ? view_base_layout : page_layout %} +{% block content %} +
    + {{ form_start(form) }} + + {% for fieldForm in form.fieldsData %} + {% set fieldIdentifier = fieldForm.vars.data.fieldDefinition.identifier %} +
    + {{ form_widget(fieldForm.value, { + 'contentData': form.vars.data + }) }} +
    + {%- do fieldForm.setRendered() -%} + {% endfor %} + +
    +
    + {{ form_widget(form.register, {'attr': { + 'class': 'btn btn-block btn-primary' + }}) }} +
    +
    + + {{ form_end(form) }} +
    +{% endblock %} +``` + +In the `templates/themes//user/registration_confirmation.html.twig`, create the template for confirmation form. + +Example confirmation form: + +``` html+twig +{% extends no_layout is defined and no_layout == true ? view_base_layout : page_layout %} +{% block content %} +

    Your account has been created

    +

    + Thank you for registering an account. You can now login. +

    +{% endblock %} +``` +To add a link redirecting to the login form, in the page layout template, provide the following code: + +```html+twig +Register +``` diff --git a/docs/guide/content_rendering/layout/customize_basket.md b/docs/guide/content_rendering/layout/customize_basket.md new file mode 100644 index 0000000000..656c67981f --- /dev/null +++ b/docs/guide/content_rendering/layout/customize_basket.md @@ -0,0 +1,47 @@ +--- +description: Customize the template for the shop basket. +--- + +# Customize basket + +To customize the look of the basket, you override the default template. + +For example, to add a new column to the basket table that contains the VAT rate, +copy the existing `vendor/ezsystems/ezcommerce-checkout/src/bundle/Resources/views/themes/standard/Basket/page.html.twig` to `templates/themes//Basket/page.html.twig`. + +Next, modify the template to include the following changes: + +``` html+twig hl_lines="8 22 23 24 25 26" + + + {{ 'basket.page.table.product'|trans|desc('Product') }} + {{ 'basket.page.table.stock'|trans|desc('Stock') }} + {{ 'basket.page.table.quantity'|trans|desc('Quantity') }} + {{ 'basket.page.table.unit.price'|trans|desc('Unit price') }} + {{ 'basket.page.table.total.price'|trans|desc('Total price') }} + {{ 'basket.page.table.total.vat'|trans|desc('VAT') }} + {{ 'basket.page.table.total.actions'|trans|desc('Actions') }} + + + + + {% if line.remoteDataMap.isPriceValid is defined and line.remoteDataMap.isPriceValid %} + {% if showInclVat %} + {{ line.linePriceAmountGross|price_format(line.currency) }} + {% else %} + {{ line.linePriceAmountNet|price_format(line.currency) }} + {% endif %} + {% endif %} + + + {% if line.vat is defined and line.vat %} + {{ line.vat }} + {% endif %} + +``` + +Finally, for the template to be applied, you need to [override the existing shop templates](../templates/overriding_shop_templates.md): + +``` yaml +[[= include_file('code_samples/front/shop/override_navigation/config/packages/design.yaml') =]] +``` diff --git a/docs/guide/content_rendering/queries_and_controllers/built-in_query_types.md b/docs/guide/content_rendering/queries_and_controllers/built-in_query_types.md new file mode 100644 index 0000000000..d05f7f7c84 --- /dev/null +++ b/docs/guide/content_rendering/queries_and_controllers/built-in_query_types.md @@ -0,0 +1,142 @@ +--- +description: Use built-in Query types to quickly query Content items in templates. +--- + +# Built-in Query types + +## General Query type parameters + +All built-in Query types take the following optional parameters: + +- `limit` - maximum number of results to return +- `offset` - offset for search hits, used for paginating the results +- `sort` - [sort order](#sort-order) +- `filter` - additional query filters: + - `content_type` - return only results of given Content Types + - `visible_only` - return only visible results (default `true`) + - `siteaccess_aware` - return only results limited to the current SiteAccess root (default `true`) + +For example: + +``` yaml +params: + query: + query_type: 'Children' + parameters: + content: '@=content' + filter: + content_type: ['blog_post'] + visible_only: false + limit: 5 + offset: 2 + sort: 'content_name asc, date_published desc' + assign_results_to: items +``` + +### Sort order + +To provide a sort order to the `sort` parameter, use names of the Sort Clauses. +To find them, refer to [Sort Clause](../../search/sort_clause_reference/sort_clause_reference.md) +and the [relevant Sort Clause class](https://github.com/ezsystems/ezplatform-kernel/blob/1.3/eZ/Bundle/EzPublishCoreBundle/Resources/config/sort_spec.yml#L29) + +## Children + +The `Children` Query type retrieves children of the given Location. + +It takes `location` or `content` as parameters. + +``` yaml +params: + query: + query_type: 'Children' + parameters: + content: '@=content' + assign_results_to: items +``` + +!!! tip + + For an example of using the `Children` Query type, see [List content](../embed_and_list_content/list_content.md#list-children-with-query-type). + +## Siblings + +The `Siblings` Query type retrieves Locations that have the same parent as the provided Content item or Location. + +It takes `location` or `content` as parameters. + +``` yaml +params: + query: + query_type: 'Siblings' + parameters: + content: '@=content' + assign_results_to: items +``` + +!!! tip + + For an example of using the `Siblings` Query type, see [Embed related content](../embed_and_list_content/embed_content.md#embed-siblings-with-query-type). + +## Ancestors + +The `Ancestors` Query type retrieves all ancestors (direct parents and their parents) of the provided Location. + +It takes `location` or `content` as parameters. + +``` yaml +params: + query: + query_type: 'Ancestors' + parameters: + content: '@=content' + assign_results_to: items +``` + +## RelatedToContent + +The `RelatedToContent` Query type retrieves content that is a reverse relation to the provided Content item. + +!!! tip + + Reverse relations mean that the Query type shows Content items that are *related to* the provided Content item. + For example, if a blog post contains a link to an article, you can use a `RelatedToContent` query + to find the blog post from the article. + To find all relations of a Content item (in this example, all content that the blog post is related to), + refer to [Embed content](../embed_and_list_content/embed_content.md#embed-relations-with-a-custom-controller). + +It takes `content` or `field` as required parameters. +`field` indicates the Relation or RelationList Field that contains the Relations. + +``` yaml +params: + query: + query_type: 'RelatedToContent' + parameters: + content: '@=content' + field: 'relations' + assign_results_to: items +``` + +## GeoLocation + +The `GeoLocation` Query type retrieves content by distance of the location provided in a MapLocation Field. + +It takes the following parameters: + +- `field` - MapLocation Field identifier +- `distance` - distance to check for +- `latitude` and `longitude` - coordinates of the location to check distance to +- (optional) `operator` - operator to check value against, by default `<=` + +``` yaml +params: + query: + query_type: 'GeoLocation' + parameters: + field: 'location' + distance: 200 + latitude: '@=content.getFieldValue("location").latitude' + longitude: '@=content.getFieldValue("location").longitude' + operator: '<' + assign_results_to: items +``` diff --git a/docs/guide/content_rendering/queries_and_controllers/content_queries.md b/docs/guide/content_rendering/queries_and_controllers/content_queries.md new file mode 100644 index 0000000000..eed4c2b97e --- /dev/null +++ b/docs/guide/content_rendering/queries_and_controllers/content_queries.md @@ -0,0 +1,157 @@ +--- +description: Query content by using Query types and content query Field. +--- + +# Content queries + +With content queries you can find and render specific content according to criteria that you define. + +You can use queries to list or embed Content items, such as: + +- [children in a folder](../embed_and_list_content/list_content.md#children-query-type) +- related articles +- [most recent blog posts](custom_query_type.md) +- recommended products + +Content queries use the built-in Query controller which simplifies querying. +For more complex cases, you can build custom [controllers](controllers.md). + +## Query types + +The Query controller offers a set of [built-in Query types](built-in_query_types.md). +You can use them in the content view configuration, or in the [Content query Field](#content-query-field). + +You can also write [custom Query types](custom_query_type.md) for the cases that are not covered by the built-in ones. + +### Query type configuration + +To use a Query type, select the Query controller (`ez_query`) in the [content view configuration](../templates/template_configuration.md) +and select the Query type under `params.query.query_type`: + +``` yaml hl_lines="2 6" +folder: + controller: ez_query::contentQueryAction + template: '@ezdesign/full/folder.html.twig' + params: + query: + query_type: 'Children' + parameters: + content: '@=content' + assign_results_to: items + match: + Identifier\ContentType: folder +``` + +Use one of the following Query controller methods: + +- `locationQueryAction` runs a Location Search +- `contentQueryAction` runs a content Search +- `contentInfoQueryAction` runs a ContentInfo search +- `pagingQueryAction` returns a `PagerFanta` object and can be used to quickly [paginate query results](#pagination) + +See the [Search](../../search/search.md) documentation page for more details about different types of search. + +All Query types take the following parameters: + +- `query_type` is the name of the Query type to use. +- `parameters` can include: + - arbitrary values + - expressions based on the `content`, `location` and `view` variables. + For example, `@=location.id` is evaluated to the current Location's ID. +- `assign_results_to` declares the Twig variable that contains the search results. + +!!! tip + + Search results are a `SearchResult` object, which contains `SearchHit` objects. + To get the content or Locations that are in search results, you access the `valueObject` + of the `SearchHit`. + +### Pagination + +To paginate the results of a query, use the `pagingQueryAction` of the Query controller +and assign a limit per page in `params.query.limit`: + +``` yaml hl_lines="4 12" +[[= include_file('code_samples/front/query_pagination/config/packages/views.yaml', 8, 22) =]] +``` + +Use the [`pagerfanta`](https://www.babdev.com/open-source/packages/pagerfanta/docs/2.x/intro) function to render pagination controls: + +``` html+twig hl_lines="5 6 7 8" +[[= include_file('code_samples/front/query_pagination/templates/themes/my_theme/full/folder.html.twig') =]] +``` + +## Content query Field + +The [Content query Field](../../../api/field_types_reference/contentqueryfield.md) is a Field that defines a query. +The results of the query are available in the Field value. + +![Content query Field definition](../../img/content_query_field_definition.png) + +### Query type + +When adding the Field to a Content Type definition, select the Query type in the **Query type** dropdown. +All Query types in the application are available, both [built-in](built-in_query_types.md) and [custom ones](custom_query_type.md). + +### Returned types + +Select the Content Type of items you want to return in the **Returned type** dropdown. +To take it into account, your Query type must filter on the Content Type. +Provide the selected Content Type through the `returnedType` variable: + +``` +contentType: '@=returnedType' +``` + +### Pagination + +Select **Enable pagination** and set the number of items per page to paginate the results. + +You can override the pagination settings from Field definition +by setting the `enablePagination`, `disablePagination` or `itemsPerPage` parameters when rendering the Content query Field: + +``` html+twig +{{ ez_render_field(content, 'query', { + location: location|default(null), 'parameters': { + 'enablePagination': true, + 'itemsPerPage': 8 + } +}) }} +``` + +You can also define an offset for the results. +Provide the offset in the Query type, or in parameters: + +``` +offset: 3 +``` + +If pagination is disabled and an offset value is defined, the query's offset is added to the offset calculated for a page. +For example, with `offset` 5 and `itemsPerPage` 10, the first page starts with 5, the second page starts with 15, and so on. + +Without offset defined, pagination defines the starting number for each page. +For example, with `itemsPerPage` 10, first page starts with 0, second page starts with 10, and so on. + +### Parameters + +The following variables are available in parameter expressions: + +- `returnedType` - the identifier of the Content Type selected in the **Returned type** dropdown +- `content` - the current Content item +- `location` - the current Location of the Content item +- `mainLocation` - the main Location of the Content item +- `contentInfo` - the current Content item's ContentInfo + +### Content view configuration + +To render a Content query Field, in the content view configuration, use the `content_query_field` view type: + +``` yaml +[[= include_file('code_samples/front/list_content/config/packages/views.yaml', 8, 9) =]][[= include_file('code_samples/front/list_content/config/packages/views.yaml', 22, 28) =]] +``` + +The identifier of the Content query Field must be matched by +using the `'@EzSystems\EzPlatformQueryFieldType\eZ\ContentView\FieldDefinitionIdentifierMatcher'` matcher. + +Query results are provided to the template in the `items` variable. +See [List content](../embed_and_list_content/list_content.md#content-query-field) for an example of using the Content query Field. diff --git a/docs/guide/content_rendering/queries_and_controllers/controllers.md b/docs/guide/content_rendering/queries_and_controllers/controllers.md new file mode 100644 index 0000000000..3f85037eb3 --- /dev/null +++ b/docs/guide/content_rendering/queries_and_controllers/controllers.md @@ -0,0 +1,28 @@ +--- +description: Use controllers to customize rendering and querying content in your site. +--- + +# Controllers + +By configuring a controller you can modify and enhance the way in which the built-in content view controller renders content. + +You indicate which controller to use in the [content view configuration](../templates/template_configuration.md), under the `controller` key: + +``` yaml +[[= include_file('code_samples/front/embed_content/config/packages/views.yaml', 23, 26) =]][[= include_file('code_samples/front/embed_content/config/packages/views.yaml', 28, 30) =]] +``` + +``` php +[[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 2, 9) =]] +``` + +For a full example of using a custom controller, see [Embed content](../embed_and_list_content/embed_content.md#custom-controller). + +If you do not want to use the default view controller and only use a custom one, +use the same configuration, but do not provide the `template` key. +You have to indicate the template to use from the controller itself. + +!!! tip "Permissions for custom controllers" + + See [permission documentation](../../permissions.md#permissions-for-custom-controllers) for information + about access control for custom controllers. diff --git a/docs/guide/content_rendering/queries_and_controllers/custom_query_type.md b/docs/guide/content_rendering/queries_and_controllers/custom_query_type.md new file mode 100644 index 0000000000..d4e21afb7b --- /dev/null +++ b/docs/guide/content_rendering/queries_and_controllers/custom_query_type.md @@ -0,0 +1,68 @@ +--- +description: Create a Query type to search for content according to your custom needs. +--- + +# Create a custom Query type + +If you need to perform a more complex query than the [built-in Query types](built-in_query_types.md) allow, +you can create a custom Query type. + +The following example shows how to create a custom Query type +that renders the latest Content items of selected Types. + +First, add the following `LatestContentQueryType.php` file to `src/QueryType`: + +``` php +[[= include_file('code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php') =]] +``` + +!!! tip + + When the custom Query type is in the `App` namespace, like in the example above, it is registered automatically as a service. + Otherwise, register it with the `ezplatform.query_type` service tag. + +The name defined in `getName()` is the one you use to identify the Query type in content view configuration. + +``` php +[[= include_file('code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php', 10, 14) =]] +``` + +!!! caution + + Query type name must be unique. + +The `getQuery()` method constructs the query based on Search Criteria and Sort Clauses. +See [Content search](../../../api/public_php_api_search.md) for more information about queries +and [Search reference](../../search/criteria_reference/search_criteria_reference.md) for a reference of available Criteria and Sort Clauses. + +The `getSupportedParameters()` method provides the parameters you can set in content view configuration. + +``` php +[[= include_file('code_samples/front/custom_query_type/src/QueryType/LatestContentQueryType.php', 31, 35) =]] +``` + +!!! note + + To have more control over the details of parameters, use the [Options resolver-based Query type](#options-resolver-based-query-type). + +Then, in the content view configuration, indicate that the content view should use the custom Query type: + +``` hl_lines="10" +[[= include_file('code_samples/front/custom_query_type/config/packages/views.yaml', 8, 21) =]] +``` + +## Options resolver-based Query type + +Additionally, your custom Query type can extend the `OptionsResolverBasedQueryType` abstract class. +This gives you more flexibility when defining parameters. + +In the `configureOptions()` method you can define the allowed parameters, their types and default values. + +``` php hl_lines="34 35 36 37 38 39 40" +[[= include_file('code_samples/front/custom_query_type/src/QueryType/OptionsBasedLatestContentQueryType.php') =]] +``` + +!!! note + + In contrast with the previous example, a Query type that extends `OptionsResolverBasedQueryType` + must implement the `doGetQuery()` method instead of `getQuery()`. diff --git a/docs/guide/content_rendering/render_content/render_content.md b/docs/guide/content_rendering/render_content/render_content.md new file mode 100644 index 0000000000..a50ea89dae --- /dev/null +++ b/docs/guide/content_rendering/render_content/render_content.md @@ -0,0 +1,99 @@ +--- +description: Customize rendering of Content items on the site front end by using templates with proper content view configuration. +--- + +# Render content + +Content is rendered automatically by using default, basic templates. +To render content with a custom template, you create a template file +and inform the system, through configuration, when to use this template. + +You do it by using the [content view configuration](../templates/template_configuration.md). + +For example, to apply a custom template to all articles, use the following configuration: + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 4, 7) =]][[= include_file('code_samples/front/render_content/config/packages/views.yaml', 9, 15) =]] +``` + +This configuration defines a `full` view for all Content items that fulfill the conditions in `match`. +`match` indicates that all Content items with the Content Type `article` should use this configuration. +The indicated `template` is `@ezdesign/full/article.html.twig`. + +!!! tip "Designs" + + This configuration uses the [design engine](../design_engine/design_engine.md), as indicated by the `@ezdesign` in the template path. + In this example, the theme used by the design is `my_theme`. + + Using the design engine is recommended, but you can also set direct paths to templates, for example: + + ``` yaml + template: 'full/article.html.twig' + ``` + + You must then ensure that the `templates/full` folder contains the template file. + +The configuration requires that you add the `article.html.twig` template file to `templates/themes//full`, +in this example, `templates/themes/my_theme/full`. + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 3, 18) =]] +``` + +## Get content information + +To render general content information, such as content name, +use the [`ez_content_name()`](../twig_function_reference/content_twig_functions.md#ez_content_name) Twig function. + +Content name is based on the [content name pattern](../../content_model.md#content-name-pattern) of the Content Type. + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 3, 4) =]] +``` + +You can get general information about the content, Location and view parameters by using the [available variables](../templates/templates.md#template-variables). +For example, to get the publication date of the current Content item, use: + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 5, 6) =]] +``` + +[[= include_file('docs/snippets/rendering_dump_variable.md') =]] + +## Render Fields + +You can render a single Field of a Content item by using the [`ez_render_field()`](../twig_function_reference/field_twig_functions.md#ez_render_field) Twig function. +It takes the Content item and the identifier of the Field as arguments: + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 7, 8) =]] +``` + +You can pass additional arguments to this function, for example, an HTML class: + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 9, 14) =]] +``` + +### Field templates + +You can use a custom Field template by passing the template as an argument to [`ez_render_field()`](../twig_function_reference/field_twig_functions.md#ez_render_field): + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/full/article.html.twig', 15, 18) =]] +``` + +In this case you must place the `author.html.twig` template in `templates/themes//fields`, +for example `templates/themes/my_theme/fields`. + +``` html+twig +[[= include_file('code_samples/front/render_content/templates/themes/my_theme/fields/author.html.twig') =]] +``` + +The Field template must be placed in a block that corresponds to the Field Type identifier, +in this case `{% block ezauthor_field %}`. + +!!! tip "Template blocks" + + Twig blocks are used to include templates in one another. + For more information about relationships between templates, see [Connecting templates](../templates/templates.md#connecting-templates). diff --git a/docs/guide/content_rendering/render_content/render_page.md b/docs/guide/content_rendering/render_content/render_page.md new file mode 100644 index 0000000000..c4b6e55c99 --- /dev/null +++ b/docs/guide/content_rendering/render_content/render_page.md @@ -0,0 +1,85 @@ +--- +description: Prepare templates for Page layouts and render Page blocks. +edition: experience +--- + +# Render a Page + +Page is a special Content Type that contains a [Page Field](../../../api/field_types_reference/pagefield.md). + +A Page Field is a layout composed of zones. Each zone can contain multiple blocks. + +## Render a layout + +### Configure layout + +The default, built-in Page layout has only one zone. +You can create other layouts in configuration, under the `ezplatform_page_fieldtype.layouts` key. + +To create a new layout called "Right sidebar", use the following configuration: + +``` yaml +[[= include_file('code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml', 0, 13) =]] +``` + +### Add layout template + +A layout template renders all the zones of the layout. + +Each zone must have a `data-ez-zone-id` attribute with the number of the zone. + +The best way to display blocks in the zone is to iterate over a blocks array and render the blocks in a loop. +Each block must have the `landing-page__block block_{{ block.type }}` classes and the `data-ez-block-id="{{ block.id }}` attribute. + +To render the "Right sidebar" layout, add the following template to `templates/themes/my_theme/layouts/sidebar.html.twig`: + +``` html+twig hl_lines="5" +[[= include_file('code_samples/front/render_page/templates/themes/my_theme/layouts/sidebar.html.twig') =]] +``` + +## Render a block + +Every built-in Page block has a default template, [which you can override](#override-default-block-templates). +Every Page block can also have multiple other templates. +The editor chooses a template when creating a block in the Page Builder. + +### Block configuration + +You can add new block templates by using configuration, for example, for the Content List block: + +``` yaml +[[= include_file('code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml', 0, 1) =]][[= include_file('code_samples/front/render_page/config/packages/ezplatform_page_fieldtype.yaml', 13, 19) =]] +``` + +!!! tip + + Use the same configuration to provide a template for [custom blocks](../../page/create_custom_page_block.md) you create. + +### Block template + +Create the block template file in the provided path, for example, `templates/themes/my_theme/blocks/contentlist.html.twig`: + +``` html+twig +[[= include_file('code_samples/front/render_page/templates/themes/my_theme/blocks/contentlist.html.twig') =]] +``` + +### Override default block templates + +To override the default block template, create a new template. +Place it in a path that mirrors the original default template from the bundle folder. +For example: +`templates/bundles/EzPlatformPageFieldTypeBundle/blocks/contentlist.html.twig`. + +!!! tip + + To use a different file structure when overriding default templates, + add an import statement to the template. + + For example, in `templates/bundles/EzPlatformPageFieldTypeBundle/blocks/contentlist.html.twig`: + + ``` html+twig + {% import 'templates/blocks/contentlist/new_default.html.twig'} + ``` + + Then, place the actual template in the imported file `templates/blocks/contentlist/new_default.html.twig`. + diff --git a/docs/guide/content_rendering/render_content/render_product.md b/docs/guide/content_rendering/render_content/render_product.md new file mode 100644 index 0000000000..629e42b4ee --- /dev/null +++ b/docs/guide/content_rendering/render_content/render_product.md @@ -0,0 +1,24 @@ +--- +description: Prepare templates for rendering products from the catalog. +--- + +# Render a product + +To customize the template for a product, first, you need to [override the existing shop templates](../templates/overriding_shop_templates.md): + +``` yaml +[[= include_file('code_samples/front/shop/override_navigation/config/packages/design.yaml') =]] +``` + +The built-in templates that are responsible for rendering a product are stored in +`vendor/ezsystems/ezcommerce-shop-ui/src/bundle/Resources/views/themes/standard/Catalog`. +You can override any partial template in the folder. + +For example, to modify the general layout of the product page, create a +`templates/themes/my_theme/Catalog/product_layout.html.twig` file: + +``` html+twig +[[= include_file('code_samples/front/shop/render_product/templates/themes/my_theme/Catalog/product_layout.html.twig') =]] +``` + +In the template you can access the product via the `catalogElement` object. diff --git a/docs/guide/content_rendering/templates/custom_view_matcher.md b/docs/guide/content_rendering/templates/custom_view_matcher.md new file mode 100644 index 0000000000..e3af12c1d1 --- /dev/null +++ b/docs/guide/content_rendering/templates/custom_view_matcher.md @@ -0,0 +1,43 @@ +--- +description: You can create custom view matchers to configure template and controller usage for specific custom cases. +--- + +# Create custom view matcher + +In addition to the [built-in view matchers](view_matcher_reference.md), +you can also create custom matchers to use in [template configuration](template_configuration.md#view-rules-and-matching). + +To do it, create a matcher class that extends `eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued`. + +## Matcher class + +The matcher class must implement the following methods: + +- `matchLocation` - checks if a Location object matches. +- `matchContentInfo` - checks if a ContentInfo object matches. +- `match` - checks if the View object matches. + +The following example shows how to implement an `Owner` matcher. +This matcher identifies Content items that have the provided owner or owners. + +``` php hl_lines="48" +[[= include_file('code_samples/front/view_matcher/src/View/Matcher/Owner.php') =]] +``` + +The matcher checks whether the owner of the current content (by its ContentInfo or Location) +matches any of the values passed in configuration (line 48). + +## View configuration + +To apply the matcher in view configuration, indicate the matcher by its fully qualified class name, preceded by `\`. + +The following configuration uses a special template to render articles owned by the users with provided logins: + +``` yaml +[[= include_file('code_samples/front/view_matcher/config/packages/views.yaml') =]] +``` + +!!! note + + If you use a matcher that is a service instead of a simple class, + tag the service with `ezplatform.view.matcher`. diff --git a/docs/guide/content_rendering/templates/overriding_shop_templates.md b/docs/guide/content_rendering/templates/overriding_shop_templates.md new file mode 100644 index 0000000000..108830ba63 --- /dev/null +++ b/docs/guide/content_rendering/templates/overriding_shop_templates.md @@ -0,0 +1,29 @@ +# Overriding shop templates + +To override existing shop templates, first create a [design](../design_engine/design_engine.md) for your shop. + +``` yaml +[[= include_file('code_samples/front/shop/override_navigation/config/packages/design.yaml') =]] +``` + +!!! note "Template theme paths" + + All shop bundles contain an `ez_design.yml` file which is used to define the `templates_theme_path` path to the templates. + Without the [template theme path](../design_engine/design_engine.md#additional-theme-paths), the templates are not recognized by the design engine. + +Then, in `ezplatform.yaml`, indicate that the design should be used for the relevant [scope](../../multisite/multisite_configuration.md#scope), +for example, for the `site_group` SiteAccess group: + +``` yaml + site_group: + design: my_design +``` + +Finally, place your override templates in `templates/themes/`. + +For example, to override the left menu in the product catalog view, place the template in +`templates/themes/my_theme/Navigation/left_menu.html.twig`: + +``` html+twig +[[= include_file('code_samples/front/shop/override_navigation/templates/themes/my_theme/Navigation/left_menu.html.twig') =]] +``` diff --git a/docs/guide/content_rendering/templates/template_configuration.md b/docs/guide/content_rendering/templates/template_configuration.md new file mode 100644 index 0000000000..815a857a3b --- /dev/null +++ b/docs/guide/content_rendering/templates/template_configuration.md @@ -0,0 +1,92 @@ +--- +description: Template configuration defines which templates are used for which content and in what cases. +--- + +# Template configuration + +You configure how templates are used under the `content_view` key. + +The following example configuration defines template usage for several cases: + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 4, 8) =]][[= include_file('code_samples/front/render_content/config/packages/views.yaml', 9, 29) =]] +``` + +## Scope + +The content view configuration must be placed under `ezplatform.system.`. + +Scope defines the [SiteAccesses](../../multisite/multisite.md) for which the configuration is valid. +It may be a SiteAccess, a SiteAccess group, or one of the [generic configuration scopes](../../multisite/multisite_configuration.md#scope). + +## Page layout + +`pagelayout` defines the general layout of the whole site. +Other templates can [extend the page layout](#page-layout). + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 7, 8) =]] +``` + +## View types + +`content_view` defines rules for rendering content. +Rules are grouped per *view type*. + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 4, 8) =]][[= include_file('code_samples/front/render_content/config/packages/views.yaml', 9, 11) =]] +``` + +The default, built-in views are: + +- `full` - used when the Content item is displayed by itself, as a full page +- `line` - used when content is displayed as an item in a list, for example a list of the contents of a folder +- `text_linked` - used for a text section that is a link +- `embed` - used when one Content item is embedded in another, as a block +- `embed-inline` - used when a Content item is embedded inline in another +- `asset_image` - used when an image asset is embedded in another Content item + +The built-in views have built-in default templates. +You can define any other custom views. For each custom view, you must define a custom template. + +!!! tip "Direct path to previewing view types" + + You can preview content in a specific view type by using a direct path to the built-in view controller: + + `/view/content///true/` + + For example: + + `/view/content/55/embed/true/57` + +## View rules and matching + +Each rule must have a name unique per view type. +For each rule you must define the matching conditions. +The `match` key can contain one or more [view matchers](view_matcher_reference.md), including [custom ones](custom_view_matcher.md). + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 15, 20) =]] +``` + +`template` indicates which template to use. + +`controller` indicates which [controller](../queries_and_controllers/controllers.md) and which method to use when rendering the content. +You can use it together with the `template` key, or without it. + +`params` can provide additional parameters to the content view. +Use them, for example, with [Query types](../queries_and_controllers/content_queries.md#query-types) +or to provide [custom Twig variables](templates.md#custom-template-variables) to the template. + +### Combining matchers + +When you use more than one matcher in one rule, both conditions must match for the rule to apply. + +``` yaml +match: + Identifier\ContentType: [article, blog_post] + Identifier\Section: news +``` + +In the example above, content that is either an article or a blog post is matched, +but it must be in the "News" Section. diff --git a/docs/guide/content_rendering/templates/templates.md b/docs/guide/content_rendering/templates/templates.md new file mode 100644 index 0000000000..e4639f3702 --- /dev/null +++ b/docs/guide/content_rendering/templates/templates.md @@ -0,0 +1,110 @@ +--- +description: Ibexa DXP uses the Twig template engine to customize the rendering of content in the site. +--- + +# Templates + +You can customize the layout and look of your website with templates. +Templates use the Twig template engine. + +!!! tip + + Learn more about Twig templates from [Twig documentation](https://twig.symfony.com/doc/2.x/templates.html). + +## Connecting templates + +Templates can inherit from other templates. +Use this, for example, to inherit a general page layout including a [navigation menu](../layout/add_menu.md) in article templates. + +To inherit from other templates, a template must extend the parent templates +using the [`extends()`](https://twig.symfony.com/doc/3.x/tags/extends.html) Twig function. +To extend a parent template, the child template must contain Twig blocks. +These blocks are inserted in the parent template in relevant places. + +For example, to extend the [general layout of the page](template_configuration.md#view-rules-and-matching), which includes header, footer, navigation, and so on, +in the child template place the content in a `content` block: + +``` html+twig +{% extends '@ezdesign/pagelayout.html.twig' %} + +{% block content %} +{% endblock %} +``` + +The parent template (in this case, `pagelayout.html.twig`) must leave a place for this block: + +``` html+twig +{% block content %} +{% endblock %} +``` + +## Template variables + +In templates, you can use variables related to the current Content item, +as well as general variables related to the current view and general application settings. + +[[= include_file('docs/snippets/rendering_dump_variable.md') =]] + +Main variables include: + +|Variable |Description| +|------|------| +|`content`|Content item, containing all Fields and version information (VersionInfo). | +|`location`|Location object. Contains meta information on the Content (ContentInfo). | +|`ezplatform.siteaccess`| Current [SiteAccess](../../multisite/multisite.md). | +|`ezplatform.rootLocation`| Root Location object. | +|`ezplatform.requestedUriString`| Requested URI string. | +|`ezplatform.systemUriString`| System URI string. System URI is the URI for internal content controller. If the current route is not a URL alias, then the current PathInfo is returned. | +|`ezplatform.viewParameters`| View parameters as a hash. | +|`ezplatform.viewParametersString`| View parameters as a string. | +|`ezplatform.translationSiteAccess`| Translation SiteAccess for a given language (null if the SiteAccess cannot be found). | +|`ezplatform.availableLanguages`| List of available languages. | +|`ezplatform.configResolver`| [Config resolver](../../configuration/config_dynamic.md#configresolver). | + +### Custom template variables + +You can create custom Twig variables for use in templates. +Set the variables per SiteAccess or SiteAccess group ([scope](../../multisite/multisite_configuration.md#scope)), or per content view. + +To configure a custom template variable per scope, use the `twig_variables` key: + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 4, 7) =]][[= include_file('code_samples/front/render_content/config/packages/views.yaml', 31, 33) =]] +``` + +You can access this variable directly in all templates in that scope: + +``` html+twig +{{ custom_variable }} +``` + +Variables set for a specific content view (under `params`) are only available when this view is matched: + +``` yaml +[[= include_file('code_samples/front/render_content/config/packages/views.yaml', 24, 31) =]] +``` + +Custom variables can be nested: + +``` yaml +twig_variables: + custom_variable: + nested_variable: 'variable_value' +``` + +``` html+twig +{{ custom_variable.nested_variable }} +``` + +You can use [Symfony Expression language]([[= symfony_doc =]]/components/expression_language.html) +to access other values, for example: + +``` yaml +params: + custom_variable: "@=content.contentType.identifier" +``` + +!!! note + + A custom variable can overwrite an existing variable, + so it is good practice to avoid existing variable names such as `content` or `location`. diff --git a/docs/guide/content_rendering/templates/view_matcher_reference.md b/docs/guide/content_rendering/templates/view_matcher_reference.md new file mode 100644 index 0000000000..3611bec10e --- /dev/null +++ b/docs/guide/content_rendering/templates/view_matcher_reference.md @@ -0,0 +1,155 @@ +--- +description: View matchers are used in template configuration to decide when to use which template and controller. +--- + +# View matcher reference + +You can use the following matchers to [match content views](template_configuration.md#view-rules-and-matching): + +| Identifier | Matches | +|------|------| +| [`Id\Content`](#idcontent) | ID number of the Content item. | +| [`Id\ContentType`](#idcontenttype) | ID number of the Content Type that the Content item belongs to. | +| [`Identifier\ContentType`](#identifiercontenttype) | Identifier of the Content Type that the Content item belongs to. | +| [`Id\ContentTypeGroup`](#idcontenttypegroup) | ID number of the group containing the Content Type that the Content item belongs to. | +| [`Id\Location`](#idlocation) | ID number of a Location. | +| [`Id\LocationRemote`](#idlocationremote) | Remote ID number of a Location. | +| [`Id\ParentContentType`](#idparentcontenttype) | ID number of the parent Content Type. | +| [`Identifier\ParentContentType`](#identifierparentcontenttype) | Identifier of the parent Content Type. | +| [`Id\ParentLocation`](#idparentlocation) | ID number of the parent Location. | +| [`Id\Remote`](#idremote) | Remote ID of a Content item. | +| [`Id\Section`](#idsection) | ID number of the Section that the Content item belongs to. | +| [`Identifier\Section`](#identifiersection) | Identifier of the Section that the Content item belongs to. | +| [`Depth`](#depth) | Depth of the Location. The depth of a top level Location is 1. | +| [`UrlAlias`](#urlalias) | Virtual URL of the Location. | + +!!! tip + + You can also create [custom view matchers](custom_view_matcher.md). + +## Id\Content + +Matches the ID number of a Content item. + +``` yaml +match: + Id\Content: 145 +``` + +## Id\ContentType + +Matches the ID number of a Content Type that the Content item belongs to. + +``` yaml +match: + Id\ContentType: 2 +``` + +## Identifier\ContentType + +Matches the identifier of the Content Type that the Content item belongs to. + +``` yaml +match: + Identifier\ContentType: [blog_post] +``` + +## Id\ContentTypeGroup + +Matches the ID number of the Content Type Group that the Content item belongs to. + +``` yaml +match: + Id\ContentTypeGroup: 1 +``` + +## Id\Location + +Matches the ID number of a Location. In the case of a Content item, matched against the main Location. + +``` yaml +match: + Id\Location: 144 +``` + +## Id\LocationRemote + +Matches the Remote ID number of a Location. In the case of a Content item, matched against the main Location. + +``` yaml +match: + Id\LocationRemote: 5b1e33529082b68ad3a41b9089136a0a +``` + +## Id\ParentContentType + +Matches the ID number of the parent Content Type. In the case of a Content item, matched against the main Location. + +``` yaml +match: + Id\ParentContentType: 42 +``` + +## Identifier\ParentContentType + +Matches the identifier of the parent Content Type. In the case of a Content item, matched against the main Location. + +``` yaml +match: + Identifier\ParentContentType: blog +``` + +## Id\ParentLocation + +Matches the ID number of the parent Location. In the case of a Content item, matched against the main Location. + +``` yaml +match: + Id\ParentLocation: 2 +``` + +## Id\Remote + +Matches the remote ID number of a Content item. + +``` yaml +match: + Id\Remote: 145 +``` + +## Id\Section + +Matches the ID number of the Section that the Content item belongs to. + +``` yaml +match: + Id\Section: 1 +``` + +## Identifier\Section + +Matches the identifier of the Section that the Content item belongs to. + +``` yaml +match: + Identifier\Section: standard +``` + +## Depth + +Matches the depth of the Location. The depth of a top level Location is 1. + +``` yaml +match: + Depth: 2 +``` + +## UrlAlias + +Matches the virtual URL of the Location. +Matches when the URL alias of the Location starts with the value passed. + +``` yaml +match: + UrlAlias: 'terms-and-conditions' +``` diff --git a/docs/guide/content_rendering/twig_function_reference/content_twig_functions.md b/docs/guide/content_rendering/twig_function_reference/content_twig_functions.md new file mode 100644 index 0000000000..284b332de6 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/content_twig_functions.md @@ -0,0 +1,92 @@ +--- +description: Content Twig function enable rendering whole Content items and their information. +--- + +# Content Twig functions + +- [`ez_render()`](#ez_render) renders a Content item. +- [`ez_content_name()`](#ez_content_name) renders the name of a Content item. +- [`ez_render_content_query()`](#ez_render_content_query) renders the results of a non-content related query. +- [`ez_render_location_query()`](#ez_render_location_query) renders the results of a non-content related Location query. + +## Content rendering + +### `ez_render()` + +`ez_render()` renders the indicated Content item. + +It uses the `embed` view by default, but you can pass a different view as an argument. + +You can provide `ez_render()` with either a Content item or a Location object. + +!!! tip + + Depending on whether you pass a Content item or a Location object, + the helper automatically selects and uses one of internal Twig functions: + `ez_render_content()` or `ez_render_location()`. + +|Argument|Type|Description| +|------|------|------| +|`content`
    or
    `location`|`eZ\Publish\API\Repository\Values\Content\Content`
    or
    `eZ\Publish\API\Repository\Values\Content\Location`|Content item or its Location.| +|`method`|`string`|(optional) [Rendering method](#rendering-methods). One of: `direct`, `inline`, `esi`, `ssi`.| +|`viewType`|`string`|(optional) [View type](../templates/template_configuration.md#view-types).| + +#### Rendering methods + +You can pass one of the following rendering methods to `ez_render()`: + +- `direct` - (default) renders the Content item without using a request +- `inline` - Symfony inline rendering method, sends a request to the server and inserts the response +- `esi` - uses the Symfony [Edge Side Include mechanism]([[= symfony_doc =]]/http_cache/esi.html) to render the correct tag that is handled by the reverse proxy +- `ssi` - uses the Symfony [Server Side Include mechanism]([[= symfony_doc =]]/http_cache/ssi.html) to render the correct tag that is handled by the web server + +``` html+twig +{{ ez_render(location) }} + +{{ ez_render(content, {'viewType': 'line'}) }} + +{{ ez_render(content, {'method': 'inline'}) }} +``` + +## Content information + +### `ez_content_name()` + +`ez_content_name()` renders the name of a Content item. + +The function uses prioritized languages from SiteAccess settings unless you pass another language as `forcedLanguage`. +If the Content item does not have a translation in the prioritized or passed language, +the function returns the name in the main language. + +| Argument | Type | Description | +|---------------|------|-------------| +| `content` | `eZ\Publish\API\Repository\Values\Content\Content`
    or
    `eZ\Publish\API\Repository\Values\Content\ContentInfo` | Content item or its ContentInfo object.| +| `forcedLanguage` | `string` | (optional) Language to use (for example, `fre-FR`). | + +``` html+twig +{{ ez_content_name(content) }} + +{{ ez_content_name(content, 'pol-PL') }} +``` + +## Non-content related queries + +### `ez_render_content_query()` + +`ez_render_content_query` renders the results of a non-content related query made by using a Query type. + +|Argument|Type|Description| +|------|------|------| +|`options`|array|Available options are: `query`, `pagination`, `template`.| + +!!! tip + + For an example of using `ez_render_content_query`, see [Add navigation menu](../layout/add_menu.md#render-menu-using-a-query). + +### `ez_render_location_query()` + +`ez_render_location_query` renders the results of a non-content related Location query made by using a Query type. + +|Argument|Type|Description| +|------|------|------| +|`options`|array|Available options are: `query`, `pagination`, `template`.| diff --git a/docs/guide/content_rendering/twig_function_reference/date_twig_filters.md b/docs/guide/content_rendering/twig_function_reference/date_twig_filters.md new file mode 100644 index 0000000000..ece345afb5 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/date_twig_filters.md @@ -0,0 +1,27 @@ +--- +description: Use date Twig filters to select the date and time format used in templates. +--- + +# Date Twig filters + +Date and time Twig filters format a date and time object (`DateTimeInterface`) +in one of the formats defined in [user preferences](../../../extending/extending_date_and_time.md#using-user-settings-menu). + +- `ez_full_datetime` +- `ez_full_date` +- `ez_full_time` +- `ez_short_datetime` +- `ez_short_date` +- `ez_short_time` + +If the `DateTimeInterface` argument is null, the filter returns the current date and time in the selected format. + +``` html+twig +{{ content.contentInfo.publishedDate|ez_full_datetime }} +``` + +The filters also accept an optional `timezone` parameter for displaying date and time in a chosen time zone: + +``` html+twig +{{ content.contentInfo.publishedDate|ez_short_datetime('PST') }} +``` diff --git a/docs/guide/content_rendering/twig_function_reference/field_twig_functions.md b/docs/guide/content_rendering/twig_function_reference/field_twig_functions.md new file mode 100644 index 0000000000..959ab0f2e8 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/field_twig_functions.md @@ -0,0 +1,176 @@ +--- +description: Field Twig function enable rendering content Fields, their values and their information. +--- + +# Field Twig functions + +Field Twig functions render specific Fields of a Content item +and provide information about them. + +- [`ez_render_field()`](#ez_render_field) renders the selected Field of a Content item. +- [`ez_field_value()`](#ez_field_value) returns the Field value object. +- [`ez_field()`](#ez_field) returns the Field object. + +`ez_field()` returns the *Field object*, and `ez_field_value()` returns the *Field's raw value*. +`ez_render_field()` is the Twig function intended for rendering the Field on the front page. + +You can get additional information about a Field by using the following Twig functions: + +- [`ez_field_name()`](#ez_field_name) returns the name of a Content item's Field. +- [`ez_field_description()`](#ez_field_description) returns the description of a Content item's Field. +- [`ez_field_is_empty()`](#ez_field_is_empty) returns Boolean information whether a Field of a Content item is empty. + +## Field rendering + +### `ez_render_field()` + +`ez_render_field()` renders the selected Field of a Content item. +The Field is rendered with the default template, but you can optionally pass a different template as parameter as well. + +| Argument | Type | Description | +| ------ | ----- | ----- | +| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Field belongs to. | +| `fieldDefinitionIdentifier` | `string` | Field identifier. | +| `params` | `hash` | (optional) Hash of parameters passed to the template block. | + +``` html+twig +{{ ez_render_field(content, 'title') }} + +{{ ez_render_field(content, 'image', { + 'template': '@ezdesign/fields/image.html.twig', + 'attr': {class: 'thumbnail-image'}, + 'parameters': { + 'alias': 'small' + } +}) }} +``` + +#### Parameters + +You can pass the following parameters to `ez_render_field()`: + +- `lang` - language to render the Field in (overrides the current language), must be a valid locale in xxx-YY format +- `template` - Field template to use +- `attr` - hash of HTML attributes to add to the tag +- parameters - arbitrary parameters to pass to the template block. +Some Field Types, like the [MapLocation Field Type](../../../api/field_types_reference/maplocationfield.md), expect specific parameters. + +#### Examples + +``` html+twig +{{ ez_render_field(content, 'title', { + 'attr': { + class: 'article-title' + } +}) }} +``` + +## Field values + +### `ez_field_value()` + +`ez_field_value()` returns the Field value object. + +The function returns the value of the Field only. +To render the Field with default or custom templates, use [`ez_render_field()`](#ez_render_field) instead. +If the Content item does not have a translation in the prioritized or passed language, +the function returns the value in the main language. + +| Argument | Type | Description | +|-----|------|-----| +| `content`| `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Field belongs to.| +| `fieldDefIdentifier` | `string` | Identifier of the Field. | +| `forcedLanguage` | `string` | (optional) Language to use (for example, "fre-FR"). | + +``` html+twig +{{ ez_field_value(content, 'image') }} +``` + +### `ez_field()` + +`ez_field()` returns the Field object. +The Field gives you access to the Field value, as well as the Field's definition identifier and Field Type identifier. +If the Content item does not have a translation in the prioritized or passed language, +the function returns the Field object in the main language. + +| Argument | Type | Description | +|-------|------|------| +| `content`| `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Field belongs to.| +| `fieldDefIdentifier` | `string` | Identifier of the Field. | +| `forcedLanguage` | `string` | {optional) Language to use (for example, "fre-FR"). | + +You can access the Field's value by using `(ez_field(content, 'my_field').value)`, +but it is recommended to use the dedicated [`ez_field_value()`](#ez_field_value) function for this. + +You can use `ez_field()` to access the Field Type identifier: + +``` html+twig +{{ ez_field(content, 'my_field').fieldTypeIdentifier }} +``` + +## Field information + +### `ez_field_name()` + +`ez_field_name()` returns the name of a Content item's Field. + +The function uses prioritized languages from SiteAccess settings unless you pass another language as `forcedLanguage`. +If the Content item does not have a translation in the prioritized or passed language, +the function returns the name in the main language. + +| Argument | Type | Description | +|---------------|------|-------------| +| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo` | Content item the Field belongs to. | +| `fieldDefIdentifier` | `string` | Identifier of the Field. | +| `forcedLanguage` | `string` | (optional) Language to use (for example, `fre-FR`). | + + +``` html+twig +{{ ez_field_name(content, 'title') }} + +{{ ez_field_name(content, 'title', 'ger-DE') }} +``` + +### `ez_field_description()` + +`ez_field_description()` returns the description of a Content item's Field. + +The function uses prioritized languages from SiteAccess settings unless you pass another language as `forcedLanguage`. +If the Content item does not have a translation in the prioritized or passed language, +the function returns the description in the main language. + +| Argument | Type | Description | +|---------------|------|-------------| +| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo` | Content item the Field belongs to. | +| `fieldDefIdentifier` | `string` | Identifier of the Field. | +| `forcedLanguage` | `string` | (optional) Language to use (for example, `fre-FR`). | + +``` html+twig +{{ ez_field_description(content, 'title') }} + +{{ ez_field_description(content, 'title', 'ger-DE') }} +``` + +### `ez_field_is_empty()` + +`ez_field_is_empty()` returns Boolean information whether a given Field of a Content item is empty. + +| Argument | Type | Description | +|---------------|------|-------------| +| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo` | Content item the Field belongs to. | +| `fieldDefIdentifier` | `string` | Identifier of the Field. | +| `forcedLanguage` | `string` | (optional) Language to use (for example, `fre-FR`). | + +``` html+twig +{{ ez_field_is_empty(content, 'title') }} +``` + +#### Examples + +For example, use `ez_field_is_empty()` to check whether a Field is empty or filled before rendering it: + +``` html+twig +{% if not ez_field_is_empty(content, 'image') %} + {{ ez_render_field(content, 'image') }} +{% endif %} +``` diff --git a/docs/guide/content_rendering/twig_function_reference/image_twig_functions.md b/docs/guide/content_rendering/twig_function_reference/image_twig_functions.md new file mode 100644 index 0000000000..2befa1690b --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/image_twig_functions.md @@ -0,0 +1,66 @@ +--- +description: Image Twig functions enable rendering images in a specific variation. +--- + +# Image Twig functions + +- [`ez_image_alias`](#ez_image_alias) returns the selected variation of an image Field. +- [`ez_content_field_identifier_first_filled_image`](#ez_content_field_identifier_first_filled_image) returns the identifier of the first image Field in a Content item that is not empty. + +## Image rendering + +To render images, use the [`ez_render_field()`](field_twig_functions.md#ez_render_field) Twig function +with the variation name passed as an argument, for example: + +``` html+twig +[[= include_file('docs/guide/content_rendering/twig_function_reference/field_twig_functions.md', 38, 45) =]] +``` + +## Image information + +### `ez_image_alias()` + +`ez_image_alias()` returns the selected variation of an image Field. + +| Argument | Type | Description | +|-----|-----|-----| +| `field` | `eZ\Publish\API\Repository\Values\Content\Field` | The image Field. | +| `versionInfo` | `eZ\Publish\API\Repository\Values\Content\VersionInfo` | The VersionInfo that the Field belongs to. | +| `variantName` | `string` | Name of the image variation to be used. To display the original image variation, use `original` as the variation name. | + +``` html+twig +{% set thumbnail = ez_image_alias(imageField, content.versionInfo, 'small') %} +``` + +!!! tip + + You can access the name of a variation from the variation object with `variation.name`. + You can, for example, use it as parameter in the + [`ez_render_field()`](field_twig_functions.md#ez_render_field) Twig function. + +### `ez_content_field_identifier_first_filled_image()` + +`ez_content_field_identifier_first_filled_image()` returns the identifier of the first image field that is not empty. + +!!! caution + + This function works only for [Image](../../../api/field_types_reference/imagefield.md) Fields. + It does not work for [ImageAsset](../../../api/field_types_reference/imageassetfield.md) Fields. + +| Argument | Type | Description | +| ------ |----- | ----- | +| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item to display the image for. | + +``` html+twig +{% set firstImage = ez_content_field_identifier_first_filled_image(content) %} +``` + +#### Examples + +You can use `ez_content_field_identifier_first_filled_image()` +to find and render the first existing image in an article: + +``` html+twig +{% set firstImage = ez_content_field_identifier_first_filled_image(content) %} +{{ ez_render_field(content, firstImage) }} +``` diff --git a/docs/guide/content_rendering/twig_function_reference/other_twig_filters.md b/docs/guide/content_rendering/twig_function_reference/other_twig_filters.md new file mode 100644 index 0000000000..8131a87f57 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/other_twig_filters.md @@ -0,0 +1,16 @@ +# Other Twig filters + +### `ez_icon_path()` + +`ez_icon_path()` generates a path to the selected icon from an icon set. + +|Argument|Type|Description| +|------|------|------| +|`icon`|`string`|Identifier of an icon in the icon set.| +|`set`|`string`|Identifier of the configured icon set. If empty, the default icon set is used.| + +```html+twig + + + +``` diff --git a/docs/guide/content_rendering/twig_function_reference/product_twig_functions.md b/docs/guide/content_rendering/twig_function_reference/product_twig_functions.md new file mode 100644 index 0000000000..f550f281a2 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/product_twig_functions.md @@ -0,0 +1,111 @@ +--- +description: Product Twig functions enable getting products and their attributes in templates. +--- + +# Product Twig functions + +Twig functions for rendering product Fields include `ses_render_field()`, +for rendering all Fields of a catalog element, +and three specific Twig functions for rendering price, stock, and specification Fields. + +- [`ses_render_field()`](#ses_render_field) renders a Field of a product's Catalog element. +- [`ses_render_price()`](#ses_render_price) renders the price Field of a product. +- [`ses_render_stock()`](#ses_render_stock) renders the stock Field of a product. +- [`ses_render_specification_matrix()`](#ses_render_specification_matrix) renders the specification Field of a product. + +You can also get the product objects by using the following Twig functions: + +- [`ses_product()`](#ses_product) returns the product object, based on the provided parameters. +- [`ses_variant_product_by_sku()`](#ses_variant_product_by_sku) returns the `VariantProductNode` for a product, based on its SKU. + +## Product field rendering + +### `ses_render_field()` + +`ses_render_field()` renders a Field of a product's Catalog element. + +!!! note + + The function differs from [`ez_render_field()`](field_twig_functions.md#ez_render_field): + you provide it with a Field from the `catalogElement` object, + not a Field identifier from the Content Type definition. + +| Argument | Type | Description | +|-----|-----|-----| +|`catalogElement`|`EshopBundle\Catalog\CatalogElement`|Catalog element of the product.| +|`fieldIdentifier`|`string`|Field of the Catalog element.| + +``` html+twig +{{ ses_render_field(catalogElement, 'subtitle') }} +``` + +### `ses_render_price()` + +`ses_render_price()` renders the price Field of a product. + +| Argument | Type | Description | +|-----|-----|-----| +|`catalogElement`|`EshopBundle\Catalog\CatalogElement`|Catalog element of the product.| +|`field`|`EshopBundle\Content\Fields\PriceField`|Field of the Catalog element.| +|`params`|`array`|| + +``` html+twig +{{ ses_render_price(catalogElement, catalogElement.price) }} +``` + +### `ses_render_stock()` + +`ses_render_stock()` renders the stock Field of a product. + +| Argument | Type | Description | +|-----|-----|-----| +|`field`|`EshopBundle\Content\Fields\StockField`|Field of the Catalog element.| +|`params`|`array`|| + +``` html+twig +{{ ses_render_stock(catalogElement.stock) }} +``` + +### `ses_render_specification_matrix()` + +`ses_render_specification_matrix()` renders the specification Field of a product Content item. + +| Argument | Type | Description | +|-----|-----|-----| +|`catalogElement`|`EshopBundle\Catalog\CatalogElement`|Catalog element of the product.| +|`params`|`array`|| + +``` html+twig +{{ ses_render_specification_matrix(catalogElement) }} +``` + +## Product objects + +### `ses_product()` + +`ses_product()` returns the product object identified by the passed arguments. + +Returns [OrderableProductNode](../../catalog/catalog_api/productnode.md) for products without variants, +or [OrderableVariantNode](../../catalog/product_variants/product_variant_api.md#orderablevariantnode) +for products with variants. + +| Argument | Type | Description | +|-----|-----|-----| +|`params`|`array`|Array of parameters of the product to return.| + +``` html+twig +{% set product = ses_product({'sku': 1234 }) %} +{% set product_with_variants = ses_product({'sku': 1234, 'variantCode': '1234bb' }) }} +``` + +### `ses_variant_product_by_sku()` + +`ses_variant_product_by_sku()` returns the [VariantProductNode](../../catalog/product_variants/product_variant_api.md#variantproductnode) for a product based on its SKU. + +| Argument | Type | Description | +|-----|-----|-----| +|`sky`|`string`|SKU of the product.| + +``` html+twig +{% set product = ses_variant_product_by_sku(1234) }} +``` diff --git a/docs/guide/content_rendering/twig_function_reference/twig_functions_reference.md b/docs/guide/content_rendering/twig_function_reference/twig_functions_reference.md new file mode 100644 index 0000000000..46a064d64c --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/twig_functions_reference.md @@ -0,0 +1,17 @@ +--- +description: Built-in Twig functions speed up rendering content in Twig templates. +--- + +# Twig function reference + +In addition to the [native functions provided by Twig](http://twig.sensiolabs.org/doc/functions/index.html), +and [Twig extensions provided by Symfony]([[= symfony_doc =]]/reference/twig_reference.html), +[[= product_name =]] offers the following custom Twig functions and filters: + +- [Content Twig functions](content_twig_functions.md) +- [Field Twig functions](field_twig_functions.md) +- [Product Twig functions](product_twig_functions.md) +- [Image Twig functions](image_twig_functions.md) +- [URL Twig functions](url_twig_functions.md) +- [Date Twig filters](date_twig_filters.md) +- [Other Twig filters](other_twig_filters.md) diff --git a/docs/guide/content_rendering/twig_function_reference/url_twig_functions.md b/docs/guide/content_rendering/twig_function_reference/url_twig_functions.md new file mode 100644 index 0000000000..9cb87f8788 --- /dev/null +++ b/docs/guide/content_rendering/twig_function_reference/url_twig_functions.md @@ -0,0 +1,87 @@ +--- +description: URL Twig functions enable rendering URLs and routes. +--- + +# URL Twig functions + +- [`ez_path()`](#ez_path) returns the relative URL to a Content item or Location. +- [`ez_url()`](#ez_url) returns the absolute URL to a Content item or Location. +- [`ez_urlalias()`](#ez_urlalias) generates URLs for a Location from the given arguments. +- [`ez_route()`](#ez_route) generates a RouteReference object from the given parameters. +- [`ibexa_oauth2_connect_path()`](#ibexa_oauth2_connect_path) generates a relative path for the given OAuth2 route. +- [`ibexa_oauth2_connect_url()`](#ibexa_oauth2_connect_url) generates an absolute URL for the given OAuth2 route. + +## URLs + +### `ez_path()` + +`ez_path()` returns the relative URL to a Content item or Location. + +|Argument|Type|Description| +|------|------|------| +|`name`|`string`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\API\Repository\Values\Content\Content`
    `eZ\Publish\API\Repository\Values\Content\ContentInfo`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\Core\MVC\Symfony\Routing\RouteReference`|The name of the route, Location or Content.| +|`parameters`|`array`|Route parameters.| +|`relative`|`boolean`|Whether to generate a relative path.| + +``` html+twig +{{ ez_path(location) }} +``` + +### `ez_url()` + +`ez_url()` returns the absolute URL to a Content item or Location. + +|Argument|Type|Description| +|------|------|------| +|`name`|`string`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\API\Repository\Values\Content\Content`
    `eZ\Publish\API\Repository\Values\Content\ContentInfo`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\Core\MVC\Symfony\Routing\RouteReference`|The name of the route, Location or Content.| +|`parameters`|`array`|Route parameters.| +|`schemeRelative`|`boolean`|Whether to generate a relative URL.| + +``` html+twig +{{ ez_url(location, {}, false) }} +``` + +### `ez_urlalias()` + +`ez_urlalias()` generates URLs for a Location from the given parameters. + +!!! note + + `ez_urlalias` is a not a Twig function, but a special route name. + +For more information about the use of `ez_urlalias` as a parameter of the [Symfony `path` Twig function]([[= symfony_doc =]]/reference/twig_reference.html#path), see [Links to other locations](../urls_and_routes.md). + +### `ez_route()` + +`ez_route()` generates a [RouteReference object](../urls_and_routes.md#routereference) from the given parameters. + +|Argument|Type|Description| +|------|------|------| +|`resource`|`string`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\API\Repository\Values\Content\Content`
    `eZ\Publish\API\Repository\Values\Content\ContentInfo`
    `eZ\Publish\API\Repository\Values\Content\Location`
    `eZ\Publish\Core\MVC\Symfony\Routing\RouteReference`|Resource or route name.| +|`params`|`array`|Route parameters.| + +``` html+twig +{% set routeReference = ez_route("ez_urlalias", { 'locationId': 2 }) %} +``` + +## OAuth2 + +### `ibexa_oauth2_connect_path()` + +`ibexa_oauth2_connect_path()` generates a relative path for the given [OAuth2 route](../../user_management/oauth.md). + +|Argument|Type|Description| +|------|------|------| +|`identifier`|string|Identifier of the OAuth connection.| +|`parameters`|`array`|Route parameters.| +|`relative`|`boolean`|Whether to generate a relative path.| + +### `ibexa_oauth2_connect_url()` + +`ibexa_oauth2_connect_url()` generates an absolute URL for the given [OAuth2 route](../../user_management/oauth.md). + +|Argument|Type|Description| +|------|------|------| +|`identifier`|string|Identifier of the OAuth connection.| +|`parameters`|`array`|Route parameters.| +|`schemeRelative`|`boolean`|Whether to generate a relative URL.| diff --git a/docs/guide/content_rendering/urls_and_routes.md b/docs/guide/content_rendering/urls_and_routes.md new file mode 100644 index 0000000000..af166f99fb --- /dev/null +++ b/docs/guide/content_rendering/urls_and_routes.md @@ -0,0 +1,134 @@ +--- +description: Add links to Content items or specific built-in and custom routes in your templates. +--- + +# URLs and routes + +To link to a [Location](../content_management.md#locations) or [Content item](../content_model.md#content-items), use the [`ez_path()`](twig_function_reference/url_twig_functions.md#ez_path) Twig function. +You need to provide the function with a Location, Content, ContentInfo or [RouteReference](#routereference) object: + +``` html+twig +

    Location

    + +

    Content Info

    +``` + +Use [`ez_url()`](twig_function_reference/url_twig_functions.md#ez_url) to get an absolute URL to a Content item or Location: + +``` html+twig +

    Location

    +``` + +## RouteReference + +You can use the [`ez_route()`](twig_function_reference/url_twig_functions.md#ez_route) Twig function +to create a RouteReference object based on the provided information. + +A RouteReference contains a route with its parameters and can be modified after it is created. +Here, the route is based on the ID of the Location. + +``` html+twig +{% set routeReference = ez_route("ez_urlalias", { 'locationId': 2 }) %} +

    Route

    +``` + +A route can also be based on the ID of the Content item. +The resulting link points to the Content item's main Location. + +``` html+twig +{% set routeReference = ez_route("ez_urlalias", { 'contentId': 456 }) %} +

    Route

    +``` + +With `ez_route()` you can modify the route contained in RouteReference after creation, for example, by providing additional parameters: + +``` html+twig +{% set routeReference = ez_route("ez_urlalias", { 'locationId': 2 }) %} +{% do routeReference.set("param", "param-value") %} +``` + +You can also use `ez_route()` to create links to predefined routes, such as the `ezplatform.search` route that leads to a search form page: + +``` html+twig +Search +``` + +## File download links + +To provide a download link for a file, use `ez_route()` with the `ez_content_download` route: + +``` html+twig +{% set download_route = ez_route('ez_content_download', { + 'content': file, + 'fieldIdentifier': 'file', +}) %} + +Download +``` + +## Route list + +The following built-in routes are available for the front of the website. + +!!! tip + + To view all routes existing in the system, including internal and Back-Office related ones, run: + + ``` bash + php bin/console debug:router + ``` + +### Registration + + +|Route name|Path|Description| +|---|---|---| +| `ezplatform.user.user_register` | `/user/register` | User registration form | +| `ezplatform.user.register_confirmation`
    `ezplatform.user.user_register_confirmation` | `/register-confirm`
    `/user/register-confirm` | Confirmation page after user registration | + +### Login + +|Route name|Path|Description| +|---|---|---| +|`login` | `/login` | [Login form](layout/add_login_form.md) | +|`logout`
    `silversolutionsCustomerLogout` | `/logout`
    `/profile/logout` | Logging out the current user | + +### Profile + +|Route name|Path|Description| +|---|---|---| +| `silversolutionsCustomerDetail` | `/profile` | User profile | +| `silversolutions_address_book_list` | `/profile/address_book` | User address book | + +### Password + +|Route name|Path|Description| +|---|---|---| +| `ezplatform.user_profile.change_password`
    `silversolutions_password_change` | `/user/change-password`
    `/change_password` | Form for password change| +| `ezplatform.user.forgot_password` | `/user/forgot-password` | [Form for password resetting](layout/add_forgot_password.md) | +| `ezplatform.user.forgot_password.migration` | `/user/forgot-password/migration` | Form for resetting password after expiration| +| `ezplatform.user.forgot_password.login` | `/user/forgot-password/login` | Form for resetting password based on login instead of email address | +| `ezplatform.user.reset_password` | `/user/reset-password/{hashKey}` | Form for resetting password based on a generated link | + +### Shop [[% include 'snippets/commerce_badge.md' %]] + +|Route name|Path|Description| +|---|---|---| +| `silversolutions_bestsellers` | `/bestsellers` | [Bestseller page](../bestsellers.md) | +| `silversolutions_delegate` | `/delegate` | [Delegate function](../user_management/delegate_function.md) | +| `silversolutions_undelegate` | `/undelegate` | [Undelegate function](../user_management/delegate_function.md) | + +### Content + +|Route name|Path|Description| +|---|---|---| +| `ez_content_download` | `/content/download/{contentId}/{fieldIdentifier}/{filename}` | Downloading a binary file | +| `ezplatform.content.create_no_draft` | `/content/create/nodraft/{contentTypeIdentifier}/{language}/{parentLocationId}` | [Creating a Content item without using a draft](../user_generated_content.md#creating-a-content-item-without-using-a-draft) | +| `ezplatform.content.draft.edit` | `/content/edit/draft/{contentId}/{versionNo}/{language}/{locationId}` | [Editing a Content item](../user_generated_content.md#editing-a-content-item) | +| `ezplatform.content.draft.create` | `/content/create/draft/{contentId}/{fromVersionNo}/{fromLanguage}` | [Creating a new draft](../user_generated_content.md#creating-a-new-draft) | + +### Search + +|Route name|Path|Description| +|---|---|---| +| `ezplatform.search` | `/search` | Search form | diff --git a/docs/guide/controllers.md b/docs/guide/controllers.md deleted file mode 100644 index 361167e1c7..0000000000 --- a/docs/guide/controllers.md +++ /dev/null @@ -1,776 +0,0 @@ -# Controllers - -## Custom rendering logic - -In some cases, displaying a Content item/Location via the built-in `ViewController` is not sufficient to show everything you want. In such cases you may want to **use your own custom logic** to display the current Content item/Location instead. - -Typical use cases include access to: - -- Settings (coming from `ConfigResolver` or `ServiceContainer`) -- Current Content item's `ContentType` object -- Current Location's parent -- Current Location's children count -- Main Location and alternative Locations for the current Content item -- etc. - -There are three ways in which you can apply a custom logic: - -- [Configure a custom view controller](#enriching-viewcontroller-with-a-custom-controller) alongside regular matcher rules (**recommended**). -- [Add a Symfony Response listener](#adding-a-listener) to add custom logic to all responses. -- [**Override**](#using-only-your-custom-controller) the built-in `ViewController` with the custom controller in a specific situation. - -!!! tip "Permissions for custom controllers" - - See [permission documentation](permissions.md#permissions-for-custom-controllers) for information - about access control for custom controllers. - -### Enriching ViewController with a custom controller - -**This is the recommended way of using a custom controller** - -To use your custom controller on top of the built-in `ViewController` you need to point to both the controller and the template in the configuration, for example: - -``` yaml -#ezplatform.yaml -ezplatform: - system: - default: - content_view: - full: - article: - controller: App\Controller\DefaultController::articleViewEnhancedAction - template: full/article.html.twig - match: - Identifier\ContentType: [article] -``` - -With this configuration, the following controller will forward the request to the built-in `ViewController` with some additional parameters: - -``` php -addParameters(['myCustomVariable' => "Hey, I'm a custom message!"]); - - // If you wish, you can also easily access Location and Content objects - // $location = $view->getLocation(); - // $content = $view->getContent(); - - // Set custom header for the Response - $response = new Response(); - $response->headers->add(['X-Hello' => 'World']); - $view->setResponse($response); - - return $view; - } -} -``` - -These parameters can then be used in templates, for example: - -``` html+twig - -{% extends no_layout ? view_base_layout : "pagelayout.html.twig" %} - -{% block content %} -

    {{ ez_render_field( content, 'title' ) }}

    -

    {{ myCustomVariable }}

    - {{ ez_render_field( content, 'body' ) }} -{% endblock %} -``` - -### Adding a listener - -One way to add custom logic to all responses is to use your own listener. Please refer to the [Symfony documentation](https://symfony.com/doc/5.0/event_dispatcher/before_after_filters.html#after-filters-with-the-kernel-response-event) for the details on how to achieve this. - -### Using only your custom controller - -If you want to apply only your custom controller **in a given match situation** and not use the `ViewController` at all, in the configuration you need to indicate the controller, but no template, for example: - -``` yaml -ezplatform: - system: - default: - content_view: - full: - folder: - controller: App\Controller\DefaultController::viewFolderAction - match: - Identifier\ContentType: [folder] - Identifier\Section: [standard] -``` - -In this example, as the `ViewController` is not applied, the custom controller takes care of the whole process of displaying content, including pointing to the template to be used (in this case, `full/custom_controller_folder.html.twig`): - -``` php -getLocation(); - $content = $view->getContent(); - - $response = $this->render( - 'full/custom_controller_folder.html.twig', - [ - 'location' => $location, - 'content' => $content, - 'foo' => 'Hey world!!!', - 'osTypes' => ['osx', 'linux', 'windows'] - ] - ); - - // Set custom header for the Response - $response->headers->add(['X-Hello' => 'World']); - - return $response; - } -} -``` - -Here again custom parameters can be used in the template, e.g.: - -``` html+twig -{% extends "pagelayout.html.twig" %} - -{% block content %} -

    {{ ez_render_field( content, 'name' ) }}

    -

    {{ foo }}

    -
      - {% for os in osTypes %} -
    • {{ os }}
    • - {% endfor %} -
    -{% endblock %} -``` - -## Query controller - -The Query controller is a predefined custom content view controller that runs a repository Query. - -You can use it as a custom controller in a view configuration, [alongside match rules](#enriching-viewcontroller-with-a-custom-controller). It can use properties of the viewed Content item or Location as parameters to the Query. - -The Query controller makes it easy to retrieve content without writing custom PHP code and to display the results in a template. Example use cases include: - -- List of Blog posts in a Blog -- List of Images in a Gallery - -### Built-in Query Types - -You can make use of the following built-in Query Types for quick rendering: - -#### Children - -The `Children` Query Type retrieves children of the given Location. - -It takes `location` or `content` as parameters. - -``` yaml -params: - query: - query_type: 'Children' - parameters: - content: '@=content' - assign_results_to: items -``` - -#### Siblings - -The `Siblings` Query Type retrieves Locations that have the same parent as the provided Content item or Location. - -It takes `location` or `content` as parameters. - -``` yaml -params: - query: - query_type: 'Siblings' - parameters: - content: '@=content' - assign_results_to: items -``` - -#### Ancestors - -The `Ancestors` Query Type retrieves all ancestors (direct parents and their parents) of the provided Location. - -It takes `location` or `content` as parameters. - -``` yaml -params: - query: - query_type: 'Ancestors' - parameters: - content: '@=content' - assign_results_to: items -``` - -#### RelatedToContent - -The `RelatedToContent` Query Type retrieves content that is a reverse relation to the provided Content item. - -It takes `content` or `field` as obligatory parameters. -`field` indicates the Relation or RelationList Field that contains the relations. - -``` yaml -params: - query: - query_type: 'RelatedToContent' - parameters: - content: '@=content' - field: 'relations' - assign_results_to: items -``` - -#### GeoLocation - -The `GeoLocation` Query Type retrieves content by distance of the location provided in a MapLocation Field. - -It takes the following obligatory parameters: - -- `field` - MapLocation Field identifier -- `distance` - distance to check for -- `latitude` and `longitude` - coordinates of the location to check distance to -- (optional) `operator` - operator to check value against, by default `<=` - -``` yaml -params: - query: - query_type: 'GeoLocation' - parameters: - field: 'location' - distance: 200 - latitude: '@=content.getFieldValue("location").latitude' - longitude: '@=content.getFieldValue("location").longitude' - operator: '<' - assign_results_to: items -``` - -#### General Query Type parameters - -All built-in Query Types take the following optional parameters: - -- `limit` - limit for number of search hits to return -- `offset` - offset for search hits, used for paging the results -- `sort` - sorting options -- `filter` - additional query filters: - - `content_type` - return only results of given Content Types - - `visible_only` - return only visible results (default `true`) - - `siteaccess_aware` - return only results limited to current SiteAccess root (default `true`) - -For example: - -``` yaml -params: - query: - query_type: 'Children' - parameters: - content: '@=content' - filter: - content_type: ['blog_post'] - visible_only: false - limit: 5 - offset: 2 - sort: 'content_name asc, date_published desc' - assign_results_to: items -``` - -!!! note "Sorting order" - - To provide sorting order to the `sort` parameter, use names of the Sort Clauses in - `eZ\Publish\API\Repository\Values\Content\Query\SortClause`. - -#### Using built-in Query Types - -This example assumes a "Blog" container that contains a set of "Blog post" items. The goal is, when viewing a Blog, to list the Blog posts it contains. - -Two items are required: - -- a View template - It will render the Blog, and list the Blog posts it contains -- a `content_view` configuration - It will instruct Platform, when viewing a Content item of type Blog, to use the Query Controller, the view template, and the `Children` QueryType. It will also map the ID of the viewed Blog to the QueryType parameters, and set which Twig variable the results will be assigned to. - -#### The `content_view` configuration - -We now need a view configuration that matches Content items of type "Blog", and uses the QueryController to fetch the blog posts: - -``` yaml -ezplatform: - system: - site_group: - content_view: - full: - blog: - controller: ez_query::contentQueryAction - template: content/view/full/blog.html.twig - match: - Identifier\ContentType: "blog" - params: - query: - query_type: Children - parameters: - location: '@=location' - assign_results_to: blog_posts -``` - -The view's controller action is set to the QueryController's `contentQuery` action (`ez_query::contentQueryAction`). Other actions are available that run a different type of search (contentInfo or location). - -The QueryController is configured in the `query` array, inside the `params` of the `content_view` block: - -- `query_type` specifies the QueryType to use, based on its name. -- `parameters` is a hash where parameters from the QueryType are set. Arbitrary values can be used, as well as properties from the currently viewed [Location](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Location.php) and [ContentInfo](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/ContentInfo.php). In that case, the ID of the currently viewed Location is mapped to the QueryType's `location` parameter: `location: '@=location'` -- `assign_results_to` sets which Twig variable the search results will be assigned to. - -#### The view template - -Results from the search are assigned to the `blog_posts` variable as a `SearchResult` object. In addition, since the standard ViewController is used, the currently viewed `location` and `content` are also available. - -``` yaml -

    {{ ez_content_name(content) }}

    - -{% for blog_post in blog_posts.searchHits %} -

    {{ ez_content_name(blog_post.valueObject.contentInfo) }}

    -{% endfor %} -``` - -### Creating custom Query Types - -Beyond the built-in Query Types, you can create your own. - -For example, this Query Type returns a Query that searches for the last ten published Content items, ordered by reverse publishing date. -It accepts an optional `contentType` parameter [lines 13-15] that can be set to a Content Type identifier (e.g. `article`): - -``` php - new Query\Criterion\LogicalAnd($criteria), - 'sortClauses' => [new Query\SortClause\DatePublished()], - 'limit' => isset($parameters['limit']) ? $parameters['limit'] : 10, - ]); - } - public static function getName() - { - return 'LatestContent'; - } - /** - * Returns an array listing the parameters supported by the QueryType. - * @return array - */ - public function getSupportedParameters() - { - return ['contentType', 'limit']; - } -} -``` - -In content view configuration, use the name of the Query Types as defined in `getName`. - -``` yaml -content_view: - full: - latest: - controller: ez_query::locationQueryAction - template: full/latest.html.twig - match: - Identifier\ContentType: "latest" - params: - query: - query_type: LatestContent - parameters: - parentLocationId: '@=location.id' - contentType: article - assign_results_to: latest -``` - -Your Query Type must be [registered as a service](#registering-the-querytype-into-the-service-container). - -### Configuration details - -#### `controller` - -Following Controller actions are available: - -- `locationQueryAction` runs a Location Search -- `contentQueryAction` runs a content Search -- `contentInfoQueryAction` runs a ContentInfo search -- `pagingQueryAction` returns a `PagerFanta` object and can be used to quickly [paginate query results](#paginating-with-querytypes) - -See the [Search](search/search.md) documentation page for more details about different types of search. - -#### `params` - -The Query is configured in a `query` hash in `params`, you could specify the QueryType name, additional parameters and the Twig variable that you will assign the results to for use in the template. - -- `query_type` - Name of the Query Type that will be used to run the query, defined by the class name. -- `parameters` - Query Type parameters that can be provided in two ways: - 1. As scalar values, for example an identifier, an ID, etc. - 1. Using the Expression language. This simple script language, similar to Twig syntax, lets you write expressions that get value from the current Content item and/or Location: - - For example, `@=location.id` will be evaluated to the currently viewed Location's ID.`content`, `location` and `view` are available as variables in expressions. -- `assign_results_to` - - This is the name of the Twig variable that will be assigned the results. - - Note that the results are the SearchResult object returned by the SearchService. - -### Query Type objects - -Query Type is an object that builds a Query. It is different from [Public API queries](../api/public_php_api.md). - -To make a new Query Type available to the Query Controller, you need to create a PHP class that implements the QueryType interface, then register it as such in the Service Container. - -For more information about the [Service Container on its documentation page](service_container.md). - -### The QueryType interface - -The PHP QueryType interface describes three methods: - -1. `getQuery()` -2. `getSupportedParameters()` -3. `getName()` - -``` php -interface QueryType -{ - /** - * Builds and returns the Query object - * - * The Query can be either a content or a Location one. - * - * @param array $parameters A hash of parameters that will be used to build the Query - * @return \eZ\Publish\API\Repository\Values\Content\Query - */ - public function getQuery(array $parameters = []); - - /** - * Returns an array listing the parameters supported by the QueryType - * @return array - */ - public function getSupportedParameters(); - - /** - * Returns the QueryType name - * @return string - */ - public static function getName(); -} -``` - -#### Parameters - -A QueryType may accept parameters, including string, array and other types, depending on the implementation. They can be used in any way, such as: - -- customizing an element's value (limit, Content Type identifier, etc.) -- conditionally adding/removing criteria from the query -- setting the limit/offset - -The implementations should use Symfony's `OptionsResolver` for parameter handling and resolution. - -### Naming of QueryTypes - -Each QueryType is named after what is returned by `getName()`. **Names must be unique.** A warning will be thrown during compilation if there is a conflict, and the resulting behavior will be unpredictable. - -QueryType names should use a unique namespace, in order to avoid conflicts with other bundles. We recommend that the name is prefixed with the bundle's name, e.g.: `AcmeBundle:LatestContent`. A vendor/company's name could also work for QueryTypes that are reusable throughout projects, e.g.: `Acme:LatestContent`. - -### Registering the QueryType into the service container - -QueryTypes must be registered as Symfony Services and implement the `eZ\Publish\Core\QueryType\QueryType` interface. -A `QueryType` service is registered automatically when `autoconfigure: true` is set either for that service or in `_defaults`. - -!!! tip - - In the default [[= product_name =]] installation services are autoconfigured by default for the `App` namespace, - so no additional registration is required. - -Otherwise, you can register your QueryType with a service tag: - -``` yaml -App\QueryType\LatestContent: - tags: - - { name: ezplatform.query_type, alias: LatestContent } -``` - -### Paginating with QueryTypes - -Using the `ez_query::pagingQueryAction` you can quickly get paginated results of a query: - -``` yaml hl_lines="4 13" -content_view: - full: - folder: - controller: ez_query::pagingQueryAction - template: full/folder.html.twig - match: - Identifier\ContentType: folder - params: - query: - query_type: LocationChildren - parameters: - parentLocationId: '@=location.id' - limit: 5 - assign_results_to: items -``` - -`limit` defines how many query results are listed on a page. - -!!! tip - - See [Content view configuration](#the-content_view-configuration) for details on this configuration. - -Pagination controls are provided in the `pagerfanta` Twig function: - -``` html+twig hl_lines="4" -{% for item in items %} -

    {{ ez_content_name(item.valueObject.contentInfo) }}

    -{% endfor %} -{{ pagerfanta( items, 'ez', {'routeName': location } ) }} -``` - -### The OptionsResolverBasedQueryType abstract class - -An abstract class based on Symfony's `OptionsResolver` makes the implementation of QueryTypes with parameters easier. - -It provides final implementations of `getQuery()` and `getDefinedParameters()`. - -A `doGetQuery()` method must be implemented instead of `getQuery()`. It is called with the parameters processed by the OptionsResolver, meaning that the values have been validated, and default values have been set. - -In addition, the `configureOptions(OptionsResolver $resolver)` method must configure the OptionsResolver. - -The LatestContentQueryType from the [example above](#querytype-example-latest-content) can benefit from the abstract implementation: - -- validate that `type` is a string, but make it optional -- validate that `limit` is an int, with a default value of 10 - -!!! note - - For further information see the [Symfony's Options Resolver documentation page](http://symfony.com/doc/5.0/components/options_resolver.html) - -``` php - new Query\Criterion\LogicalAnd($criteria), - 'sortClauses' => [ - new Query\SortClause\DatePublished() - ], - 'limit' => $parameters['limit'], - ]); - } - - public static function getName() - { - return 'LatestContent'; - } - - protected function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefined(['type', 'limit']); - $resolver->setAllowedTypes('type', 'string'); - $resolver->setAllowedTypes('limit', 'int'); - $resolver->setDefault('limit', 10); - } -} -``` - -### Using QueryTypes from PHP code - -All QueryTypes are registered in the QueryType registry, which is a Symfony Service injectable as `ezpublish.query_type.registry`: - -``` yaml -services: - App\Controller\LatestContentController: - autowire: true - autoconfigure: true - arguments: - $registry: '@ezpublish.query_type.registry' -``` - -Example of PHP code: - -```php - -searchService = $searchService; - $this->registry = $registry; - } - - public function showLatestContentAction(string $template, string $contentType): Response - { - $queryType = $this->registry->getQueryType('LatestContent'); - $query = $queryType->getQuery(['type' => $contentType]); - $searchResults = $this->searchService->findContent($query); - - return $this->render($template, ['results' => $searchResults]); - } -} - -``` - -### Content query Field Type - -The [Content query Field Type](../api/field_type_reference.md#content-query-field-type) -enables you to configure a content query that will use parameters from a Field definition. -The results will be available in a Content item's Field. - -Use it by adding a Content query Field Type to your Content Type. - -Select a predefined [Query Type](#query-controller) from a list -and provide the parameters that are required by the Query Type, e.g.: - -``` -parentLocationId: '@=mainLocation.id' -``` - -Select the Content Type of items you want to return in the **Returned type** dropdown. -To take it into account, your Query Type must filter on the Content Type. -Provide the selected Content Type through the `returnedType` variable: - -``` -contentType: '@=returnedType' -``` - -The following variables are available in parameter expressions: - -- `returnedType` - the identifier of the Content Type selected in the **Returned type** dropdown -- `content` - the current Content item -- `contentInfo` - the current Content item's ContentInfo -- `mainLocation` - the current Content item's main Location - -#### Pagination - -You can paginate the query results by checking the **Enable pagination** box and selecting a limit of results per page. - -The following optional parameters are available: - -- `enablePagination` - when true, pagination will be enabled even if it is disabled in the Field definition -- `disablePagination` - when true, pagination will be disabled even if it is enabled in the Field definition -- `itemsPerPage` - limit of results per page, overriding the limit set in Field definition. It is required if `enablePagination` is set to true and pagination is disabled in the Field definition - -For example: - -``` -{{ ez_render_field(content, 'posts', {'parameters': {'enablePagination': true, 'itemsPerPage': 8}}) }} -``` - -You can also define an offset for the results. Provide the offset in the Query Type, or in parameters: - -``` -offset: 3 -``` - -If pagination is disabled and an offset value is defined, the query's offset is added to the offset calculated for a page -(for example, with `offset = 5` and `itemsPerPage = 10`, the first page starts with 5, the second page starts with 15, etc.). - -Without offset defined, pagination defines the starting number for each page -(for example, with `itemsPerPage = 10`, first page starts with 0, second page starts with 10, etc.). - -#### Content query Field Type view - -Configure the Content query Field Type's view using the `content_query_field` view type: - -``` yaml -content_view: - content_query_field: - blog_posts: - match: - Identifier\ContentType: blog - '@EzSystems\EzPlatformQueryFieldType\eZ\ContentView\FieldDefinitionIdentifierMatcher': posts - template: "blog_posts.html.twig" -``` - -Query results are provided to the template in the `items` variable: - -``` html+twig -{% for item in items %} - {{ render(controller("ez_content::viewAction", { - "contentId": item.id, - "content": item, - "viewType": itemViewType - })) }} -{% endfor %} -``` - -The default view type is `line`, defined under `itemViewType`. -You can change it by passing a different view to `viewType` in the template, e.g.: -`"viewType": "list"`. - -The `isPaginationEnabled` parameter indicates if pagination is enabled. -When pagination is enabled, `items` is an instance of PagerFanta: - -``` -{% if isPaginationEnabled %} - {{ pagerfanta( items, 'ez', { - 'pageParameter': pageParameter, - 'routeName': 'ez_urlalias', - 'routeParams' : {'location': location } - } ) }} -{% endif %} -``` -In case of pagination, `pageParameter` contains the page parameter to use as the PagerFanta `pageParameter` argument. diff --git a/docs/guide/custom_policies.md b/docs/guide/custom_policies.md index aabaedbe8f..b03a46cbde 100644 --- a/docs/guide/custom_policies.md +++ b/docs/guide/custom_policies.md @@ -1,8 +1,12 @@ +--- +description: Create a custom Policy to cover non-standard permission needs. +--- + # Custom Policies The content Repository uses [Roles and Policies](permissions.md) to give Users access to different functions of the system. -Any bundle can expose available Policies via a `PolicyProvider` which can be added to EzPublishCoreBundle's DIC extension. +Any bundle can expose available Policies via a `PolicyProvider` which can be added to EzPublishCoreBundle's [service container](../api/public_php_api.md#service-container) extension. ## PolicyProvider @@ -12,9 +16,9 @@ A `PolicyProvider` object provides a hash containing declared modules, functio - Each module can provide *functions* (e.g. in `content/read` "content" is the module, "read" is the function) - Each function can provide a collection of Limitations. -First level key is the module name, value is a hash of available functions, with function name as key. -Function value is an array of available Limitations, identified by the alias declared in `LimitationType` service tag. -If no Limitation is provided, value can be `null` or an empty array. +First level key is the module name which is limited to characters within the set `A-Za-z0-9_`, value is a hash of +available functions, with function name as key. Function value is an array of available Limitations, identified +by the alias declared in `LimitationType` service tag. If no Limitation is provided, value can be `null` or an empty array. ``` php [ @@ -107,7 +111,7 @@ class Kernel extends BaseKernel { use MicroKernelTrait; - protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + protected function build(ContainerBuilder $container): void { // ... diff --git a/docs/guide/customers/customer_api/configuration_for_customer_data.md b/docs/guide/customers/customer_api/configuration_for_customer_data.md index e1b294400b..fb1876a9b2 100644 --- a/docs/guide/customers/customer_api/configuration_for_customer_data.md +++ b/docs/guide/customers/customer_api/configuration_for_customer_data.md @@ -1,10 +1,9 @@ -# Configuration for customer data [[% include 'snippets/commerce_badge.md' %]] +# Configuration for customer data ## User Groups for private and business customers To separate business and private users in one installation, there are two User Groups in the "Shop user" group. -You can configure those groups by using `user_group_location.business` and `user_group_location.private` -in `config/ecommerce_parameters.yml`: +You can configure those groups in shop configuration, by using `user_group_location.business` and `user_group_location.private`: ``` yaml siso_core.default.user_group_location.business: 385 diff --git a/docs/guide/customers/customer_api/customer_api.md b/docs/guide/customers/customer_api/customer_api.md index ef3cd7b71c..dbcaf1dc45 100644 --- a/docs/guide/customers/customer_api/customer_api.md +++ b/docs/guide/customers/customer_api/customer_api.md @@ -1,4 +1,4 @@ -# Customer API [[% include 'snippets/commerce_badge.md' %]] +# Customer API ## Retrieving data in PHP diff --git a/docs/guide/customers/customer_api/customer_profile_data.md b/docs/guide/customers/customer_api/customer_profile_data.md index 5c64efe55c..b909c1ad93 100644 --- a/docs/guide/customers/customer_api/customer_profile_data.md +++ b/docs/guide/customers/customer_api/customer_profile_data.md @@ -1,4 +1,4 @@ -# Customer profile data [[% include 'snippets/commerce_badge.md' %]] +# Customer profile data ## Customer profile data model diff --git a/docs/guide/customers/customer_templates.md b/docs/guide/customers/customer_templates.md index 06d7e2c64c..f484ce1ad8 100644 --- a/docs/guide/customers/customer_templates.md +++ b/docs/guide/customers/customer_templates.md @@ -1,4 +1,4 @@ -# Customer templates [[% include 'snippets/commerce_badge.md' %]] +# Customer templates The global `ses.profile` Twig variable contains the main information about the current user, their addresses and data from the ERP. diff --git a/docs/guide/customers/customers.md b/docs/guide/customers/customers.md index 2ae6eb71b1..5b6ae5a678 100644 --- a/docs/guide/customers/customers.md +++ b/docs/guide/customers/customers.md @@ -1,22 +1,8 @@ -# Customers [[% include 'snippets/commerce_badge.md' %]] +# Customers Customers are stored as User Content items in the database. -[[= product_name_com =]] adds the following Fields to the User Content Type that are required for the shop: - -- first name, last name -- salutation -- customer profile data -- customer number, contact number -- budget per order and per month (used by the Customer Center) - -### User Groups - -Each shop stores private and business customers in different User Groups. - -![](../img/customers_1.png) - -### Customers in ERP system +## Customers in ERP system The customers are directly connected to the ERP system if they have a customer number and/or a contact number. @@ -37,7 +23,9 @@ The ERP system provides: ## Customer data model -[[= product_name_com =]] uses the UBL standard to model customer data. The most important type is the Party which describes an address. +[[= product_name =]] uses the UBL standard to model customer data. +The most important data type is Party. +Party describes an address. For each user, the following information is stored. If the user has a customer number, the following information is updated from the ERP after login: diff --git a/docs/guide/customers/managing_delivery_addresses.md b/docs/guide/customers/managing_delivery_addresses.md index 9bfe8755ca..2d4b7acada 100644 --- a/docs/guide/customers/managing_delivery_addresses.md +++ b/docs/guide/customers/managing_delivery_addresses.md @@ -1,4 +1,4 @@ -# Managing delivery addresses [[% include 'snippets/commerce_badge.md' %]] +# Managing delivery addresses !!! note diff --git a/docs/guide/customers/vat_handling.md b/docs/guide/customers/vat_handling.md index 794b030311..a5e72ba931 100644 --- a/docs/guide/customers/vat_handling.md +++ b/docs/guide/customers/vat_handling.md @@ -1,4 +1,4 @@ -# VAT handling [[% include 'snippets/commerce_badge.md' %]] +# VAT handling The VAT handling for customers is controlled by: diff --git a/docs/guide/data_migration/add_data_migration_matcher.md b/docs/guide/data_migration/add_data_migration_matcher.md new file mode 100644 index 0000000000..077cbada34 --- /dev/null +++ b/docs/guide/data_migration/add_data_migration_matcher.md @@ -0,0 +1,59 @@ +--- +description: Create a matcher for handling data migrations. +--- + +# Create data migration matcher + +[Matchers in data migrations](exporting_and_importing_data.md#match-property) enable you to select which data from the Repository to export. + +In addition to the built-in matchers, you can create custom matchers for content. + +The following example creates a matcher for Section identifiers. + +## Create normalizer + +To do this, first add a normalizer which handles the conversion between objects and the YAML format used for data migration. + +Matchers are instances of `FilteringCriterion`, so a custom normalizer needs to denormalize into an instance of `FilteringCriterion`. + +!!! tip "Normalizers" + + To learn more about normalizers, refer to [Symfony documentation]([[= symfony_doc =]]/components/serializer.html). + +Create the normalizer in `src/Migrations/Matcher/SectionIdentifierNormalizer.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Matcher/SectionIdentifierNormalizer.php') =]] +``` + +Register the normalizer as a service: + +``` yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 10, 13) =]] +``` + +!!! note "Normalizer order" + + User-defined normalizers are always executed before the built-in ones. + However, you can additionally set the priority of your normalizers. + + Check the priorities of all normalization services by using: + + ``` bash + php bin/console debug:container --tag ibexa.migrations.serializer.normalizer + ``` + +## Create generator + +Additionally, if you want to export data using the `ibexa:migrations:generate` command, you need a generator. +Create the generator in `src/Migrations/Matcher/SectionIdentifierGenerator.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Matcher/SectionIdentifierGenerator.php') =]] +``` + +Register the generator as a service: + +``` yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 14, 17) =]] +``` diff --git a/docs/guide/data_migration/create_migration_action.md b/docs/guide/data_migration/create_migration_action.md new file mode 100644 index 0000000000..efb8a731ec --- /dev/null +++ b/docs/guide/data_migration/create_migration_action.md @@ -0,0 +1,46 @@ +--- +description: Create a custom action to use while performing data migration. +--- + +# Create data migration action + +To create an [action](data_migration_actions.md) that is performed after a migration step, you need: + +- An action class, to store any additional data that you might require. +- An action denormalizer, to convert YAML definition into your action class. +- An action executor, to handle the action. + +The following example shows how to create an action that assigns a Content item to a Section. + +First, create an action class, in `src/Migrations/Action/AssignSection.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Action/AssignSection.php') =]] +``` + +Then you need a denormalizer to convert data that comes from YAML into an action object, +in `src/Migrations/Action/AssignSectionDenormalizer.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Action/AssignSectionDenormalizer.php') =]] +``` + +Then, tag the action denormalizer so it is recognized by the serializer used for migrations. + +``` yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 0, 5) =]] +``` + +And finally, add an executor to perform the action, in `src/Migrations/Action/AssignSectionExecutor.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Action/AssignSectionExecutor.php') =]] +``` + +Tag the executor with `ibexa.migrations.executor.action.` tag, where `` is the "type" of the step +that executor works with (`content`, `content_type`, `location`, and so on). +The tag has to have a `key` property with the action type. + +```yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 6, 9) =]] +``` diff --git a/docs/guide/data_migration/create_migration_step.md b/docs/guide/data_migration/create_migration_step.md new file mode 100644 index 0000000000..278c812821 --- /dev/null +++ b/docs/guide/data_migration/create_migration_step.md @@ -0,0 +1,66 @@ +--- +description: Create a custom step for data migrations. +--- + +# Create data migration step + +A data migration step is a single operation in data migration process +that combines a mode (for example: `create`, `update`, `delete`) +and a type (for example: `content`, `section`, `currency`), +with optional additional information depending on the specific step. + +Besides the [built-in migrations steps](exporting_and_importing_data.md#mode), you can also create custom ones. + +To create a custom migration step, you need: + +- A step class, to store any additional data that you might require. +- A step normalizer, to convert YAML definition into your step class. +- A step executor, to handle the step. + +The following example shows how to create a step that replaces all `ezstring` Fields +that have an old company name with "New Company Name". + +## Create step class + +First, create a step class, in `src/Migrations/Step/ReplaceNameStep.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Step/ReplaceNameStep.php') =]] +``` + +## Create normalizer + +Then you need a normalizer to convert data that comes from YAML into a step object, +in `src/Migrations/Step/ReplaceNameStepNormalizer.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Step/ReplaceNameStepNormalizer.php') =]] +``` + +Then, tag the step normalizer, so it is recognized by the serializer used for migrations. + +``` yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 18, 23) =]] +``` + +## Create executor + +And finally, create an executor to perform the step, in `src/Migrations/Step/ReplaceNameExecutor.php`: + +``` php +[[= include_file('code_samples/data_migration/src/Migrations/Step/ReplaceNameStepExecutor.php') =]] +``` + +Tag the executor with `ibexa.migrations.step_executor` tag. + +```yaml +[[= include_file('code_samples/data_migration/config/custom_services.yaml', 23, 27) =]] +``` + +Then you can create a migration file that represents this step in your application: + +```yaml +- type: company_name + mode: replace + replacement: 'New Company Name' # as declared in normalizer, this is optional +``` diff --git a/docs/guide/data_migration/data_migration.md b/docs/guide/data_migration/data_migration.md new file mode 100644 index 0000000000..cc2feb299b --- /dev/null +++ b/docs/guide/data_migration/data_migration.md @@ -0,0 +1,32 @@ +--- +description: Data migration enables you to import and export Repository data by using YAML files. +--- + +# Data migration + +Data migration allows exporting and importing selected data from an Ibexa DXP installation. + +*Exporting* data consists in saving selected Repository information in YAML format. +*Importing* reads migration YAML files and creates or modifies Repository content based on them. +You can migrate your Repository data, that is Content items, as well as Content Types, languages, Object states, Sections, and so on, +between installations. + +You can use migrations in projects that require the same data to be present across multiple instances. +They can be useful for project templates. Migrations are able to store shared data, so they can be applied for each new project you start, +or incrementally upgrade older projects to your new standard, if needed. +They are a developer-friendly tool that allows you to share data without writing code. + +You can run data migrations either with a command, or with the [PHP API](public_php_api_managing_migrations.md). + +[[= cards([ + "guide/data_migration/exporting_and_importing_data", + "guide/data_migration/data_migration_actions", + "guide/data_migration/migration_management", +], force_version="3.3") =]] + +!!! caution "Do not enable EzMigrationBundle2" + + If you are migrating your data either with [`kaliop-uk/ezmigrationbundle`](https://github.com/kaliop-uk/ezmigrationbundle) or [`ezsystems/ezmigrationbundle`](https://github.com/ezsystems/EzMigrationBundle), do not install the [`tanoconsulting/ezmigrationbundle2`](https://github.com/tanoconsulting/ezmigrationbundle2) package, or your application will stop working due to multiple duplicated classes. + + As of v3.3.3, the `ezmigrationbundle` package has been removed to mitigate this issue. + It is recommended that you use the default `ibexa/migrations` package to migrate your data. diff --git a/docs/guide/data_migration/data_migration_actions.md b/docs/guide/data_migration/data_migration_actions.md new file mode 100644 index 0000000000..a39077202a --- /dev/null +++ b/docs/guide/data_migration/data_migration_actions.md @@ -0,0 +1,181 @@ +--- +description: Data migration actions enable you to run special operations while executing data migrations, such as assigning Roles, Sections, Objects states, and so on. +--- + +# Data migration actions + +Some migration steps can contain a special `actions` property. +You can find which migration steps support actions in the table below: + +||`create`|`update`|`delete`| +|---|:---:|:---:|:---:| +|`content`|✔|✔|✔| +|`content_type`|✔|✔|✔| +|`role`|✔|✔|| +|`user`|✔|✔|| +|`user_group`|✔|✔|| + +Actions are optional operations that can be run after the main "body" of a migration has been executed +(that is, content has been created / updated, Object state has been added, and so on). +Their purpose is to allow additional operations to be performed as part of this particular migration. +They are executed inside the same transaction, so in the event of failure they cause database rollback to occur. + +For example, when updating a Content Type object, some fields might be removed: +``` yaml +- + type: content_type + mode: update + match: + field: content_type_identifier + value: article + actions: + - { action: assign_content_type_group, value: 'Media' } + - { action: unassign_content_type_group, value: 'Content' } + - { action: remove_field_by_identifier, value: 'short_title' } + - { action: remove_drafts, value: null } +``` + +When executed, this migration: + +- Finds Content Type using its identifier (`article`) +- Assigns Content Type Group "Media" +- Removes it from Content Type Group "Content" +- Removes the `short_title` Field +- Removes its existing drafts, if any. + +## Available migration actions + +The following migration actions are available out of the box: + +- `assign_object_state` (Content Create) +- `assign_parent_location` (Content Create / Update) +- `assign_section` (Content Update) +- `hide` (Content Create / Update) +- `reveal` (Content Create / Update) +- `assign_content_type_group` (Content Type Create / Update) +- `remove_drafts` (Content Type Update) +- `remove_field_by_identifier` (Content Type Update) +- `unassign_content_type_group` (Content Type Update) +- `assign_role_to_user` (Role Create / Update) +- `assign_role_to_user_group` (Role Create / Update) +- `assign_user_to_role` (User Create / Update) +- `assign_user_group_to_role` (User Group Create / Update) +- `unassign_role_user_group` (User Group Update) + +In contrast with Kaliop migrations, actions provide you with ability to perform additional operations and extend +the migration functionality. +See [creating your own Actions](create_migration_action.md). + +## Action usage examples + +### Content + +mode: Create +``` yaml + actions: + - { action: assign_object_state, identifier: locked, groupIdentifier: ez_lock } + - { action: assign_parent_location, value: 2 } + - { action: hide } +``` + +mode: Update +``` yaml + actions: + - { action: assign_parent_location, value: 2 } + - { action: assign_section, id: 4 } + - { action: assign_section, identifier: 'media' } +``` + +### Content Types + +mode: Create +``` yaml + actions: + - { action: assign_content_type_group, value: 'Media' } +``` + +mode: Update +``` yaml + actions: + - { action: assign_content_type_group, value: 'Media' } + - { action: unassign_content_type_group, value: 'Content' } + - { action: remove_field_by_identifier, value: 'short_title' } + - { action: remove_drafts, value: null } +``` + +### Roles + +mode: Create and Update +``` yaml + actions: + - + action: assign_role_to_user_group + remote_id: 'remote_id_152454854' + - + action: assign_role_to_user_group + id: 42 + - + action: assign_role_to_user + id: 42 + - + action: assign_role_to_user + email: 'mail@invalid.c' + - + action: assign_role_to_user + login: foo +``` + +### Users + +mode: Create and Update +``` yaml + actions: + - + action: assign_user_to_role + identifier: foo + - + action: assign_user_to_role + id: 2 + - + action: assign_user_to_role + id: 2 + limitation: + type: Section + values: + - 1 +``` + +### User Groups + +mode: Create and Update +``` yaml + actions: + - + action: assign_user_group_to_role + identifier: Editor + - + action: assign_user_group_to_role + id: 2 + - + action: assign_user_group_to_role + id: 1 + limitation: + type: Section + values: + - 1 +``` +!!! note + + In the `assign_user_group_to_role` action, Limitation type Section can only use Section ID: + +mode: Update +``` yaml + actions: + - + action: unassign_role_user_group + id: 1 +``` + +!!! note + + In the `unassign_role_user_group` action, the ID is Role assignment ID from the `ezuser_role` table. diff --git a/docs/guide/data_migration/exporting_and_importing_data.md b/docs/guide/data_migration/exporting_and_importing_data.md new file mode 100644 index 0000000000..10746faa10 --- /dev/null +++ b/docs/guide/data_migration/exporting_and_importing_data.md @@ -0,0 +1,342 @@ +--- +description: Export Repository data to use in future data migrations. +--- + +# Exporting data + +To see an example of migrations in action, export data already present in your installation. + +To export Repository content, use the `ibexa:migrations:generate` command. +This command generates a YAML file with the requested part of the Repository. +The file is located by default in the `src/Migrations/Ibexa/migrations` folder +or in [a custom folder that you configure](migration_management.md#migration-folders). +You can later use this file to import the data. + +``` bash +php bin/console ibexa:migrations:generate --type=content --mode=create +``` + +This generates a file containing all Content items. +Below you can see part of the output of the default Ibexa DXP installation. + +``` yaml +- + type: content + mode: create + metadata: + contentType: user_group + mainTranslation: eng-GB + creatorId: 14 + modificationDate: '2002-10-06T17:19:56+02:00' + publicationDate: '2002-10-06T17:19:56+02:00' + remoteId: f5c88a2209584891056f987fd965b0ba + alwaysAvailable: true + section: + id: 2 + identifier: users + location: + parentLocationId: 1 + parentLocationRemoteId: null + locationRemoteId: 3f6d92f8044aed134f32153517850f5a + hidden: false + sortField: 1 + sortOrder: 1 + priority: 0 + fields: + - + fieldDefIdentifier: name + languageCode: eng-GB + value: Users + - + fieldDefIdentifier: description + languageCode: eng-GB + value: 'Main group' + references: + - + name: ref__content__user_group__users + type: content_id + - + name: ref_location__user_group__users + type: location_id + - + name: ref_path__user_group__users + type: path +``` + +The output contains all the possible information for a future migration command. +Parts of it can be removed or modified. +You can treat it as a template for another Content item for user group. +For example, you could: + +- Remove `references` if you don't intend to store IDs for future use (see [migration references](migration_management.md#references)) +- Remove `publicationDate`, `modificationDate`, `locationRemoteId`, + as those are generated if not passed (just like in PHP API) +- Add [`actions`](data_migration_actions.md) +- Add fields for other languages present in the system. + +Similarly, you can create update and delete operations. +They are particularly useful combined with `match-property`. +This option is automatically added as part of `match` expression in the update/delete migration: + +``` bash +php bin/console ibexa:migrations:generate --type=content_type --mode=update --match-property=content_type_identifier --value=article +``` + +```yaml +- + type: content_type + mode: update + match: + field: content_type_identifier + value: article + metadata: + identifier: article + mainTranslation: eng-GB + modifierId: 14 + modificationDate: '2012-07-24T14:35:34+00:00' + remoteId: c15b600eb9198b1924063b5a68758232 + urlAliasSchema: '' + nameSchema: '' + container: true + defaultAlwaysAvailable: false + defaultSortField: 1 + defaultSortOrder: 1 + translations: + eng-GB: + name: Article + fields: + - + identifier: title + type: ezstring + position: 1 + translations: + eng-GB: + name: Title + required: true + searchable: true + infoCollector: false + translatable: true + category: '' + defaultValue: 'New article' + fieldSettings: { } + validatorConfiguration: + StringLengthValidator: + maxStringLength: 255 + minStringLength: null + # - ... + +``` + +Note that you should test your migrations. See [Executing migrations](#executing-migrations). + +!!! tip + + Migration command can be executed with database rollback at the end with the `--dry-run` option. + +## type + +The mandatory `--type` option defines the type of Repository data to export. +The following types are available: + +- `content` +- `content_type` +- `role` +- `content_type_group` +- `user` +- `user_group` +- `language` +- `object_state_group` +- `object_state` +- `section` +- `location` + +If you do not provide the `--type` option, the command asks you to select a type of data. + +## mode + +The mandatory `--mode` option defines the action that importing the file performs. +The following modes are available: + +- `create` - creates new items +- `update` - updates an existing item. Only covers specified fields and properties. If the item does not exist, causes an error. +- `delete` - deletes an existing item. If the item does not exist, causes an error. + +If you do not provide the `--mode` option, the command asks you to select the mode. + +The following data migration steps are available: + +||`create`|`update`|`delete`| +|---|:---:|:---:|:---:| +|`content`|✔|✔|✔| +|`content_type`|✔|✔|| +|`role`|✔|✔|✔| +|`content_type_group`|✔|✔|| +|`user`|✔|✔|| +|`user_group`|✔||✔| +|`language`|✔||| +|`object_state_group`|✔||| +|`object_state`|✔||| +|`section`|✔|✔|| +|`location`||✔|| + +## match-property + +The optional `--match-property` option, together with `value`, enables you to select which data from the Repository to export. +`match-property` defines what property should be used as a criterion for selecting data. +The following properties are available (per type): + +- `content` + - `content_id` + - `content_type_id` + - `content_type_group_id` + - `content_type_identifier` + - `content_remote_id` + - `location_id` + - `location_remote_id` + - `parent_location_id` + - `user_id` +- `content_type` + - `content_type_identifier` +- `content_type_group` + - `content_type_group_id` + - `content_type_group_identifier` +- `language` + - `language_code` +- `location` + - `location_remote_id` + - `location_id` +- `object_state` + - `object_state_id` + - `object_state_identifier` +- `object_state_group` + - `object_state_group_id` + - `object_state_group_identifier` +- `role` + - `identifier` + - `id` +- `section` + - `section_id` + - `section_identifier` +- `user` + - `login` + - `email` +- `user_group` + - `id` + - `remoteId` + +You can extend the list of available matchers by creating [a custom one](add_data_migration_matcher.md). + +## value + +The optional `--value` option, together with `match-property`, filters the Repository content that the command exports. +`value` defines which values of the `match-property` should be included in the export. + +For example, to export only Article Content items, use the `content_type_identifier` match property with `article` as the value: + +``` bash +php bin/console ibexa:migrations:generate --type=content --mode=create --match-property=content_type_identifier --value=article +``` + +!!! note + + The same `match-property` and `value` is added to generated `update` and `delete` type migration files. + +## file + +The optional `--file` option defines the name of the YAML file to export to. + +``` bash +php bin/console ibexa:migrations:generate --type=content --mode=create --file=my_data_export.yaml +``` + +!!! note + + When migrating multiple files at once (for example when calling `ibexa:migrations:migrate` without options), + they are executed in alphabetical order. + +## user-context + +The optional `--user-context` option enables you to run the export command as a specified User. +The command only exports Repository data that the selected User has access to. +By default the admin account is used, unless specifically overridden by this option or in +bundle configuration (`ibexa_migrations.default_user_login`). + +``` bash +php bin/console ibexa:migrations:generate --type=content --mode=create --user-context=jessica_andaya +``` + +## Executing migrations + +To import Repository data from YAML files, run the `ibexa:migrations:migrate` command. + +Place your import file in the `src/Migrations/Ibexa/migrations` folder +or in [a custom folder that you configure](migration_management.md#migration-folders). +The command takes the file name within this folder as parameter. +If file is not specified, all files within this directory are used. + +``` bash +php bin/console ibexa:migrations:migrate --file=my_data_export.yaml +``` + +Ibexa Migrations store execution metadata in `ibexa_migrations` database table. This allows incremental upgrades: +the `ibexa:migrations:migrate` command ignores files that it had previously executed. + +## Repeatable steps + +You can run a set of one or more similar migration steps multiple times by using the special `repeatable` migration type. + +A repeatable migration performs the defined migration steps as many times as the `iterations` setting declares. + +``` yaml hl_lines="4" +[[= include_file('code_samples/data_migration/examples/repeatable_step.yaml', 0, 5) =]] +``` + +!!! tip + + You can use repeatable migration steps, for example, + to quickly generate large numbers of Content items for testing purposes. + +You can vary the operations using the iteration counter. + +For example, to create five Folders, with names ranging from "Folder 0" to "Folder 4", you can run the following migration using the iteration counter `i`: + +``` yaml hl_lines="16" +[[= include_file('code_samples/data_migration/examples/repeatable_step.yaml', 0, 16) =]] +``` + +To vary the content name, the migration above uses [Symfony expression syntax](#expression-syntax). + +In the example above, the expression is enclosed in `###` and the repeated string `SSS`. + +!!! note + + Iteration counter is assigned to `i` by default, but you can modify it in the `iteration_counter_name` setting. + +### Generating fake data + +You can also generate fake data with the help of [`FakerPHP`](https://fakerphp.github.io/). + +To use it, first install Faker on your system: + +``` bash +composer require fakerphp/faker +``` + +Then, you can use `faker()` in expressions, for example: + +``` yaml +[[= include_file('code_samples/data_migration/examples/repeatable_step.yaml', 16, 19) =]] +``` + +This step generates Field values with fake personal names. + +## Expression syntax + +You can use [Symfony expression syntax](https://symfony.com/doc/current/components/expression_language/syntax.html) in data migrations. +It is especially useful in [repeatable steps](#repeatable-steps), +where you can use it to generate varied content in migration steps. + +The expression syntax uses the following structure: `### ###` + +The `IDENTIFIER` can be any repeated string that encloses the actual expression. + diff --git a/docs/guide/data_migration/migration_management.md b/docs/guide/data_migration/migration_management.md new file mode 100644 index 0000000000..bf5b06fca3 --- /dev/null +++ b/docs/guide/data_migration/migration_management.md @@ -0,0 +1,168 @@ +--- +description: Manage data migrations by adding files, converting from Kaliop migration bundle, checking migration status, and setting up configuration. +--- + +# Managing migrations + +## Converting migration files + +If you want to convert a file from the format used by the +[Kaliop migration bundle](https://github.com/kaliop-uk/ezmigrationbundle) +to the current migration format, use the `ibexa:migrations:kaliop:convert` command. + +The source file must use Kaliop mode and type combinations. +The converter handles Kaliop types that are different from Ibexa types. + +``` bash +php bin/console ibexa:migrations:kaliop:convert --input=kaliop_format.yaml --output=ibexa_format.yaml +``` + +You can also convert multiple files using `ibexa:migrations:kaliop:bulk-convert`: + +``` bash +php bin/console ibexa:migrations:kaliop:bulk-convert --recursive kaliop_files ibexa_files +``` + +If you do not specify the output folder, the command overwrites the input files. + +## Adding migration files + +Use the `ibexa:migrations:import` command to add files to the migration folder defined in configuration +(by default, `src/Migrations/Ibexa/migrations`). + +``` bash +php bin/console ibexa:migrations:import my_data_export.yaml +``` + +## Checking migration status + +To check the status of migration files in the migration folder defined in configuration, +run the following command: + +``` bash +php bin/console ibexa:migrations:status +``` + +The command lists the migration files and indicates which of them have already been migrated. + +## Migration folders + +The default migration folder is `src/Migrations/Ibexa/migrations`. + +You can configure a different folder by using the following settings: + +``` yaml +ibexa_migrations: + migration_directory: %kernel.project_dir%/src/Migrations/MyMigrations/ + migrations_files_subdir: migration_files +``` + +## Preview configuration + +You can get default configuration along with option descriptions by executing the following command: + +```bash +bin/console config:dump-reference ibexa_migrations +``` + +## References + +References are key-value pairs necessary when one migration depends on another. + +Since some migrations generate object properties (like IDs) during their execution, which cannot be known in advance, +references provide migrations with the ability to use previously created object properties in further migrations. +They can be subsequently used by passing them in their desired place with `reference:` prefix. + +The example below creates a Content item of type "folder", and stores its Location path as `"ref_path__folder__media"`. +Then this reference is reused as part of a new role, as a limitation. + +```yaml +- + type: content + mode: create + metadata: + contentType: folder + mainTranslation: eng-US + alwaysAvailable: true + section: 3 + objectStates: { } + location: + parentLocationId: 1 + hidden: false + sortField: !php/const eZ\Publish\API\Repository\Values\Content\Location::SORT_FIELD_NAME + sortOrder: 1 + priority: 0 + fields: + - + fieldDefIdentifier: name + languageCode: eng-US + value: Media + # - ... + actions: { } + references: + - + name: ref__content__folder__media + type: content_id + - + name: ref_location__folder__media + type: location_id + - + name: ref_path__folder__media + type: path + +- + type: role + mode: create + metadata: + identifier: foo + policies: + - + module: content + function: 'read' + limitations: + - + identifier: Subtree + values: ['reference:ref_path__folder__media'] + +``` + +By default, reference files are located in a separate directory `src/Migrations/Ibexa/references` +(see [previewing reference](#preview-configuration) +`ibexa_migrations.migration_directory` and `ibexa_migrations.references_files_subdir` options). + +Reference files are **NOT** loaded by default. A separate step (type: "reference", mode: "load", with filename as "value") +is required. Similarly, saving a reference file is done using type: "reference", mode: "save" step, with filename. + +For example: +```yaml +- + type: reference + mode: load + filename: 'references.yaml' + +- + type: reference + mode: save + # You can also use 'references.yaml', in this case it is overridden + filename: 'new_references.yaml' +``` + +!!! note + + You don't need to save references if they are used in the same migration file. + References are stored in memory during migration, whether they are used or not. + +## Available reference types + +- `content` + - content_id + - location_id + - path +- `content_type` + - content_type_id +- `language` + - language_id +- `role` + - role_id +- `user_group` + - user_group_id diff --git a/docs/guide/data_providers/access_dataprovider_via_php.md b/docs/guide/data_providers/access_dataprovider_via_php.md deleted file mode 100644 index 3ce3090c85..0000000000 --- a/docs/guide/data_providers/access_dataprovider_via_php.md +++ /dev/null @@ -1,54 +0,0 @@ -# Accessing data provider via PHP [[% include 'snippets/commerce_badge.md' %]] - -## Catalog Data Provider Service - -Example usage in Controller or services: - -``` php -/** @var $catalogService \Silversolutions\Bundle\EshopBundle\Services\Catalog\CatalogDataProviderService */ -$catalogService = $this->get('silver_catalog.data_provider_service'); - -/** @var $catalogElement \Silversolutions\Bundle\EshopBundle\Catalog\CatalogElement */ -/* Get product by Url/Request */ -$catalogElement = $catalogService->getDataProvider()->lookupByUrl($urlService->getSeoUrl($request), $ezHelper->getCurrentLanguageCode()); -/* Get product by sku */ -$catalogElement = $catalogService->getDataProvider()->fetchElementBySku( - $productNodeSku, - array() -); -``` - -This service injects different data providers to the Catalog Service using external configuration. -You can choose the provider depending on SiteAccess, URL, etc. - -## Implementation - -To choose the proper catalog provider, use [tags and compiler pass.](http://symfony.com/doc/3.4/components/dependency_injection/tags.html) - -### Compiler pass - -New compiler pass (`CatalogDataProviderOperationsPass`) collects all services which are tagged with `catalog_data_provider_operation`. -For example: - -`` - -The compiler pass calls `setDataProviderService` from `CatalogDataProviderService` and sets all available providers. - -When the actual call to catalog data provider service is made, the proper provider is chosen depending on the SiteAccess. - -``` php -/** -* Array, which holds the catalog data providers services as dependencies. -* -* @var CatalogDataProvider[] -*/ -protected $dataProviders; - -... - -public function getDataProvider() -{ - // return the data provider by SiteAccess - return $this->dataProviders[$this->configResolver->getParameter('catalog_data_provider', 'silver_eshop')]; -} -``` diff --git a/docs/guide/data_providers/content_model_dataprovider.md b/docs/guide/data_providers/content_model_dataprovider.md deleted file mode 100644 index 66383e4c16..0000000000 --- a/docs/guide/data_providers/content_model_dataprovider.md +++ /dev/null @@ -1,57 +0,0 @@ -# Content model data provider [[% include 'snippets/commerce_badge.md' %]] - -The content model data provider provides an implementation for fetching catalogs and products from the database. - -Products are stored directly in the content structure. -They can use the features provided by the content model such as languages, Objects states, versioning, etc. -The product catalog can be maintained in the Back Office. - -## Configuration - -The data provider uses configuration to limit the fetched catalog elements. - -The following default setup filters content of type `ses_category` for the navigation service -(which uses this data provider to fetch the category objects). -The sort order is controlled by the Priority field and the publish date. - -The second level key defines the scope, or `filterType`, for which the specific filter definitions are valid. -In this example, the `navigation` is passed by the navigation service's fetch. - -``` yaml -silver_eshop.default.ez5_catalog_data_provider.filter: - navigation: - contentTypes: [ "ses_category" ] - limit: 20 - sortClauses: - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\Location\\Priority" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_DESC" - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\DatePublished" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_ASC" - catalogList: - contentTypes: ["ses_category", "ses_product"] - sortClauses: - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\Location\\Priority" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_DESC" - productList: - contentTypes: ["ses_product"] - sortClauses: - - - clause: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query\\SortClause\\Location\\Priority" - order: "\\eZ\\Publish\\API\\Repository\\Values\\Content\\Query::SORT_DESC" -``` - -!!! note "Important" - - If a product has multiple Locations, the shop ensures that the proper Location is returned, - so the URL of the product is correct. - - By default, hidden items (e.g. products) are not fetched. - -|Parameter|Description| -|--- |--- | -|`contentTypes`|Identifier of the Content Types defined in the system| -|`limit`|Default limit to be used when no limit is given| -|`sortClauses`|Sorting clauses| diff --git a/docs/guide/data_providers/data_providers.md b/docs/guide/data_providers/data_providers.md index d39ed93d1d..ff1d2d6eeb 100644 --- a/docs/guide/data_providers/data_providers.md +++ b/docs/guide/data_providers/data_providers.md @@ -1,54 +1,65 @@ -# Data providers [[% include 'snippets/commerce_badge.md' %]] +# Data providers Data providers define where product information is stored. -You can use one of two data providers: the content model data provider and the eContent data provider. +You can use one of the following data providers: the [Repository data provider](repository_data_provider.md) +or the [eContent data provider](../econtent/econtent.md). + +## Repository vs. eContent data provider + +| Feature | Repository | eContent | +| ------------------------------------ | -------------------------------------------- | -------------------------------------------- | +| Flexible data model | yes | yes | +| Translations | yes | yes | +| Interface for editing | yes | no | +| Versioning | yes | no | +| Fast imports | no | yes | +| Support of large catalogs | no | yes | +| Staging (live and temporary space) | no | yes | ## Switching the data provider -You can switch the data provider either with the command line or manually. -Only one data provider can be activated per installation. +You can switch the data provider either [with the command line](#command-line-switching) or [manually](#manual-switching). +Only one data provider can be active at the same time. -### Command line switching +### Command-line switching -The `silversolutions:switchdataprovider` command switches between the content model data provider and eContent. +The `ibexa:commerce:switch-data-provider` command switches between the Repository and eContent data provider. It is useful especially for testing purposes. The command takes the following options: |Option|Notes| |--- |--- | -|`--new-root-node`|Default value is `56` for content model data provider, `2` for eContent| -|`--location-id`|Default value is `56`. This is the Location ID of the "Product catalog" Content item in [[= product_name =]]. If you are using another Location ID, change this parameter| +|`--new-root-node`|The default values are: `56` for Repository data provider, `2` for eContent.| +|`--location-id`|The default value is `56`. This is the default Location ID of the "Product catalog" Content item. If you are using another Location ID, change this parameter.| + +To switch to eContent data provider, use the following command: ``` bash -# switch to eContent -php bin/console silversolutions:switchdataprovider econtent --location-id=56 --new-root-node=2 -php bin/console silversolutions:indexecontent --no-debug -php bin/console silversolutions:indexecontent swap --no-debug - -# switch to content model -php bin/console silversolutions:switchdataprovider ez +php bin/console ibexa:commerce:switch-data-provider econtent --location-id=56 --new-root-node=2 +php bin/console ibexa:commerce:index-econtent --no-debug +php bin/console ibexa:commerce:index-econtent swap --no-debug ``` -Remember to clear the cache after you execute the command: +To switch to the Repository data provider, use the following command: ``` bash -php bin/console cache:clear --env=prod +php bin/console ibexa:commerce:switch-data-provider ez ``` +After you execute the command, clear the cache. + ### Data provider switch process -Switching the data provider introduces some changes to your project: +Switching the data provider introduces the following changes to your project: #### Changes to `config/packages/ecommerce.yml` -``` -# eContent data provider -resource: '../../vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/config/config_data_provider_econtent.yml' or - -# content model data provider -resource: '../../vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/config/config_data_provider_ez.yml' +``` yaml +- { resource: '@SilversolutionsEshopBundle/Resources/config/config_data_provider_econtent.yml' } +# or +- { resource: '@SilversolutionsEshopBundle/Resources/config/config_data_provider_ez.yml' } ``` #### Changes to the "Product catalog" Content item @@ -57,9 +68,6 @@ The Product catalog's root node is set accordingly. ![](../img/product_catalog.png) -- For eContent, the root node is set to `2` by default, for content model it is `56` (in case the demo data is used). -- The Location ID for the Product catalog can be set to a different value by using the `--location-id parameter`. - ### Manual switching #### 1. Load root element @@ -68,29 +76,19 @@ First, ensure that you are loading the correct root element for your provider. ![](../img/manual_switching.png) -#### 2. Change configuration - -The following settings are already set up in the configuration files `econtent_search.yml` or `ez_search.yml`: - -``` yaml -silver_eshop.default.catalog_data_provider: ez5 -# or for eContent -silver_eshop.default.catalog_data_provider: econtent -``` - -#### 3. Configure search services +#### 2. Configure search services Change the alias for search services for every type (product, catalog, content). ``` xml # set up alias for product search -# for eZ dataprovider - - +# for Repository data provider + + -# for econtent - - +# for eContent + + # set up alias for catalog search @@ -101,41 +99,41 @@ Change the alias for search services for every type (product, catalog, content). ``` -#### 4. Set up search groups +#### 3. Set up search groups Set up search groups and configuration accordingly. Check the complete configuration from the vendor as well: -- `vendor/silversolutions/silver.e-shop/src/Siso/Bundle/SearchBundle/Resources/config/econtent_search.yml` -- `vendor/silversolutions/silver.e-shop/src/Siso/Bundle/SearchBundle/Resources/config/ez_search.yml` +- `Siso/Bundle/SearchBundle/Resources/config/econtent_search.yml` +- `Siso/Bundle/SearchBundle/Resources/config/ez_search.yml` -The path for products in econtent e.g. is `/2/` and for eZ `/1/2` +The path for products in eContent is `/2/` and for Repository it is `/1/2`. ``` yaml siso_search.default.groups.search: - product: - types: - - ses_product - path: '/1/2/' - section: 1 - visibility: true - content: - types: - - st_module - - folder - - article - - landing_page - - blog_post - - event - path: '/1/2/' - section: 1 - visibility: true - files: - types: - - file - - video - path: '/1/43/' - section: 3 - visibility: true + product: + types: + - ses_product + path: '/1/2/' + section: 1 + visibility: true + content: + types: + - st_module + - folder + - article + - landing_page + - blog_post + - event + path: '/1/2/' + section: 1 + visibility: true + files: + types: + - file + - video + path: '/1/43/' + section: 3 + visibility: true ``` diff --git a/docs/guide/data_providers/repository_data_provider.md b/docs/guide/data_providers/repository_data_provider.md new file mode 100644 index 0000000000..05d0a4a845 --- /dev/null +++ b/docs/guide/data_providers/repository_data_provider.md @@ -0,0 +1,24 @@ +# Repository data provider + +The Repository data provider is the default provider for catalog and product data. + +Products are stored directly in the content structure as Content items. +Products can have the characteristics provided by the Repository, such as languages, Objects states, versioning, etc. +You can maintain the product catalog in the Back Office. + +## Configuration + +The data provider uses configuration to limit the fetched catalog elements. + +The following configuration sets the limit for fetching elements in navigation: + +``` yaml +silver_eshop.default.ez5_catalog_data_provider.filter: + navigation: + contentTypes: [ "ses_category" ] + limit: 20 +``` + +!!! note + + By default, hidden items (for example, products) are not fetched. diff --git a/docs/guide/databases.md b/docs/guide/databases.md index cbb9838bcc..ba38fcfa31 100644 --- a/docs/guide/databases.md +++ b/docs/guide/databases.md @@ -1,3 +1,7 @@ +--- +description: Ibexa DXP can use MySQL, PostgreSQL or MariaDB as its database. +--- + # Databases ## Using PostgreSQL diff --git a/docs/guide/design_engine.md b/docs/guide/design_engine.md deleted file mode 100644 index 6d17681d45..0000000000 --- a/docs/guide/design_engine.md +++ /dev/null @@ -1,271 +0,0 @@ -# Design engine - -You can provide design themes for your application, with an automatic fallback system -using the design engine from the [ezplatform-design-engine](https://github.com/ezsystems/ezplatform-design-engine) bundle. -It is very similar to the [eZ Publish legacy design fallback system](https://doc.ez.no/eZ-Publish/Technical-manual/5.x/Concepts-and-basics/Designs/Design-combinations). - -When you call a given template or asset, the system will look for it in the first configured theme. -If it cannot be found, the system will fall back to all other configured themes for your current SiteAccess. - -Under the hood, the theming system uses Twig namespaces. As such, Twig is the only supported template engine. -For assets, the system uses the Symfony Asset component with asset packages. - -## Terminology - -- **Theme**: A labeled collection of templates and assets. - - Typically a directory containing templates. For example, templates located under `templates/themes/my_theme` - are part of `my_theme` theme. - -!!! caution - - After creating a new folder either in a project's or your bundle's `templates/themes/` directory - you need to clear Symfony cache (`bin/console cache:clear`), even if you are working in the dev environment. - -- **Design**: A collection of themes. - - The order of themes within a design is important as it defines the fallback order. - A design is identified with a name. One design can be used per SiteAccess. - -### Default designs - -By default two designs are included: - -`admin` covers templates for the Back Office. It contains the `admin` theme. - -`standard` covers the default content rendering templates. It contains the `standard` theme. - -When the `ez_platform_standard_design.override_kernel_templates` setting is `true`, -the `standard` theme is automatically mapped to the directory in kernel containing the templates. - -``` yaml -ez_platform_standard_design: - override_kernel_templates: true -``` - -!!! caution - - If you encounter problems during upgrading, disable the override by setting `override_kernel_templates` to `false`. - -The standard design overrides the following Twig templates by prefixing them with the `@ezdesign` namespace: - -- `viewbase_layout.html.twig` -- `pagelayout.html.twig` -- `default/content/full.html.twig` -- `default/content/line.html.twig` -- `default/content/text_linked.html.twig` -- `default/content/embed.html.twig` -- `default/content/embed_image.html.twig` -- `default/block/block.html.twig` -- `content_fields.html.twig` -- `fielddefinition_settings.html.twig` - -!!! caution - - `ez_platform_standard_design.override_kernel_templates` is set to `true` in new installations. - However, when upgrading from older version it will be set to false to avoid overriding existing configuration. - -## Configuration - -To define and use a design, you need to: - -1. Declare it, with a name and a collection of themes to use -1. Use it for your SiteAccess - -Here is a simple example: - -```yaml -ezdesign: - # You declare all available designs under "design_list". - design_list: - # my_design will be composed of "theme1" and "theme2" - # "theme1" will be tried first. If a template cannot be found in "theme1", "theme2" will be tried out. - my_design: [theme1, theme2] - -ezplatform: - # ... - system: - my_siteaccess: - # my_siteaccess will use "my_design" - design: my_design -``` - -!!! note - - Default design for a SiteAccess is `standard` which contains no themes. - If you use the `@ezdesign` Twig namespace and/or the `ezdesign` asset package, the system will always fall back to - application level and override directories for templates/assets lookup. - -## Referencing current design - -To reference current design in your code, use the `ConfigResolver` service (`ezpublish.config.resolver`): - -```php -$currentDesign = $this->getConfigResolver->getParameter('design'); -``` - -## Design usage with assets - -For assets, a special **`ezdesign` asset package** is available. - -```html+twig - - - - -foo -``` - -Using the `ezdesign` package will resolve current design with theme fallback. - -By convention, an asset theme directory can be located in: -- `/Resources/public/themes/` -- `assets/themes/` - -Typical paths can be for example: -- `/Resources/public/themes/foo/` => Assets will be part of the `foo` theme. -- `/Resources/public/themes/bar/` => Assets will be part of the `bar` theme. -- `assets/themes/biz/` => Assets will be part of the `biz` theme. - -It is also possible to use `assets` as a global override directory. -If called asset is present **directly under this directory**, it will always be considered first. - -!!! caution - - You must have *installed* your assets with `assets:install` command, so that your public resources are - *installed* into the `assets/` directory. - -### Fallback order - -The default fallback order is: -- Application assets directory: `assets/` -- Application theme directory: `assets/themes//` -- Bundle theme directory: `web/bundles//themes//` - -Calling `asset("js/foo.js", "ezdesign")` can for example be resolved to `web/bundles/app/themes/my_theme/js/foo.js`. - -### Performance and asset resolution - -When using themes, paths for assets are resolved at runtime. -This is due to how the Symfony Asset component is integrated with Twig. -This can cause significant performance impact because of I/O calls when looping over all potential theme directories, -especially when using a lot of different designs and themes. - -To work around this issue, assets resolution can be provisioned at compilation time. -Provisioning is the **default behavior in non-debug mode** (e.g. `prod` environment). -In debug mode (e.g. `dev` environment), assets are being resolved at runtime. - -This behavior can, however, be controlled by the `disable_assets_pre_resolution` setting. - -```yaml -# ezplatform_prod.yaml -ezdesign: - # Force runtime resolution - # Default value is '%kernel.debug%' - disable_assets_pre_resolution: true -``` - -## Design usage with templates - -By convention, a theme directory must be located under `/Resources/views/themes/` or global -`templates/themes/` directories. - -Typical paths can be for example: -- `templates/themes/foo/` => Templates will be part of the `foo` theme. -- `templates/themes/bar/` => Templates will be part of the `bar` theme. -- `src/Acme/TestBundle/Resources/views/themes/the_best/` => Templates will be part of `the_best` theme. - -In order to use the configured design with templates, you need to use the **`@ezdesign`** special **Twig namespace**. - -```html+twig -{# Will load 'some_template.html.twig' directly under one of the specified theme directories #} -{{ include("@ezdesign/some_template.html.twig") }} - -{# Will load 'another_template.html.twig', located under 'full/' directory, which is located under one of the specified theme directories #} -{{ include("@ezdesign/full/another_template.html.twig") }} -``` - -You can also use `@ezdesign` notation in your template selection rules: - -```yaml -ezplatform: - system: - my_siteaccess: - content_view: - full: - home: - template: '@ezdesign/full/home.html.twig' -``` - -!!! tip - - You may also use this notation in controllers. - -### Fallback order - -The default fallback order is: -- Application theme directory: `templates/themes//` -- Bundle theme directory: `src//Resources/views/themes//` - -!!! note - - Bundle fallback order is the instantiation order in `bundles.php`. - -#### Additional theme paths - -In addition to the convention described above, it is also possible to add arbitrary Twig template directories -to a theme from configuration. This can be useful when you want to define templates from third-party bundles -as part of one of your themes, or when upgrading your application in order to use [[= product_name =]] design engine, -when your existing templates are not yet following the convention. - -```yaml -ezdesign: - design_list: - my_design: [my_theme, some_other_theme] - templates_theme_paths: - # FOSUserBundle templates will be part of "my_theme" theme - my_theme: - - '%kernel.project_dir%/vendor/friendsofsymfony/user-bundle/Resources/views' -``` - -!!! note "Paths precedence" - - Directories following the convention will **always** have precedence over the ones defined - in config. This ensures that it is always possible to override a template from the application. - -#### Additional override paths - -It is possible to add additional global override directories: - -```yaml -ezdesign: - templates_override_paths: - - '%kernel.project_dir%/src/another_override_directory' - - /some/other/directory -``` - -### PHPStorm support - -`@ezdesign` Twig namespace is a *virtual* namespace, and as such is not automatically recognized by the PHPStorm Symfony plugin -for `goto` actions. - -`EzPlatformDesignEngine` will generate a `ide-twig.json` file which will contain all detected theme paths for templates in your project. -It is activated by default in debug mode (`%kernel.debug%`). - -By default, this config file will be stored at your project root (`%kernel.project_dir%`), but you can customize the path -if your PHPStorm project root doesn't match your Symfony project root. - -!!! note - - `ide-twig.json` **must** be stored at your PHPStorm project root. - -Default config: - -```yaml -ezdesign: - phpstorm: - # Activates PHPStorm support - enabled: '%kernel.debug%' - # Path where to store PHPStorm configuration file for additional Twig namespaces (ide-twig.json). - twig_config_path: '%kernel.project_dir%' -``` diff --git a/docs/guide/devops.md b/docs/guide/devops.md index 218a429143..17c801be46 100644 --- a/docs/guide/devops.md +++ b/docs/guide/devops.md @@ -1,10 +1,14 @@ +--- +description: See various tools that can help you debug your Ibexa DXP installation. +--- + # Devops ## Cache clearing ### Clearing file cache using the Symfony cache:clear command -Symfony provides a command for clearing cache. It will delete all file-based caches, which mainly consist of Twig template, Symfony container, and Symfony route cache, but also everything else stored in the cache folder. Out of the box on a single-server setup this includes Content cache. For further information on the command's use, see its help text: +Symfony provides a command for clearing cache. It will delete all file-based caches, which mainly consist of a Twig template, a [service container](../api/public_php_api.md#service-container), and the Symfony route cache, but also everything else stored in the cache folder. Out of the box on a single-server setup this includes Content cache. For further information on the command's use, see its help text: ``` bash php bin/console --env=prod cache:clear -h @@ -20,9 +24,9 @@ php bin/console --env=prod cache:clear -h ### Clearing content cache on a cluster setup -For a [cluster](clustering.md) setup, the content cache ([HTTP cache](http_cache.md) and [Persistence cache](persistence_cache.md)) must be set up to be shared among the servers. And while all relevant cache is cleared for you on Repository changes when using the APIs, there might be times where you'll need to clear cache manually:  +For a [cluster](clustering.md) setup, the content cache ([HTTP cache](cache/http_cache.md) and [Persistence cache](persistence_cache.md)) must be set up to be shared among the servers. And while all relevant cache is cleared for you on Repository changes when using the APIs, there might be times where you'll need to clear cache manually:  -- Varnish: [Cache purge](http_cache.md#cache-purging) +- Varnish: [Cache purge](cache/symfony_reverse_proxy.md#using-varnish-or-fastly) - Persistence Cache: [Using Cache service](persistence_cache.md#using-cache-service) ## Web Debug Toolbar @@ -33,11 +37,11 @@ When running [[= product_name =]] in the `dev` environment you have access to th #### SPI (persistence) -This section provides the number of non-cached [SPI](repository.md#spi) calls and handlers. You can see details of these calls in the [Symfony Profiler](http://symfony.com/doc/5.0/profiler.html) page. +This section provides the number of non-cached [SPI](repository.md#spi) calls and handlers. You can see details of these calls in the [Symfony Profiler]([[= symfony_doc =]]/profiler.html) page. #### SiteAccess -Here you can see the name of the current SiteAccess and how it was matched. For reference see the [list of possible SiteAccess matchers](siteaccess_matching.md#available-matchers). +Here you can see the name of the current SiteAccess and how it was matched. For reference see the [list of possible SiteAccess matchers](multisite/siteaccess_matching.md#available-siteaccess-matchers). ## Logging and debug configuration diff --git a/docs/guide/displaying_children_of_a_content_item.md b/docs/guide/displaying_children_of_a_content_item.md deleted file mode 100644 index ee4fb11475..0000000000 --- a/docs/guide/displaying_children_of_a_content_item.md +++ /dev/null @@ -1,254 +0,0 @@ -# Displaying Content item children - -One of the basic design tasks you may need to complete when creating your website is configuring one page to display all of its children. -For example you can configure a blog displaying all blog posts or a folder showing all articles it contains. - -There are three ways to make a Content item display its children: - -1. [Using the Content query Field Type](#using-the-content-query-field-type) -1. [Using the Query Controller](#using-the-query-controller) -1. [Using a Custom Controller](#using-a-custom-controller) - -This procedure demonstrates how to use these three methods to display all children of a Content item with the Content Type Folder. - -## Using the Content query Field Type - -The Query Controller is a pre-defined custom content view Controller that runs a Repository Query -that you can use together with the Content query Field Type. - -This example uses the built-in `Children` Query Type -which retrieves the children of the current Location. - -To use the Query Type with a Content query Field, add the Field to your Folder Content Type's definition. - -Select "Children" as the Query Type. Provide the `content` parameter -that the Query type requires: - -``` -content: '@=content' -``` - -You can paginate the query results by checking the **Enable pagination** box and selecting a limit of results per page. - -To customize the display template, in your [standard view configuration](../guide/content_rendering.md#configuring-views-the-viewprovider) file, -under `content_view`, add a section that indicates the matcher and template to use: - -``` yaml -content_query_field: - folder_children: - match: - Identifier\ContentType: folder - '@EzSystems\EzPlatformQueryFieldType\eZ\ContentView\FieldDefinitionIdentifierMatcher': children - template: content_query/folder.html.twig -``` - -Then, provide the template in `templates/content_query/folder.html.twig`. -The query results are available in the `items` variable: - -``` html+twig -

    {{ ez_content_name(content) }}

    - -{% for item in items %} -

    {{ ez_content_name(item.contentInfo) }}

    -{% endfor %} - -{% if isPaginationEnabled %} - {{ pagerfanta( items, 'ez', { - 'pageParameter': pageParameter, - 'routeName': '_ez_content_view', - 'routeParams' : {'contentId': content.id, 'locationId': location.id } - } ) }} -{% endif %} -``` - -## Using the Query Controller - -You can also use the `Children` Query Type together with the Query Controller. - -In your [standard view configuration](../guide/content_rendering.md#configuring-views-the-viewprovider) file, under `content_view`, add a section that indicates when this Controller will be used. It is similar to regular view config, but contains additional information: - -``` yaml -folder: - controller: ez_query::contentQueryAction - template: full/folder.html.twig - match: - Identifier\ContentType: folder - params: - query: - query_type: Children - parameters: - location: '@=location' - assign_results_to: items -``` - -In this case the `controller` key points to the Query Controller's `contentQuery` action. `assign_results_to` identifies the parameter containing all the retrieved children that will later be used in the templates, like here in `templates/full/folder.html.twig`: - -``` html+twig -

    {{ ez_content_name(content) }}

    - -{% for item in items.searchHits %} -

    {{ ez_content_name(item.valueObject.contentInfo) }}

    -{% endfor %} -``` - -This template makes use of the `items` specified in `assign_results_to` to list every child of the folder. - -## Using a Custom Controller - -There are three different ways of using a Custom Controller. See [Custom Controller](../guide/controllers.md#custom-rendering-logic) section. - -In this case, we will be using the Custom Controller alongside the built-in ViewController. - -Configuring for the use of a Custom Controller starts with pointing to it in your standard view configuration (which you can keep in `ezplatform.yaml` or as a separate file, for example `views.yaml`): - -``` yaml -folder: - controller: App\Controller\FolderController::showAction - template: full/folder.html.twig - match: - Identifier\ContentType: folder -``` - -You can see here the standard view config consisting of the `template` and `match` keys. -Under the `controller` key, you need to provide the path to the Controller and the action. -They are defined in `src/Controller/FolderController.php`: - -``` php -searchService = $searchService; - $this->configResolver = $configResolver; - $this->childrenCriteria = $childrenCriteria; - } - - /** - * Displays blog posts and gallery images on home page. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - public function showAction(ContentView $view) - { - $view->addParameters([ - 'items' => $this->fetchItems($view->getLocation(), 25), - ]); - return $view; - } - - private function fetchItems($location, $limit) - { - $languages = $this->configResolver->getParameter('languages'); - $query = new Query(); - - $query->query = $this->childrenCriteria->generateChildCriterion($location, $languages); - $query->performCount = false; - $query->limit = $limit; - $query->sortClauses = [ - new SortClause\DatePublished(Query::SORT_DESC), - ]; - $results = $this->searchService->findContent($query); - $items = []; - foreach ($results->searchHits as $item) { - $items[] = $item->valueObject; - } - return $items; - } - -} -``` - -As you can see, this Controller makes use of the `generateChildCriterion`, -which means you need to provide an `src/Criteria/Children.php` file containing this function: - -``` php -id), - new Criterion\Subtree($location->pathString), - new Criterion\LanguageCode($languages), - ]); - } -} -``` - -Next, you must register these two services in `config/services.yaml`: - -``` yaml -services: - app.criteria.children: - class: App\Criteria\Children - - App\Controller\FolderController: - class: App\Controller\FolderController - arguments: - - '@ezpublish.api.service.search' - - '@ezpublish.config.resolver' - - '@app.criteria.children' - tags: - - { name: controller.service_arguments } -``` - -Finally, let's use the Controller in a `templates/full/folder.html.twig` template: - -``` html+twig -

    {{ ez_content_name(content) }}

    - -{% for item in items %} -

    {{ ez_content_name(item) }}

    -{% endfor %} -``` - -This template makes use of the `items` specified in the Controller file to list every child of the folder. diff --git a/docs/guide/econtent/econtent.md b/docs/guide/econtent/econtent.md index 8db16aebd1..fb58eb11fa 100644 --- a/docs/guide/econtent/econtent.md +++ b/docs/guide/econtent/econtent.md @@ -1,39 +1,20 @@ -# eContent [[% include 'snippets/commerce_badge.md' %]] +# eContent -The eContent storage provider can store product data in an efficient way. -It enables storing data (mostly for products and product groups) in database tables with a simple structure. +The eContent data provider stores product data in database tables with a simple structure. -During the import the database tables are filled and the eContent data provider and eContent factory are there to get the information from the database and create catalog elements. +The eContent data provider and eContent factory get the information from the database to create catalog elements. -Main advantages of using eContent as the data provider: +Using eContent has the following limitations: -- Fast imports (e.g. from ERP or PIM systems) -- Supports more than one million products -- Search-ready -- Fast access -- Avoids storing products in the content model -- Allows imports during production and switching catalogs - -## Content model vs. eContent data provider +- eContent products are not visible in the Back Office. +- An eContent product cannot be embedded by using the standard embed feature. -[[= product_name_com =]] uses an almost generic way to access the catalog. -The catalog can be stored in the content model or in eContent. +## eContent Back Office [[% include 'snippets/commerce_badge.md' %]] -If eContent is used, there are a few restrictions that have to be considered: +You can see an overview of the data model for eContent in the Back Office in **Control center**. -- A product cannot be embedded by using the standard embed feature. [[= product_name_com =]] offers an alternative feature which enables embedding products in RichtText Fields. -- eContent products are not visible in the Back Office. +The model itself has to be defined by using SQL. +The **eContent types** tab shows an overview of: -It depends on the requirements of the customer to decide which provider should be used. The following table compares the main features: - -| Feature | content model | eContent | -| ------------------------------------ | -------------------------------------------- | -------------------------------------------- | -| Flexible data model | yes | yes | -| Translations | yes | yes | -| Interface for editing | yes | no | -| Versioning | yes | no | -| Simple segmentation | yes | yes | -| Extended segmentation | no | yes | -| Fast imports (e.g. from PIM) | no | yes | -| Support of large catalogs | no | yes | -| Staging (live and temporary space) | no | yes | +- the defined eContent types (for example, `product_group` or `product`) +- the list of attributes per eContent type diff --git a/docs/guide/econtent/econtent_api.md b/docs/guide/econtent/econtent_api.md deleted file mode 100644 index 918de5e52f..0000000000 --- a/docs/guide/econtent/econtent_api.md +++ /dev/null @@ -1,49 +0,0 @@ -# eContent API [[% include 'snippets/commerce_badge.md' %]] - -eContent provides a flexible way to define project-specific attributes for products and categories (and other product-related types of data). - -[[= product_name_com =]] requires that some of the attributes use predefined identifiers. - -## Categories - -|Identifier|Type|Description|Standard mapping|Stored in| -|--- |--- |--- |--- |--- | -|`ses_name`|ezstring|The name of the category|`catalogElement.name`|data_text| -|`code`|ezstring|A unique ID for the category||| -|`text`|ezstring|Longer text for the category|`catalogElement.text`|data_text| - -## Products - -|Identifier|Type|Description|Standard mapping|Stored in| -|--- |--- |--- |--- |--- | -|`ses_name`|ezstring|The name of the product|catalogElement.name|data_text| -|`ses_sku`|ezstring|The unique SKU|catalogElement.sku|data_text| -|`ses_subtitle`|ezstring|The subtitle used in product listing pages|catalogElement.text and catalogElement.subtitle
    catalogElement.text will be replaced by the new `subtitle` attribute in the future|data_text| -|`ses_short_description`|ezstring|A short description|catalogElement.shortDescription|data_text| -|`ses_long_description`|ezstring||catalogElement.longDescription|data_text| -|`ses_unit_price`|ezprice|A list price for the product in the default currency|catalogElement.price|data_float| -|`ses_vat_code`|ezstring||catalogElement.vatCode|data_text|| -|`ses_unit`|ezstring|The unit of the product|catalogElement.unit|data_text| -|`ses_image_main|ezstring|A relative path to the main image|catalogElement.mainImage|data_text| -|`ses_specification`|ezstring|A JSON-formatted set of data that contains grouped specifications|catalogElement.specifications|data_text| - -Data for variant products: - -| Identifier | Type |Description | Standard mapping | Stored in | -| -------- | -------- | ---------- | ------------------- | ---------- | -| `ses_variants` | ezstring | Contains the SKU/variant code, name, list price and characteristic for the variants | Will be converted to build a variantProductNode | data\_text | - -Optional attributes: - -|Identifier|type|description|standard mapping|Stored in| -|--- |--- |--- |--- |--- | -|ses_country_of_origin||||data_text| -|ses_ean||||data_text| -|ses_brand||||data_text| -|ses_manufacturer||||data_text| -|ses_manfacturer_sku||||data_text| -|ses_video||||data_text| -|ses_image_list||Additional images|catalogElement.imageList|data_text| -|ses_max_order_quantity|ezinteger|The max quantity to be ordered per order|catalogElement.maxOrderQuantity|data_int| -|ses_min_order_quantity|ezinteger||catalogElement.minOrderQuantity|data_int| -|ses_discontinued|ezboolean|If the product is discontinued, it can be ordered only if stock > 0|Stored in the dataMap|data_int| diff --git a/docs/guide/econtent/econtent_configuration.md b/docs/guide/econtent/econtent_configuration.md index b018e85910..d6e5f3e1c5 100644 --- a/docs/guide/econtent/econtent_configuration.md +++ b/docs/guide/econtent/econtent_configuration.md @@ -1,262 +1,23 @@ -# eContent xonfiguration [[% include 'snippets/commerce_badge.md' %]] - -## Data provider - -The following configuration is available in `econtent.yml`. - -`catalog_data_provider` Configures the shop to use eCcontent as data provider: - -``` yaml -#possible values: econtent or ez5 -silver_eshop.default.catalog_data_provider: econtent -``` +# eContent configuration ## Languages -The indexer for eContent needs to know which languages are used in your project and if you are using a fallback language. +The indexer for eContent needs to know which languages are used in your project and whether you are using a fallback language. Adapt this setting according to your project needs: ``` yaml siso_search.default.index_econtent_languages: - ger-DE: eng-GB #language: ger-DE, fallback: eng-GB - eng-GB: ger-DE #language: eng-GB, ger-DE + # eng-GB is the fallback language for ger-DE + ger-DE: eng-GB ``` -`silver_econtent.default.languages` defined the languages used for eContent. -If there are other languages present in database they will be ignored. +`silver_econtent..languages` defines the languages used for eContent per SiteAccess. +Other languages present in the database are ignored. ``` yaml -#definition for econtent languages -silver_econtent.default.languages: [ eng-GB, ger-DE] - -#language for "eng" SiteAccess with no fallback. -silver_econtent.eng.languages: [ eng-GB ] - -#language for "ger" SiteAccess with English as fallback. silver_econtent.ger.languages: [ ger-DE, eng-GB ] ``` -The second language specified here is the fallback language. -This means that if content is not found in the first language, it will use the second language instead. - -## Database tables - -``` yaml -silver_econtent.default.table_object: sve_object -silver_econtent.import.table_object: sve_object_tmp -silver_econtent.default.table_object_attributes: sve_object_attributes -silver_econtent.import.table_object_attributes: sve_object_attributes_tmp -silver_econtent.default.table_class: sve_class -silver_econtent.default.table_class_attributes: sve_class_attributes -silver_econtent.default.table_externaldata: ses_externaldata -``` - -## Catalog segmentation - -``` yaml -# Used for catalog segmentation: enabled or disabled -silver_econtent.default.section_filter: disabled -# Defines the name of the table to be used -silver_econtent.default.table_catalog_filter: sve_object_catalog -# The 'where' condition in order to limit product catalog -silver_econtent.default.filter_SQL_where: -# A catalog segmentation code used for this SiteAccess -silver_econtent.default.catalog_filter_default_catalogcode: -``` - -## Root node - -Make sure your root node in the `save_object` table has this ID: - -``` -silver_econtent.default.root_node_id: 2 -``` - -``` -Example: - -node_id class_id parent_id change_date blocked hidden priority section url_alias path_string depth main_node_id -2 1 0 NULL 0 0 1 1 Highlite /2/ 1 2 -``` - -## Navigation - -`contentTypes` is the ID specified in `sve_class`. In the following example 1 is for product groups and 2 is for products. -`limit` is the amount to show in navigation service. - -``` yaml -#navigation configuration -silver_eshop.default.econtent_catalog_data_provider.filter: - navigation: - contentTypes: 1 - limit: 20 -``` - -## Factory configuration - -Definition for the methods that create a catalog node: - -``` -#configuration for the factory -silver_eshop.default.econtent_catalog_factory.product_group: createCatalogNode -silver_eshop.default.econtent_catalog_factory.product: createOrderableProductNode -``` - -## SKU - -This is the definition for the field that is used for SKU. -It is required for building the catalog element and is linked to `sve_class_attributes.attribute_id`. - -``` yaml -silver_econtent.default.sku_id: 202 -``` - -``` -attribute_id class_id attribute_name ezdatatype sort_field -202 2 ses_sku ezstring data_text -``` - -## Class ID - -This specifies the ID of product group elements found in `se_class.class_id`: - -``` yaml -silver_econtent.default.class_id_catalog: 1 -``` - -``` -class_id class_name name_identifier -1 product_group 101 -``` - -## Content extraction - -The following configuration defines how content is extracted from product groups: - -```yaml -silver_econtent.default.mapping.product_group: - identifier: - id: "node_id" - extract: false - parentElementIdentifier: - id: "parent_id" - extract: false - url: - id: "url_alias" - extract: false - name: - id: "ses_name" - extract: "extractText" - text: - id: "text" - extract: "extractText" - image: - id: "image" - extract: "extractImage" -``` - -The following configuration defines how content is extracted from products. - -It specifies the method used to extract the value. If it is false, the value is extracted directly from the database. - -### Image - -The image path to the product image should be relative to `web/var/assets/product_images` -(the path is stored in the `silver_eshop.default.catalog_factory.assets` setting). - -Specification data: - -Specification data has to be stored in a JSON-formatted (type `ezstring`) example (one group "technic" is used). - -``` -{ - "technic": [ - { - "label": "Größe", - "value": "1,69 cm (0.667 Zoll)" - }, - { - "label": "Kompatibilität", - "value": "Epson Stylus Pro 9600, " - } - ] -} -``` - -Important: current the identifier in eContent has to be named `ses_specification`. - -The attributes of the specification data are indexed in Solr as well. - -``` yaml -silver_econtent.default.mapping.product: - identifier: - id: "node_id" - extract: false - parentElementIdentifier: - id: "parent_id" - extract: false - url: - id: "url_alias" - extract: false - sku: - id: "ses_sku" - extract: "extractText" - manufacturerSku: - id: "ses_manufacturer_sku" - extract: "extractText" - name: - id: "ses_name" - extract: "extractText" - ean: - id: "ses_ean" - extract: "extractText" - text: - id: "ses_subtitle" - extract: "extractText" - subtitle: - id: "ses_subtitle" - extract: "extractText" - image: - id: "ses_image_main" - extract: "extractImage" - shortDescription: - id: "ses_short_description" - extract: "extractTextBlock" - longDescription: - id: "ses_long_description" - extract: "extractTextBlock" - price: - id: "ses_unit_price" - extract: "extractPrice" - cacheIdentifier: - id: "ses_sku" - extract: "extractCacheIdentifier" -``` - -## Field prefix - -This definition is used by this plugin: `SisoNavEcontentImporterPlugin` - -It defines the prefix of the field name that Fields from [[= product_name =]] have in eContent. - -``` yaml -silver_econtent.default.ez_datatype_attribute_prefix: ezdata_ -``` - -## Data from an external data source - -For every catalog element `EcontentCatalogFactory` collects all information from the `ses_externaldata` table for the given SKU. The SKU is taken from the `data_text` column in `sve_object_attributes` table. It collects different data like PIM, SAP and TYP codes. Then it matches the external data with catalog element attributes and also puts all data in `dataMap`. - -Configuration for `externalDataTypes` is used for searching different content in the `externalData` table. - -``` yaml -silver_econtent.default.externalDataType: - pim: - identifier: pim - sap: - identifier: sap - typ: - identifier: Typ1 - prefix: TY_ -``` +The second language specified is the fallback language. +This means that if content is not found in the first language, second language will be used. diff --git a/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_and_indexing_data_sql.md b/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_and_indexing_data_sql.md deleted file mode 100644 index 975ca4dbf7..0000000000 --- a/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_and_indexing_data_sql.md +++ /dev/null @@ -1,171 +0,0 @@ -# Importing and indexing data (SQL) [[% include 'snippets/commerce_badge.md' %]] - -## Query examples - -These queries can be useful for testing imported data or debugging. - -``` sql -/* Get all elements with all tables joined: */ -SELECT - * -FROM - sve_object -JOIN - sve_object_attributes ON ( sve_object.node_id = sve_object_attributes.node_id ) -JOIN - sve_class_attributes ON ( sve_object_attributes.attribute_id = sve_class_attributes.attribute_id ); - -/* Get all products */ -SELECT - * -FROM - sve_object -JOIN - sve_object_attributes ON ( sve_object.node_id = sve_object_attributes.node_id ) -JOIN - sve_class_attributes ON ( sve_object_attributes.attribute_id = sve_class_attributes.attribute_id ) -WHERE - sve_object.class_id = 2 /* Class id 2 is for products */; - -/* Get all product groups */ -SELECT - * -FROM - sve_object -JOIN - sve_object_attributes ON ( sve_object.node_id = sve_object_attributes.node_id ) -JOIN - sve_class_attributes ON ( sve_object_attributes.attribute_id = sve_class_attributes.attribute_id ) -WHERE - sve_object.class_id = 1 /* Class id 1 is for product groups*/ ; - -/* Get product node ids and names*/ -SELECT - sve_object.node_id, - sve_object_attributes.data_text -FROM - sve_object -JOIN - sve_object_attributes ON ( sve_object.node_id = sve_object_attributes.node_id ) -JOIN - sve_class_attributes ON ( sve_object_attributes.attribute_id = sve_class_attributes.attribute_id ) -WHERE - sve_object.class_id = 2 -AND - sve_class_attributes.attribute_id = 201 -ORDER BY - sve_object_attributes.data_text; -``` - -### Import process for eContent - -Tables `sve_class` and `sve_class_attributes` have data definitions and `sve_object` and `sve_object_attributes` have the data itself. - -`sve_class` and `sve_class_attributes` should be modified only when data definition is changed, for example: - -``` sql -/* Insert data in sve_class table */ - -INSERT INTO `sve_class` (`class_id`, `class_name`, `name_identifier`) -VALUES - (1,'product_group',101), - (2,'product',201); - -/* Insert data in sve_class_attributes table */ -INSERT INTO `sve_class_attributes` (`attribute_id`, `class_id`, `attribute_name`, `ezdatatype`, `sort_field`) -VALUES - (101,1,'ses_name','ezstring','data_text'), - (102,1,'number','ezstring','data_text'), - (103,1,'code','ezstring','data_text'), - (201,2,'ses_name','ezstring','data_text'), - (202,2,'ses_sku','ezstring','data_text'), - (203,2,'product_sort_field','ezstring','data_text'), - (204,2,'sub_headline','ezstring','data_text'), - (205,2,'marker','ezstring','data_text'), - (206,2,'length','ezfloat','data_float'); - -/* This tables should be modified only when data definition is changed. */ -``` - -An example of importing `sve_object` and `sve_object_attributes` data: - -``` sql -/* Insert data in sve_object table */ -INSERT INTO `sve_object` (`node_id`, `class_id`, `parent_id`, `change_date`, `blocked`, `hidden`, `priority`, `section`, `url_alias`, `path_string`, `depth`, `main_node_id`) -VALUES - (2,1,0,NULL,0,0,1,1,'Highlite','/2/',1,2), - (11,1,2,NULL,0,0,1,1,'Highlite/DMT','/2/11/',2,11), - (1000001,1,11,NULL,0,0,1,1,'Highlite/DMT/New-Products','/2/11/1000001/',3,1000001), - (1000101,1,11,NULL,0,0,1,1,'Highlite/DMT/Outlet-Sales','/2/11/1000101/',3,1000101), - (25290,1,11,NULL,0,0,1,1,'Highlite/DMT/Media-Gear','/2/11/25290/',3,25290), - (125290,1,25290,NULL,0,0,1,1,'Highlite/DMT/Media-Gear/Screens','/2/11/25290/125290/',4,125290), - (225290,2,125290,NULL,0,0,1,1,'Highlite/DMT/Media-Gear/Screens/Proscreen-manual','/2/11/25290/125290/225290/',5,225290), - (225291,2,125290,NULL,0,0,2,1,'Highlite/DMT/Media-Gear/Screens/Proscreen-manual_1','/2/11/25290/125290/225291/',5,225291), - (220841,2,125290,NULL,0,0,3,1,'Highlite/DMT/Media-Gear/Screens/Projection-Screen-4-3','/2/11/25290/125290/220841/',5,220841), - (225292,2,125290,NULL,0,0,4,1,'Highlite/DMT/Media-Gear/Screens/Proscreen-manual_2','/2/11/25290/125290/225292/',5,225292), - (220842,2,125290,NULL,0,0,5,1,'Highlite/DMT/Media-Gear/Screens/Projection-Screen-4-3-Manual','/2/11/25290/125290/220842/',5,220842); - -/* Insert data in sve_obect_attributes table */ - -INSERT INTO `sve_object_attributes` (`node_id`, `attribute_id`, `data_float`, `data_int`, `data_text`, `language`) -VALUES - (2,101,NULL,NULL,'Highlite','eng-GB'), - (11,101,NULL,NULL,'DMT','eng-GB'), - (11,102,NULL,NULL,'00.10','eng-GB'), - (11,102,NULL,NULL,'00.10','ger-DE'), - (1000001,101,NULL,NULL,'Neuheiten','ger-DE'), - (1000001,101,NULL,NULL,'New Products','eng-GB'), - (1000001,102,NULL,NULL,'00.00','eng-GB'), - (1000001,103,NULL,NULL,'NEW','eng-GB'), - (1000101,101,NULL,NULL,'Auslaufartikel','ger-DE'), - (1000101,101,NULL,NULL,'Outlet Sales','eng-GB'), - (1000101,102,NULL,NULL,'ZZZ','eng-GB'), - (1000101,103,NULL,NULL,'DECLINE','eng-GB'), - (25290,101,NULL,NULL,'Media Gear','eng-GB'), - (125290,101,NULL,NULL,'Screens','eng-GB'), - (125290,102,NULL,NULL,'00.10.020','eng-GB'), - (125290,102,NULL,NULL,'00.10.020','ger-DE'), - (225290,201,NULL,NULL,'Proscreen manual','eng-GB'), - (225290,202,NULL,NULL,'100350','eng-GB'), - (225290,203,NULL,NULL,'00.10.020/005','eng-GB'), - (225290,203,NULL,NULL,'00.10.020/005','ger-DE'), - (225290,204,NULL,NULL,'72\" 4:3','eng-GB'), - (225290,204,NULL,NULL,'72\" 4:3','ger-DE'); -``` - -Remember to index the products in Solr, see [Staging system](../../econtent_features/staging_system.md) and [Indexing eContent data](../../econtent_features/indexing_econtent_data/indexing_econtent_data.md) - -### Import procedure using temp tables - -Tables `sve_object` and `sve_object_attributes` have two clone temporary tables called `sve_object_tmp` and `sve_object_attributes_tmp`. -These temporary tables are used to import new data and keep data available while importing by means of the normal tables. -After an import is finished, you can execute a command to rename the temporary tables to normal tables: - -``` bash -php bin/console silversolutions:econtent-tables-swap -``` - -### Languages - -The language definitions of eContent elements are defined in `sve_object_attributes`. Every attribute row has a language definition, for example: - -|node_id|attribute_id | data_float | data_int | data_text | language| -|---|---|---|---|---|---| -|1000001|101 | NULL | NULL | Neuheiten | ger-DE| -|1000001|101 | NULL | NULL | New Products | eng-GB| - -There is no language hierarchy. Only languages specified in configuration are indexed: - -``` yaml -siso_search.default.index_econtent_languages: [ ger-DE, eng-GB ] -``` - -### eContent Indexer - -For information about indexing eContent, see [Indexing econtent data](../../econtent_features/indexing_econtent_data/indexing_econtent_data.md). - -For information about creating custom plugins for indexing, see [Custom indexer plugin for eContent](../econtent_search_cookbook/custom_indexer_plugin_for_econtent). - -### Data model attributes in eContent - -If you need to enrich an eContent site with data from the content model, use a command from the `SisoNavEcontentImporterPlugin` plugin. diff --git a/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_products_api.md b/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_products_api.md deleted file mode 100644 index e0a7459eb1..0000000000 --- a/docs/guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_products_api.md +++ /dev/null @@ -1,126 +0,0 @@ -# Importing products (API) [[% include 'snippets/commerce_badge.md' %]] - -Importing products by means of the API includes the following parts of the system: - -- eContent repository: helper methods for automatic metadata generation. -- catalog data (categories, products and related attributes) need to be stored individually for every language used in the shop -- available classes for new database objects can be obtained from the `sve_class` table (by default only two classes: 1 = category, 2 = product) - -## Attributes - -Every catalog class defines a number of valid attributes for their elements. The name identifier, ID and Content Type of all attributes are stored in the table `sve_class_attributes`. - -The `node_id` of the product, `attribute_id` and language code fields are required. - -After a new product is created, attributes can be associated with the product using its `node_id`. -In addition, creating a new attribute requires its ID and the language code. - -Attributes use three data types to store their data (text, float and int). -The API provides a corresponding setter method for every data type that needs to be called explicitly on import. - -|Data type|Method| -|--- |--- | -|float|setDataFloat| -|int|setDataInt| -|text/string|setDataText| - -## Importing a category - -A category has `class_id` = 1. - -Categories are used to group products by setting the `parent_id` of the product to the `node_id` of the category. -The root node of the tree is 2 (the parent of the first category). - -For example: - -``` php -// get the Doctrine repository -$objectRepo = $this->em->getRepository(SveObject::class); - -$categoryName = 'My category'; -$categoryNodeId = $objectRepo->getNextNodeId(); - -$econtentCategory = new SveObject(); -$econtentCategory->setClassId(1); // 2 = product, 1 = category -$econtentCategory->setParentId(2); // root node -$econtentCategory->setBlocked(false); -$econtentCategory->setHidden(false); -$econtentCategory->setSection(1); // 1 = Standard - -$econtentCategory->setChangeDate(new \DateTime()); -$objectRepo->generateMetaData($econtentCategory, $categoryNodeId, $categoryName); -$this->em->persist($econtentCategory); -$this->em->flush(); - -// import an attribute for the new category -$econtentAttribute = new SveObjectAttributes(); -$econtentAttribute->setNodeId($categoryNodeId); -$econtentAttribute->setAttributeId(101); // 201 = ses_name -$econtentAttribute->setLanguage('eng-GB'); - -$econtentAttribute->setDataText($categoryName); -$this->em->persist($econtentAttribute); -$this->em->flush(); -``` - -## Importing a product - -A product has `class_id` = 2. - -Creating a product requires the `parent_id` (its Location in the product tree/catalog). -Changing the `parent_id` moves the product in the tree. After creation, the product's `node_id` serves as its unique identifier. - -Product entities provide getters and setters for their attributes. - -To import into the `*_tmp` table, use `$nodeId = $objectRepo->getNextNodeId();`. - -Use the `generateMetaData()` method to generate metadata such as the URL alias of the product. - -The following example creates a product object in the database and sets its properties. -It creates attributes and associates them with the object. -Different attributes have different data types (text is default). - -``` php -// create a product and set its data fields -$productNodeId = $objectRepo->getNextNodeId(); - -$econtentProduct = new SveObject(); -$econtentProduct->setClassId(2); // 2 = product, 1 = category -$econtentProduct->setParentId($categoryNodeId); -$econtentProduct->setMainNodeId($productNodeId); -$econtentProduct->setBlocked(false); -$econtentProduct->setHidden(false); -$econtentProduct->setSection(1); // 1 = Standard - -$econtentProduct->setChangeDate(new \DateTime()); -$objectRepo->generateMetaData($econtentProduct, $productNodeId, 'New Product'); - -$this->em->persist($econtentProduct); -$this->em->flush(); - -// import an attribute for the new product -$econtentAttribute = new SveObjectAttributes(); -$econtentAttribute->setNodeId($productNodeId); -$econtentAttribute->setAttributeId(201); // 201 = ses_name -$econtentAttribute->setLanguage('eng-GB'); - -$econtentAttribute->setDataText((string)'product name'); - -$this->em->persist($econtentAttribute); -$this->em->flush(); -``` - -Create the index for the new products (in temporary area): - -``` bash -php bin/console silversolutions:indexecontent -``` - -After the import, the database tables and Solr cores have to be switched: - -``` -php bin/console silversolutions:indexecontent swap -php bin/console silversolutions:econtent-tables-swap -``` - -For more information, see [Staging system](../../econtent_features/staging_system.md) and [Indexing econtent data](../../econtent_features/indexing_econtent_data/indexing_econtent_data.md). diff --git a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_indexer_plugin_for_econtent.md b/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_indexer_plugin_for_econtent.md deleted file mode 100644 index e02cba9060..0000000000 --- a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_indexer_plugin_for_econtent.md +++ /dev/null @@ -1,111 +0,0 @@ -# Custom indexer plugin for eContent [[% include 'snippets/commerce_badge.md' %]] - -The following example shows how to create a plugin for eContent that indexes an additional field -based on a serialised array that comes from the database. - -The database stores keys and values for the articles. They come in a serialised format, for example: - -``` -ses_datamap_additional_data = a:2:{s:4:"Code";s:3:"AXW";s:4:"Name";s:20:"Awesome Speaker AX50";} -``` - -The indexer creates the following output: - -- Index field `additional_product_code` with value "AXW" -- Index field `additional_product_name` with value "Awesome Speaker AX50" - -### Create a plugin class - -Create a plugin class that implements `EcontentIndexerPluginInterface` - -``` php -class AdditionalDataIndexerPlugin implements EcontentIndexerPluginInterface -{ - public function buildIndexFields(array $econtentData, $elementType) - { - } -} -``` - -### Create a service definition - -You must register the class as a service: - -``` xml -YOURNAMESPACE\AdditionalDataIndexerPlugin - - - - -``` - -### Create custom logic - -In your custom logic, you have to loop through the product data map. - -eContent data map supports the following data types: - -- strings, stored in a `data_text` field -- integers, stored in a `data_int` field -- floats, stored in a `data_float` field - -For array support use serialised arrays. - -``` php -class AdditionalDataIndexerPlugin implements EcontentIndexerPluginInterface -{ - public function buildIndexFields(array $econtentData, $elementType) - { - foreach ($econtentData['data_map'] as $attribute) { - if ($attribute['attribute_name'] === 'ses_datamap_additional_data ') { - // If the stored attribute is a string, the value will be in a field name data_text - $additionalData = unserialize($attribute['data_text']); - if ( - is_array($additionalData) && - isset($additionalData['ArrayFieldHash']['array'][0]['code']) && - isset($additionalData['ArrayFieldHash']['array'][0]['name']) - ) { - return array ( - 'additional_data_code' => $additionalData['ArrayFieldHash']['array'][0]['code'], - 'additional_data_name' => trim($additionalData['ArrayFieldHash']['array'][0]['name']) - ); - } - } - return null; - } -} -``` - -This creates two new Solr fields with the following names and values: - -- The prefix (here `ses_product_ses_datamap_ext_`) is added automatically and contains the Content Type (here `product`) as well -- The suffix `_s` is added because the indexer detects the value as a string. -The indexer also supports these additional data types with the corresponding suffix: - -|Suffix|Data type| -|---|---| -|String|`_s`| -|Array (Multiple strings)|`_ms`| -|Float|`_f`| -|Integer|`_i`| -|long|`_l`| - -The data type is specified in `EcontentFieldNameResolver`. - -!!! note "Long data types" - - Solr accepts the following min and max integer values: - - ``` - SOLR_MAX_INT = 2147483647; - SOLR_MIN_INT = -2147483648; - ``` - -``` -ses_product_ses_datamap_ext_additional_data_code_s: "AXW" -ses_product_ses_datamap_ext_additional_data_name_s: "Awesome Speaker AX50" -``` - -After executing the indexer command line, you should be able to see the new indexed content in Solr. - -For instructions about running the eContent indexer, see [Indexing econtent data](../../econtent_features/indexing_econtent_data/indexing_econtent_data.md). diff --git a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_sorting_handler_for_econtent.md b/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_sorting_handler_for_econtent.md deleted file mode 100644 index 87bc1caf1a..0000000000 --- a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_sorting_handler_for_econtent.md +++ /dev/null @@ -1,122 +0,0 @@ -# Custom sorting handler for eContent [[% include 'snippets/commerce_badge.md' %]] - -This example shows how to create a custom sorting that uses priority field. - -This creates a new sort criterion for use with Solr. You can specify the Solr field name for sorting. -In this example it will be a field that stores product priority, but it can be any Solr field you want. - -## 1. Create a class - -Create a new class that extends `AbstractSortCriterion`: - -``` php -getDirection() === AbstractSortCriterion::DESC - ? Query::SORT_DESC - : Query::SORT_ASC; - - $query->addSort($indexName, $direction); - } - - /** - * @param SearchClauseInterface $searchClause - * @return bool - */ - public function canHandle(SearchClauseInterface $searchClause) - { - return $searchClause instanceof PriorityFieldSorting; - } -} -``` - -## 3. Register the class as a service - -Register the handler as a service in `services.xml` by providing the namespace of the handler class -and adding the tag name to the service definition. - -The handler is then used whenever the search clause is an instance of `PriorityFieldSorting`. - -This means that when you add this search clause to `eshopQuery`, the method executes the handler. - -``` xml -MyProject\Bundle\ProjectBundle\Service\Search\PriorityFieldSortingHandler - - - - -``` - -You can now use the handler in the following way: - -``` php -$query = new EshopQuery(); - -// Insert here all the query options you like. - -// This is our new sorting criteria. -$query->setSortCriteria( - array( - new PriorityFieldSorting( - array( - 'direction' => 'desc' - ) - ), - ) -); -``` - -A query could have several sorting criteria: - -``` php -$eshopQuery->setSortCriteria( - array( - new MyCustomSorting1(array('direction' => 'asc')), - new MyCustomSorting2(array('direction' => 'desc')), - new MyCustomSorting3(array('direction' => 'desc')), - ... - ) -``` - -The order of the sorting criteria is important. In the example above the sorting is in the order they are set into the array. - -!!! note "Modifying existing SortHandlers" - - Existing `SortHandlers` can't be overriden by simply extending them. - If you need to change the behavior of a `SortHandler`, you must use the new implementation explicitly, - that is instantiate the new class, as shown in the example above. diff --git a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/solr_minimum_should_match.md b/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/solr_minimum_should_match.md deleted file mode 100644 index 60d177d452..0000000000 --- a/docs/guide/econtent/econtent_cookbook/econtent_search_cookbook/solr_minimum_should_match.md +++ /dev/null @@ -1,11 +0,0 @@ -# Solr Minimum Should Match [[% include 'snippets/commerce_badge.md' %]] - -Currently Solr's `mm` (Minimum Should Match) parameter only works for eContent search and can be specified with the following parameter: - -``` yaml -#Solr minimun match specification string -siso_search.default.min_match_specification_string: '3<80%' -``` - -For more information about the parameter, see -[Solr reference](https://lucene.apache.org/solr/guide/7_4/the-dismax-query-parser.html#TheDisMaxQueryParser-Themm_MinimumShouldMatch_Parameter) diff --git a/docs/guide/econtent/econtent_cookbook/extending_econtentcatalogfactory.md b/docs/guide/econtent/econtent_cookbook/extending_econtentcatalogfactory.md deleted file mode 100644 index 8ebdbe6c34..0000000000 --- a/docs/guide/econtent/econtent_cookbook/extending_econtentcatalogfactory.md +++ /dev/null @@ -1,109 +0,0 @@ -# Extending EcontentCatalogFactory [[% include 'snippets/commerce_badge.md' %]] - -Catalog objects are created with `EcontentCatalogFactory`. -This involves both product and product groups, or any other type of elements that are part of the catalog. - -Sometimes projects need to extend this class to provide additional functionality or logic to the catalog elements. - -Catalog objects have default properties and a data map with additional fields. -This data map is flexible and enables adding new custom properties to objects, -such as file objects, arrays of files, images, custom values based on standard values, flags, etc. - -The following example shows how to extend `EcontentCatalogFactory` to enable adding a video to the product data map. - -## Step 1: Create a class - -Create a new class `MyProjectEcontentCatalogFactory` that extends `EcontentCatalogFactory`. - -``` php -MyProject\Bundle\ProjectBundle\Services\Factory\CornelsenEcontentCatalogFactory -``` - -## Step 3: Create an extraction method - -Create a new extraction method for the video. For this example, assume that the object has one video and its name is the SKU with the mp4 extension. - -Every new element that is added to the catalog element must be placed in its data map. -The catalog element data map supports different Field objects as its elements. -You can find the available Field elements in `EshopBundle/Content/Fields` - -This example uses the File Field object and the [Symfony finder object](http://symfony.com/doc/current/components/finder.html) to support file system access. - -``` php -/** - * This method will extract all videos of specified extensions. - * - * @param $fieldIdentifier - * @param $dataMap - * @return FileField - */ -protected function extractVideo($fieldIdentifier, $dataMap) -{ - $attributes = $this->getFieldFromDataMap($fieldIdentifier, $dataMap); - $fileName = $attributes['data_text'].'mp4'; - $finder = new Finder(); - try { - $finder->files()->in('videos/')->name($fileName); - /** @var \Symfony\Component\Finder\SplFileInfo $file */ - foreach ($finder as $file) { - $fileName = $file->getRelativePathname(); - $fileSize = $file->getSize(); - $fileRecord = array( - 'fileName' => $fileName, - 'fileSize' => $fileSize, - 'path' => 'videos/'.$fileName, - ); - } - } catch (\InvalidArgumentException $e) { } - - return new FileField($fileRecord); -} -``` - -## Step 4. Call your method from the data map - -Add the call to `extractVideo()` in `fillCatalogElementDataMap()`. - -The `fillCatalogElementDataMap()` method is responsible for generating the data map of the catalog element. - -``` php -/** - * Fills the data map of a catalog element - * - * @param CatalogElement $catalogElement - * @param array $dataMap - * @return CatalogElement - */ -protected function fillCatalogElementDataMap(CatalogElement $catalogElement, array $dataMap = array()) -{ - /* Before returning the catalog element you can add the video to the data map: */ - - // Videos are added to products only: - if ($catalogElement instanceof ProductNode) { - $videoField = $this->extractVideo('ses_sku', $dataMap); - $catalogElement->addFieldToDataMap('video', $videoField); - } - - return $catalogElement; -} -``` - -Your catalog element now should have a video file in its data map. - -You can also add arrays of fields to the data map. In such a case the object will be an `ArrayField`, and its contents will be Field objects. -You can have an Array Field with different file fields, text fields or any other kind of defined field. diff --git a/docs/guide/econtent/econtent_faq.md b/docs/guide/econtent/econtent_faq.md deleted file mode 100644 index d21686e03e..0000000000 --- a/docs/guide/econtent/econtent_faq.md +++ /dev/null @@ -1,101 +0,0 @@ -# eContent FAQ [[% include 'snippets/commerce_badge.md' %]] - -## After using eContent the top navigation is empty - -Check your data inside eContent. - -Make sure that for all objects the attribute which is defined as `name_identifier` in `sve_class` is stored in `sve_object_attributes` - -``` sql -select * from sve_class; -+----------+---------------+-----------------+ -| class_id | class_name | name_identifier | -+----------+---------------+-----------------+ -| 1 | product_group | 101 | -| 2 | product | 205 | -+----------+---------------+-----------------+ - - select * from sve_object_attributes where node_id=1003; -+---------+--------------+----------+------------+----------+--------------------------------------------+ -| node_id | attribute_id | language | data_float | data_int | data_text | -+---------+--------------+----------+------------+----------+--------------------------------------------+ -| 1003 | 202 | ger-DE | NULL | NULL | 80810 | -| 1003 | 205 | ger-DE | NULL | NULL | Projection Bulb EFP GZ6.35 Philips12V 100W | -| 1003 | 209 | ger-DE | 13.5 | NULL | NULL | -+---------+--------------+----------+------------+----------+--------------------------------------------+ -``` - -Check if the root element also has at least this data inside the `sve_object_attributes` table: - -``` sql -select * from sve_object_attributes where node_id=2; -+---------+--------------+----------+------------+----------+-----------+ -| node_id | attribute_id | language | data_float | data_int | data_text | -+---------+--------------+----------+------------+----------+-----------+ -| 2 | 101 | ger-DE | NULL | NULL | Produkte | -+---------+--------------+----------+------------+----------+-----------+ -``` - -Check if the correct class ID is set in the configuration: - -- check if the `types` setting contains the correct `class_id` used in eContent for `product_groups`. It is usually 1 -- check if the fields used for the labels are set: `label_fields` - -``` yaml -siso_core.default.navigation.catalog: - #the class id has to be specified here - types: ['1'] - sections: [1, 2] - enable_priority_zero: true - label_fields: ['ses_category_ses_name_value_s','name_s'] - additional_fields: ['ses_category_ses_code_value_s', 'ses_category_ses_name_value_s' ] -``` - -Check if the field mapping is correct (the same has to be done for products: `silver_econtent.default.mapping.product`): - -``` yaml -silver_econtent.default.mapping.product_group: - identifier: - id: "node_id" - extract: false - parentElementIdentifier: - id: "parent_id" - extract: false - url: - id: "url_alias" - extract: false - name: - id: "name" - extract: "extractText" -``` - -## Products are not displayed in the detail page - -Make sure that the main attributes are defined: - -- `name_identifier` -- `ses_sku` -- `ses_price` - -## The search says products have been found but none are displayed - -Check if you have issues while building the catalog elements. - -In dev environment, exceptions are logged to `silver.eshop.log`. - -## How can I improve the speed of getting a product by SKU? - -Doctrine does not allow creating an index based on a part of a string. -This is why it can be helpful to setup an index manually: - -``` -create index ind_datatext on sve_object_attributes (data_text(20)); -create index ind_datatext on sve_object_attributes_tmp (data_text(20)); -``` - -This example assumes that the SKU has a maximum length of 20, which should be fine for almost all projects. - -## Facets are lowercased - -If facet names are lowercase, you need to set the `index_facet_fields` parameter for the specific fields which should not be lowercase. -See [Indexing econtent data](econtent_features/indexing_econtent_data/indexing_econtent_data.md). diff --git a/docs/guide/econtent/econtent_features/catalog_segmentation.md b/docs/guide/econtent/econtent_features/catalog_segmentation.md deleted file mode 100644 index b588f780c8..0000000000 --- a/docs/guide/econtent/econtent_features/catalog_segmentation.md +++ /dev/null @@ -1,119 +0,0 @@ -# Catalog segmentation [[% include 'snippets/commerce_badge.md' %]] - -Catalogs, especially in B2B shops, are often segmented even by customers. - -An ERP system can provide a segmentation code such as "certified". -A customer with this code can get more products than an anonymous user or a customer without the code. - -A customer can even have individual products which are available for this customer only. - -## How it works - -eContent can use an additional database table to segment the catalog. - -``` -desc sve_object_catalog; -+--------------+------------------+------+-----+---------+-------+ -| Field | Type | Null | Key | Default | Extra | -+--------------+------------------+------+-----+---------+-------+ -| node_id | int(11) unsigned | NO | PRI | NULL | | -| catalog_code | char(20) | NO | PRI | | | -+--------------+------------------+------+-----+---------+-------+ -``` - -For each `node_id` one or more `catalog_codes` can be defined. - -The configuration is per SiteAccess or SiteAccess group. -You can extend the eContent service to change the logic for providing the catalog codes. - -## Implement different catalog filtering - -You can configure eContent so that catalog elements have assigned roles (so different users can see different content). - -Adjust configuration like in the following example: - -``` -silver_econtent.default.filter_SQL_where: 'sve_object_catalog.node_id = obj.node_id AND sve_object_catalog.catalog_code IN (%%catalog_code%%)' - -silver_econtent.default.catalog_filter_default_catalogcode: - - ANONYMOUS - -silver_econtent.default.section_filter: enabled -``` - -`admin` does not normally use segmentation, but you can enable it for this SiteAccess as well: - -`silver_econtent.admin.section_filter: disabled` - -## Custom segmentation logic - -If you need custom logic, override `ConfigEcontentCatalogSegmentationService` to return the catalog codes -which are used in your context (e.g. depending on the user). - -Catalog segmentation uses a service to define which users are allowed to see the content specified in segmentated parts of the catalog. - -The default configuration uses `ConfigEcontentCatalogSegmentationService` -that returns a collection of catalog codes specified in the configuration. -The service implements `EcontentCatalogSegmentationServiceInterface`. - -You can implement this interface when you need a custom algorithm -for showing specific products to specific users. - -The following example service replaces the default implementation: - -``` php -userGroup = $groupCode; - } - - /** - * Place the logic that fetches the desired catalog codes here: - */ - public function getCatalogCodes() - { - // Get catalog codes from Ez users or from ERP (static array here) - if ($this->userGroup === 'VIP') { - return array('VIP', 'NORMAL') - } else { - return array('NORMAL'); - } - } -} -``` - -## Indexing catalog codes - -The eContent indexer indexes all catalog codes from an element in a multi-value field called `main_catalog_segments_ms`. - -This field stores all catalog codes for a given element. - -``` -# Example of Solr index -"main_catalog_segments_ms": [ - "ALL", - "NORMAL" -] -``` diff --git a/docs/guide/econtent/econtent_features/econtent_back_office.md b/docs/guide/econtent/econtent_features/econtent_back_office.md deleted file mode 100644 index 93a45a06d9..0000000000 --- a/docs/guide/econtent/econtent_features/econtent_back_office.md +++ /dev/null @@ -1,17 +0,0 @@ -# eContent Back Office [[% include 'snippets/commerce_badge.md' %]] - -You can see an overview of the data model in **Control center** -> **eContent**. - -The model itself has to be defined by a developer (using SQL). The tab **eContent types** shows an overview about: - -- defined eContent types (e.g. `product_group`, `product`) -- the list of attributes per eContent type - -For each eContent Field Type the list of attributes is displayed: - -- Name: identifier of this attribute -- ID: internal unique ID -- Type -- Mapping: shows where the field can be found when using the `CatalogElement`. -The mapping is defined in a yml file: `silver_econtent.default.mapping.product` -(For more information, see [econtent - Configuration](../econtent_configuration.md)). diff --git a/docs/guide/econtent/econtent_features/econtent_database_model.md b/docs/guide/econtent/econtent_features/econtent_database_model.md deleted file mode 100644 index 8e0a42da39..0000000000 --- a/docs/guide/econtent/econtent_features/econtent_database_model.md +++ /dev/null @@ -1,247 +0,0 @@ -# eContent database model [[% include 'snippets/commerce_badge.md' %]] - -eContent uses four main database tables and two optional ones: - -|Table|Staging|Purpose| -|--- |--- |--- | -|`sve_class`||Stores types of catalog elements (e.g. product, product_group). Each type has an identifier which has a relation with `sve_class_attributes`.| -|`sve_class_attributes`||Stores all possible catalog element attributes (name, type) for different catalog elements (`class_id`).| -|`sve_object`|yes|Stores all catalog elements and general information about them (URL, parent, depth, etc.).| -|`sve_object_attributes`|yes|Stores all attributes for the given catalog element depending on language.| -|`ses_externaldata`||(optional) Stores additional information for `sve_object_attributes` of `ses_externaldata`. Collects more information for that kind of catalog element.| -|`sve_object_catalog`|yes|(optional) Used for segmentation.| - -For staging purposes, the database tables use the `_tmp` prefix (e.g. `sve_object_tmp`). -The staging tables can be used to import a complete product catalog without affecting the production catalogue. -For more information, see [eContent staging system](staging_system.md). - -!!! note - - [[= product_name_com =]] uses Doctrine entities to create these tables. - - It is impossible to create indexes for the table `sve_object_attributes` for the `data_text` attribute with Doctrine. You must create the indexes manually every time you run - `php bin/console doctrine:schema:update --force` - -## Metadata for products and product groups - -The table `sve_object` contains one entry for each product group, product, etc. -You can arrange data in a tree structure by using the field `parent_id`, which is the Location ID of the parent. -Location IDs start from 2 due to compatibility with the [[= product_name =]] data structure. - -This table contains several other pieces of information in addition to Content Type ID and Location ID, for example: - -- time of last change -- Location ID of the parent -- flag that indicates whether the item is blocked -- priority -- URL alias - readable URL of this document (for example `/shop/toys/kids/wooden_toy`) -- depth -- main Location ID - if the item has multiple Locations, this parameter defines the first appearance in this tree, where all data referenced to this object is stored - -!!! note - - Although tables have relationships, there are no constraints defined in database definition. - -### Table `sve_object` - -|Field|Type|Null|Key|Default|Extra| -|--- |--- |--- |--- |--- |--- | -|`node_id`|int(10) unsigned||PRI|0|| -|`class_id`|int(10) unsigned||MUL|0|| -|`parent_id`|int(10) unsigned|||0|| -|`change_date`|datetime|YES||NULL|| -|`blocked`|tinyint(3) unsigned|||0|| -|`priority`|smallint(5) unsigned|||0|| -|`section`|tinyint(3) unsigned|||0|| -|`url_alias`|text|YES|MUL|NULL|| -|`path_string`|varchar(255)|YES|MUL|NULL|| -|`depth`|tinyint(3) unsigned|YES||NULL|| -|`main_node_id`|int(10) unsigned||MUL|0|| -|`hidden`|tinyint(4)|YES||0|| - -Example: - -``` code -mysql > select node_id, class_id, parent_id, blocked, hidden, priority from sve_object limit 1,3; -+---------+----------+-----------+---------+--------+----------+ -| node_id | class_id | parent_id | blocked | hidden | priority | -+---------+----------+-----------+---------+--------+----------+ -| 3 | 1 | 2 | 0 | 0 | 7 | -| 4 | 1 | 3 | 0 | 0 | 1 | -| 5 | 2 | 4 | 0 | 0 | 0 | -+---------+----------+-----------+---------+--------+----------+ - -select node_id, url_alias, path_string, depth from sve_object limit 1,3; -+---------+--------------------------------------------------------------------+-------------+-------+ -| node_id | url_alias | path_string | depth | -+---------+--------------------------------------------------------------------+-------------+-------+ -| 3 | Produkte/Plakate | /2/3/ | 2 | -| 4 | Produkte/Plakate/Motivplakate | /2/3/4/ | 3 | -| 5 | Produkte/Plakate/Motivplakate/Bildung-macht-Zukunft | /2/3/4/5/ | 4 | -+---------+--------------------------------------------------------------------+-------------+-------+ -``` - -## Attributes for products and product groups - -You can create multiple data fields for each entry in `sve_object_attributes`. -Each of them can be set in its own language. The language codes follow the ISO-639 standard (for example, 'ger-DE' or 'eng-US'). - -Each entry consists of the following fields: - -- `attribute_id` - ID of this attribute (see `sve_class_attributes`) -- `node_id` - internal Location ID -- `data_int`, `data_float`, `data_text` - value of the attribute depending on the data type (see `sve_class_attributes`) - -### Table `sve_object_attributes` - -|Field|Type|Null|Key|Default|Extra| -|--- |--- |--- |--- |--- |--- | -|`node_id`|int(10) unsigned||PRI|0|| -|`attribute_id`|int(10) unsigned||PRI|0|| -|`data_float`|float|YES||NULL|| -|`data_int`|int(11)|YES||NULL|| -|`data_text`|text|YES||NULL|| -|`language`|varchar(6)||PRI|ger-DE|| - -### Table `sve_class` - -This table describes all available Content Types. The class fields are defined in `sve_class_attributes`. - -|Field|Type|Null|Key|Default|Extra| -|--- |--- |--- |--- |--- |--- | -|`class_id`|int(10) unsigned||PRI|0|| -|`class_name`|varchar(255)|YES||NULL|| -|`name_identifier`|int(10) unsigned|||0|| - -### Table `sve_class_attributes` - -The table describes the Content Type Fields. - -|Field|Type|Null|Key|Default|Extra| -|--- |--- |--- |--- |--- |--- | -|`attribute_id`|int(10) unsigned||PRI|0|| -|`class_id`|int(10) unsigned|||0|| -|`attribute_name`|varchar(255)|YES||NULL|| -|`ezdatatype`|varchar(255)||||| -|`sort_field`|varchar(255)|||data_text|| - -The following datatypes are supported: - -#### ezstring - -The data is stored in the `data_text` field as a flat string. - -#### ezinteger - -The data is stored in the `data_int` field. - -#### ezprice - -The price information is stored in the `data_float` field. The `data_text` field contains information about the VAT value in percent and a flag that indicates whether the price includes VAT or not. - -Example: - -``` code -+---------+--------------+------------+----------+-----------+----------+ -| node_id | attribute_id | data_float | data_int | data_text | language | -+---------+--------------+------------+----------+-----------+----------+ -| 5 | 210 | 0.952 | 0 | 19,1 | ger-DE | -+---------+--------------+------------+----------+-----------+----------+ -``` - -In this example, the price is 0.952 EUR (currency is defined per shop), including 19% VAT. - -#### ezmatrix - -This Content Type enables storing data organized in rows in columns in one field. -It is useful for dynamic attributes. - -For example: - -``` -+---------+--------------+------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+ -| node_id | attribute_id | data_float | data_int | data_text | language | -+---------+--------------+------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+ -| 249 | 240 | 0 | 0 | FilenameName | ger-DE | -+---------+--------------+------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------+ -``` - -- columns: definition of the columns with an ID and a Label -- rows: containing the data by rows and columns. For example, if a table contains two rows and two columns, four `` tags must be generated. - -``` xml - - - - - Filename - Name - - - test/30ccf286d5c8cf1a3ebb16e75cb0adc4.pdf - GS60-Montageanleitung.pdf - -``` - -## Examples - -``` -# Classes defined for econtent - -mysql > SELECT * FROM sve_class; -class_id class_name name_identifier -1 warengruppe 101 -2 bestellprodukt 201 - -# attributes for the classes 1 and 2 - -mysql > SELECT * FROM sve_class_attributes; -attribute_id class_id attribute_name ezdatatype sort_field -101 1 name ezstring data_text -102 1 navisionid ezstring data_text -201 2 name ezstring data_text -202 2 navisionid ezstring data_text -203 2 description ezstring data_text -204 2 vendor_no ezstring data_text -205 2 unit_list_price ezstring data_text - - -# object table -mysql > SELECT * FROM sve_object; -node_id class_id parent_id change... blocked priority section url_alias path... depth main... -2 1 0 NULL 0 1 0 shop /2/ 1 2 -3 1 2 NULL 0 1 0 shop/wg /2/3 2 3 -4 2 3 NULL 0 1 0 shop/wg/bsp /2/3/4 3 4 - - -# attributes - -SELECT * FROM sve_object_attributes; -node_id attribute_id data_float data_int data_text language -2 101 NULL NULL Shop ger-DE -2 102 NULL NULL FOLDER_ID ger-DE -3 101 NULL NULL Wg ger-DE -3 102 NULL NULL FOLDER_ID ger-DE -4 201 NULL NULL Bsp ger-DE -4 202 NULL NULL ITEM_NO ger-DE -4 203 NULL NULL Beschreibung ger-DE -4 204 NULL NULL 019-DA-12 ger-DE -4 205 NULL NULL 19.95 ger-DE -``` - -### Table `ses_externaldata` - -This table describes all external data from SAP, PIM, TYP, etc. The content is encoded in JSON. -Content Type Fields are defined in `sve_class_attributes`. - -|Field|Type|Null|Key|Default|Extra| -|--- |--- |--- |--- |--- |--- | -|`id`|int(10) unsigned||PRI||| -|`sku`|varchar(40)||||| -|`field_id`|varchar(40)||||| -|`language_code`|varchar(8)||||| -|`ses_field_type`|varchar(20)||||| -|`content`|longtext||||json encoded| - -Matching external data from `sve_class_attributes` of type `ses_externaldata` is done by `data_text`. -It must match the SKU (e.g. 000000000000167738). diff --git a/docs/guide/econtent/econtent_features/indexing_econtent_data/econtent_indexer.md b/docs/guide/econtent/econtent_features/indexing_econtent_data/econtent_indexer.md deleted file mode 100644 index fb88238649..0000000000 --- a/docs/guide/econtent/econtent_features/indexing_econtent_data/econtent_indexer.md +++ /dev/null @@ -1,64 +0,0 @@ -# eContent indexer [[% include 'snippets/commerce_badge.md' %]] - -## eContent class elements - -Solr index is built with data from the following eContent tables: - -- `sve_class` -- `sve_class_attributes` -- `sve_object` -- `sve_object_attributes` -- `sve_object_catalog` - -eContent supports n levels of class objects (stored in `sve_class`), and so does the indexer. - -In the default configuration you can have only two classes: product and categories, -but the model is flexible and it can handle several class types, -for example: category_a, sub_category, product, or any other hierarchical schema. - -All `sve_class` elements have a common name field. This name is used by the indexer as a common Solr field name for all types. - -The name is specified in `sve_object_attributes` and its identifier is specified in the `sve_class` table. -This name is stored in Solr with field name `name_s`. - -## Standard Solr field names - -|Solr Field Name|Description|Database Field|Example| -|--- |--- |--- |--- | -|`id`|ID of index element. It is built by concatenating Content Type + Location ID + language.||`econtent11gerde`| -|`content_id`|Node ID of an element.|sve_object.node_id|`11`| -|`document_type_id`|The type of document. For eContent the value is `econtent`||`econtent`| -|`type_id`|The ID of the Content Type.|sve_class.class_id|`1`| -|`type_name_id`|The name of the Content Type.|sve_class.class_name|`product_group`| -|`section_id`|The Section is a number that can be used later to separate content.|sve_object.section_id|`1`| -|`meta_indexed_language_code_s`|Language code of the specified element.||`ger-DE`| -|`main_location_id`|The `node_id` of the element.|sve_object.node_id|`11`| -|`main_node_id`|The `node_id` of the element.|sve_object.node_id|`11`| -|`main_location_parent_id`|The `node_id` of the parent element.|sve_object.parent_id|`2`| -|`main_location_path_id`|The path of the element is the tree node ID of all parent elements separated by a slash.|sve_object.path_string|`/2/11/`| -|`main_location_visible_b`|A boolean value to determine whether the element is visible or not. The value indexed here is the negated value from the database. That is because, in database, the field specifies whether the element is hidden or not, and in Solr, the field specifies whether the element is visible or not.|NOT sve_object.hidden|`true`| -|`meta_blocked_b`|A boolean value to determine whether the element is blocked or not.|sve.object.blocked|`false`| -|`meta_depth_i`|The depth of the element. It should be the amount of elements in `path_string`|sve_object.depth|`2`| -|`name_s`|The name of the element as specified by the class ID identifier.|sve_object_attributes.data_text
    With attribute ID determined in sve_class|`DMT`| -|`meta_modified_dt`|The date time value from the database in Solr datetime format.|sve_object.change_date|`2016-06-21T08:52:40Z`| -|`main_catalog_segments_ms`|A multivalue with all catalog code names.|sve_object_catalog.catalog_code|`ALL`, `NORMAL`| -|`is_main_b`|True if `content_id` is the main location ID.|sve_object.node_id|`true`| - -## Language and fallback language - -Each indexed language can have a fallback language. -In the following example, the key of the array is the language, and the content is the fallback language. - -``` yaml -# indexer configuration for econtent -siso_search.default.index_econtent_languages: - ger-DE: eng-GB #language: ger-DE, fallback: eng-GB - eng-GB: ger-DE #language: eng-GB, ger-DE - -``` - -This means that if a Content item in language `ger-DE` has some missing data, -the missing data is taken from the fallback language data (if there is any). - -eContent languages are defined per attribute, so it is possible to have an object with a full set of attributes in one language -and a smaller set of attributes in another language. diff --git a/docs/guide/econtent/econtent_features/indexing_econtent_data/indexing_econtent_data.md b/docs/guide/econtent/econtent_features/indexing_econtent_data/indexing_econtent_data.md deleted file mode 100644 index 50f8dbd76d..0000000000 --- a/docs/guide/econtent/econtent_features/indexing_econtent_data/indexing_econtent_data.md +++ /dev/null @@ -1,40 +0,0 @@ -# Indexing eContent data [[% include 'snippets/commerce_badge.md' %]] - -If [[= product_name_com =]] is configured to use eContent as data provider, the following things have to be taken into consideration when indexing. - -## Default indexing - -eContent data can be indexed by executing the following command: - -``` bash -php bin/console silversolutions:indexecontent -``` - -eContent Solr configuration uses two cores to keep search services available while indexing. -This means that the indexer indexes eContent data in a back core, -and then the indexer can be executed again with a swap parameter to swap the cores. - -|Indexer options|Results| -|--- |--- | -|`-c 5000 -r --siteaccess=import`|`-c` specifies the chunk size (how many elements are indexed at a same time).
    `-r` deletes all previous eContent data.
    Important: the parameter `siteaccess=import` informs the indexer to take the product data from the temporary tables.| -|`--live-core`|Indexes directly on the live core (and not on the back core).
    If you use `-r` and `--live-core`, the delete operation takes place before starting the indexer, so the full indexed content will not be available while indexing.| -|`swap`|Does not index, but swaps the cores instead.| - -### Additional parameters - -||| -|--- |--- | -|`siso_search.default.index_econtent_languages: [ ger-DE, eng-GB ]`|Specifies the languages to index.| -|`siso_search.default.solr_spellcheck: true`|Enables or disables Solr spellcheck (aka "Did you mean?" functionality)| -|`siso_search.default.index_facet_fields:`|Solr string fields are lowercased, therefore this configuration is used to specify an additional Solr `_id` field.
    ID fields are not lowercased, which results in facets that can preserve their original case.
    Field names must be specified without the `_s` suffix.| - -## Custom indexing using plugins - -eContent indexer also supports a plugin architecture for adding additional logic to the indexing process. - -The plugin can index price ranges. Given a product price, it creates a range index based on that price. -For example, for the price of $150, the output can be 100-200. - -In order to work the plugins must implement the `EcontentIndexerPluginInterface` interface. - -For more information, see [Custom indexer plugin for eContent](../../econtent_cookbook/econtent_search_cookbook/custom_indexer_plugin_for_econtent.md) diff --git a/docs/guide/econtent/econtent_features/indexing_econtent_data/solr_cores_for_econtent.md b/docs/guide/econtent/econtent_features/indexing_econtent_data/solr_cores_for_econtent.md deleted file mode 100644 index 616f224940..0000000000 --- a/docs/guide/econtent/econtent_features/indexing_econtent_data/solr_cores_for_econtent.md +++ /dev/null @@ -1,69 +0,0 @@ -# Solr cores for eContent [[% include 'snippets/commerce_badge.md' %]] - -The main goal of dedicated Solr cores for the eContent data provider is to import and re-index data (e.g. products) with a temporary Solr core. -In this case the application is not disturbed while the importer and indexer are working in temporary cores. - -After the processes are finished, the temporary cores are swapped with the production cores and the application starts using fresh data. - -## Adapt configuration for two Solr eContent cores - -The standard configuration for [[= product_name_com =]] supports two Solr eContent cores by default (see `app/config/ezcommerce_advanced.yml`). - -In `ezpublish/config/config.yml`: - -``` yaml -nelmio_solarium: - endpoints: - default: - host: '%siso_search.solr.host%' - port: '%siso_search.solr.port%' - path: '%siso_search.solr.path%' - core: '%siso_search.solr.core%' - timeout: 30 - siso_core_admin: - host: '%siso_search.solr.host%' - port: '%siso_search.solr.port%' - path: '%siso_search.solr.path%' - core: admin - timeout: 30 - siso_econtent: - host: '%siso_search.solr.host%' - port: '%siso_search.solr.port%' - path: '%siso_search.solr.path%' - core: econtent - timeout: 30 - siso_econtent_back: - host: '%siso_search.solr.host%' - port: '%siso_search.solr.port%' - path: '%siso_search.solr.path%' - core: econtent_back - timeout: 30 - clients: - default: - endpoints: - - default - econtent: - endpoints: - - siso_econtent - - siso_econtent_back - - siso_core_admin -``` - -## Add parameters - -``` yaml -parameters: - siso_search.solr.host: 127.0.0.1 - siso_search.solr.port: 8983 - siso_search.solr.path: /solr - siso_search.solr.core: collection1 - -``` - -## Manual changes in Solr - -[[= product_name_com =]] comes with an bash script which installs the Solr engine and adapts the configuration for Solr: - -``` bash -bash ./install-solr.sh 8983 -``` diff --git a/docs/guide/econtent/econtent_features/staging_system.md b/docs/guide/econtent/econtent_features/staging_system.md deleted file mode 100644 index 9dcbe0f886..0000000000 --- a/docs/guide/econtent/econtent_features/staging_system.md +++ /dev/null @@ -1,52 +0,0 @@ -# Staging system [[% include 'snippets/commerce_badge.md' %]] - -eContent supports a staging area. The product catalog can exist either as a live version which is used for the customer, or a staging (temporary) version, which can be used for staging and importing purposes. - -[[= product_name_com =]] offers tools to switch between the production and staging versions. - -## Best practice - -Depending on your import strategy, you can set up a staging system in the following ways: - -### Full import - -You can make a full import to temporary tables without modifying the production system. -This strategy supports large product catalogs as well. -After importing data the result of the import can be checked (e.g. number of products compared between the live and temporary tables) -and, depending on the results, the new catalog can be made live. - -### Delta update - -You can make a delta update, which directly uses the production system. - -### Import SQL data - -The staging system can be used for emergency situations as well. -If a product catalog encounters issues (legacy issues, wrong data or structure), -an SQL dump from past imports can be imported without interrupting the production system. - -## Live and staging areas - -Create an index for the new products (temporary area). The following command indexes the data from the temporary tables to a temporary core: - -``` bash -php bin/console silversolutions:indexecontent --siteaccess=import -``` - -Use a SiteAccess (default: `import`) for the indexing process. The SiteAccess import should cover: - -- all languages used in eContent (see `ezplatform.yml`) -- by default, the table set for this SiteAccess is configured to use the temporary tables - -The product import can, for example, use the temporary tables. The import process does not stop the production system. It still uses the production version. - -## Switching between temporary and live versions - -After the import, you must switch the database tables and Solr cores. Execute the following commands: - -``` bash -php bin/console silversolutions:indexecontent swap -php bin/console silversolutions:econtent-tables-swap -``` - -Depending on the project, you should purge the HTTP cache for the product catalog. diff --git a/docs/guide/econtent/econtent_features/variants_in_econtent.md b/docs/guide/econtent/econtent_features/variants_in_econtent.md deleted file mode 100644 index 8eb40f27e1..0000000000 --- a/docs/guide/econtent/econtent_features/variants_in_econtent.md +++ /dev/null @@ -1,137 +0,0 @@ -# Variants in eContent [[% include 'snippets/commerce_badge.md' %]] - -`ses_variants` can be used to store variant data for a product. - -The variants must be stored in a JSON format. -For more information, see [Product Variants](../../../guide/catalog/product_variants/product_variants.md). - -``` json -[ - { - "sku": { - "label": "Sku", - "value": "0002" - }, - "variantCode": { - "label": "Variant Code", - "value": "D4142" - }, - "description": { - "label": "Description", - "value": "Bubble light bulb small" - }, - "characteristicCode1": { - "label": "small", - "value": "small" - }, - "characteristicLabel1": { - "label": "Color", - "value": "Color" - }, - "characteristicCode2": { - "label": "Silver", - "value": "Silver" - }, - "characteristicLabel2": { - "label": "", - "value": "" - }, - "priceNet": { - "label": "Listprice net", - "value": "220" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - }, - { - "sku": { - "label": "Sku", - "value": "0002" - }, - "variantCode": { - "label": "Variant Code", - "value": "D4143" - }, - "description": { - "label": "Description", - "value": "Bubble light bulb large" - }, - "characteristicCode1": { - "label": "large", - "value": "large" - }, - "characteristicLabel1": { - "label": "Size", - "value": "Size" - }, - "characteristicCode2": { - "label": "Silver", - "value": "Silver" - }, - "characteristicLabel2": { - "label": "Color", - "value": "Color" - }, - "priceNet": { - "label": "Listprice net", - "value": "220" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - }, - { - "sku": { - "label": "Sku", - "value": "0002" - }, - "variantCode": { - "label": "Variant Code", - "value": "5532" - }, - "description": { - "label": "Description", - "value": "Alibaba Single Door Silver Refrigerator" - }, - "characteristicCode1": { - "label": 30, - "value": 30 - }, - "characteristicLabel1": { - "label": "Size", - "value": "Size" - }, - "characteristicCode2": { - "label": "White", - "value": "White" - }, - "characteristicLabel2": { - "label": "Color", - "value": "Color" - }, - "priceNet": { - "label": "Listprice net", - "value": "330" - }, - "vatPercent": { - "label": "VAT percent", - "value": "" - }, - "dataMap_countryOfOrigin": { - "label": "dataMap Country of Origin", - "value": "" - } - } -] -``` diff --git a/docs/guide/econtent/indexing_econtent_data.md b/docs/guide/econtent/indexing_econtent_data.md new file mode 100644 index 0000000000..fa7e9e097e --- /dev/null +++ b/docs/guide/econtent/indexing_econtent_data.md @@ -0,0 +1,27 @@ +# Indexing eContent data + +eContent data can be indexed by executing the following command: + +``` bash +php bin/console ibexa:commerce:index-econtent +``` + +eContent Solr configuration uses two cores to keep search services available while indexing. +This means that the indexer indexes eContent data in the back core, +and then the indexer can be executed again with a swap parameter to swap the cores. + +|Command options|Results| +|--- |--- | +|`-c 5000`|Specifies the chunk size (how many elements are indexed at the same time).| +|`-r`|Deletes all previous eContent data.| +|`--siteaccess=import`|Instructs the indexer to take the product data from the temporary tables.| +|`--live-core`|Indexes directly on the live core (and not on the back core).
    If you use `-r` together `--live-core`, the delete operation takes place before the indexer starts, so the full indexed content is not be available while indexing.| +|`swap`|Does not index, but swaps the cores instead.| + +### Additional parameters + +||| +|--- |--- | +|`siso_search.default.index_econtent_languages`|Specifies the languages to index.| +|`siso_search.default.solr_spellcheck`|Enables or disables the Solr spellchecker (the "Did you mean?" functionality).| +|`siso_search.default.index_facet_fields`|Solr string fields are lowercased, therefore this configuration is used to specify an additional Solr `_id` field.
    ID fields are not lowercased, which results in facets that can preserve their original case.
    Field names must be specified without the `_s` suffix.| diff --git a/docs/guide/econtent/staging_system.md b/docs/guide/econtent/staging_system.md new file mode 100644 index 0000000000..47df97fd8f --- /dev/null +++ b/docs/guide/econtent/staging_system.md @@ -0,0 +1,32 @@ +# Staging system + +eContent supports a staging area. +The product catalog can exist either as a live version which is used for the customer, +or a temporary version, which can be used for staging and importing purposes. + +## Live and staging areas + +To set up staging, create an index for the new products (temporary area). +The following command indexes the data from the temporary tables to a temporary core: + +``` bash +php bin/console ibexa:commerce:index-econtent --siteaccess=import +``` + +Use a SiteAccess (default: `import`) for the indexing process. +The import SiteAccess should cover all languages used in eContent (see `ezplatform.yaml`). +By default, the table set for this SiteAccess is configured to use the temporary tables + +The import process does not stop the production system. + +## Switching between temporary and live versions + +After the import, switch the database tables and Solr cores. +Execute the following commands: + +``` bash +php bin/console ibexa:commerce:index-econtent swap +php bin/console ibexa:commerce:swap-econtent-tables +``` + +Depending on the project, you might want to purge the HTTP cache for the product catalog. diff --git a/docs/guide/environments.md b/docs/guide/environments.md index 7344899779..16ef74f22e 100644 --- a/docs/guide/environments.md +++ b/docs/guide/environments.md @@ -1,3 +1,7 @@ +--- +description: In Ibexa DXP you can use environment provided by Symfony in virtual host configuration, as well as to create custom environments. +--- + # Environments Environment configuration is provided by Symfony. [[= product_name =]] additionally enables you to specify environments in virtual host configuration. @@ -5,20 +9,20 @@ You can configure several environments, such as production, development or stagi !!! tip - See also [Environments in Symfony doc](https://symfony.com/doc/5.0/configuration.html#configuration-environments) + See also [Environments in Symfony doc]([[= symfony_doc =]]/configuration.html#configuration-environments). ## Web server configuration -For example, using Apache, in the `VirtualHost` example in [doc/apache2/](https://github.com/ezsystems/ezplatform/tree/master/doc/apache2) in your installation, the required `VirtualHost` configurations have been already included. You can switch to the desired environment by setting the `ENVIRONMENT` variable to `prod`, `dev` or another custom value, like in the following example: +For example, using Apache, in the [`VirtualHost` example](https://raw.githubusercontent.com/ibexa/post-install/main/resources/templates/apache2/vhost.template) in your installation, the required `VirtualHost` configurations have been already included. You can switch to the desired environment by setting the `ENVIRONMENT` variable to `prod`, `dev` or another custom value, like in the following example: ``` # Environment. # Possible values: "prod" and "dev" out-of-the-box, other values possible with proper configuration # Defaults to "prod" if omitted (uses SetEnvIf so value can be used in rewrite rules) -SetEnvIf Request_URI ".*" SYMFONY_ENV="dev" +SetEnvIf Request_URI ".*" APP_ENV="dev" ``` -## Using custom environments +## Custom environments If you want to use a custom environment (something other than `prod` and `dev`), you need to place dedicated configuration files in a separate folder: `config/packages//config_.yaml` diff --git a/docs/guide/erp_integration/checkout_order/failed_order_process/failed_order_process.md b/docs/guide/erp_integration/checkout_order/failed_order_process/failed_order_process.md deleted file mode 100644 index 35ad6e3067..0000000000 --- a/docs/guide/erp_integration/checkout_order/failed_order_process/failed_order_process.md +++ /dev/null @@ -1,49 +0,0 @@ -# Failed order process [[% include 'snippets/experience_badge.md' %]] - -For usability reasons, it is not advisable to let the whole checkout fail if the communication to the ERP system is not working correctly. -When the submission of an order to the ERP system fails, the order must be stored for a later retry. - -## The OrderFailedEvent - -The `Silversolutions\Bundle\EshopBundle\Event\Erp\OrderFailedEvent` event (event ID `siso_erp.order_failed`) is dispatched by `WebConnectorErpService` if an error occurs during the communication with the remote ERP system. -It should also be dispatched by other implementations of `AbstractErpService` under similar circumstances. - -!!! note - - If the class attribute `$maxCount` is not set or null, listeners for this event ignore the maximum number of retries for failed / lost orders. - - The order is submitted again (for back-end manipulation of orders). - -## ErpErrorServiceInterface - -The `Silversolutions\Bundle\EshopBundle\Services\ErpErrorServiceInterface` interface declares the `processFailedOrder()` method which is intended to process failed orders. - -### Standard implementation - -The `Silversolutions\Bundle\EshopBundle\Services\StandardErpErrorService` interface (service ID `siso_erp.erp_error_service`) is only implemented in an event listener. -This listener is subscribed to `OrderFailedEvent` and set up with the highest priority. - -!!! note - - As this service provides some crucial information to the basket object (as ERP error message and fail counter) it should have the highest priority in all setups. - -The standard implementation uses the basket states in order to realize the queue of failed orders. -Orders with the state `ordered_failed` are considered to be queued. - -The number of failed tries is stored in the basket's `$erpFailCounter` attribute. -The maximum allowed number of tries is configured using `siso_checkout.default.max_failed_order`. - -Only basket objects with the states `payed` and `ordered_failed` are expected to be transmitted as order and can be processed as a failed order. -If the given basket object has a different state, a `RuntimeException` is thrown. - -## OrderFailedNotifyListener - -If an order failed, an administrator is informed about it by sending an e-mail. For that purpose the `Silversolutions\Bundle\EshopBundle\EventListener\Erp\OrderFailedNotifyListener` (service ID `siso_eshop.order_failed_listener`) listener exists for the `siso_erp.order_failed` event. -This listener has a lower priority than `StandardErpErrorService`, which ensures that the ERP error message is already set in the basket (given by the event object). - -The following container parameters are passed to the service: - -- `ses_swiftmailer` -- `siso_eshop.order_failed.subject` - -The `mailReceiver` subelement under `ses_swiftmailer` is used as the recipient for the notification. diff --git a/docs/guide/erp_integration/checkout_order/failed_order_process/lost_orders.md b/docs/guide/erp_integration/checkout_order/failed_order_process/lost_orders.md deleted file mode 100644 index 41ee7a0e83..0000000000 --- a/docs/guide/erp_integration/checkout_order/failed_order_process/lost_orders.md +++ /dev/null @@ -1,83 +0,0 @@ -# Lost orders [[% include 'snippets/experience_badge.md' %]] - -## Sending lost orders from Backend - -If sending of an order to ERP fails, the order is stored in a database with a special state `ordered_failed`. -You can find lost order in the Back office tab **eCommerce/Order Management**. -Filter by **Order failed**. - -![](../../../img/lost_orders.png) - -There are several actions, that can be executed with lost orders: - -|Action|Description|Result| -|--- |--- |--- | -|transfer to ERP|the lost order is resent to ERP|The user sees an error or success message depending whether the lost order could be resent to ERP or not. If the sending the lost order failed, the administrator gets an email. If sending of lost order was successful, the customer who made this order gets a confirmation email| -|remove lost order|The lost order is not removed, the state of lost order is changed to `confirmed`|The lost order does not appear in the list anymore| - -## Technical implementation - -### LostOrderService - -`Siso\Bundle\CheckoutBundle\Service\LostOrderService` (service ID `siso_checkout.lost_order_service`) is responsible for handling all lost order features. - -#### Service methods: - -|Method|Description|Parameters|Peturns| -|--- |--- |--- |--- | -|`createFailedOrderList()``|creates a list of lost orders||Basket[]| - -### LostOrderController - -The `LostOrderController` controller handles all lost order actions - -|Action|Parameters|Description|Policy| -|--- |--- |--- |--- | -|`processLostOrderAction()`|`$basketId`|Send the lost order to ERP|siso_policy/lostorder_process| -|`deleteLostOrderAction()`|`$basketId`|Set the state of lost order to `confirmed`|siso_policy/lostorder_process| - -### Email notifications - -Every time an order cannot be placed, the shop administrator gets an email. -The email can be defined in the configuration: - -``` yaml -parameters: - siso_core.default.ses_swiftmailer: - lostOrderEmailReceiver: %ses_eshop.lostorder_email% -``` - -![Example email about a lost order](../../../img/lost_orders_2.png) - -Templates are located in: - -``` -vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views/Emails/NotificationMail_FailedOrder.html.twig -vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views/Emails/NotificationMail_FailedOrder.txt.twig -``` - -## Lost order command - -You can also resend lost orders using a command-line tool: - -``` bash -php bin/console silversolutions:lostorder:process [id] -``` - -!!! note - - The command sends the lost order in the same way as through the PHP code. - If the sending of lost order fails, the administrator gets an email. - If the sending was successful, the customer who made the order gets confirmation email. - - In the context of the command-line tool there is no request. - Because of this no images embedded in email will be sent because it is not possible to generate the image path. - -!!! caution - - To use the CLI the host must be set, otherwise the URLs and assets are not correct. - - ``` - parameters: - siso_core.default.host: localhost - ``` diff --git a/docs/guide/erp_integration/checkout_order/order_submission/order_format.md b/docs/guide/erp_integration/checkout_order/order_submission/order_format.md deleted file mode 100644 index e09c054f32..0000000000 --- a/docs/guide/erp_integration/checkout_order/order_submission/order_format.md +++ /dev/null @@ -1,133 +0,0 @@ -# Order format [[% include 'snippets/experience_badge.md' %]] - -The data which is sent to the ERP is described in the XML specification (`vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml/request.createSalesOrder.xml`). - -## Order - -``` xml - - - - EUR - - 2005-06-20 - sample - - - - 1234 - - - 10000 - - - - - - - - - - - - - - - - 2005-06-30 - 18:00:00.0Z - - - - - 31 - 2007-01-01 - IBAN - A12345 - - - IS000001261234560101901239 - EUR - - SEISISRE - Central bank of Iceland - - - - - - - - - - - 3WEEKS - - 3WEEKS - - - - Freetext note on line 1 - Freetext note on line 2 - - - - - - -``` - -## Order lines - -``` xml - - - 10000 - 1 - - - - - - - - - - 0 - - - - - true|false - - - - - - 2950 - 1 - - - Web-Connector - für SAP, Magento oder NAV - - 1000 - 123 - - - ABC - - - ABC - - - - - - - - - -``` diff --git a/docs/guide/erp_integration/checkout_order/order_submission/order_submission.md b/docs/guide/erp_integration/checkout_order/order_submission/order_submission.md deleted file mode 100644 index 932c9848d0..0000000000 --- a/docs/guide/erp_integration/checkout_order/order_submission/order_submission.md +++ /dev/null @@ -1,115 +0,0 @@ -# Order Submission [[% include 'snippets/experience_badge.md' %]] - -Depending on the chosen payment method, the order is completed after: - -- submitting the [order summary](../../../checkout/checkout_api/forms/order_summary.md) -- receiving a notification from the payment gateway -- returning from the payment gateway - -During completion, order data is sent to the ERP system. On success, the state of the submitted basket changes to `confirmed`. -If it fails, the state becomes `ordered_failed` and an email is sent to administrator. - -## BasketGuidService - -After the summary of the checkout is confirmed by the user, the payment process is initiated. -To identify the order across all systems (shop, payment gateway, ERP system), a GUID is generated and stored to the basket. -The generation of that ID is left to the `BasketGuidService` service (service ID `silver_basket.guid_service`). -This way it is easy to override the standard generation routine. - -## WebConnectorErpService - -`AbstractErpService` defines the `submitOrder(Basket $basket)` method - -The standard implementation of this method is found in `\Silversolutions\Bundle\EshopBundle\Services\WebConnectorErpService` - -This method prepares the [`CreateSalesOrderMessage`](../../erp_communication/erp_components\erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md) out of the basket's data and sends it to the ERP using [ERP transport](../../erp_communication/erp_components/erp_component_transport.md). -In case of an error during remote communication the [failed order process](../failed_order_process/failed_order_process.md) is triggered using `OrderFailedEvent`. - -## Event before order is submitted - -Before the order is sent to the ERP an event is triggered. -The corresponding event listener can modify the basket data or perform any additional action. -It can set the status to `failed` and set en error message. - -If the event status is `failed`, the order is not submitted and is treated as a lost order. - -It is not possible to use services in appropriate event listeners that require the session, -because the process can also be executed from the command line in the lost order process. - -For example it is not possible to use the `CustomerProfileDataService` (`ses.customer_profile_data.ez_erp`), and the Repository (`ezpublish.api.repository`) must be used instead in order to fetch the user data. - -## Sending additional data in the order - -You can extend the message with additional fields in two ways: - -- Extend the `CreateSalesOrderMessage` class. This solution requires maintenance effort in case of an update of the shop's base implementation, as changes to the base message must be merged to the extended version. -- Write the data into [one of the SesExtension](../../erp_communication/erp_components\erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md) [tree elements (see `ses_tree` in table)](../../erp_communication/erp_components\erp_component_messages/erp_message_class_generator.md)). -This solution should only be used to add simple, single-valued fields. -During the communication with the remote service (e.g. Web.Connector), the tree attributes are mapped between PHP arrays and XML data implicitly without any structure definition. -This can cause conflicts for XML elements that occur multiple times, and sequential PHP arrays. - -### Example - -Sometimes it is necessary to send additional project-specific data in the order. -If you need to send the data to ERP, you can get it from basket (`dataMap`) or from customer data. - -In such a case, implement an event listener which subscribes to `MessageRequestEvent`. - -#### Service implementation - -``` php -class MessageRequestListener -{ - /** - * This method must be registered as a listener to the - * 'silver_eshop.request_message' event. - * - * @param MessageRequestEvent $event - */ - public function onOrderRequest(MessageRequestEvent $event) - { - $message = $event->getMessage(); - if ($message instanceof CreateSalesOrderMessage) { - /** @var Order $request */ - $request = $message->getRequestDocument(); - $userId = $request->BuyerCustomerParty->SupplierAssignedAccountID->value; - - if (!empty($userId)) { - try { - $userService = $this->ezRepository->getUserService(); - $ezUser = $userService->loadUser($userId); - if ($ezUser->getFieldValue(EzHelperService::USER_CUSTOMER_PROFILE_DATA)->text != '') { - $customerProfileDataBase64Serialize = $ezUser->getFieldValue(EzHelperService::USER_CUSTOMER_PROFILE_DATA)->text; - $customerProfileDataSerialized = base64_decode($customerProfileDataBase64Serialize); - $customerProfileData = unserialize($customerProfileDataSerialized); - //read customer data and set them in the SesExtension field - if ($customerProfileData instanceof CustomerProfileData) { - $customerType = $customerProfileData->sesUser->customerType; - $title = $customerProfileData->getDataMap()->hasAttribute('title') - ? $customerProfileData->getDataMap()->getAttribute('title') : ''; - $academicTitle = $customerProfileData->getDataMap()->hasAttribute('academicTitle') - ? $customerProfileData->getDataMap()->getAttribute('academicTitle') : ''; - - $request->SesExtension->value['customerType'] = isset($customerType) ? $customerType : ''; - $request->SesExtension->value['title'] = isset($title) ? $title : ''; - $request->SesExtension->value['academicTitle'] = isset($academicTitle) ? $academicTitle : ''; - } - } catch (\Exception $e) { - // must be injected, somehow - $this->logger->error('Exception:' . $e->getMessage()); - } - } - } - } -} -``` - -#### Service configuration - -``` xml -Path\To\Your\Class\MessageRequestListener - - - - -``` diff --git a/docs/guide/erp_integration/connecting_shop_to_erp.md b/docs/guide/erp_integration/connecting_shop_to_erp.md deleted file mode 100644 index 7c7b31d65c..0000000000 --- a/docs/guide/erp_integration/connecting_shop_to_erp.md +++ /dev/null @@ -1,253 +0,0 @@ -# Connecting shop to ERP [[% include 'snippets/experience_badge.md' %]] - -[[= product_name_exp =]] can use both webservices/SOAP and REST transfer protocols to connect to ERP. - -You can extend the transport layer if you need an additional protocol or way to access the ERP/CRM. - -One way to connect to an SAP or Microsoft Dynamics NAV system is to use the Web.Connector (separately licensed product) -which already comes with prepared interfaces for those ERP systems. - -There are other options besides using the Web.Connector: - -- If the ERP offers a webservice/REST interface, you can connect [[= product_name_exp =]] directly with the ERP. -- You can use an enterprise service bus (ESB). - -![](../img/web_connector.png) - -## Request process - -### Step 1: Shop sends data request - -The shop requests data using a request object. - -For example: - -The shop sends a request using the generated ERP classes for the selected customer. - -Customer is set in `BuyerCustomerParty/Party/PartyIdentification/ID` - -Mapping is done using specification in `@EshopBundle/Resources/specifications/xml/request.selectCustomer.xml`: - -``` xml - - - - - 10000 - - - -``` - -### Step 2: Request is mapped - -The shop maps the UBL request to a specific request for the ERP (optional step). - -For example: - -The request is mapped to `0100000`. - -Mapping is done using specification in `app/Resources/xsl/request.selectcustomer.xsl`: - -``` xml - - - - - - - - - - -``` - -### Step 3: Web.Connector receives request - -The Web.Connector receives the request and maps the request into an ERP request. - -For example: - -The Web.Connector converts the request in the language of the ERP to: `0100000`. - -Mapping is done using specification in `mapping/nav_ws/xsl/request.selectcustomer.xsl`: - -``` xml - - - - - - - - - - -``` - -### Step 4: Web.Connectors sends request to ERP - -The Web.Connector sends the request to the ERP. - -### Step 5: ERP processes the request - -The ERP processes the requests and sends an answer. - -For example: - -Data from ERP: - -``` xml - - 28;EgA... - 0100000 - _blank_ - Demo customer - - Office - -
    Demo street
    - 12555 - Berlin - DE - 0301212 - 03012121 - info@test.de - - - - SAMMEL - NATIONAL - NATIONAL - - info@test.de - - - - EINZUG - - - 44;3gAAAAJ7BzAAMQAwADAAMAAwADAAAAACe/9MADAAMQ==9;1326112700; - 0100000 - L01 - Demo customer - Max Mueller -
    Molkereistr. 1a
    - Berlin - 12555 - -
    - -
    -
    -``` - -### Step 6: Answer is sent from ERP - -The Web.Connector gets an answer from ERP and maps it to an answer for the shop. - -For example: - -The Web.Connector maps data for shop using a 1:1 conversion. - -Mapping is done using specification in `mapping/nav_ws/xsl/response.selectcustomer.xsl`. - -In this case the answer is not be mapped and mapping is done in the shop: - -``` xml - - - - - - - - -``` - -### Step 7: Shop receives the request - -The shop receives the request and maps it to UBL (optional step). - -For example: - -The shop converts the answer into UBL using a mapping and converts the response to a UBL object as defined in the specification -(`@EshopBundle/Resources/specifications/xml/response.selectCustomer.xml`). - -### Step 8: UBL response ready - -The application gets a proper UBL response object. - -!!! note "Specification for requests and responses" - - The shop uses a specification which describes how the request to the ERP (our Web.Connector) and the answer from the ERP will look like. - - The specification is written in XML and located in `resources/specifications/xml`. It includes always two files: a request and a response specification. - - It is used to [generate PHP objects](erp_communication/erp_components/erp_component_messages/erp_message_class_generator.md) which can be used to define a request or get the response back from the ERP. - -## Web.Connector setup - -Yo work with Web.Connectors, you need to set up the URL to the Web.Connector: - -`webconnector_url: https:///webconnector/webconnector_opentrans.php5` - -and the configuration for each message: - -``` yaml -silver_erp.config.messages: - # ... - select_customer: - message_class: "Silversolutions\\Bundle\\EshopBundle\\Message\\SelectCustomerMessage" - response_document_class: "\\Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\Document\\CustomerResponse" - webservice_operation: "SV_OPENTRANS_SELECT_CUSTOMERINFO" - mapping_identifier: "selectcustomer" -``` - -If `mapping_identifier` is defined, [[= product_name_exp =]] uses a mapping (XSLT based). - -The Web.Connector performs mapping for each message according to the following configuration: - -``` php -$cfg->setSetting('WebserviceHandler_Config', 'MethodSettings', array( - // ... - 'SV_OPENTRANS_SELECT_CUSTOMERINFO' => array( - 'remote_reference' => 'customer', - 'method' => 'Read', - 'mapping' => 'selectCustomer', - 'mapping_parameters' => array( - 'WEBSITE' => 'HOME', - ), - ), -``` - -## Retrieving data using PHP API - -To get customer information via the Web.Connector: - -``` php -$erpService = $this->getContainer()->get('silver_erp.facade'); - -$customerNo = "1000"; -$selectCustomerResponse = $erpService->selectCustomer($customerNo); -``` - -The answer is an object as defined in `@EshopBundle/Resources/specifications/xml/response.selectCustomer.xml`. - -To retrieve products from ERP in a controller: - -``` php -/** @var WebConnectorErpService $importer */ -$importer = $this->get('silver_erp.facade'); -//Starting SKU -$startSku = '1..'; -//Number of items to be retrieved (optional, default is 10) -$itemMaxCount = 30; -$items = $importer->selectItem($startSku, $itemMaxCount); -foreach($items as $products) -{ - foreach($products as $product) - { - //Prints the product's name - echo (string)$product->ProductMeta->name->TextField->text->translations->translation[0]->value; - //Prints the product's price - echo (string)$product->ProductMeta->price->PriceField->price->price->value; - } -} -``` diff --git a/docs/guide/erp_integration/erp_communication.md b/docs/guide/erp_integration/erp_communication.md new file mode 100644 index 0000000000..b268dafb8a --- /dev/null +++ b/docs/guide/erp_integration/erp_communication.md @@ -0,0 +1,54 @@ +--- +edition: commerce +--- + +# ERP communication + +The shop comes with a predefined set of messages that are used to communicate with ERP: + +|Message|Description| +|--- |--- | +|`calculate_sales_price`|Calculates prices based on ERP's business logic| +|`createsalesorder`|Creates an order| +|`select_customer`|Gets customer data from ERP| +|`select_contact`|Gets contact data for a person from ERP| +|`create_contact`|Creates a contact in ERP| +|`updatecustomer`|Updates a customer in ERP| +|`orderdetail`|Gets details about an order| +|`invoice_detail`|Gets details about an invoice| +|`delivery_note_detail`|Gets details about a delivery note| +|`orderlist`|Gets a list of orders| +|`invoice_list`|Gets a list of invoice| +|`delivery_note_list`|Gets a list of delivery notes| +|`creditmemolist`|Gets a list of credit memos| +|`creditmemodetail`|Gets details about a credit memo| +|`readdeliveryaddress`|Gets delivery address data for the provided party ID| +|`updatedeliveryaddress`|Updates the ERP data of an existing delivery address| +|`createdeliveryaddress`|Creates a new delivery address for the provided party ID| +|`deletedeliveryaddress`|Deletes a delivery address| + +You can find the standard messages in `EshopBundle/Resources/config/messages.yml`. + +## Order history ERP messages + +[Order history](../order_history/order_history.md) uses the following ERP messages: + +|Message|Process type in ERP|Webservice operation| +|--- |--- |--- | +|`invoice_detail`|`READPOSTEDSALESINVOICE`|`SV_OPENTRANS_GET_ORDERSTATUS`| +|`invoice_list`|`READPOSTEDSALESINVOICELIST`|`SV_OPENTRANS_GET_ORDERLIST`| +|`delivery_note_detail`|`READPOSTEDSALESSHIPMENT`|`SV_OPENTRANS_GET_ORDERSTATUS`| +|`delivery_note_list`|`READPOSTEDSALESSHIPMENTLIST`|`SV_OPENTRANS_GET_ORDERLIST`| +|`credit_memo_detail`|`READPOSTEDSALESCRMEMO`|`SV_OPENTRANS_GET_ORDERSTATUS`| +|`credit_memo_list`|`READPOSTEDSALESCRMEMOLIST`|`SV_OPENTRANS_GET_ORDERLIST`| +|`order_detail`|`READSALESDOCUMENT`|`SV_OPENTRANS_GET_ORDERSTATUS`| +|`order_list`|`READSALESDOCUMENTLIST`|`SV_OPENTRANS_GET_ORDERLIST`| + +## Additional lines + +A response that ERP sends to the shop can contain more information about a product than was requested. +This information can include additional shipping costs, vouchers, bonus products, discounts, etc. +This information is handled by a service that adds lines to the basket or to a basket line. + +`WebConnectorService` passes the whole ERP response object to [`RemotePriceProvider`](remotepriceprovider.md). +`RemotePriceProvider` creates additional lines in the price response if it gets additional price information from ERP that has not been requested. diff --git a/docs/guide/erp_integration/erp_communication/additionallines.md b/docs/guide/erp_integration/erp_communication/additionallines.md deleted file mode 100644 index ea22693164..0000000000 --- a/docs/guide/erp_integration/erp_communication/additionallines.md +++ /dev/null @@ -1,46 +0,0 @@ -# Additional lines [[% include 'snippets/experience_badge.md' %]] - -A response sent to the shop from ERP can contain more information about product than was requested. -This additional information can include additional shipping costs, vouchers, another product for free, promotions, etc. -This information is handled by the service which adds the proper lines to basket or basket line. - -Additional lines in the basket are stored as `$additionalLines[]`. -Additional lines in a basket line are stored as `$assignedLines[]`. - -## WebConnectorService - -`WebConnectorService` passes the whole ERP response object to the [RemotePriceProvider](../remotepriceprovider.md). -`RemotePriceProvider` creates additional lines in the price response, if it gets additional price information from ERP that has not been requested. - -## AdditionalLine - -`AdditionalLine` defines the class for additional lines that can be related to the basket or basket line. -Additional lines can increase or decrease the order sum. - -|Attribute|Type|Usage| -|--- |--- |--- | -|`sku`|string|SKU or resource number of the cost| -|`name`|string|Name of the cost e.g. 'Shipping costs DHL standard'| -|`identifier`|string|Identifier of the cost e.g. `shippingCosts`| -|`price`|PriceField|Price of the cost including and excluding VAT| -|`characteristic`|string|Characteristic of the cost. Allowed values:
    - `expense`: increases the total amount, has a price
    - `voucher`: decreases the total amount, has a price
    - `addOn`: doesn't have a price| - -## Storing additional lines - -Additional lines can be stored in the basket and/or basket line, for example: - -``` html+twig -{% for line in basket.additionalLines %} - Name: {{ line.name }} - Price: {{ ses_render_price(null, line.price, - { - 'outputPrice': {'property': 'priceInclVat', 'raw' : true} - }) }} -{% endfor %} -``` - -## AdditionalLinesService - -`AdditionalLinesService` (service ID: `siso_basket.additional_lines_service`) is used to calculate the additional lines and store and fetch them from basket/basket line. - -It implements all methods from `AdditionalLinesServiceInterface`. diff --git a/docs/guide/erp_integration/erp_communication/erp_communication.md b/docs/guide/erp_integration/erp_communication/erp_communication.md deleted file mode 100644 index 33e05d0d00..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_communication.md +++ /dev/null @@ -1,134 +0,0 @@ -# ERP communication [[% include 'snippets/experience_badge.md' %]] - -[[= product_name_exp =]] uses the logic of an ERP system in different situations: - -- During login process, to get customer data -- In product detail page (configurable), to get prices and stock infos -- In basket -- In checkout -- During registration, to create contacts or customers -- In order history, to get documents from the ERP -- When importing products - -The shop comes with a predefined set of messages: - -|Message|Description| -|--- |--- | -|`calculate_sales_price`|Calculates prices using the business logic of the ERP| -|`createsalesorder`|Creates an order| -|`select_customer`|Gets customer data from the ERP| -|`select_contact`|Gets contact data for a person from the ERP| -|`create_contact`|Creates a contact in the ERP| -|`updatecustomer`|Updates a customer in the ERP| -|`orderdetail`|Gets details about an order| -|`invoice_detail`|Gets details about an invoice| -|`delivery_note_detail`|Gets details about a delivery note| -|`orderlist`|Gets a list of orders| -|`invoice_list`|Gets a list of invoice| -|`delivery_note_list`|Gets a list of delivery notes| -|`creditmemolist`|Gets a list of credit memos| -|`creditmemodetail`|Gets details about a credit memo| -|`readdeliveryaddress`|Gets data of a delivery address for specified party ID| -|`updatedeliveryaddress`|Updates the ERP data of an existing delivery address| -|`createdeliveryaddress`|Creates a new delivery address for a specified party ID| -|`deletedeliveryaddress`|Deletes a specific delivery address| - -You can find the standard messages in `EshopBundle/Resources/config/messages.yml`. - -Each bundle can extend these messages or define its own messages if required. - -## Configuration - -Make sure you have symlinks in the `app/Resources` folder: - -``` bash -# If the 'Resources' directory does not exist, create it first -mkdir app/Resources - -cd app/Resources/ -ln -s ../../vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase - -# if you have local message, add this symlink as well: -ln -s ../../src//Bundle/CommonBundle/Resources/xsl -``` - -For more information about the ERP message mapping, see [ERP Component: Mapping](erp_components/erp_component_mapping.md). - -## Debugging - -Log messages are logged to the database. A command-line tool offers a feature to check which messages were exchanged with the ERP system. - -This command waits for the next message and displays the request and result as XML: - -??? note "Example message log" - - ``` xml - sudo -u _www php bin/console siso_tools:display_erp_log_command - - 2016-12-14 17:44:16 Start new entry in the Erp Log Table with ID 11441 - Start ** mapping_type ** - responseEnd ** mapping_type ** - Start ** stylesheet ** - app/Resources/xslbase/response.noop.xslEnd ** stylesheet ** - Start ** input_xml ** - - - 0 - - 2016-12-14 - - ...... - End ** input_xml ** - Start ** output_xml ** - - - 0 - - 2016-12-14 - - - - Melanie Bourner - - - 10000 - - - Färberstraße 14 - - - Berlin - 12 - - - - DE - - - - - ... - End ** output_xml ** - End new entry in the Erp Log Table. Date: 2016-12-14 17:44:16 - Previous information of the Measuring Point = "250_mapping" - ``` - -You can search for a specific message using the `--search-text` option: - -``` bash -php bin/console siso_tools:display_erp_log_command --search-text 123456788 -``` - -To dump 100 last messages, use the `--dump-last-messages` option: - -``` bash -php bin/console siso_tools:display_erp_log_command --dump-last-messages 20 > /tmp/erp_messaages.txt -``` - -You can remove messages from the database with the `--delete-messages` option. - -The following example removes messages older than three days: - -``` bash -php bin/console siso_tools:display_erp_log_command --delete-messages 3 -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_mapping.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_mapping.md deleted file mode 100644 index 28b53714be..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_mapping.md +++ /dev/null @@ -1,105 +0,0 @@ -# ERP mapping component [[% include 'snippets/experience_badge.md' %]] - -Mapping is used to transform a specific data format into another. -The abstract class `AbstractMessageMappingService` defines the interface for all mapping services. - -## XsltDocumentMappingService - -`XsltDocumentMappingService` is a mapping service that uses an XSLT processor to perform the mapping. - -XSL files have to be stored in two special directories inside of the `/app/Resources` directory: - -- `xslbase` contains the standard implementation. -- `xsl` contains specific adaptations of selected message mappings. - -The files in these directories follow the naming scheme: `request|response..xsl`. -The first part determines the direction of the communication. -The second part (`messageCode`) is passed to the `map*()` methods and defines which message is being mapped. -`FileLocatorInterface` is used to resolve the XSL files. - -Example `request.productsearch.xsl`: - -``` xml - - - - - - - - - - - - - - - - -``` - -Example configuration: - -``` yaml -silver_erp.config.messages: - search_product_info: - message_class: "Name\\ProjectBundle\\Entity\\Messages\\SearchProductInfoMessage" - response_document_class: "\\oasis\\names\\specification\\ubl\\schema\\xsd\\OrderResponse_2\\OrderResponse" - webservice_operation: "http://webserviceurl" - mapping_identifier: productsearch -``` - -## Multiple values / unbounded cardinality - -Currently, in PHP code, the messages are converted to arrays without considering XSD, -so it is necessary to mark these elements to be converted to an array, even if it occurs only once. -This is done in a special attribute in the target structure. -The attribute's name is `singleElementArrays` and its content must be the respective elements' unqualified, space-separated names. -For example: - -- `` -- `` - -## DefaultValues for undefined attributes - -`DefaultValuesService` can be used to set default values in outgoing or incoming ERP messages. - -Default values are written on runtime into the objects if a specified field does exist and is empty. -It is possible to set any attribute or the value of a field. -If a specified field value/path does not exist, the service tries to build the object hierarchy by reflection -with the help of the `phpDocHeaders` from the UBL classes. - -To set a default value, the complete path for the specified field must be used. For example: - -`BuyerCustomerParty/PostalAddress/Country/IdentificationCode/value: "DE"` - -## Configuration via `default_values.yml` - -Configuration format: - -``` yaml -parameters: - silver_erp.config.default_values.request_mappings: - : - : - - silver_erp.config.default_values.response_mappings: - : - : -``` - -Example: Setting the value of the field `DocumentCurrencyCode` to `EUR` in an incoming `OrderResponse`: - -``` yaml -parameters: - silver_erp.config.default_values.response_mappings: - OrderResponse: - DocumentCurrencyCode/value: "EUR" - BuyerCustomerParty/PostalAddress/Country/IdentificationCode/value: "DE" - Delivery[]/DeliveryAddress/Country/value: "DE" -``` - -### NoopDocumentMappingService - -`NoopDocumentMappingService` is a mapping service that does no mapping at all. -It can be used if a dependency to an `AbstractDocumentMappingService` is required but no mapping is necessary nor available. diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_component_messages.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_component_messages.md deleted file mode 100644 index 4de6151f49..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_component_messages.md +++ /dev/null @@ -1,136 +0,0 @@ -# ERP message component [[% include 'snippets/experience_badge.md' %]] - -Communication uses specific messages classes that derive from `AbstractMessage`. -The abstract base class defines following properties: - -``` php -/** - * Object for requests, generated from UBL definitions - * - * @var object - */ -private $requestDocument; - -/** - * Object for responses, generated from UBL definitions - * - * @var object - */ -private $responseDocument; - -/** - * @var int - */ -private $responseStatus = self::RESPONSE_STATUS_NOT_SENT; - -/** - * @var string - */ -private $responseError = ''; -``` - -These properties are publicly accessible by the respective getter and setter methods. -Since the properties could be changed anywhere, it is necessary to validate the value of the getter methods before further accessing (e.g. access to supposed members of a response object). - -Derived and non-abstract message classes can provide methods for data feeding into the requests and response members -or retrieving specific data from them (e.g. address records in customer data messages). -But the classes may also just be empty stubs and leave data handling to other parts of the application. - -The response status is set by the transport service according to the result of the ERP communication. -Following statuses are defined: - -``` php -/** - * The message was not sent, yet. This status is the initial value after - * instantiation. - */ -const RESPONSE_STATUS_NOT_SENT = -1; - -/** - * The message was successfully sent and a valid response was received. - */ -const RESPONSE_STATUS_OK = 0; - -/** - * The message was processed by the remote service, but the ERP responded - * with an error. - */ -const RESPONSE_STATUS_ERP_ERROR = 11; - -/** - * The message was processed by the remote service, but the ERP was busy - * and the request timed out. - */ -const RESPONSE_STATUS_ERP_TIMEOUT = 12; - -/** - * The message was processed by the remote service, but the service itself - * caused an error or the ERP was not reachable. - */ -const RESPONSE_STATUS_SERVICE_ERROR = 21; - -/** - * The message could not be sent to the remote service. - */ -const RESPONSE_STATUS_CONNECTION_ERROR = 22; -``` - -The implementation of the [transport service](../erp_component_transport.md) is responsible for the evaluation of the result of the communication and must set the response status accordingly. - -## Documents - -Message documents, as been seen in above, should be plain PHP objects. -These classes are intended to store the actual structured data which is mentioned above. -Currently all document classes are created mechanically by XSD2PHP. -This tool creates PHP classes out of XML Schema Definitions, -which may provide the opportunity to validate created data against that definitions. -The created classes only define public member attributes, so you can access data with: -`$object->memberVar->subMember->value = 'value';` - -## Instantiation of message classes - -See [ERP message instantiation](erp_message_instantiation.md) for more details. - -## Creating a new listener - -Create a new class and add a method, that accepts an `InquireMessageEvent` event: - -``` php -class ExampleMessageFactoryListener { - - const EXAMPLEREQUEST = 'ExampleRequestMessage'; - - public function onMessageInquired(InquireMessageEvent $event) - { - $messageIdentifier = $event->getMessageIdentifier(); - $messageObject = null; - - if ($messageIdentifier === self::EXAMPLEREQUEST) { - $messageObject = new ExampleMessage(); - } - - if ($messageObject != null) { - $event->setMessage($messageObject); - $event->stopPropagation(); - } - } -} -``` - -Register the new listener to the inquire message event: - -``` yaml -services: - example.message_inquiry_listener: - class: "\Example\MessageInquiryListener" - tags: - - {name: kernel.event_listener, event: silver_erp.inquire_message, method: onMessageInquired} -``` - -Event listeners are defined as tagged services. The `name` and the `event` parameter of the tag must be `kernel.event_listener` and `silver_erp.inquire_message` respectively. -The `method` parameter must be the name of the method of the specified class which should be called if the event is dispatched. -This method must define a parameter of type `InquireMessageEvent` in order to retrieve the event object. - -## Class model - -![](../../../../img/class_model.png) diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md deleted file mode 100644 index 002b425773..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md +++ /dev/null @@ -1,212 +0,0 @@ -# ERP Message: CalculateSalesOrder / CreateSalesOrder [[% include 'snippets/experience_badge.md' %]] - -!!! note - - Due to technical restrictions of the message generator, the messages `CalculateSalesOrder` and `CreateSalesOrder` are identical (except for the name). Changes in one specification XML file would cause the generator to change the PHP files of the other message, too. If one specification is changed, the other file must be changed, too. - -`CalculateSalesOrder` fetches prices which are calculated by the ERP system depending on the request data. -Request data covers the customer number, the item numbers with quantities and additional data like coupon codes or a campaign. -In the standard implementation it only sends customer number and item data. - -`CreateSalesOrder` submits an order to the ERP system. -If the order can be processed successfully, the ERP responds with a valid number in the `SalesOrderID` element. - -The optional parameter `variantCode` in line data is mapped to the UBL field `ExtendedID`. - -## Request XML - -``` xml - - - - EUR - - 2005-06-20 - sample - - - - 1234 - - - 10000 - - - - - - - - - - - - - - - - 2005-06-30 - 18:00:00.0Z - - - - - 31 - 2007-01-01 - IBAN - A12345 - - - IS000001261234560101901239 - EUR - - SEISISRE - Central bank of Iceland - - - - - - - - - - - 3WEEKS - - - Freetext note on line 1 - Freetext note on line 2 - - - - - - -``` - -Elements: - -- `UUID` - the GUID for the order document -- `SalesOrderID` - reserved for the ERP's order ID. It is normally not sent in the request. -- `DocumentCurrencyCode` - the desired currency. -- `BuyerCustomerParty`/`Party`/`PartyIdentification`/`ID` - although PartyIdentification's cardinality is unbounded, it is used only once and defines the requested customer number. - -## Response XML - -``` xml - - - - - - 2013-06-17 - - - - - - - - - - - - - - - - - 1262.50 - - - 1262.50 - 6312.50 - 6312.50 - - -``` - -Elements: - -- The parties should be the same as in [SelectCustomer](erp_message_select_customer.md) -- `IssueDate`- only used in the `CreateSalesOrder` message -- `DocumentCurrencyCode` - defines the desired currency. -- `Delivery`/`DeliveryAddress` - the same as in [SelectCustomer](erp_message_select_customer.md) -- `LegalMonetaryTotal` - contains information about summarized prices - - `PayableAmount` - is a required field in the UBL standard - -### Reusable Party element - -See [Reusable Party element](erp_message_select_customer.md#reusable-party-element) - -### Reusable LineItem element - -``` xml - - - 10000 - 1 - - - - - - - - - - 0 - - - - - true|false - - - - - - 2950 - 1 - - - Web-Connector - für SAP, Magento oder NAV - - 1000 - 123 - - - ABC - - - ABC - - - - - - - - - -``` - -Elements: - -- `ID` - line ID, assigned by the customer. It is rarely used. Possible usage is if the shop system assigns a line ID which differs from the ERP system. The field is mandatory, even if it is empty. -- `SalesOrderID` - seller ID for the line record. May be set by the ERP system. -- `Quantity` - requested number of items. -- `Delivery`/`MaximumQuantity` - stores the stock info -- `Delivery`/`LatestDeliveryDate` - stores the availability. Must be an ISO date string. -- `Price`/`PriceAmount` - unit list price -- `Item` - item catalog information. There are more item identification fields in this record (besides `SellersItemIdentification`). -- `SesExtension` - - `OnStock` - boolean 1=Yes, 0=no - - `StockNumeric` - the number of units on stock - - `VatCode` - string that identifies tax class - - `PriceIsIncVat` - flag which indicates whether `PriceAmount` includes VAT. - - beyond that `SesExtension` can contain additional data from the ERP diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_class_generator.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_class_generator.md deleted file mode 100644 index 8865a2a1bf..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_class_generator.md +++ /dev/null @@ -1,273 +0,0 @@ -# ERP Message-Class-Generator [[% include 'snippets/experience_badge.md' %]] - -## XML to PHP generator - -The XML to PHP generator is used to create PHP objects out of sample XML files. - -This component: - -- Reads a pair of custom XML documents of a specified ERP message (request and response) -- Generates PHP objects with a constructor which initializes all members -- Generates a message class which contains a reference to the request and response POPO -- Generates a factory class for the generated message class -- Generates all necessary configuration files which may be imported or copied into the projects configuration - -"Message" refers to a unit of a request and response document. -"Document" is a single XML document for ERP communication. One document may be sent to the ERP and another document may be received from the ERP. - -Document example: - -``` xml - - - - 1234 - Example & Co. -
    - Peter - Smith - Long Street 12 - 12345 - Big City - Random County -
    -
    -
    -``` - -## Usage - -### XML format (necessary additional attributes) - -For the automated generation of PHP classes, additional information in the XML is necessary. -The XML can be extended with the following attributes: - -#### `ses_type` - -Required for elements which are defined in external XML files and provide standard elements. - -If an element value is a complex type which is intended to be reused in several messages (e.g. a business party record) it must be defined in this attribute. -The value represents a space-separated list of child element names that are supposed to be of the reusable type. The name of the elements is also the name of the applicable classes. - -The names of the types should be prefixed with either `ses:` or `cust:` - -- `ses:` type classes are stored in the [[= product_name_exp =]] path. XML definition files are always read from the [[= product_name_exp =]] path. -- `cust:` type classes are stored in the defined target directory. XML definition files are always read from the source path. - -For each type an XML file must be stored in the `src` folder which defines the reusable datatype which is named .xml - -``` xml -Example (request.selectContact.xml): - - - - - -``` - -Example (`Party.xml`): - -``` xml - - - -
    - - - - - - -
    -
    -``` - -#### `ses_unbounded` - -Not required, but must be set for elements that may occur more than once. - -Contains a (space-separated) list of sub-element names, that have `maxOccurence="unbounded"` in XSD. These elements must always be arrays in PHP classes/objects. - -Example (`Party.xml`): - -``` xml - - - -
    - - - - - - -
    -
    -``` - -#### `ses_tree` - -Not required, but must be set for XML elements that should be serialized and deserialized without being defined by the generated PHP classes. - -This is a space-separated list of elements which are intended to contain arbitrary tree data. It is mainly used for the SesExtension field. Elements defined in this attribute are considered by serialization processes to contain any XML tree structure, which is directly converted into an PHP, accordingly. - -Example (`Party.xml`): - -``` xml - - - -
    - - - - - - -
    - - - -
    -``` - -### `ses_desc` - -Optional but recommended. - -Used for documentation purposes, for example to document possible value-formats of a field. - -This value is written in the PHP docHeader of the member. (Not implemented, yet) - -Example: - -``` xml -2013-01-11 -``` - -### Usage of the generator script - -Workflow of the message generation: - -1\. Create request and response XML files - -The XML content should be available in a concept documentation in form of example XML or at least as a common structured data description. - -- The file names must fit to the following scheme: `request|response..xml` whereas the content of `` will be the base name for the generated files. -- You must define one file for each: request and response. -- The files must contain well-formed XML code. - -Examples: - -`request.party.xml`: - -``` xml - - - - - -``` - -`Party.xml`: - -``` xml - - - - -
    - - - - - - -
    -
    -``` - -`response.party.xml`: - -``` xml - - - - -``` - -2\. Extend the XML according to the format described above, if necessary. - -3\. Upload the XML documents to the resources folder. - -For standard messages: - -- `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xml/request.party.xml` -- `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xml/response.party.xml` - -For non standard messages - -- `src/Example/Bundle/ExampleBundle/Resources/xml/request.party.xml` -- `src/Example/Bundle/ExampleBundle/Resources/xml/response.party.xml` - -4\. Start the generation (check for any error messages): - -Standard schema for command line: - -``` -php bin/console silversolutions:generatemessages --message --sourceDir --targetDir -``` - -Example: - -``` -php bin/console silversolutions:generatemessages --message party --sourceDir src/Example/Bundle/ExampleBundle/Resources/xml --targetDir src/Example/Bundle/ExampleBundle -``` - -The target and source directories must exist. The target path must contain an `src` directory. All directories beneath `src` are considered as namespaces according to PSR-0. -The target path should be a Symfony bundle because all created sub-directories, like `Resources`, are intended to be used in a Symfony bundle. - -Using the `--force` parameter overwrites all existing files. Use this parameter with caution as it also overwrites standard classes if used in the defined message. - -The script now generates all necessary files into the following directories. Depending of the type of the class, `` can be `SilversolutionsEshopBundle` (for `ses_standard` classes) or the defined target directory (in this example `src/Example/Bundle/ExampleBundle`): - -- `/Services/Factory/PartyFactoryListener.php` -- `/Resources/config/partyfactorylistener.service.yml` -- `/Resources/config/partymessage.message.yml` - -`/Resources/config/` stores configuration that must be included into the Symfony application in order to activate the generated message factory. - --`/Entities/Messages/Document/PartyMessage.php` - -### Creating and working with messages - -After creating messages via `MessageInquiryService`, you can change the values of a document. - -For example, the following code sends an RMA message: - -``` php -get('service_id'); - -/** @var \Silversolutions\Bundle\EshopBundle\Services\MessageInquiryService $inqService */ -$inqService = $this->get('silver_erp.message_inquiry_service'); -$message = $inqService->inquireMessage(PartyFactoryListener::PARTY); -$request = $message->getRequestDocument(); -$request->Id->value = '23456'; -$request->Name->value = 'Example & Co.'; -$request->Address[] = new \Example\Bundle\ExampleBundle\Entities\Messages\Document\Address(); -$request->Address[0]->FirstName->value = 'Peter'; -$request->Address[0]->LastName->value = 'Smith'; -// ... set further document values here ... - -/** @var \Silversolutions\Bundle\EshopBundle\Services\Transport\AbstractMessageTransport $transport */ -$transport = $this->get('silver_erp.message_transport'); -$response = $transport->sendMessage($message)->getResponseDocument(); -$status = $response->Status->value; -// ... further work with the response via $response -``` - -- The converter is implemented within the `SilversolutionsEshopBundle` (`Command/GenerateMessagesCommand.php` and `Generator/XmlToPhpGenerator.php`) -- The target directory for standard elements / messages is `SilversolutionsEshopBundle`, it is not configurable. -- The generated PHP classes and configurations are not validated automatically. This should be done manually after the generation. diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_instantiation.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_instantiation.md deleted file mode 100644 index 69164d8844..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_instantiation.md +++ /dev/null @@ -1,41 +0,0 @@ -# ERP Message Instantiation [[% include 'snippets/experience_badge.md' %]] - -Message creation uses the mechanics of the [Symfony event dispatcher system.](https://symfony.com/doc/3.4/components/event_dispatcher.html) - -When a new message is required, a specific service delivers a message object of specific type (e.g. `calculate_sales_order`). -[[= product_name_exp =]] then dispatches an `InquireMessageEvent` event within that service, -containing the requested message type and a reference to an `AbstractMessage` object. -Specific listeners which can create messages can register/subscribe to that event and check if they can create that type of message. -In Symfony you can register new listeners for events as services via configuration. -This enables a flexible way to add new custom messages and their factories per module -and the possibility to overwrite the creation of certain messages (by overriding the service definition). - -Example message creation workflow: - -1. A catalog data provider needs to create a new message. It calls `MessageInquiryService::inquireMessage('MessageIdentifer')`. The service should be injected as a dependency and not instantiated directly. -1. The new event object is instantiated with the respective message identifier attribute (`MessageIdentifier`) and a null reference to its `AbstractMessage` attribute. -1. The event is dispatched by `EventDispatcher`. -1. All registered listeners are called with the event object. -1. Every listener checks whether it is responsible for the respective message type, and: - - If it is responsible, it creates the message, appends the message object to the event object and stops further event propagation via `$eventstopPropagation()` - - If it is not responsible, it does nothing -1. After the event processing is done, `MessageInquiryService` checks if the message was created, and: - - If created, it returns the message object - - If not created, it throws `NoMessageFactoryAvailableException`. - -To get a message instance in a Symfony controller class, use the following code: - -``` php -$messageInquiry = $this->get('silver_erp.message_inquiry_service'); -try { - $exampleRequestMessage = $messageInquiry->inquireMessage( - ExampleMessageFactoryListener::EXAMPLEREQUEST - ); -} catch (NoMessageFactoryAvailableException $e) { - // Do exception handling -} -``` - -## Model - -![](../../../../img/message_instatiation_model.png) diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_invoice_detail.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_invoice_detail.md deleted file mode 100644 index 88e4369c93..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_invoice_detail.md +++ /dev/null @@ -1,132 +0,0 @@ -# ERP Message: InvoiceDetail [[% include 'snippets/experience_badge.md' %]] - -`InvoiceDetail` fetches the detailed data for an existing invoice note. - -## Request XML - -``` xml - - - 10000 - -``` - -`InvoiceID` is the identification number of the requested invoice note - -## Response XML - -``` xml - - - - - - SalesInvoice - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -Elements: - -- `ID` - The requested document identification number -- `IssueDate` - The creation date of the document -- `OrderReference/ID` - The ID of the order related to the invoice note -- `OrderReference/SalesOrderID` - Currently the same as ID -- `OrderReference/IssueDate` - The creation date of the related order -- `AccountingCustomerParty` - Contains the party record for the business party the invoice is destined to (bill to) -- `BuyerCustomerParty` - Contains the party record for the business party which is the actual buyer (sell to) -- `Delivery` - Contains the delivery address information - - `SesExtension/ShipmentMethod` - A code for shipment method (e.g. DHL Standard) - - `SesExtension/ShippingAgentCode` - A code for the carrier who takes care of the transportation -- `PaymentMeans/PaymentDueDate` - The date until the invoice has to be settled -- `PaymentMeans/PayeeFinancialAccount/CurrencyCode` - The currency code for the invoice note -- `PaymentTerms` - Contains text about the terms of the payment -- `TaxTotal/TaxAmount` - The total amount of taxes for the invoice -- `LegalMonetaryTotal/TaxExclusiveAmount` - The total amount free of all taxes (net) -- `LegalMonetaryTotal/TaxInclusiveAmount` - The total amount including all taxes (gross) -- `LegalMonetaryTotal/PayableAmount` - The amount which has to be paid (net or gross depends on setup) -- `InvoiceLine` - May occur more than once. Contains information about the ordered articles - - `Item/Description` - Description of the ordered item - - `Item/Name` - Currently the same as Description - - `Item/BuyersItemIdentification/ID` - Currently the same as the `SellersItemIdentification`. May be used if customers have their own identifications for items. - - `Item/SellersItemIdentification/ID` - Item identification number. - - `Price/PriceAmount` - The base price of the item - - `Price/BaseQuantity` - The ordered quantity - - `SesExtension/Amount` - The amount of this invoice line (may be net or gross) - - `SesExtension/TaxInclusiveAmount` - The line amount inclusive taxes (gross) - - `SesExtension/UnitOfMeasureCode` - A code determining the unit of measure (e.g. pieces) - - `SesExtension/ShipmentDate` - The day when the order was shipped - -### Reusable Party element - -See [Reusable Party element](erp_message_select_customer.md#reusable-party-element) diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_select_customer.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_select_customer.md deleted file mode 100644 index 670a2327c0..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_select_customer.md +++ /dev/null @@ -1,153 +0,0 @@ -# ERP Message: SelectCustomer [[% include 'snippets/experience_badge.md' %]] - -`SelectCustomer` fetches customer information, like all types of addresses etc., from the ERP system. -It is stored in the session afterwards to avoid duplicate calls to the ERP. - -## Request XML - -``` xml - - - - - 10000 - - - -``` - -## Response XML - -``` xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -Elements: - -- `BuyerCustomerParty` - The party record which contains all information about the person / organization, which buys items. -- `SellerSupplierParty` - The party record which contains all information about the person / organization, which sells items. -- `AccountingCustomerParty` - The party record which contains all information about the person / organization, which is intended to receive and settle the invoice documents. -- `OriginatorCustomerParty` - Additonal customer party which may be necessary if two parties are included in an order process. For example, if a different department in an organization handles the purchase of items that another departments originally requested. -- `Delivery` - Records that contain delivery information (including delivery party and address). - -### Reusable Party element - -``` xml - - - 10000 - - - Möbel-Meller KG - - - Tischlerstr. 4-10 - - 4-10 - Berlin - 12555 - Berlin - BER - - Gartenhaus - - - DE - Deutschland - - Development - - - - - - Frank - Dege - Herr - - - - - -``` - -Elements: - -- `PartyIdentification` - Although this element could occur several times (with different scheme codes), in this case it is only used once for a single party ID string. -- `PostalAddress` - Contains all address information. There are more fields. - - `AddressLine`/`Line` - Can be used to define additional lines which are not defined in particular. - -### Reusable Contact element - -``` xml - - KT1001 - Mr Fred Churchill - +44 127 2653214 - +44 127 2653215 - fred@iytcorporation.gov.uk - - - - - - - -``` - -### Reusable DeliveryAddress element - -This element is currently not used in any standard message. - -``` xml - - - - Tischlerstr. 4-10 - - - Möbel-Meller KG - - - - - Düsseldorf - 48436 - - - - - - - - - -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_selectcontact.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_selectcontact.md deleted file mode 100644 index b9484bcba5..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_selectcontact.md +++ /dev/null @@ -1,46 +0,0 @@ -# ERP Message: SelectContact [[% include 'snippets/experience_badge.md' %]] - -`SelectContact` fetches contact information, like all types of contact related data, from the ERP system or CRM system. - -## Request XML - -``` xml - - - - - 10000 - - - -``` - -## Response XML - -``` xml - - - - - - - -``` - -### Reusable Contact element - -``` xml - - KT1001 - Mr Fred Churchill - +44 127 2653214 - +44 127 2653215 - fred@iytcorporation.gov.uk - - - - - - - -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_updatecustomer.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_updatecustomer.md deleted file mode 100644 index 51402b22ca..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_updatecustomer.md +++ /dev/null @@ -1,128 +0,0 @@ -# ERP Message: UpdateCustomer [[% include 'snippets/experience_badge.md' %]] - -`UpdateCustomer` sends customer information, such as addresses, to the ERP system. - -Customer update message identifier for service: - -``` php -$this->container->get('silver_erp.message_inquiry_service')->inquireMessage(UpdateCustomerFactoryListener::UPDATECUSTOMER); -``` - -## Request XML - -``` xml - - - - -``` - -## Response XML - -``` xml - - - - -``` - -### Reusable Party element - -See [Reusable Party element](erp_message_select_customer.md#reusable-party-element) for more information. - -## Mapping - -### Request XSL - -``` xml - - - - UPDATECUSTOMER - HOME - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - -
    -
    -
    -``` - -### Response XSL - -``` xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <xsl:value-of select="SALUTATION"/> - - - - - - - - - - -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_messages_addresses.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_messages_addresses.md deleted file mode 100644 index 1f67afe7fc..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_messages_addresses.md +++ /dev/null @@ -1,130 +0,0 @@ -# ERP Messages: addresses [[% include 'snippets/experience_badge.md' %]] - -`ReadDeliveryAddress`, `UpdateDeliveryAddress`, `CreateDeliveryAddress`, `DeleteDeliveryAddress` -are messages for delivery or shipping addresses. -All those messages have a similar structure. For Read and Delete, the ERP system expects only the `PartyIdentification` in the request. -The Delete response only contains a status response as text. - -## ReadDeliveryAddress - -See [Reusable DeliveryParty element](#reusable-deliveryparty-element) - -### Request - -``` xml - - - -``` - -### Response - -``` xml - - - -``` - -## UpdateDeliveryAddress - -See [Reusable DeliveryParty element](#reusable-deliveryparty-element) - -### Request - -``` xml - - - -``` - -### Response - -``` xml - - - -``` - -## CreateDeliveryAddress - -See [Reusable DeliveryParty element](#reusable-deliveryparty-element) - -### Request - -``` xml - - - -``` - -### Response - -``` xml - - - -``` - -## DeleteDeliveryAddress - -See [Reusable DeliveryParty element](#reusable-deliveryparty-element) - -### Request - -``` xml - - - -``` - -### Response - -``` xml - - - -``` - -## Reusable DeliveryParty element - -``` xml - - - - 10000 - - - Möbel-Meller KG - - - Tischlerstr. 4-10 - - 4-10 - Berlin - 12555 - Berlin - BER - - Gartenhaus - - - DE - Deutschland - - Development - - - - - - Frank - Dege - Herr - - - - - -``` - -See [Reusable Contact element](erp_message_select_customer.md#reusable-contact-element) diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_service.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_service.md deleted file mode 100644 index c62c509e14..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_service.md +++ /dev/null @@ -1,107 +0,0 @@ -# ERP service component [[% include 'snippets/experience_badge.md' %]] - -To provide a simple interface to the ERP communication, the `AbstractErpService` abstract class is defined. -This class is derived from Symfony service classes. -For example the `WebConnectorErpService` implements this abstract class for the communication to the Web.Connector. - -For every basic request to the ERP one class methods exists (e.g. for price calculation of some products). Currently this includes: - -``` php -public function calculatePrices(PriceRequest $priceRequest); -public function submitOrder(Basket $basket, array $params = array()); -public function selectCustomer($customerNumber, $maxCount = 1, array $params = array()); -public function selectContact($customerNumber, $contactNumber = '', array $params = array()); -``` - -## Service method implementation - -An implementation for the `selectCustomer()` method can look like the following: - -``` php -class ExampleErpService extends AbstractErpService -{ - /** - * @var Silversolutions\Bundle\EshopBundle\Services\MessageInquiryService - */ - protected $messageInquiry; - /** - * @var Silversolutions\Bundle\EshopBundle\Services\Transport\AbstractMessageTransport - */ - protected $transport; - - // {...} - - public function selectCustomer($customerNumber, $maxCount = 1, array $params = array()) - { -``` - -First you invoke the message factory to create an instance of the ERP message you want to send. - -``` php - // try to get message instance - try { - /** @var SelectCustomerMessage $selectCustomerMessage */ - $selectCustomerMessage = $this->messageInquiry->inquireMessage( - StandardMessageFactoryListener::SELECTCUSTOMER - ); - } catch (MessageInquiryException $messageException) { - // {Do some logging or appropriate exception handling} - return null; - } -``` - -Then you have to feed the data which was given to the method into the received message object. - -``` php - // initialize request values and send message - if (!$selectCustomerMessage instanceof SelectCustomerMessage) { - $context = array('message' => $selectCustomerMessage); - // {Do some logging or appropriate exception handling} - return null; - } - $selectCustomerMessage->setCustomerNumber($customerNumber); - $selectCustomerMessage->setMaxCount($maxCount); -``` - -Next you have to get an instance of the transport service and give the message instance to the `sendMessage()` method. -It returns the same instance which you passed as argument, but if is successful, that instance holds the response now. - -``` php - try { - $response = $this->transport->sendMessage($selectCustomerMessage)->getResponseDocument(); - if (!$response instanceof OrderResponse) { - $context = array('response' => $response); - // {Do some logging or appropriate exception handling} - return null; - } - } catch (\RuntimeException $rtException) { - // {Do some logging or appropriate exception handling} - return null; - } -``` - -Now you can return the response of the message directly or prepare the response that is defined by this service method. - -``` php - return $response; - } -``` - -## Symfony service configuration - -If you reimplement the abstract service, you have to register your class as a service: - -``` yaml -services: - silver_erp.facade: - class: "\Example\Namespace\ExampleErpService" - arguments: ["@silver_erp.message_inquiry_service", "@silver_erp.message_transport"] -``` - -The service ID has to be `silver_erp.facade`. That way this implementation is automatically used when the ERP service is injected as a dependency. -The arguments in the example above are another two services of the ERP bundle. -These are injected as dependency of the example service class. -Since the message inquiry service and the transport service are necessary for all ERP communication, -these two dependencies are the least of all ERP service implementations. - -This redefines the default service configuration. Make sure that your configuration has a higher priority than the ERP bundle in your Symfony project setup. diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_transport.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_component_transport.md deleted file mode 100644 index 5ca0617937..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_component_transport.md +++ /dev/null @@ -1,169 +0,0 @@ -# ERP transport component [[% include 'snippets/experience_badge.md' %]] - -The transport component provides an interface to the physical transportation of the messages. -This includes the serialization of the data into a stream that the physical transportation is capable to process. -Because serialized data is the most abstract form of data (generally a string), -the mapping invocation is also placed in this component. -For more information about mapping itself, see [ERP mapping component](erp_component_mapping.md). - -## Abstract message transport class - -All transport classes must derive from `AbstractMessageTransport`. -This class provides a public but final `sendMessage()` method that defines a process that all transport classes must provide. -It calls an abstract protected `internalSendMessage()` method which has to do the actual communication and message processing. -Furthermore, it checks a semaphore service which indicates whether the remote ERP service is available -and sets that semaphore afterwards if an error occurred during communication. - -The overall process is: - -1. Dispatch a `MessageRequestEvent` which enables listeners to change the request before it is sent. -1. Do the actual message transportation to the remote service: - 1. Check the semaphore service if communication is not blocked. `ErpDisabledException` is thrown and caught if it is blocked. - 1. Call `internalSendMessage()` - 1. If `ErpUnavailableException` is thrown by `internalSendMessage()`, the semaphore is set to blocked. -1. Dispatch a `MessageResponseEvent` which enables listeners to change the response before it is passed back to the standard business logic. `MessageExceptionEvent` is dispatched instead if an error occurrs. - -### Events - -There are two events defined which are dispatched during the transport: - -- `\Silversolutions\Bundle\EshopBundle\Message\Event\TransportMessageEvents::REQUEST_MESSAGE = 'silver_eshop.request_message'` - the `silver_eshop.request_message` event is dispatched when a message is about to be sent by the transport layer (before a potential mapping). -The event listener receives an instance of `Silversolutions\Bundle\EshopBundle\Message\Event\MessageRequestEvent`. -- `\Silversolutions\Bundle\EshopBundle\Message\Event\TransportMessageEvents::RESPONSE_MESSAGE = 'silver_eshop.response_message'` - the `silver_eshop.response_message` event is dispatched when a message was received and unserialized by the transport layer (after a potential mapping). -The event listener receives an instance of `Silversolutions\Bundle\EshopBundle\Message\Event\MessageResponseEvent`. -- `\Silversolutions\Bundle\EshopBundle\Message\Event\TransportMessageEvents::RESPONSE_MESSAGE = 'silver_eshop.exception_message'` - this `silver_eshop.exception_message` event is dispatched when an exception occurs while sending the message. -The event listener receives an instance of `Silversolutions\Bundle\EshopBundle\Message\Event\MessageExceptionEvent`. - -### Test the transport channel - -AbstractErpTransport has an additional method: `testCommunication()`. - -This method is intended to invoke an actual request by using the transport and return the status of the response in form of a boolean value (`true` = success; `false` = failure) - -Parameters: - -|Type|Definition|Description| -|--- |--- |--- | -|string|`& $errorText = ''`|This optional parameter gets a variable per reference to which an error text is written (if any occurs).| - -## ERP semaphore service - -The `Silversolutions\Bundle\EshopBundle\Services\ErpSemaphoreServiceInterface` interface indicates the status of availability of the remote ERP system. This interface provides two methods. - -### testAvailability() - -This method is intended to check the status of the semaphore. -If the semaphore is locked, `Silversolutions\Bundle\EshopBundle\Exceptions\ErpDisabledException` is thrown, otherwise nothing happens. -The semaphore is locked if `setUnavailable()` was invoked before and the timeout hasn't expired. - -### setUnavailable() - -This method is intended to set the status of the semaphore to locked and set/reset the timeout for that status. -The timeout has to be set the service implementation somehow. Injection is recommended. - -### StashErpSemaphoreService - -`StashErpSemaphoreService` (`Silversolutions\Bundle\EshopBundle\Services\StashErpSemaphoreService`) -uses the Symfony Stash API to persist the status of the semaphore between requests. -It is configured with the following parameters: - -- `siso_erp.erp_semaphore.stash_item_id` - A string which identifies the semaphore within the stash pool. -- `siso_erp.erp_semaphore.max_lock_time` - An integer which defines the semaphore's timeout in seconds. - -This service has a dependency to `Stash\Interfaces\PoolInterface` injected into the constructor. - -### FileErpSemaphoreService - -`FileErpSemaphoreService` (`Silversolutions\Bundle\EshopBundle\Services\FileErpSemaphoreService`) -uses simple file system functions for persisting the status of the semaphore between requests. -It is configured with the following parameters: - -- `siso_erp.erp_semaphore.lock_file` - defines the path to the file which should be used to determine the status of the semaphore. -- `siso_erp.erp_semaphore.max_lock_time` - an integer which defines the semaphore's timeout in seconds. - -!!! note - - This implementation might not be safe for cluster configurations. - -## Web.Connector transport - -This class is the transport implementation that communicates with the Web.Connector. - -Currently only the SOAP RPC mode is implemented. - -Serializing: - -For serializing [Symfony's serializer API](https://github.com/symfony/Serializer) is used. -For this a new (un-)serializer was created: DomXmlEncoder. -This class leverages a fork of the XSD2PHP library and can transform the document class instances automatically to XML code and backwards. -During deserialization, fields that are not defined in the destination response class are ignored -and it is logged using the logging dependency of the transport. - -Mapping: - -Additionally, if an instance of `AbstractDocumentMappingService` is injected by the method `setMappingService()`, -the normalized array of the request and response are passed to this mapping service. -The subclass of the mapper must be able to handle array data. -For example, the `XsltDocumentMappingService` may be used. If no service is injected, no mapping is done at all. - -Injecting configuration: - -All configuration must be injected using the respective setter methods. -These method calls are already defined in the service configuration of the EshopBundle. -All default values are defined in `webconnector.yml` and may or must be overwritten in the project configuration. - -### Configuration parameters (webconnector.yml) - -Example: - -``` yaml -silver_erp.config.web_connector.service_location: "http://webconnector.example.com:81/webconnector/webconnector_opentrans.php5" -silver_erp.config.web_connector.service_uri: "http://www.silversolutions.de" -silver_erp.config.web_connector.default_parameters: - user: serviceUser - password: $secret! - erp_parameters: - timeout: 10000 -``` - -#### `silver_erp.config.web_connector.service_location` - -This is a string field. It defines a URL which directs the Web.Connector's web-service. - -#### `silver_erp.config.web_connector.service_uri` - -This is a string field. - -#### `silver_erp.config.web_connector.default_parameters` - -This is an array map field. All ERP operations of the Web.Connector have the same parameter signature. -The last parameter is the given message data. The others can be configured in this configuration parameter: - -- `user`: Web.Connector authentication user name. -- `password`: Web.Connector authentication user password. -- `erp_parameters`: another array map defining additional parameters. - -### Message configuration parameters (messages.yml) - -Example: - -``` yaml -silver_erp.config.messages: - example_message: - message_class: "Namespace\\ExampleMessage" - response_document_class: "Namespace\\DocumentClass" - webservice_operation: SV_OPENTRANS_CALCULATE_PRICE - mapping_identifier: example - another_message: ... -``` - -#### `silver_erp.config.messages` - -This is an array map field. It contains several keys which themselves contain records for the respective message configuration. -The keys do not have any functional meaning, they only offer a distinction of the messages in the configuration itself. - -- `message_class`: The fully-qualified class name of the message (instance of `AbstractMessage`) -- `response_document_class`: The fully-qualified class name of the response document. This is used to instantiate the response in the transport before the raw data is fed into the instance. -- `webservice_operation`: The name of the SOAP web-service operation for this message. -- `mapping_identifier`: The base name of the mapping, if any is set up. -- `debug`: When set, the message's request and response data is written to the log file shortly before and after SOAP call. diff --git a/docs/guide/erp_integration/erp_communication/erp_components/erp_components.md b/docs/guide/erp_integration/erp_communication/erp_components/erp_components.md deleted file mode 100644 index cb57b6166a..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_components/erp_components.md +++ /dev/null @@ -1,26 +0,0 @@ -# ERP components [[% include 'snippets/experience_badge.md' %]] - -## Components of the ERP module - -The ERP module consists of four main components. -These components are not reflected by the namespace since most of the implementation is bundled in Symfony services -and located within the namespace `Services`. - -- Service - The bundle provides a simple one-class API to the standard methods. -Although this is also a Symfony service, it does not mean that all Symfony services of this bundle are placed within that component. -- Messages - This component covers all classes that handle with the messages which are used to communicate with the ERP system. -It includes an event-observer pattern which provides a hook into the message creation and data feeding. -- Transport - The transport component is responsible for the serialization and network communication to the remote system. -- Mapping - The mapping API is used to transform a specific message format into another. -This may be necessary if the remote system expects a different data structure than the one which is used in the [[= product_name_exp =]]. - -The general process looks like this: - -1. Invocation of one of the ERP service methods. The service method accepts all the data that is necessary for the ERP request and does the complete process of combining all following steps that are necessary for the communication. -1. The message component creates the respective message object which is then fed with the data of the request. During this step an event is dispatched to registered observers which may add change or remove data from the message object. -1. The message object is passed to the transport component, which: - 1. serializes the data, - 1. optionally maps the serialized structure into the targeted format, - 1. sends it to the ERP remote system, which may be a web-service, specifically the Web.Connector, in the most cases. -1. After the remote system responds, the steps are performed in reverse order back to the ERP service method. -1. The service method returns the response in the way defined by the method, for example a specific class instance. diff --git a/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/adapt_the_mappings_for_erp_functions.md b/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/adapt_the_mappings_for_erp_functions.md deleted file mode 100644 index 3c65ea64b3..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/adapt_the_mappings_for_erp_functions.md +++ /dev/null @@ -1,38 +0,0 @@ -# Adapt the mappings for ERP functions [[% include 'snippets/experience_badge.md' %]] - -[[= product_name_exp =]] uses a flexible way to map the request and response between the shop and the ERP system. - -By default [[= product_name_exp =]] comes with a prepared mapping stored in the vendor bundle. -The mappings can be extended in your project bundles. - -Your bundle has to be registered as a mapping bundle: - -``` yaml -siso_erp.default.mapping_bundles: - - 'MyProjectBundle' - - 'SilversolutionsEshopBundle' -``` - -For each ERP message a mapping can be defined. In the following example the mapping identifier is `createorder`: - -``` yaml -siso_erp.default.message_settings.createsalesorder: - message_class: "Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\CreateSalesOrderMessage" - response_document_class: "\\Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\Document\\OrderResponse" - webservice_operation: "SV_OPENTRANS_CREATE_ORDER" - mapping_identifier: "createorder" -``` - -The mapping files are using xslt and are located in `EshopBundle/Resources/mapping/wc3-nav/xsl`. -The folder `wc3-nav` can be configured in order to support different mappings in one installation (`siso_erp.default.target_code: 'wc3-nav'`). - -The responsible mappings files for the `createorder` message are: - -- `request.createorder.xsl` -- `response.createorder.xsl` - -If you want to override the mapping, create the mapping files in your bundle structure: - -- `MyBundle/Resources/mapping/wc3-nav/` - -See [Create a standard message (UpdateCustomer)](../../guides/creating_a_new_erp_message/create_standard_message.md) to learn how to adapt the mapping using xslt. diff --git a/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/example_ubl_for_price_calculation.md b/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/example_ubl_for_price_calculation.md deleted file mode 100644 index f48467938c..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/example_ubl_for_price_calculation.md +++ /dev/null @@ -1,754 +0,0 @@ -# Example UBL for price calculation [[% include 'snippets/experience_badge.md' %]] - -## Price request - -The request contains information about: - -- the buyer and customer number -- the delivery address -- the products and quantities -- additional information depending on the requirements - -??? note - - ``` xml - - - EUR - 2019-02-05 - - - - - 20000 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <MiddleName/> - <SesExtension/> - </Person> - <SesExtension/> - </Party> - </BuyerCustomerParty> - <SellerSupplierParty> - <Party> - <PostalAddress> - <StreetName/> - <AdditionalStreetName/> - <BuildingNumber/> - <CityName/> - <PostalZone/> - <CountrySubentity/> - <CountrySubentityCode/> - <Country> - <IdentificationCode/> - <Name/> - </Country> - <Department/> - <SesExtension/> - </PostalAddress> - <Contact> - <ID/> - <Name/> - <Telephone/> - <Telefax/> - <ElectronicMail/> - <OtherCommunication/> - <Note/> - <SesExtension/> - </Contact> - <Person> - <FirstName/> - <FamilyName/> - <Title/> - <MiddleName/> - <SesExtension/> - </Person> - <SesExtension/> - </Party> - </SellerSupplierParty> - <AccountingCustomerParty> - <Party> - <PartyName> - <Name>Frank Dege</Name> - </PartyName> - <PartyName> - <Name>silversolutions</Name> - </PartyName> - <PostalAddress> - <StreetName>Faerberstr</StreetName> - <AdditionalStreetName/> - <BuildingNumber/> - <CityName>Berlin Perth</CityName> - <PostalZone>12555</PostalZone> - <CountrySubentity>Deutschland</CountrySubentity> - <CountrySubentityCode/> - <Country> - <IdentificationCode>DE</IdentificationCode> - <Name/> - </Country> - <Department/> - <SesExtension/> - </PostalAddress> - <Contact> - <ID/> - <Name/> - <Telephone>03065481990</Telephone> - <Telefax/> - <ElectronicMail>nospam@ez.no</ElectronicMail> - <OtherCommunication/> - <Note/> - <SesExtension/> - </Contact> - <Person> - <FirstName/> - <FamilyName/> - <Title/> - <MiddleName/> - <SesExtension/> - </Person> - <SesExtension> - <forceInvoice/> - </SesExtension> - </Party> - </AccountingCustomerParty> - <Delivery> - <RequestedDeliveryPeriod> - <EndDate/> - <EndTime/> - </RequestedDeliveryPeriod> - <DeliveryParty> - <PartyName> - <Name>Frank Dege</Name> - </PartyName> - <PartyName> - <Name>silversolutions</Name> - </PartyName> - <PostalAddress> - <StreetName>Faerberstr</StreetName> - <AdditionalStreetName/> - <BuildingNumber/> - <CityName>Berlin Perth</CityName> - <PostalZone>12555</PostalZone> - <CountrySubentity>Deutschland</CountrySubentity> - <CountrySubentityCode/> - <Country> - <IdentificationCode>DE</IdentificationCode> - <Name/> - </Country> - <Department/> - <SesExtension/> - </PostalAddress> - <Contact> - <ID/> - <Name/> - <Telephone>03065481990</Telephone> - <Telefax/> - <ElectronicMail>nospam@ez.no</ElectronicMail> - <OtherCommunication/> - <Note/> - <SesExtension/> - </Contact> - <Person> - <FirstName/> - <FamilyName/> - <Title/> - <MiddleName/> - <SesExtension/> - </Person> - <SesExtension> - <status>sameAsInvoice</status> - <forceDelivery/> - </SesExtension> - </DeliveryParty> - </Delivery> - <PaymentMeans> - <PaymentMeansCode>invoice</PaymentMeansCode> - <PaymentDueDate/> - <PaymentChannelCode/> - <InstructionID/> - <PayeeFinancialAccount> - <ID/> - <CurrencyCode/> - <FinancialInstitutionBranch> - <ID/> - <Name/> - </FinancialInstitutionBranch> - </PayeeFinancialAccount> - <CardAccount> - <PrimaryAccountNumberID/> - <NetworkID/> - <ExpiryDate/> - </CardAccount> - </PaymentMeans> - <TransactionConditions> - <ID/> - <ActionCode/> - </TransactionConditions> - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>20078</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension> - <lineRemark/> - <scaledPrices/> - <special_offers>Customer price</special_offers> - <special_offers>Buy 3 pay 2</special_offers> - <stock>d:10;</stock> - <isPriceValid>1</isPriceValid> - <id>15653</id> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>364.14</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </OrderLine> - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>K30803</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension> - <lineRemark/> - <scaledPrices/> - <special_offers>Customer price</special_offers> - <special_offers>Buy 3 pay 2</special_offers> - <stock>d:10;</stock> - <isPriceValid>1</isPriceValid> - <id>15654</id> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>265.37</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </OrderLine> - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>HR2095/92</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension> - <lineRemark/> - <scaledPrices/> - <special_offers>Customer price</special_offers> - <special_offers>Buy 3 pay 2</special_offers> - <stock>d:10;</stock> - <isPriceValid>1</isPriceValid> - <id>15651</id> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>276.08</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </OrderLine> - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>20078</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension> - <lineRemark/> - <scaledPrices/> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>364.14</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - <stock>d:10;</stock> - <isPriceValid>1</isPriceValid> - <id>15602</id> - <special_offers>Customer price</special_offers> - <special_offers>Buy 3 pay 2</special_offers> - </SesExtension> - </OrderLine> - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>GJ116AA</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension> - <lineRemark/> - <scaledPrices/> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>209.44</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - <stock>d:10;</stock> - <isPriceValid>1</isPriceValid> - <special_offers>Customer price</special_offers> - <special_offers>Buy 3 pay 2</special_offers> - </SesExtension> - </OrderLine> - <SesExtension> - <ContactNumber>KT100210</ContactNumber> - <ShippingMethodCode>standard</ShippingMethodCode> - <pricesValid>1</pricesValid> - <priceResponseSourceType>remote</priceResponseSourceType> - <context_id>basket</context_id> - </SesExtension> - </Order> - ``` - -## Price response - -The response contains information about - -- calculates prices from the ERP (see tag `LineItem`) -- shipping costs (the shipping costs are `LineItems` as well but marked as -- additional information - -??? note - - ``` - <?xml version="1.0"?> - <OrderResponse singleElementArrays="Delivery OrderLine"> - <SalesOrderID>0</SalesOrderID> - <DocumentCurrencyCode/> - <IssueDate>2019-02-05</IssueDate> - <CustomerReference/> - <BuyerCustomerParty> - <Party> - <PartyName> - <Name>Blütenhaus GmbH</Name> - </PartyName> - <PartyIdentification> - <ID>20000</ID> - </PartyIdentification> - <Person> - <FirstName/> - <FamilyName/> - </Person> - <PostalAddress singleElementArrays="AddressLine"> - <StreetName>Ferdinands Höh 5</StreetName> - <AdditionalStreetName/> - <BuildingName/> - <BuildingNumber/> - <CityName>München</CityName> - <PostalZone>80997</PostalZone> - <CountrySubentity/> - <AddressLine> - <Line/> - </AddressLine> - <Country> - <IdentificationCode/> - </Country> - </PostalAddress> - </Party> - </BuyerCustomerParty> - <AccountingCustomerParty> - <Party> - <PartyName> - <Name>Blütenhaus GmbH</Name> - </PartyName> - <PartyIdentification> - <ID>20000</ID> - </PartyIdentification> - <Person> - <FirstName/> - <FamilyName/> - </Person> - <PostalAddress singleElementArrays="AddressLine"> - <StreetName>Ferdinands Höh 5</StreetName> - <AdditionalStreetName/> - <BuildingName/> - <BuildingNumber/> - <CityName>München</CityName> - <PostalZone>80997</PostalZone> - <CountrySubentity/> - <AddressLine> - <Line/> - </AddressLine> - <Country> - <IdentificationCode/> - </Country> - </PostalAddress> - </Party> - </AccountingCustomerParty> - <Delivery> - <DeliveryParty/> - </Delivery> - <OrderLine> - <LineItem> - <ID>10000</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>306.00</PriceAmount> - </Price> - <TotalTaxAmount>58.14</TotalTaxAmount> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>20078</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>2-Teiliger Sonnenschutz-Anzug UV Careful</Name> - <Description>Bestway</Description> - </Item> - <SesExtension> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>364.14</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </LineItem> - </OrderLine> - <OrderLine> - <LineItem> - <ID>20000</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>223.00</PriceAmount> - </Price> - <TotalTaxAmount>42.37</TotalTaxAmount> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>K30803</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>K30803</Name> - <Description>Tefal</Description> - </Item> - <SesExtension> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>265.37</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </LineItem> - </OrderLine> - <OrderLine> - <LineItem> - <ID>30000</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>232.00</PriceAmount> - </Price> - <TotalTaxAmount>44.08</TotalTaxAmount> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>HR2095/92</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>HR2095/92</Name> - <Description>Philips</Description> - </Item> - <SesExtension> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>276.08</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </LineItem> - </OrderLine> - <OrderLine> - <LineItem> - <ID>40000</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>306.00</PriceAmount> - </Price> - <TotalTaxAmount>58.14</TotalTaxAmount> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>20078</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>2-Teiliger Sonnenschutz-Anzug UV Careful</Name> - <Description>Bestway</Description> - </Item> - <SesExtension> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>364.14</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </LineItem> - </OrderLine> - <OrderLine> - <LineItem> - <ID>50000</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>176.00</PriceAmount> - </Price> - <TotalTaxAmount>33.44</TotalTaxAmount> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>GJ116AA</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>(SFF) Solenoid-Schloss und Gehäusesensor</Name> - <Description>HP</Description> - </Item> - <SesExtension> - <LineType>2</LineType> - <StockNumeric>10</StockNumeric> - <StockBoolean>0</StockBoolean> - <PriceAmountGross>209.44</PriceAmountGross> - <PriceIsIncVat>0</PriceIsIncVat> - <BelongsToLine/> - </SesExtension> - </LineItem> - </OrderLine> - <LegalMonetaryTotal> - <LineExtensionAmount>1,243</LineExtensionAmount> - </LegalMonetaryTotal> - <SesExtension> - <PriceIsIncVat/> - </SesExtension> - </OrderResponse> - ``` diff --git a/docs/guide/erp_integration/erp_communication/erp_configuration/configuration_for_webservice_based_erps.md b/docs/guide/erp_integration/erp_communication/erp_configuration/configuration_for_webservice_based_erps.md deleted file mode 100644 index 3c02219a13..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_configuration/configuration_for_webservice_based_erps.md +++ /dev/null @@ -1,19 +0,0 @@ -# Configuration for webservice-based ERPs [[% include 'snippets/experience_badge.md' %]] - -If your ERP systems offers different URLs/webservices for each feature, -you can define the endpoint to the Webservice per messages. -The parameter `webservice_wsdl` can be set to the URL of the responsible webservice. - -``` yaml -siso_erp.default.message_settings.createsalesorder: - message_class: "Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\CreateSalesOrderMessage" - response_document_class: "\\Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\Document\\OrderResponse" - webservice_operation: "MyWebserviceMethod" - mapping_identifier: "createorder" - webservice_wsdl: "https://mywebservice.com/WS/..../CreateOrderServices" - -``` - -`webservice_operation` is the name of the method defined in the WSDL. - -`webservice_wsdl` is the URL to the webservice itself. diff --git a/docs/guide/erp_integration/erp_communication/erp_configuration/curl_configuration.md b/docs/guide/erp_integration/erp_communication/erp_configuration/curl_configuration.md deleted file mode 100644 index d9f9448d0a..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_configuration/curl_configuration.md +++ /dev/null @@ -1,76 +0,0 @@ -# cURL configuration [[% include 'snippets/experience_badge.md' %]] - -## cURL - -``` yaml -parameters: - silver_erp.config.curl.target_url: "http://rest.example.host:81/service" - silver_erp.config.curl.options: - CURLOPT_HTTPHEADER: - - "Content-Type: application/xml" -``` - -### silver_erp.config.curl.target_url - -`silver_erp.config.curl.target_url` is the URL that is addressed by cURL to send the message data. - -### silver_erp.config.curl.options - -`curl.options` is an associative array that lets you define all possible values that can be set by [curl_setopt()](http://www.php.net/manual/en/function.curl-setopt.php). -This parameter defines the default values used for every message. -You can override it using the override parameter in the respective message configuration. - -### silver_erp.config.messages - -There are some transport-specific settings in the `silver_erp.config.messages` parameter: - -- You can define a different URL which should be used for the communication. That URL is configured in the `webservice_operation` field. -- You can override the values in the `silver_erp.config.curl.options` parameter. This is done in an additional field `override`. - -For example: - -``` yaml -silver_erp.config.curl.options: - CURLOPT_HTTPHEADER: - - "Content-Type: application/xml" - -silver_erp.config.messages: - search_product_info: - message_class: "Namespace\\ExampleMessage" - response_document_class: "\\oasis\\names\\specification\\ubl\\schema\\xsd\\OrderResponse_2\\OrderResponse" - webservice_operation: "http://otherhost.example.com/example-message" - mapping_identifier: "example" - override: - silver_erp.config.curl.options: - CURLOPT_HTTPPROXYTUNNEL: true - CURLOPT_PROXY: "proxy.host:1234" -``` - -In this case, for the `search_product_info` message, additionally to the `CURLOPT_HTTPHEADER` some proxy settings are set up in the cURL handle: `CURLOPT_HTTPPROXYTUNNEL` and `CURLOPT_PROXY`. - -## Messages - -Messages are configured in the `silver_erp.config.messages` parameter. - -This parameter is an associative array which itself holds further associative arrays. -The top-level keys are identifiers for the messages which are currently not evaluated in the process logic. -The keys within a particular message configuration are the following: - -- `message_class` is the fully-qualified class name of the class that holds the request and response document instances. -It is used by the transport implementations to determine the configuration for the given message object. -- `response_document_class` is the class that is instantiated by the transport implementation with the received data of the response. -- `webservice_operation` is a string value that determines the triggered operation of the transport - Its specific value depends on the transport implementation. -- `mapping_identifier` is a string that configures the mapping identifier for this specific message. - -For example: - -``` yaml -parameters: - silver_erp.config.messages: - calculate_sales_price: - message_class: "Silversolutions\\Bundle\\EshopBundle\\Message\\CalculateSalesPriceMessage" - response_document_class: "oasis\\names\\specification\\ubl\\schema\\xsd\\OrderResponse_2\\OrderResponse" - webservice_operation: SV_OPENTRANS_CALCULATE_PRICE - mapping_identifier: calcprice -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_configuration/web_connector_configuration.md b/docs/guide/erp_integration/erp_communication/erp_configuration/web_connector_configuration.md deleted file mode 100644 index 64c64aecb5..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_configuration/web_connector_configuration.md +++ /dev/null @@ -1,57 +0,0 @@ -# Web.Connector configuration [[% include 'snippets/experience_badge.md' %]] - -## General parameters - -``` yaml -parameters: - siso_erp.default.web_connector.service_location: "http://mydomain.com/mywebconnector" - siso_erp.default.web_connector.username: admin - siso_erp.default.web_connector.password: passwo - siso_erp.default.web_connector.soapTimeout: 5 - siso_erp.default.web_connector.erpTimeout: 5 - siso_erp.default.web_connector.allow_self_signed_ssl: true -``` - -!!! note - - Check the configuration for the Web.Connector URL in the Back Office (Configuration settings) as well. - The Back Office settings will set the default setting and may override the settings. - -`siso_erp.default.web_connector.url` - the service location defines the remote service URL of the Web.Connector. -`siso_erp.default.web_connector.username` - defines the user name for Web.Connector -`siso_erp.default.web_connector.password` - defines the password for Web.Connector -`siso_erp.default.web_connector.soapTimeout` - defines the SOAP timeout. -`siso_erp.default.web_connector.erpTimeout` - defines the timeout for ERP. -`siso_erp.default.web_connector.allow_self_signed_ssl` - defines for SOAP request if a self-signed SSL certificate is allowed. - -## Check ERP status - -If a message to the ERP system fails the shop will send a test message to the ERP in order to check - -- if it is a general issue and the ERP is offline or -- if just this one message failed - -If it is a general error the shop sets the ERP connection to offline. -All requests after setting the connection to offline are not be sent to the ERP and are immediately handled as an error. - -This parameter sets the ERP offline for 60 seconds before another request is sent. - -``` yaml -siso_erp.erp_semaphore.max_lock_time: 60 -``` - -The status is stored in stash using the key defined in `siso_erp.erp_semaphore.stash_item_id`. - -## Configuration for Web.Connector installation - -The Web.Connector supports the mapping of simple XML messages from one (source) structure to another (target) structure. -The rules for that mapping are defined in XSL files in a configurable directory (`mapping/nav_nas` is currently default). -This configured directory must contain two subdirectories: `xslbase` and `xsl`. The base mapping rules are delivered in the `xslbase` directory. Variations from the base mapping can be overridden in the `xsl` directory (files must have the same name). - -This mapping defines the translation of the UBL-based message to the format used by the ERP system. - -In order to activate this mapping in the Web.Connector, set up the corresponding configuration file: - -``` php -$cfg->setSetting('Mapping', 'XsltPathOffset', 'mapping/noop/'); -``` diff --git a/docs/guide/erp_integration/erp_communication/erp_faq.md b/docs/guide/erp_integration/erp_communication/erp_faq.md deleted file mode 100644 index a6408d7662..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_faq.md +++ /dev/null @@ -1,10 +0,0 @@ -# ERP FAQ [[% include 'snippets/experience_badge.md' %]] - -## Cannot determine message object - -If you see an exception "The message object could not be determined", -the YAML file with the message may be missing. - -When a message is generated, a new YAML file is created. -The YAML file has to be included. Usually all YAML files generated by the ERP command-line script are defined in `parameters.yml` in your bundle. -This file has to be loaded e.g. in `app/config/config.yml`. diff --git a/docs/guide/erp_integration/erp_communication/erp_logging.md b/docs/guide/erp_integration/erp_communication/erp_logging.md deleted file mode 100644 index bec387fb8c..0000000000 --- a/docs/guide/erp_integration/erp_communication/erp_logging.md +++ /dev/null @@ -1,308 +0,0 @@ -# ERP Logging [[% include 'snippets/experience_badge.md' %]] - -All communication (request- and response messages) that is performed by the [ERP transport](erp_components/erp_component_transport.md) is recorded by the `siso_erp.logger`. -It is a special service of the class `Monolog/Logger`. - -To learn more about logging in [[= product_name_exp =]] in general, see [Logging](../../../guide/logging/logging.md). - -## Adding a log entry - -You can add a new log entry in the following way: - -``` php -/** @var \Silversolutions\Bundle\EshopBundle\Message\AbstractMessage $message */ -/** @var \Psr\Log\LoggerInterface $erpLogger */ -$erpLogger->debug( - 'This message text is overridden with message_data from context array by the ErpMessageProcessor', - array( - 'message_identifier' => get_class($message), - 'message_data' => $message->getRequestDocument(), - 'measuring_point' => '190_late_request_point', - ) -); -``` - -## ErpLog entity and repository - -ERP messages are logged in a database for more sophisticated administrative handling. -Database logging is handled by [the Doctrine logger](../../../guide/logging/logging_api.md). - -### ErpLog - -Class `Silversolutions\Bundle\EshopBundle\Entity\ErpLog` is the ERP-message logging extension of `AbstractLog`. -It adds the following attributes: - -- `$requestId` (string) - uniquely identifies every HTTP request across all logs. -- `$sessionId` (string) - uniquely identifies every client session across all logs and requests. -- `$userId` (string) - the identifier for the user (mostly User Content item ID). -- `$measuringPoint` (string) - the specific point in the process of ERP communication when this message is logged. -- `$responseStatus` (integer) - status code of the ERP response. Not set in request logs. -- `$errorText` (string) - details of an error, if occurred, empty otherwise. -- `$processingTime` (integer) - time in microseconds that has passed from request to response. -- `$messageIdentifier` (string) - identifies the type of message. - -### ErpMessageProcessor - -The ERP log entity has no field for the ERP message content. -It is intended to log this data within the log-message attribute. - -Class `Silversolutions\Bundle\EshopBundle\Service\Logging\ErpMessageProcessor` is a Monolog data processor -that replaces the original log message with the serialized content of context/message_data if this context field is set. - -### ErpMappingProcessor - -ERP-message mapping is an optional step in the communication process. As for this, the mapped content is not always logged. If a mapper wants to log messages, they must put their content into a mapping_data field in the log context and this processor will take care of it. - -Class `Silversolutions\Bundle\EshopBundle\Service\Logging\` is a Monolog data processor -that replaces the original log message with the serialized content of context/mapping_data if this context field is set. -It also adds the measuring points for mapping. - -### ErpLogRepositoryInterface - -Interface `Silversolutions\Bundle\EshopBundle\Entity\ErpLogRepositoryInterface` is supposed to define methods -that are used to obtain statistical data about ERP communication from the logs. -It currently has no concrete methods. - -### ErpOrmLogRepository - -Class `Silversolutions\Bundle\EshopBundle\Entity\ErpOrmLogRepository` is the doctrine repository class for ErpLog entities. -It implements `ErpLogRepositoryInterface` and is supposed to provide its statistical methods. -Currently, it exists to comply with the doctrine conventions only and to inject it into the ERP logging instance of `DoctrineHandler`. - -## Configuration - -### Container setup for ERP logging - -In `ses_services.xml`: - -``` xml -<parameters> - <parameter key="siso_erp.logging_processor.message.class">Silversolutions\Bundle\EshopBundle\Service\Logging\ErpMessageProcessor</parameter> - <parameter key="siso_erp.logging_processor.mapping.class">Silversolutions\Bundle\EshopBundle\Service\Logging\ErpMappingProcessor</parameter> - <parameter key="siso_erp.logging_repository.doctrine.class">Silversolutions\Bundle\EshopBundle\Entity\ErpOrmLogRepository</parameter> -</parameters> -<services> - <!-- Define the repository as an injectable service --> - <service id="siso_erp.logging_repository.doctrine" class="%siso_erp.logging_repository.doctrine.class%"> - <factory service="doctrine.orm.default_entity_manager" method="getRepository" /> - <argument type="string">SilversolutionsEshopBundle:ErpLog</argument> - </service> - <!-- Define the doctrine handler that logs into the ERP log repository --> - <service id="siso_erp.logging_handler.doctrine" class="%siso_tools.logging_handler.doctrine.class%"> - <!-- Inject the ERP log repository --> - <call method="setLogRepository"> - <argument type="service" id="siso_erp.logging_repository.doctrine" /> - </call> - <!-- Inject the doctrine formatter --> - <call method="setFormatter"> - <argument type="service" id="siso_tools.logging_formatter.doctrine"/> - </call> - </service> - <!-- Define the doctrine handler that logs into the ERP log file --> - <service id="siso_erp.logging_handler.file" class="%monolog.handler.stream.class%"> - <argument type="string">%kernel.logs_dir%/%kernel.environment%-siso.eshop.erp.log</argument> - </service> - <!-- Define the processor that copies the ERP-message content to the log-message field --> - <service id="siso_erp.logging_processor.message" class="%siso_erp.logging_processor.message.class%"> - </service> - <!-- Define the processor that copies the mapped content to the log-message field --> - <service id="siso_erp.logging_processor.mapping" class="%siso_erp.logging_processor.mapping.class%"> - </service> - <!-- This is the actual logger that is to be injected into the MessageTransport and the Mapper - or any other instance, which is invoked in ERP message communication --> - <service id="siso_erp.logger" class="%monolog.logger.class%"> - <!-- The logging channel --> - <argument type="string">silver_eshop_erp</argument> - <!-- Inject the doctrine handler --> - <call method="pushHandler"> - <argument type="service" id="siso_erp.logging_handler.doctrine" /> - </call> - <!-- Inject the file handler --> - <call method="pushHandler"> - <argument type="service" id="siso_erp.logging_handler.file" /> - </call> - <!-- Register all processors that add the session data and copy the message content --> - <call method="pushProcessor"> - <argument type="collection" > - <argument type="service" id="siso_tools.logging_processor.request_data" /> - <argument type="string">processRecord</argument> - </argument> - </call> - <call method="pushProcessor"> - <argument type="collection" > - <argument type="service" id="siso_erp.logging_processor.message" /> - <argument type="string">processRecord</argument> - </argument> - </call> - <call method="pushProcessor"> - <argument type="collection" > - <argument type="service" id="siso_erp.logging_processor.mapping" /> - <argument type="string">processRecord</argument> - </argument> - </call> - </service> -``` - -### Entity table `ses_log_erp` - -Currently, ORM is defined by annotations in `Silversolutions\Bundle\EshopBundle\Entity\ErpLog`. - -Example of a generated MySQL table: - -``` sql -CREATE TABLE `ses_log_erp` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `log_timestamp` datetime NOT NULL, - `log_channel` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - `log_level` int(11) NOT NULL, - `log_message` longtext COLLATE utf8_unicode_ci, - `request_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `session_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `measuring_point` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `response_status` int(11) DEFAULT NULL, - `user_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - `error_text` longtext COLLATE utf8_unicode_ci, - `processing_time` int(11) DEFAULT NULL, - `message_identifier` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=671 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -``` - -## Logging architecture: measuring points - -During communication between the [[= product_name_exp =]] and ERP, messages pass several instances of processing. -In order to reconstruct how a message is processed, [the transport layer](erp_components/erp_component_transport.md) -writes a message's content multiple times into the log - once for every important process step. -These points in time of writing a message into the log are called measuring points. -As the message communication is basically split into a request and a response, there are always two mirrored measuring points. -In the log, measuring points are identified by a simple string in the `measuringPoint` (or `measuring_point`) field. -These identifiers are a combination of two pieces of information separated with `_`: - -||Description|Example| -|---|---|---| -|sequentially-ordered number (chronological index in the communication)|Technically, the numbers can be chosen arbitrarily (besides of the correct order). Currently, all requests are in the 100-199 range and responses are in the 200-299 range.|`120`| -|code word for hierarchical position (in the process stack)|There should be always two logs with the same code word for a single message communication, one for the request and one for the response.|`complete`| - -For example: `120_complete` - -### `120_complete` - -This measuring point specifies the point before specific transport implementation and after all event handlers. -The content includes all changes from the handlers and is a serialized object. -It contains the state of data which goes either into the mapping or directly into the specific transport (e.g. SOAP). -The processing time is calculated including the handlers (starting time-stamp before the event). - -??? note "Example content" - - ``` - O:80:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\BuyerCustomerParty":1:{s:5:"Party";O:85:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\BuyerCustomerPartyParty":1:{s:19:"PartyIdentification";a:1:{i:0;O:104:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\BuyerCustomerPartyPartyPartyIdentification":1:{s:2:"ID";s:6:"D00210";}}}} - ``` - -### `150_mapping` - -This measuring point specifies the point after serializing the object data into an XML string. -The mapping step is optional and depends on the concrete implementation of the transport and the configuration of the message. -The content is a serialized array, which contains the following fields: - -- `mapping_type` - identifies the first part of the XSL file name (`request`) -- `stylesheet` - fully-qualified path of the XSL file -- `input_xml` - XML content before XSLT mapping -- `output_xml` - XML content after XSLT mapping - -??? note "Example content" - - ``` - a:4:{s:12:"mapping_type";s:7:"request";s:10:"stylesheet";s:88:"app/Resources/xslbase/request.selectcontact.xsl";s:9:"input_xml";s:178:"<?xml version="1.0" encoding="UTF-8"?> - <BuyerCustomerParty> - <Party> - <PartyIdentification> - <ID>D00210</ID> - </PartyIdentification> - </Party> - </BuyerCustomerParty> - ";s:10:"output_xml";s:228:"<?xml version="1.0"?> - <WC_USER_MANAGEMENT><PROCESS_TYPE>SELECTCONTACT</PROCESS_TYPE><WEB_SITE>HOME</WEB_SITE><NUMBER>D00210</NUMBER><MAXCOUNT>10</MAXCOUNT><LAST_DATE_MODIFIED/><LOGIN_ID/><ONLINE_USER_TYPE/></WC_USER_MANAGEMENT> - ";} - ``` - -### `180_soap` - -This measuring point specifies the point right before the SOAP communication, after mapping and events. -The content is a serialized array that is passed as parameters to the SoapClient's request method. - -??? note "Example content" - - ``` - a:4:{s:4:"user";s:5:"admin";s:8:"password";s:6:"passwo";s:14:"erp_parameters";a:1:{s:7:"timeout";i:10000;}s:4:"data";s:228:"<?xml version="1.0"?> - <WC_USER_MANAGEMENT><PROCESS_TYPE>SELECTCONTACT</PROCESS_TYPE><WEB_SITE>HOME</WEB_SITE><NUMBER>D00210</NUMBER><MAXCOUNT>10</MAXCOUNT><LAST_DATE_MODIFIED/><LOGIN_ID/><ONLINE_USER_TYPE/></WC_USER_MANAGEMENT> - ";} - ``` - -### `220_soap` - -This measuring point specifies the point right after the SOAP communication, before mapping and events. -The content is a serialized array that is returned from the SoapClient's request method. - -??? note "Example content" - - ``` - a:2:{s:18:"WC_USER_MANAGEMENT";a:4:{s:12:"PROCESS_TYPE";s:13:"SELECTCONTACT";s:4:"GUID";s:46:"{96284C22-3F6C-4AE4-8CD3-137087BB0B74}\1202183";s:8:"WEB_SITE";s:4:"HOME";s:7:"CONTACT";a:4:{i:0;a:18:{s:16:"ONLINE_USER_TYPE";s:1:"0";s:6:"NUMBER";s:8:"KT000197";s:9:"JOB_TITLE";s:0:"";s:6:"E_MAIL";s:0:"";s:12:"ON_BEHALF_OF";s:0:"";s:4:"NAME";s:28:"Company for customercenter 1";s:7:"ADDRESS";s:14:"Färberstr. 26";s:23:"ADDITIONAL_ADDRESS_INFO";s:0:"";s:4:"CITY";s:6:"Berlin";s:12:"COUNTRY_CODE";s:2:"DE";s:10:"FAX_NUMBER";s:0:"";s:8:"ZIP_CODE";s:5:"12555";s:12:"PHONE_NUMBER";s:12:"030/65481990";s:13:"LANGUAGE_CODE";s:0:"";s:8:"LOGIN_ID";s:0:"";s:16:"PASSWORD_PADDING";s:0:"";s:8:"PASSWORD";s:0:"";s:8:"PRIORITY";s:1:"3";}i:1;a:18:{s:16:"ONLINE_USER_TYPE";s:1:"1";s:6:"NUMBER";s:8:"KT000199";s:9:"JOB_TITLE";s:0:"";s:6:"E_MAIL";s:27:"test_max@silversolutions.de";s:12:"ON_BEHALF_OF";s:6:"D00210";s:4:"NAME";s:14:"Max Mustermann";s:7:"ADDRESS";s:14:"Färberstr. 26";s:23:"ADDITIONAL_ADDRESS_INFO";s:0:"";s:4:"CITY";s:6:"Berlin";s:12:"COUNTRY_CODE";s:2:"DE";s:10:"FAX_NUMBER";s:0:"";s:8:"ZIP_CODE";s:5:"12555";s:12:"PHONE_NUMBER";s:12:"030/65481990";s:13:"LANGUAGE_CODE";s:3:"DEU";s:8:"LOGIN_ID";s:0:"";s:16:"PASSWORD_PADDING";s:0:"";s:8:"PASSWORD";s:0:"";s:8:"PRIORITY";s:1:"3";}i:2;a:18:{s:16:"ONLINE_USER_TYPE";s:1:"1";s:6:"NUMBER";s:8:"KT000200";s:9:"JOB_TITLE";s:0:"";s:6:"E_MAIL";s:0:"";s:12:"ON_BEHALF_OF";s:6:"D00210";s:4:"NAME";s:10:"Tina Tinte";s:7:"ADDRESS";s:14:"Färberstr. 26";s:23:"ADDITIONAL_ADDRESS_INFO";s:0:"";s:4:"CITY";s:6:"Berlin";s:12:"COUNTRY_CODE";s:2:"DE";s:10:"FAX_NUMBER";s:0:"";s:8:"ZIP_CODE";s:5:"12555";s:12:"PHONE_NUMBER";s:12:"030/65481990";s:13:"LANGUAGE_CODE";s:3:"DEU";s:8:"LOGIN_ID";s:0:"";s:16:"PASSWORD_PADDING";s:0:"";s:8:"PASSWORD";s:0:"";s:8:"PRIORITY";s:1:"3";}i:3;a:18:{s:16:"ONLINE_USER_TYPE";s:1:"1";s:6:"NUMBER";s:8:"KT000202";s:9:"JOB_TITLE";s:0:"";s:6:"E_MAIL";s:29:"test_willi@silversolutions.de";s:12:"ON_BEHALF_OF";s:6:"D00210";s:4:"NAME";s:12:"Willi Wonker";s:7:"ADDRESS";s:14:"Färberstr. 26";s:23:"ADDITIONAL_ADDRESS_INFO";s:0:"";s:4:"CITY";s:6:"Berlin";s:12:"COUNTRY_CODE";s:2:"DE";s:10:"FAX_NUMBER";s:0:"";s:8:"ZIP_CODE";s:5:"12555";s:12:"PHONE_NUMBER";s:12:"030/65481990";s:13:"LANGUAGE_CODE";s:3:"ENG";s:8:"LOGIN_ID";s:0:"";s:16:"PASSWORD_PADDING";s:0:"";s:8:"PASSWORD";s:0:"";s:8:"PRIORITY";s:1:"3";}}}s:6:"status";a:2:{s:4:"code";i:0;s:10:"erp_status";i:0;}} - ``` - -### `250_mapping` - -This measuring point specifies the point before the deserialization of the received XML data. -The mapping step is optional and depends on the concrete implementation of the transport and the configuration of the message. -The content is a serialized array which contains the following fields: - -- `mapping_type` - identifies the first part of the XSL file name (`response`) -- `stylesheet` - fully-qualified path of the XSL file -- `input_xml` - XML content before XSLT mapping -- `output_xml` - XML content after XSLT mapping - -??? note "Example content" - - ``` - a:4:{s:12:"mapping_type";s:8:"response";s:10:"stylesheet";s:89:"app/Resources/xslbase/response.selectcontact.xsl";s:9:"input_xml";s:2442:"<?xml version="1.0" encoding="UTF-8"?> - <WC_USER_MANAGEMENT><PROCESS_TYPE>SELECTCONTACT</PROCESS_TYPE><GUID>{96284C22-3F6C-4AE4-8CD3-137087BB0B74}\1202183</GUID><WEB_SITE>HOME</WEB_SITE><CONTACT><ONLINE_USER_TYPE>0</ONLINE_USER_TYPE><NUMBER>KT000197</NUMBER><JOB_TITLE></JOB_TITLE><E_MAIL></E_MAIL><ON_BEHALF_OF></ON_BEHALF_OF><NAME>Company for customercenter 1</NAME><ADDRESS>Färberstr. 26</ADDRESS><ADDITIONAL_ADDRESS_INFO></ADDITIONAL_ADDRESS_INFO><CITY>Berlin</CITY><COUNTRY_CODE>DE</COUNTRY_CODE><FAX_NUMBER></FAX_NUMBER><ZIP_CODE>12555</ZIP_CODE><PHONE_NUMBER>030/65481990</PHONE_NUMBER><LANGUAGE_CODE></LANGUAGE_CODE><LOGIN_ID></LOGIN_ID><PASSWORD_PADDING></PASSWORD_PADDING><PASSWORD></PASSWORD><PRIORITY>3</PRIORITY></CONTACT><CONTACT><ONLINE_USER_TYPE>1</ONLINE_USER_TYPE><NUMBER>KT000199</NUMBER><JOB_TITLE></JOB_TITLE><E_MAIL>test_max@silversolutions.de</E_MAIL><ON_BEHALF_OF>D00210</ON_BEHALF_OF><NAME>Max Mustermann</NAME><ADDRESS>Färberstr. 26</ADDRESS><ADDITIONAL_ADDRESS_INFO></ADDITIONAL_ADDRESS_INFO><CITY>Berlin</CITY><COUNTRY_CODE>DE</COUNTRY_CODE><FAX_NUMBER></FAX_NUMBER><ZIP_CODE>12555</ZIP_CODE><PHONE_NUMBER>030/65481990</PHONE_NUMBER><LANGUAGE_CODE>DEU</LANGUAGE_CODE><LOGIN_ID></LOGIN_ID><PASSWORD_PADDING></PASSWORD_PADDING><PASSWORD></PASSWORD><PRIORITY>3</PRIORITY></CONTACT><CONTACT><ONLINE_USER_TYPE>1</ONLINE_USER_TYPE><NUMBER>KT000200</NUMBER><JOB_TITLE></JOB_TITLE><E_MAIL></E_MAIL><ON_BEHALF_OF>D00210</ON_BEHALF_OF><NAME>Tina Tinte</NAME><ADDRESS>Färberstr. 26</ADDRESS><ADDITIONAL_ADDRESS_INFO></ADDITIONAL_ADDRESS_INFO><CITY>Berlin</CITY><COUNTRY_CODE>DE</COUNTRY_CODE><FAX_NUMBER></FAX_NUMBER><ZIP_CODE>12555</ZIP_CODE><PHONE_NUMBER>030/65481990</PHONE_NUMBER><LANGUAGE_CODE>DEU</LANGUAGE_CODE><LOGIN_ID></LOGIN_ID><PASSWORD_PADDING></PASSWORD_PADDING><PASSWORD></PASSWORD><PRIORITY>3</PRIORITY></CONTACT><CONTACT><ONLINE_USER_TYPE>1</ONLINE_USER_TYPE><NUMBER>KT000202</NUMBER><JOB_TITLE></JOB_TITLE><E_MAIL>test_willi@silversolutions.de</E_MAIL><ON_BEHALF_OF>D00210</ON_BEHALF_OF><NAME>Willi Wonker</NAME><ADDRESS>Färberstr. 26</ADDRESS><ADDITIONAL_ADDRESS_INFO></ADDITIONAL_ADDRESS_INFO><CITY>Berlin</CITY><COUNTRY_CODE>DE</COUNTRY_CODE><FAX_NUMBER></FAX_NUMBER><ZIP_CODE>12555</ZIP_CODE><PHONE_NUMBER>030/65481990</PHONE_NUMBER><LANGUAGE_CODE>ENG</LANGUAGE_CODE><LOGIN_ID></LOGIN_ID><PASSWORD_PADDING></PASSWORD_PADDING><PASSWORD></PASSWORD><PRIORITY>3</PRIORITY></CONTACT></WC_USER_MANAGEMENT> - ";s:10:"output_xml";s:834:"<?xml version="1.0"?> - <ContactResponse><ErpContact><Contact><ID>KT000199</ID><Name>Max Mustermann</Name><Telephone>030/65481990</Telephone><Telefax/><ElectronicMail>test_max@silversolutions.de</ElectronicMail><OtherCommunication/><Note/><SesExtension><LanguageCode>DEU</LanguageCode><IsMain/></SesExtension></Contact><Contact><ID>KT000200</ID><Name>Tina Tinte</Name><Telephone>030/65481990</Telephone><Telefax/><ElectronicMail/><OtherCommunication/><Note/><SesExtension><LanguageCode>DEU</LanguageCode><IsMain/></SesExtension></Contact><Contact><ID>KT000202</ID><Name>Willi Wonker</Name><Telephone>030/65481990</Telephone><Telefax/><ElectronicMail>test_willi@silversolutions.de</ElectronicMail><OtherCommunication/><Note/><SesExtension><LanguageCode>ENG</LanguageCode><IsMain/></SesExtension></Contact></ErpContact></ContactResponse> - ";} - ``` - -### `280_complete` - -This measuring point specifies the point after specific transport implementation and after all event handlers. -The content includes all changes from the handlers and is a serialized object. -It contains the state of data that is finally available for the instance that initiated the message. -The processing time is calculated including the handlers (final time-stamp after the event). - -??? note "Example content" - - ``` - O:77:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\ContactResponse":1:{s:10:"ErpContact";O:87:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\ContactResponseErpContact":1:{s:7:"Contact";a:3:{i:0;O:69:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\Contact":8:{s:2:"ID";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:8:"KT000199";}s:4:"Name";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:14:"Max Mustermann";}s:9:"Telephone";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:12:"030/65481990";}s:7:"Telefax";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:14:"ElectronicMail";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:27:"test_max@silversolutions.de";}s:18:"OtherCommunication";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:4:"Note";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:12:"SesExtension";O:72:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\TreeObject":1:{s:5:"value";a:2:{s:12:"LanguageCode";s:3:"DEU";s:6:"IsMain";s:0:"";}}}i:1;O:69:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\Contact":8:{s:2:"ID";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:8:"KT000200";}s:4:"Name";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:10:"Tina Tinte";}s:9:"Telephone";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:12:"030/65481990";}s:7:"Telefax";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:14:"ElectronicMail";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:18:"OtherCommunication";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:4:"Note";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:12:"SesExtension";O:72:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\TreeObject":1:{s:5:"value";a:2:{s:12:"LanguageCode";s:3:"DEU";s:6:"IsMain";s:0:"";}}}i:2;O:69:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\Contact":8:{s:2:"ID";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:8:"KT000202";}s:4:"Name";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:12:"Willi Wonker";}s:9:"Telephone";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:12:"030/65481990";}s:7:"Telefax";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:14:"ElectronicMail";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:29:"test_willi@silversolutions.de";}s:18:"OtherCommunication";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:4:"Note";O:74:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\StringObject":1:{s:5:"value";s:0:"";}s:12:"SesExtension";O:72:"Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\TreeObject":1:{s:5:"value";a:2:{s:12:"LanguageCode";s:3:"ENG";s:6:"IsMain";s:0:"";}}}}}} - ``` - -## Errors during communication - -In case of an error during communication with the Web.Connector the error is logged additionally into the standard `silver.eshop.log` log file. - -??? note "Example" - - ``` - [2014-12-23 09:53:15] silver.eshop.ERROR: ERP request returned an error. Web-Connector: "Send of message 841785 failed " ERP: "" {"request":{"Order":{"DocumentCurrencyCode":"EUR","BuyerCustomerParty":{"SupplierAssignedAccountID":"","Party":{"PartyIdentification":{"ID":"10000"}}},"SellerSupplierParty":{"Party":{"PostalAddress":{"StreetName":"","AdditionalStreetName":"","BuildingNumber":"","CityName":"","PostalZone":"","CountrySubentity":"","CountrySubentityCode":"","Country":{"IdentificationCode":"","Name":""},"Department":"","SesExtension":""},"Contact":{"ID":"","Name":"","Telephone":"","Telefax":"","ElectronicMail":"","OtherCommunication":"","Note":"","SesExtension":""},"Person":{"FirstName":"","FamilyName":"","Title":"","MiddleName":"","SesExtension":""},"SesExtension":""}},"AccountingCustomerParty":{"Party":{"PostalAddress":{"StreetName":"","AdditionalStreetName":"","BuildingNumber":"","CityName":"","PostalZone":"","CountrySubentity":"","CountrySubentityCode":"","Country":{"IdentificationCode":"","Name":""},"Department":"","SesExtension":""},"Contact":{"ID":"","Name":"","Telephone":"","Telefax":"","ElectronicMail":"","OtherCommunication":"","Note":"","SesExtension":""},"Person":{"FirstName":"","FamilyName":"","Title":"","MiddleName":"","SesExtension":""},"SesExtension":""}},"Delivery":{"RequestedDeliveryPeriod":{"EndDate":"","EndTime":""},"DeliveryParty":{"PostalAddress":{"StreetName":"","AdditionalStreetName":"","BuildingNumber":"","CityName":"","PostalZone":"","CountrySubentity":"","CountrySubentityCode":"","Country":{"IdentificationCode":"","Name":""},"Department":"","SesExtension":""},"Contact":{"ID":"","Name":"","Telephone":"","Telefax":"","ElectronicMail":"","OtherCommunication":"","Note":"","SesExtension":""},"Person":{"FirstName":"","FamilyName":"","Title":"","MiddleName":"","SesExtension":""},"SesExtension":""}},"PaymentMeans":{"PaymentMeansCode":"","PaymentDueDate":"","PaymentChannelCode":"","InstructionID":"","PayeeFinancialAccount":{"ID":"","CurrencyCode":"","FinancialInstitutionBranch":{"ID":"","Name":""}},"CardAccount":{"PrimaryAccountNumberID":"","NetworkID":"","ExpiryDate":""}},"TransactionConditions":{"ID":"","ActionCode":""},"OrderLine":{"LineItem":{"ID":"","SalesOrderID":"","Quantity":"1","LineExtensionAmount":"","TotalTaxAmount":"","MinimumQuantity":"","MaximumQuantity":"","MinimumBackorderQuantity":"","MaximumBackorderQuantity":"","PartialDeliveryIndicator":"","BackOrderAllowedIndicator":"","Price":{"PriceAmount":"","BaseQuantity":""},"Item":{"Name":"","SellersItemIdentification":{"ID":"12003","ExtendedID":""},"BuyersItemIdentification":{"ID":""}},"SesExtension":""},"SesExtension":""},"SesExtension":""}},"response":{"status":{"code":2,"erp_status":0},"error":"Send of message 841785 failed","erp_message":""}} [] - ``` - -The text after "ERP request returned an error. Web-Connector:" defines the reason for the error. -The content of the message depends on the ERP system. -In this case the error message is: "Send of message 841785 failed" - -If SOAP causes an exception, it is logged as well. The logged data contains the request and response with header information. - -??? note "Example" - - ``` - [2015-09-25 11:14:45] silver.eshop.ERROR: Error during SOAP communication: Array ( [request] => <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.silversolutions.de" xmlns:xsd="h - ttp://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body> - <ns1:SV_OPENTRANS_SELECT_CUSTOMERINFO><user xsi:type="xsd:string">admin</user><password xsi:type="xsd:string">passwo</password><erp_parameters xsi:type="ns2:Map"><item><key xsi:type="xsd:string">timeout</key><value xsi:type="xsd:int">10000</value></item></erp_parameters><data xsi:type="ns2:Map"><item><key xsi:type="xsd:string">BuyerCustomerParty</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">Party</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">PartyIdentification</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">ID</key><value xsi:type="xsd:string">D00210</value></item></value></item><item><key xsi:type="xsd:string">PostalAddress</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">StreetName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">AdditionalStreetName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">BuildingNumber</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">CityName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">PostalZone</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">CountrySubentity</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">CountrySubentityCode</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Country</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">IdentificationCode</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Name</key><value xsi:type="xsd:string"></value></item></value></item><item><key xsi:type="xsd:string">Department</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">SesExtension</key><value xsi:type="xsd:string"></value></item></value></item><item><key xsi:type="xsd:string">Contact</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">ID</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Name</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Telephone</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Telefax</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">ElectronicMail</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">OtherCommunication</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Note</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">SesExtension</key><value xsi:type="xsd:string"></value></item></value></item><item><key xsi:type="xsd:string">Person</key><value xsi:type="ns2:Map"><item><key xsi:type="xsd:string">FirstName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">FamilyName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">Title</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">MiddleName</key><value xsi:type="xsd:string"></value></item><item><key xsi:type="xsd:string">SesExtension</key><value xsi:type="xsd:string"></value></item></value></item><item><key xsi:type="xsd:string">SesExtension</key><value xsi:type="xsd:string"></value></item></value></item></value></item></data></ns1:SV_OPENTRANS_SELECT_CUSTOMERINFO></SOAP-ENV:Body></SOAP-ENV:Envelope> [requestHeaders] => POST /webconnector/webcon_opentrans/webconnector_opentrans.ph HTTP/1.1 Host: 192.168.2.76:81 Connection: Keep-Alive User-Agent: PHP-SOAP/5.5.9-1ubuntu4.12 Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.silversolutions.de#SV_OPENTRANS_SELECT_CUSTOMERINFO" Content-Length: 3892 [response] => [responseHeaders] => HTTP/1.1 404 Not Found Date: Fri, 25 Sep 2015 09:14:44 GMT Server: Apache Content-Length: 315 Keep-Alive: timeout=15, max=100 Connection: Keep-Alive Content-Type: text/html; charset=iso-8859-1 ) {"exception":"[object] (SoapFault(code: 0): Not Found at /var/www/silver.e-shop.application/vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Services/Transport/WebConnectorMessageTransport.php:420)","fault_code":"HTTP","fault_text":"Not Found"} [] - ``` diff --git a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_project_specific_message.md b/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_project_specific_message.md deleted file mode 100644 index b064843fbc..0000000000 --- a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_project_specific_message.md +++ /dev/null @@ -1,188 +0,0 @@ -# Create project-specific message [[% include 'snippets/experience_badge.md' %]] - -## ERP expectations - -ERP expects the a request and response with the following XML: - -Request SELECTMODIFIEDDATA: - -``` xml -<WC_MANAGEMENT> - <PROCESS_TYPE>GETNEWCONTACT</PROCESS_TYPE> - <GUID>1</GUID> - <WEB_SITE>HOME</WEB_SITE> - <ENTRY_NO>43</ENTRY_NO> - <MAXCOUNT>10</MAXCOUNT> -</WC_MANAGEMENT> -``` - -Response SELECTMODIFIEDDATA: - -``` xml -<WC_MANAGEMENT> - <PROCESS_TYPE>GETNEWCONTACT</PROCESS_TYPE> - <GUID /> - <WEB_SITE>HOME</WEB_SITE> - <ENTRY_NO>43</ENTRY_NO> - <MAXCOUNT>10</MAXCOUNT> - <CUSTOMER> - <LINE> - <CUSTOMER_NO>POITOU03</CUSTOMER_NO> - <CONTACT_NO>CON0358</CONTACT_NO> - <ENTRY_NO>43</ENTRY_NO> - </LINE> - </CUSTOMER> - <LAST_ENTRY_NO>43</LAST_ENTRY_NO> -</WC_MANAGEMENT> -``` - -!!! tip - - You have to know the webservice operation that is used for this message. - - If you see that all XML elements are written with capital letters, usually it means that the webservice operation is `SV_RAW_MESSAGE`. - -## Define request and response document - -The request and response documents are used as a frame for the generating PHP classes that are used in shop. -The generator uses them to create the classes automatically. - -!!! tip - - Instead of the `src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml` folder - you can use a different structure, for example: `src/MyCompany/Bundle/MyCompanyBundle/Resources/xml`. - - The path to this folder set up anyway when you use command to generate the messages. - -### Message name - -Create two .xml files, one for request, one for response, -in `src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml`. - -!!! note "Naming conventions" - - The files must be named: - - ``` - request.[messageName].xml - response.[messageName].xml - ``` - - For example: - - ``` - src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml/request.selectModifiedData.xml - src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml/response.selectModifiedData.xml - ``` - -### Request XML - -Copy the content of the request `SELECTMODIFIEDDATA` and use it as content for your request .xml: - -`src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml/request.selectModifiedData.xml` - -``` xml -<?xml version="1.0" encoding="UTF-8"?> -<WC_MANAGEMENT> - <PROCESS_TYPE>GETNEWCONTACT</PROCESS_TYPE> - <GUID>1</GUID> - <WEB_SITE>HOME</WEB_SITE> - <ENTRY_NO>43</ENTRY_NO> - <MAXCOUNT>10</MAXCOUNT> -</WC_MANAGEMENT> -``` - -!!! tip - - The root tag must be unique in the target directory (in this example, `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle`). - -### Response XML - -Copy the content of the response `SELECTMODIFIEDDATA` and use it as content for your response .xml: - -`src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml/response.selectModifiedData.xml` - -``` xml -<?xml version="1.0" encoding="UTF-8"?> -<WC_USER_MANAGEMENT ses_unbounded="LINE"> - <PROCESS_TYPE /> - <GUID /> - <WEB_SITE /> - <ENTRY_NO /> - <MAXCOUNT /> - <CUSTOMER> - <LINE> - <CUSTOMER_NO /> - <CONTACT_NO /> - <ENTRY_NO /> - </LINE> - </CUSTOMER> - <LAST_ENTRY_NO /> -</WC_USER_MANAGEMENT> -``` - -## Generator - -Now you can use the generator to generate the classes automatically: - -``` bash -//Usage -php bin/console silversolutions:generatemessages --message [message name] --sourceDir [path to the request and response .xml dir] --targetDir [path to the target bundle] - -//Example -php bin/console silversolutions:generatemessages --message selectModifiedData --sourceDir src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml --targetDir src/MyCompany/Bundle/MyCompanyBundle - -//Re-generation of messages -//If you are re-creating the messages (e.g. the request .xml has been adapted) you have to use to optional --force parameter -php bin/console silversolutions:generatemessages --message selectModifiedData --sourceDir src/MyCompany/Bundle/MyCompanyBundle/Resources/specifications/xml --targetDir src/MyCompany/Bundle/MyCompanyBundle --force -``` - -## Mapping - -!!! tip "Symlinks" - - There should be a symlink to the mapping in `app/Resources`: - - `xsl` - project symlink for the specific project - always has a higher priority - `xslbase` - standard symlink for [[= product_name_exp =]] as a default - - ``` bash - cd app/Resources - ls -l - - xsl -> ../../src/MyCompany/Bundle/MyCompanyBundle/Resources/xsl - xslbase -> ../../vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase - ``` - -Mapping is done inside the `src/MyCompany/Bundle/MyCompanyBundle/Resources/xsl` folder. - -!!! tip - - If you are creating a new message that is specific only for the project and you are not using any standard .xml documents as a part of your message, you usually don't need to define any kind of mapping. - -## Message configuration - -You also have to configure the message. The [generator](#generator) has created some frames already. - -You can create a special .yml file only for message configuration, such as `project.messages.yml`. - -``` xml -//Import the auto-generated listener here -imports: - - { resource: "@MyCompanyBundle/Resources/config/selectmodifieddatafactorylistener.service.yml" } - -//Add message configuration -parameters: - silver_erp.config.messages: - ... - //take a look in the auto-generated src/MyCompany/Bundle/MyCompanyBundle/Resources/config/selectmodifieddatamessage.message.yml - selectmodifieddata: - //path to the auto-generated message - message_class: "\\MyCompany\\Bundle\\MyCompanyBundle\\Entities\\Messages\\SelectModifiedDataMessage" - //path to the auto-generated response class - response_document_class: "\\MyCompany\\Bundle\\MyCompanyBundle\\Entities\\Messages\\Document\\SelectModifiedDataResponse" - //name of the webservice operation - webservice_operation: "SV_RAW_MESSAGE" - //mapping name - if you don't have any mapping, leave it empty - mapping_identifier: "" -``` diff --git a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_standard_message.md b/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_standard_message.md deleted file mode 100644 index 458c6bbafc..0000000000 --- a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_standard_message.md +++ /dev/null @@ -1,504 +0,0 @@ -# Create standard message [[% include 'snippets/experience_badge.md' %]] - -## ERP expectations - -ERP expects the a request and response with the following XML: - -??? note "Request UPDATECUSTOMER" - - ``` - <WC_USER_MANAGEMENT> - <PROCESS_TYPE>UPDATECUSTOMER</PROCESS_TYPE> - <GUID>1</GUID> - <WEB_SITE>HOME</WEB_SITE> - <NUMBER>1000006</NUMBER> - <BLOCKED /> - <CURRENCY /> - <WEB_NEWSLETTER>1</WEB_NEWSLETTER> - <JOB_TITLE /> - <E_MAIL></E_MAIL> - <NAME>Toni Kreucher</NAME> - <NAME_2 /> - <ADDRESS /> - <ADDITIONAL_ADDRESS_INFO /> - <CITY>Hamburg</CITY> - <COUNTRY_CODE /> - <COUNTY /> - <RESPONSIBILITY_CENTER /> - <COMPANY_NUMBER></COMPANY_NUMBER> - <FAX_NUMBER></FAX_NUMBER> - <POST_CODE /> - <PHONE_NUMBER></PHONE_NUMBER> - <HOME_PAGE /> - <E_MAIL /> - <LANGUAGE_CODE>DEU</LANGUAGE_CODE> - <SALUTATION /> - <SALESPERSON_CODE /> - <CUSTOMER_POSTING_GROUP /> - <GEN_BUS_POSTING_GROUP /> - <VAT_BUS_POSTING_GROUP /> - <CONTACT_PERSON /> - <CREDIT_LIMIT_LCY /> - </WC_USER_MANAGEMENT> - ``` - -??? note "Response UPDATECUSTOMER" - - ``` - <WC_USER_MANAGEMENT> - <PROCESS_TYPE>HOME</PROCESS_TYPE> - <GUID>1</GUID> - <WEB_SITE>UPDATECUSTOMER</WEB_SITE> - <NUMBER>1000006</NUMBER> - <NAME>Toni Kreucher</NAME> - <CITY>Hamburg</CITY> - <LANGUAGE_CODE>DEU</LANGUAGE_CODE> - <CUSTOMER_POSTING_GROUP>INLAND</CUSTOMER_POSTING_GROUP> - <GEN_BUS_POSTING_GROUP>INLAND</GEN_BUS_POSTING_GROUP> - <VAT_BUS_POSTING_GROUP>INLAND</VAT_BUS_POSTING_GROUP> - <WEB_NEWSLETTER>1</WEB_NEWSLETTER> - <COUNT_OF_SHIP_TO_ADRESSES>0</COUNT_OF_SHIP_TO_ADRESSES> - <CUST_PRICE_GROUP> - <CODE>DVGW</CODE> - <END_DATE>17530101</END_DATE> - <ASSOCIATION_NO></ASSOCIATION_NO> - </CUST_PRICE_GROUP> - </WC_USER_MANAGEMENT> - ``` - -!!! tip - - You have to know the webservice operation that is used for this message. - - If you see that all XML elements are written with capital letters, usually it means that the webservice operation is `SV_RAW_MESSAGE`. - -## Define request and response document - -The request and response documents are used as a frame for the generating PHP classes that are used in shop. -The generator uses them to create the classes automatically. - -### Message name - -Create two .xml files, one for request, one for response, -in `/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml`. - -!!! note "Naming conventions" - - The files must be named: - - ``` - request.[messageName].xml - response.[messageName].xml - ``` - - For example: - - ``` - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml/request.updateCustomer.xml - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml/response.updateCustomer.xml - ``` - -### Request XML - -#### Root Tag - -Create a root tag in your request document: - -``` xml -<?xml version="1.0" encoding="UTF-8"?> -<RequestUpdateCustomer> -</RequestUpdateCustomer> -``` - -!!! tip - - The root tag must be unique in the target directory (in this example, `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle`). - -#### Content - -Now you have to fill it with some content. -You can use standard .xml documents to become a part of our message. -These documents are located in `/vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml` as well: - -``` -Contact.xml -DeliveryParty.xml -LineItem.xml -Party.xml - -//You can also create your own structure and define an own document, it can be used in every message -MyCustomXml.xml -``` - -#### Content structure - -If you take a look inside these documents and compare the structure with the `UPDATECUSTOMER` that is expected by ERP -you can find out that there are many commonalities with `Party.xml`. - -##### UPDATECUSTOMER - -``` xml -<WC_USER_MANAGEMENT> - <PROCESS_TYPE>UPDATECUSTOMER</PROCESS_TYPE> - <GUID>1</GUID> - <WEB_SITE>HOME</WEB_SITE> - <NUMBER>1000006</NUMBER> - <BLOCKED /> - <CURRENCY /> - <WEB_NEWSLETTER>1</WEB_NEWSLETTER> - <JOB_TITLE /> - <E_MAIL></E_MAIL> - <NAME>Toni Kreucher</NAME> - <NAME_2 /> - <ADDRESS /> - <ADDITIONAL_ADDRESS_INFO /> - <CITY>Hamburg</CITY> - <COUNTRY_CODE /> - <COUNTY /> - <RESPONSIBILITY_CENTER /> - <COMPANY_NUMBER></COMPANY_NUMBER> - <FAX_NUMBER></FAX_NUMBER> - <POST_CODE /> - <PHONE_NUMBER></PHONE_NUMBER> - <HOME_PAGE /> - <E_MAIL /> - <LANGUAGE_CODE>DEU</LANGUAGE_CODE> - <SALUTATION /> - <SALESPERSON_CODE /> - <CUSTOMER_POSTING_GROUP /> - <GEN_BUS_POSTING_GROUP /> - <VAT_BUS_POSTING_GROUP /> - <CONTACT_PERSON /> - <CREDIT_LIMIT_LCY /> -</WC_USER_MANAGEMENT> -``` - -##### Party.xml - -``` xml -<Party ses_unbounded="PartyIdentification PartyName" ses_type="ses:Contact" ses_tree="SesExtension"> - <PartyIdentification> - <ID>10000</ID> - </PartyIdentification> - <PartyName> - <Name>Möbel-Meller KG</Name> - </PartyName> - <PostalAddress ses_unbounded="AddressLine" ses_tree="SesExtension"> - <StreetName>Tischlerstr. 4-10</StreetName> - <AdditionalStreetName /> - <BuildingNumber>4-10</BuildingNumber> - <CityName>Berlin</CityName> - <PostalZone>12555</PostalZone> - <CountrySubentity>Berlin</CountrySubentity> - <CountrySubentityCode>BER</CountrySubentityCode> - <AddressLine> - <Line>Gartenhaus</Line> - </AddressLine> - <Country> - <IdentificationCode>DE</IdentificationCode> - <Name>Deutschland</Name> - </Country> - <Department>Development</Department> - <SesExtension /> - </PostalAddress> - <Contact> - </Contact> - <Person ses_tree="SesExtension"> - <FirstName>Frank</FirstName> - <FamilyName>Dege</FamilyName> - <Title>Herr - - - - - -``` - -For this reason Party.xml is good enough to be used as a part of the message: - -``` xml - - - - -``` - -!!! note "`ses_type`" - - Pay attention if you want to use an external XML document inside your message. - In this case `ses_type` is required and the second part after the colon must be the referred file name (excluding .xml). - - The prefix `ses:` causes the message generator to look up the external XML file within SilversolutionsEshopBundle. - It also generates the PHP objects within SilversolutionsEshopBundle. - With any other prefix (which should be `cust:`) the file is looked up in the specified source directory and generated to the specified target directory. - -!!! tip - - There are several XML attributes (`ses_unbounded`, `ses_type`), that can be used. See [XML format (necessary additional attributes)](../../erp_components/erp_component_messages/erp_message_class_generator.md) for more information. - -### Response XML - -#### Root Tag - -Create a root tag in your response document: - -``` xml - - - -``` - -#### Content - -Fill your response document with content: - -``` xml - - - - -``` - -## Generator - -Now you can use the generator to generate the classes automatically: - -``` bash -//Usage -php bin/console silversolutions:generatemessages --message [message name] --sourceDir [path to the request and response .xml dir] --targetDir [path to the target bundle] - -//Example -php bin/console silversolutions:generatemessages --message updateCustomer --sourceDir vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml --targetDir vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle - -//Re-generation of messages -//If you are re-creating the messages (e.g. the request .xml has been adapted) you have to use to optional --force parameter -php bin/console silversolutions:generatemessages --message updateCustomer --sourceDir vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml --targetDir vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle --force -``` - -## Mapping - -Mapping is done during runtime. -Performing the mapping is often enough if only the mapping is overridden in the project, -instead of overriding the .xml and re-generating of the messages. - -!!! tip "Symlinks" - - There should be a symlink to the mapping in `app/Resources`: - - `xsl` - project symlink for the specific project - always has a higher priority - `xslbase` - standard symlink for [[= product_name_exp =]] as a default - - ``` bash - cd app/Resources - ls -l - - xsl -> ../../src/MyProject/MyProjectBundle/Resources/xsl - xslbase -> ../../vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase - ``` - -Mapping is done inside the `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase` folder. - -!!! note "Naming conventions" - - The mapping document does not have to same the same name as message name, - but th structure must look like this: - - ``` - request.[mappingName].xsl - response.[mappingName].xsl - ``` - - ``` - //Example - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase/request.updatecustomer.xsl - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase/response.updatecustomer.xsl - - //But you could also add: - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase/request.myMapping.xsl - vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/xslbase/response.myMapping.xsl - ``` - -### Request XSL - -As you can see [above](#content-structure), the structure of `UPDATECUSTOMER` expected by ERP and the `Party.xml` is not exactly the same. For this reason you have to add mapping - how the information of `UPDATECUSTOMER` will be mapped into the Party object. - -First, copy the structure of `UPDATECUSTOMER` and add it as a content into the request .xsl document. - -??? note - - In the line `` you have to add the element of the .xml document which you are going to map on. In this case take a look at the request .xml (`vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/specifications/xml/request.updateCustomer.xml`). - It is the root tag RequestUpdateCustomer and the Party tag. - - ``` xml - - - - UPDATECUSTOMER - HOME - - - - - - - -
    - - - - - - - - - - - - - - - - - - - -
    -
    -
    - ``` - -Then add mapping information how the attributes of `UPDATECUSTOMER` are supposed to be mapped into Party attributes. - -??? note - - ``` xml - - - - UPDATECUSTOMER - HOME - - - - - - - - - -
    - - - - - - - - - - - - - - - - - - - -
    -
    -
    - ``` - -### Response XSL - -You have to add a backward mapping from Party attributes into `UPDATECUSTOMER` attributes. For this reason copy the content of the Party.xml and use it as content for your response .xsl. -Then add mapping information on how the attributes of Party are suppose to be mapped into `UPDATECUSTOMER` attributes. - -!!! note - - Some standard .xml document such as Party.xml can contain other standard documents such as Contact.xml. - In this case you also have to copy the content of the sub-documents. - -??? note - - ``` xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <xsl:value-of select="SALUTATION"/> - - - - - - - - - - - ``` - -## Message configuration - -You also have to configure the message. The [generator](#generator) has created some frames already. - -By default the messages are configured in the `messages.yml`. - -You need to add imports and parameters: - -``` yaml -//Import the auto-generated listener here -imports: - - { resource: "@SilversolutionsEshopBundle/Resources/config/updatecustomerfactorylistener.service.yml" } - -//Add message configuration -parameters: - silver_erp.config.messages: - ... - //take a look in the auto-generated vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/config/updatecustomermessage.message.yml - updatecustomer: - //path to the auto-generated message - message_class: "Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\UpdateCustomerMessage" - //path to the auto-generated response class - response_document_class: "\\Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\Document\\ResponseUpdateCustomer" - //name of the webservice operation - webservice_operation: "SV_RAW_MESSAGE" - //mapping name - mapping_identifier: "updatecustomer" -``` diff --git a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/creating_a_new_erp_message.md b/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/creating_a_new_erp_message.md deleted file mode 100644 index d7fc28fe6f..0000000000 --- a/docs/guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/creating_a_new_erp_message.md +++ /dev/null @@ -1,247 +0,0 @@ -# Creatinge new ERP message [[% include 'snippets/experience_badge.md' %]] - -If you want to create a new message, first think about whether you want to create a [standard message](create_standard_message.md) or a [specific message in a project](create_project_specific_message.md). - -Standard messages have to meet stricter conventions like the UBL message standard. -Project-specific messages can be added in a more simple way with less effort. - -## Use case: ERP message ItemTransfer - -This example shows how to set up and use a new ERP message. -The example uses a message which requests product data from the ERP system that is often required in online shops. - -The example is using the Web.Connector to fetch data about a given SKU in real time. -The name of the product and the inventory is displayed on a webpage. - -![](../../../../img/create_erp_message_1.jpg) - -You can find more existing ERP messages in [ERP messages component](../../../erp_communication/erp_components/erp_component_messages/erp_component_messages.md). - -## Fetch products from ERP - -The Web.Connector provides the web service operation `SV_ITEM_TRANSFER`. -This method fetches data for a given SKU and returns some data about this product. -The SKU value may also be a filter such as `1..` (this is a NAV notation that lists all products where the SKU starts with `1`). - -The ERP expects the following XML as a request: - -``` xml - - 1.. - 10 - -``` - -The ERP responds with the following XML. The `ITEM` tag can be repeated for each product returned by the ERP. - -``` xml - - - - D4142 - Web.Connector - - - 0 - 32 - STÜCK - 349 - - - - - -``` - -You need to send the request as expected by the ERP, but map the response if required. - -### Create the request and response XML - -[UBL 2.0](http://www.datypic.com/sc/ubl20/ss.html) is used as base for the XML structure and customized when necessary. - -You create the request and response XML to generate message objects from as expected by the shop. - -Make sure that the toot tags are not using the same name. -It is a good practice to name the tag of the response adding a suffix such as `Response`. - -These two xml files have to be stored in the subdirectory `Resources/specifications/xml/` of your project: - -- `request.itemtransfer.xml` contains the specification for the request. -- `response.itemtransfer.xml` contains the specification for the response. - -`src/Demo/TestBundle/Resources/xml/request.itemtransfer.xml`: - -``` xml - - - 1.. - 10 - -``` - -This is the expected response of the ERP answer. Since the tag `ITEM` is provided for each product, -the attribute `ses_unbounded="ITEM"` informs the shop that it should handle this element as an array. - -`src/Demo/TestBundle/Resources/xml/response.itemtransfer.xml`: - -``` xml - - - - D4142 - VPA-150 - - - 0 - 20 - STÜCK - 349 - - - - - -``` - -!!! tip - - There are several XML attributes (`ses_unbounded`, `ses_type`), that can be used. - See [XML format (necessary additional attributes)](../../erp_components/erp_component_messages/erp_message_class_generator.md) for more information. - -### Create XSL mapping - -To send and retrieve the XML in the proper format, create a mapping. -For more information see [ERP mapping component](../../erp_components/erp_component_mapping.md). - -Two mappings have to be defined (note that the file ending is now `*.xsl`): - -- `request.itemtransfer.xsl` maps the request as modeled before into the format the ERP expects. -- `response.itemtransfer.xsl` maps the response from the ERP as modeled before into the format the shop expects. - -`src/Demo/TestBundle/Resources/xslbase/request.itemtransfer.xsl`: - -``` xml - - - - - - -``` - -`src/Demo/TestBundle/Resources/xslbase/response.itemtransfer.xsl`: - -``` xml - - - - - - - - -``` - -### Generate request/response message classes - -Now that you have the XML as expected from the shop and the mapping to/from the ERP system, -you are ready to generate the PHP objects. -For more information, see [ERP Message-Class-Generator](../../erp_components/erp_component_messages/erp_message_class_generator.md). - -``` bash -php bin/console silversolutions:generatemessages --message itemtransfer --sourceDir src/Demo/TestBundle/Resources/xml --targetDir src/Demo/TestBundle --force -``` - -!!! caution - - If you recreate the PHP objects, you have to delete the old objects first. - The "-force" parameter performs this task automatically. - This includes all classes from the order: Entities/Messages/Document and the `ItemtransferMessage.php` from Entities/Messages - as well as the .yml files from the config order: `itemtransferfactorylistener.service.yml` and `itemtransfermessage.message.yml`. - -### Add configuration to parameters.yml - -When all PHP objects are generated, you have to configure the messages so that they are accessible by the shop. - -``` yaml -imports: - - { resource: "@DemoTestBundle/Resources/config/itemtransferfactorylistener.service.yml" } - -parameters: - silver_erp.config.messages: - itemtransfer: - message_class: "Demo\\TestBundle\\Entities\\Messages\\ItemtransferMessage" - response_document_class: "\\Demo\\TestBundle\\Entities\\Messages\\Document\\ItemTransferResponse" - webservice_operation: "SV_ITEM_TRANSFER" - mapping_identifier: "itemtransfer" -``` - -!!! note - - Define the web service method which is used in the ERP system or Web.Connector. - In this example the operation `SV_ITEM_TRANSFER` is used. - -### Using request and response - -When everything is set up, you can use the request and response objects in the shop. - -Implement a controller which gets the SKU using the URL (e.g. `/itemtransfer/1000`), -establishes the connection to the ERP and loads a template. - -!!! caution - - Keep in mind that setting/getting content to/from elements always requires accessing the value attribute, for example: - - `$itemtransferRequest ->ITEM_MAXCOUNT->value = '1';` - -``` php -/** -* @Route("/itemtransfer/{sku}") -* -*/ -public function indexAction($sku) -{ - - /** @var \Silversolutions\Bundle\EshopBundle\Services\MessageInquiryService $inquiryService */ - $inquiryService = $this->get('silver_erp.message_inquiry_service'); - - /** @var \Demo\TestBundle\Entities\Messages\ItemtransferMessage $itemtransferMessage */ - $itemtransferMessage = $inquiryService->inquireMessage(\Demo\TestBundle\Services\Factory\ItemtransferFactoryListener::ITEMTRANSFER); - - /** @var \Demo\TestBundle\Entities\Messages\Document\ITEMTRANSFER $itemtransferRequest */ - $itemtransferRequest = $itemtransferMessage->getRequestDocument(); - $itemtransferRequest->ITEM_NO->value = $sku; // some item number - $itemtransferRequest->ITEM_MAXCOUNT->value = '1'; - - /** @var \Silversolutions\Bundle\EshopBundle\Services\Transport\CurlMessageTransport $erpTransport */ - $erpTransport = $this->get('silver_erp.message_transport'); - /** @var \Demo\TestBundle\Entities\Messages\Document\ITEMTRANSFER_RESPONSE $itemtransferResponse */ - $itemtransferResponse = $erpTransport->sendMessage($itemtransferMessage)->getResponseDocument(); //item.ITEM[0].DESCRIPTION.value - - return $this->render( - 'DemoTestBundle:Default:index.html.twig', - array( - 'sku' => $sku, - 'itemDescription' => $itemtransferResponse->ITEM[0]->DESCRIPTION->value, - 'itemInventory' => $itemtransferResponse->ITEM[0]->INVENTORY->value, - ) - ); -} -``` - -Template (`index.html.twig`): - -``` html+twig -

    Realtime information about sku: {{ sku }}

    - - - - - - - - - - -
    nameinventory
    {{ itemDescription }}{{ itemInventory }}
    -``` diff --git a/docs/guide/erp_integration/erp_communication/guides/getting_product_data_from_the_erp.md b/docs/guide/erp_integration/erp_communication/guides/getting_product_data_from_the_erp.md deleted file mode 100644 index 19ab6a1ab4..0000000000 --- a/docs/guide/erp_integration/erp_communication/guides/getting_product_data_from_the_erp.md +++ /dev/null @@ -1,100 +0,0 @@ -# Getting product data from ERP [[% include 'snippets/experience_badge.md' %]] - -## Retrieving products from ERP in a Symfony controller - -``` php -/** @var WebConnectorErpService $importer */ -$importer = $this->get('silver_erp.facade'); -//Starting SKU -$startSku = '1..'; -//Number of items to be retrieved (optional, default is 10) -$itemMaxCount = 30; -$items = $importer->selectItem($startSku, $itemMaxCount); -foreach($items as $products) -{ - foreach($products as $product) - { - //Prints the product's name - echo (string)$product->ProductMeta->name->TextField->text->translations->translation[0]->value; - //Prints the product's price - echo (string)$product->ProductMeta->price->PriceField->price->price->value; - } -} -``` - -## Adding new fields to map project specific fields - -Copy the file `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/mapping/wc3-nav/xsl/response.itemtransfer.xsl` in `ProjectBundleName/Resources/mapping/wc3-nav/xsl` and edit it. - -To add for example, the field `VAT_Prod_Posting_Group` from the response: - -``` -Array -( - [SoapResponse] => Array - ( - [ItemTransferResult] => Array - ( - [Item] => Array - ( - [0] => Array - ( - [No] => 0002 - [No_2] => - [Description] => Waage / Personenwaage GS 39, Glaswaage - [Search_Description] => WAAGE / PERSONENWAAGE GS 39, GLASWAAGE - [Description_2] => mit Sprachfunktion, 4 Speicher, 150kg - [Base_Unit_of_Measure] => STK - [Net_Weight] => 2.4 - [Freight_Type] => - [Country_Region_Purchased_Code] => - [VAT_Bus_Posting_Gr_Price] => - [Gen_Prod_Posting_Group] => 07-19 - [Country_Region_of_Origin_Code] => - [Tax_Group_Code] => - [VAT_Prod_Posting_Group] => 19 <-- field to add -``` - -Add the following block to the xsl file under the `` section: - -``` xml - -... - - ses_vat_prod_posting_group - - - - - - ger-DE - - - - - - - -``` - -To add a field of type array, add an array block, also in the `Datamap` section: - -``` xml - -... - - ses_attributes - - - - - - ger-DE - - - - - - - -``` diff --git a/docs/guide/erp_integration/erp_communication/guides/implementing_erp_delivery_address_creation_and_updates.md b/docs/guide/erp_integration/erp_communication/guides/implementing_erp_delivery_address_creation_and_updates.md deleted file mode 100644 index 8465d735f3..0000000000 --- a/docs/guide/erp_integration/erp_communication/guides/implementing_erp_delivery_address_creation_and_updates.md +++ /dev/null @@ -1,70 +0,0 @@ -# Implementing ERP delivery address creation and updates [[% include 'snippets/experience_badge.md' %]] - -## General DeliveryParty record - -All ERP messages for manipulation of a customer's delivery addresses use the same data structure at their core: - -``` php -/** @var \Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\DeliveryParty $deliveryParty */ -$deliveryParty->PartyIdentification[0]->ID->value = 'alphanumeric'; -$deliveryParty->PartyName[0]->Name->value = 'alphanumeric'; -$deliveryParty->PartyName[1]->Name->value = 'alphanumeric'; -$deliveryParty->PostalAddress->StreetName->value = 'alphanumeric'; -$deliveryParty->PostalAddress->AdditionalStreetName->value = 'alphanumeric'; -$deliveryParty->PostalAddress->CityName->value = 'alphanumeric'; -$deliveryParty->PostalAddress->CountrySubentityCode->value = 'alphanumeric code'; // Must exist in NAV -$deliveryParty->PostalAddress->PostalZone->value = 'post/zip code'; -$deliveryParty->SesExtension->value['Code'] = 'alphanumeric code'; -$deliveryParty->SesExtension->value['Blocked'] = 'boolean'; -$deliveryParty->SesExtension->value['Key'] = 'alphanumeric'; -``` - -Most of the fields are standard address fields. `PartyName` can be specified multiple times. -The number of lines that can be specified depends on the ERP. - -`PartyIdentification` is where the customer number is defined. The first element can always be used. - -`CountrySubentityCode` is a code that must be correctly set in ERP. - -There are some values which are only evaluated by the NAV ERP system. These are `Code`, `Blocked` and `Key`. -Those fields are transmitted in the `SesExtension` array: - -`Code` must be a value which is unique among all delivery addresses for the respective customer. -`Blocked` is a boolean which activate or deactivates the address. -`Key` is special field which is used to determine the integrity of the handled data. - -## Create a delivery address for a registered customer - -In order to create new addresses, the whole record must be created, for example: - -``` php -use Silversolutions\Bundle\EshopBundle\Services\Factory\CreateDeliveryAddressFactoryListener; -use Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\CreateDeliveryAddressRequest; -use Silversolutions\Bundle\EshopBundle\Entities\Messages\Document\CreateDeliveryAddressResponse; - -/** @var \Silversolutions\Bundle\EshopBundle\Services\MessageInquiryService $messageInq */ -$messageInq = $this->getContainer()->get('silver_erp.message_inquiry_service'); -/** @var \Silversolutions\Bundle\EshopBundle\Services\Transport\AbstractMessageTransport $messageTrans */ -$messageTrans = $this->getContainer()->get('silver_erp.message_transport'); - -$msg = $messageInq->inquireMessage(CreateDeliveryAddressFactoryListener::CREATEDELIVERYADDRESS); -/** @var CreateDeliveryAddressRequest $request */ -$request = $msg->getRequestDocument(); -$request->DeliveryParty->PartyIdentification[0] = new DeliveryPartyPartyIdentification(); -$request->DeliveryParty->PartyIdentification[0]->ID->value = '10000'; -$request->DeliveryParty->SesExtension->value['Code'] = 'TEST001'; -$request->DeliveryParty->SesExtension->value['Blocked'] = 'false'; -$request->DeliveryParty->PartyName = array( - new DeliveryPartyPartyName(), - new DeliveryPartyPartyName(), -); -$request->DeliveryParty->PartyName[0]->Name->value = 'Timothy'; -$request->DeliveryParty->PartyName[1]->Name->value = 'Tester'; -$request->DeliveryParty->PostalAddress->StreetName->value = 'Testallee 1'; -$request->DeliveryParty->PostalAddress->AdditionalStreetName->value = 'Gassenstr.'; -$request->DeliveryParty->PostalAddress->CityName->value = 'Testow'; -$request->DeliveryParty->PostalAddress->CountrySubentityCode->value = ''; // Must exist in NAV -$request->DeliveryParty->PostalAddress->PostalZone->value = '12345'; -/** @var CreateDeliveryAddressResponse $response */ -$response = $messageTrans->sendMessage($msg)->getResponseDocument(); -``` diff --git a/docs/guide/erp_integration/erp_integration.md b/docs/guide/erp_integration/erp_integration.md index e586176487..c2b64d8067 100644 --- a/docs/guide/erp_integration/erp_integration.md +++ b/docs/guide/erp_integration/erp_integration.md @@ -1,65 +1,75 @@ -# ERP integration [[% include 'snippets/experience_badge.md' %]] +--- +edition: commerce +--- -[[= product_name_exp =]] can communicate with ERP systems. It uses the data and logic already provided by ERP. +# ERP integration -This ensures that the shop is always up to date. -The complex logic and processes already implemented in the ERP can be reused. +[[= product_name =]] can communicate with ERP systems. -The customer sees their individual prices and product availability from ERP. -Orders are also transferred to the ERP system. +The communication covers, among other things, product and customer data, price and stock information, orders and order history. +[[= product_name =]] and the ERP system can exchange the following data. -### Configuring ERP integration +|Data/Process|What is exchanged|Communication process| +|--- |--- |--- | +|Customer data|Data about the customer, such as invoice and purchase address, list of delivery addresses, status, customer group, credit limit, contact data|The data for a customer is fetched from ERP when the user logs in and has a customer number.| +|Product data|Products and product groups|Product import can run, for example, every night. This data is usually provided by using an export (XML, CSV, JSON).| +|Prices|List price and volume prices, individual prices|List prices are exchanged during the product import. Individual prices are fetched from ERP when a customer is logged in and has a customer number. In that case the shop requests prices in real time from ERP. You can define per project which cases request prices from the ERP (for example, product detail page, basket and checkout).| +|Orders|Address data, Delivery address, products and customer number|When the customer makes an order, the order is sent immediately to the ERP system. If electronic payment is involved, the order is placed when the payment provider acknowledges the transaction.| +|Documents|Invoices, orders, delivery notes, credit memos|The order history feature requests such documents in real time from the ERP. This ensures that the customer sees all documents even if they placed the order by phone or fax.| -If you are using a prepared connector, see [Web.Connector configuration](erp_communication/erp_configuration/web_connector_configuration.md) for more information. +## ERP configuration -In you are using a REST-based ERP system, see [Curl Configuration](erp_communication/erp_configuration/curl_configuration.md). +To connect your shop to ERP, configure the following [ERP settings](../../guide/basket/basket.md) in the Back Office: -If your ERP supports webservices directly, see [Configuration for webservice-based ERPs](erp_communication/erp_configuration/configuration_for_webservice_based_erps.md). +- URL of the Web-Connector +- User name (configured per Web-Connector) +- Password (configured per Web-Connector) -### Adapting the mapping +The following configuration enables the use of ERP: -[[= product_name_exp =]] uses the UBL ([Universal Business Language](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=ubl)) standard to model business data. -ERP systems usually use vendor-specific structures and attribute names. -An XSLT-based mapping feature enables adapting the mapping between these formats. -See [Adapt the mappings for ERP functions](erp_communication/erp_configuration\adapt_the_mappings_for_erp_functions/adapt_the_mappings_for_erp_functions.md) for more details. +``` yaml +siso_local_order_management.default.send_order_to_erp: true +siso_order_history.default.use_local_documents: false +``` -### Monitoring the ERP connection +### Checking ERP status -The Back Office provides a monitoring service which enables checking all messages exchanged between ERP and [[= product_name_exp =]]. +If a message to the ERP system fails, the shop checks whether the whole ERP is offline, or just this one message failed. -After selecting a date range and a measuring point (recommended: "Request data before and after mapping" and "Response data before and after mapping"), -you get more details about the request sent to the ERP system and the applied mapping. +If it is a general error, the shop sets the ERP connection to offline. +When ERP is offline, requests are immediately handled as an error. -![](../img/erp_request_log.png) +The following parameter sets the ERP offline time for 60 seconds before another request is sent. -**Mapping** refers to the XSLT file used for mapping. +``` yaml +siso_erp.erp_semaphore.max_lock_time: 60 +``` -**Input-XML** and **Output-XML** display the input data and the XML converted by the XSLT transformation. +### Configuration for webservice-based ERPs -## Supported ERP systems +If the ERP system offers different URLs/webservices for each feature, +you can define the endpoint to the webservice per message. +The parameter `webservice_url` can be set to the URL of the responsible webservice. -[[= product_name_exp =]] offers out-of-the-box interfaces and connectors for: +``` yaml +siso_erp.default.message_settings.createsalesorder: + message_class: "Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\CreateSalesOrderMessage" + response_document_class: "\\Silversolutions\\Bundle\\EshopBundle\\Entities\\Messages\\Document\\OrderResponse" + webservice_operation: "CreateSalesOrder" + webservice_url: "$web_connector.service_location;siso_erp$/InboundCreateOrderIISWebService.svc?wsdl" + mapping_identifier: "createorder" +``` -- Microsoft Dynamics NAV -- Microsoft Dynamics AX -- SAP +`webservice_operation` is the name of the method defined in the WSDL. -Since [[= product_name_exp =]] offers an open interface using standards (REST, Webservices and a standard XML Format UBL), -you can adapt other ERP systems as well. +`webservice_url` is the URL to the webservice itself. -To be connected to [[= product_name_exp =]], an ERP system must be open (offer e.g. a REST or webservice interface) -and must offer the supported functions. +### Monitoring the ERP connection -### Supported processes with ERP integration +The Back Office provides a monitoring service that enables checking all messages exchanged between ERP and [[= product_name =]]. -[[= product_name_exp =]] and the ERP system exchange the following data. -The details of data exchange depend on the project-specific configuration, -because in complex configurations other systems might provide data as well (for example a PIM system). +To see it, go to **eCommerce -> Control center -> ERP request log**. -|Data/Process|What is exchanged|When| -|--- |--- |--- | -|Customer data|Data about the customer such as: invoice and purchase address, list of delivery addresses, status, customer group, credit limit, contact data|The data for a customer is fetched from the ERP when the user logs in and has a customer number| -|Product data|Products and product groups|During the import. The import is initiated for example every night or more often. This data is usually provided using an export (XML, CSV, JSON)| -|Prices|List price and volume prices, individual prices|List prices are exchanged during the product import. Individual prices are fetched from the ERP when a customer is logged in and has a customer number. In that case the shop requests prices in real time from the ERP. You can define per project which cases request prices from the ERP (e.g. product detail page, basket and checkout)| -|Orders|Address data, Delivery address, products and customer number|When the customer makes an order, the order is sent immediately to the ERP system. If electronic payment is involved, the order is placed when the payment provider acknowledges the transaction.| -|Documents|Invoices, orders, delivery notes, credit memos|The order history feature requests such documents in real time from the ERP. This ensures that the customer sees all documents even if they placed the order by phone or fax.| +Then select a date range and a measuring point to get more details about requests that were sent to the ERP system. + +![](../img/erp_request_log.png) diff --git a/docs/guide/erp_integration/erp_logging.md b/docs/guide/erp_integration/erp_logging.md new file mode 100644 index 0000000000..0953997d31 --- /dev/null +++ b/docs/guide/erp_integration/erp_logging.md @@ -0,0 +1,95 @@ +--- +edition: commerce +--- + +# ERP logging + +All communication with the ERP (requests and responses) is recorded by the `siso_erp.logger`. +It is a special service of the `Monolog/Logger` class. + +To learn more about logging in the shop, see [Logging](../logging/logging.md). + +## Accessing logs + +Log messages are logged to the database. +You can use a command to check the messages that were exchanged with the ERP system. + +The command waits for the next message and displays the request and result as XML. + +You can search for a specific message by using the `--search-text` option: + +``` bash +php bin/console ibexa:commerce:display-erp-log --search-text 123456788 +``` + +To dump the most recent messages, use the `--dump-last-messages` option: + +``` bash +php bin/console ibexa:commerce:display-erp-log --dump-last-messages 20 > /tmp/erp_messages.txt +``` + +You can remove messages from the database with the `--delete-messages` option. + +The following example removes messages older than three days: + +``` bash +php bin/console ibexa:commerce:display-erp-log --delete-messages 3 +``` + +## Measuring points + +The log communication with ERP has measuring points - points that correspond to important steps in the communication process. + +There are always two measuring points for each step, one for the request and one for the response. + +### `120_complete` + +`120_complete` indicates the point before a specific transport implementation and after all event handlers. + +The content includes all changes from the handlers and is a serialized object. +It contains the state of data which goes either into the mapping or directly into the specific transport (e.g. SOAP). +The processing time is calculated including the handlers (starting time-stamp before the event). + +### `150_mapping` + +`150_mapping` indicates the point after serializing the object data into an XML string. +The mapping step is optional and depends on the concrete implementation of the transport and the configuration of the message. + +The content is a serialized array, which contains the following fields: + +- `mapping_type` - identifies the first part of the XSL file name (`request`) +- `stylesheet` - fully-qualified path of the XSL file +- `input_xml` - XML content before XSLT mapping +- `output_xml` - XML content after XSLT mapping + +### `180_soap` + +`180_soap` indicates the point right before the SOAP communication, after mapping and events. + +The content is a serialized array that is passed as parameters to the SoapClient's request method. + +### `220_soap` + +`220_soap` indicates the point right after the SOAP communication, before mapping and events. + +The content is a serialized array that is returned from the SoapClient's request method. + +### `250_mapping` + +`250_mapping` indicates the point before the deserialization of the received XML data. +The mapping step is optional and depends on the concrete implementation of the transport and the configuration of the message. + +The content is a serialized array which contains the following fields: + +- `mapping_type` - identifies the first part of the XSL file name (`response`) +- `stylesheet` - fully-qualified path of the XSL file +- `input_xml` - XML content before XSLT mapping +- `output_xml` - XML content after XSLT mapping + +### `280_complete` + +`280_complete` indicates the point after a specific transport implementation and after all event handlers. + +The content includes all changes from the handlers and is a serialized object. +It contains the state of data that is finally available for the instance that initiated the message. +The processing time is calculated including the handlers (final time-stamp after the event). diff --git a/docs/guide/erp_integration/lost_orders.md b/docs/guide/erp_integration/lost_orders.md new file mode 100644 index 0000000000..70c4a9981e --- /dev/null +++ b/docs/guide/erp_integration/lost_orders.md @@ -0,0 +1,53 @@ +--- +edition: commerce +--- + +# Lost orders + +## Sending lost orders from the Back Office + +If sending an order to ERP fails, the order is stored in a database with a special state `ordered_failed`. +You can find lost orders in the Back Office, on the **eCommerce -> Order Management** tab. +Filter by **Order transfer failed**. + +For each lost order you can perform the following actions: + +|Action|Description|Result| +|--- |--- |--- | +|Transfer to ERP|The lost order is re-sent to ERP|The user sees an error or success message depending on whether the lost order could be re-sent to ERP or not. If sending the lost order fails, the administrator gets an email. If sending the lost order is successful, the customer who made this order gets a confirmation email.| +|Remove lost order|The lost order is not removed, the state of lost order is changed to `confirmed`|The lost order does not appear in the list anymore.| + +## Email notifications + +Every time an order cannot be placed, the shop administrator gets an email. +The administrator's email address can be defined in the following configuration: + +``` yaml +parameters: + siso_core.default.ses_swiftmailer: + lostOrderEmailReceiver: %ses_eshop.lostorder_email% +``` + +Templates are located in the following files: + +``` +EshopBundle/Resources/views/Emails/NotificationMail_FailedOrder.html.twig +EshopBundle/Resources/views/Emails/NotificationMail_FailedOrder.txt.twig +``` + +## Lost order command + +You can also re-send lost orders by using a command-line tool: + +``` bash +php bin/console ibexa:commerce:process-lost-orders [id] +``` + +!!! caution + + To use the CLI, you must set the host. Otherwise the URLs and assets are not correct. + + ``` yaml + parameters: + siso_core.default.host: localhost + ``` diff --git a/docs/guide/erp_integration/remotepriceprovider.md b/docs/guide/erp_integration/remotepriceprovider.md index 0a3038ea35..3d175ef922 100644 --- a/docs/guide/erp_integration/remotepriceprovider.md +++ b/docs/guide/erp_integration/remotepriceprovider.md @@ -1,14 +1,22 @@ -# RemotePriceProvider [[% include 'snippets/experience_badge.md' %]] +--- +edition: commerce +--- -`RemotePriceProvider` can contact an ERP system to get prices for one or more product. +# RemotePriceProvider + +`RemotePriceProvider` can contact an ERP system to get prices for one or more products. To request prices from ERP, you must provide customer and optional contact number. +If the ERP system does not provide information about VAT, the VAT can be determined in the shop. +In that case the shop uses [VatService](../pricing/price_api/localvatservice.md) to get the `vatPercent` by the `vatCode`. + ## Using customer and contact numbers -1. If the customer and contact numbers are set directly in the price request, they are used. -1. If not, and the customer and contact numbers are set in the BuyerParty, they are used. -1. If neither customer nor contact number is set and the usage of the template debitor is allowed in the configuration, -the customer and/or contact number are determined from the [StandardTemplateDebitorService](../pricing/price_api/standardtemplatedebitorservice.md) and used. +`RemotePriceProvider` uses customer and contact numbers from the price request. +If they are not set, it uses the numbers set in the Buyer Party. + +If neither customer nor contact number are set, and template debitor is enabled in the configuration, +[StandardTemplateDebitorService](../pricing/price_api/standardtemplatedebitorservice.md) determines the customer and/or contact number to use. ``` yaml siso_core.default.use_template_debitor_customer_number: true @@ -17,254 +25,7 @@ siso_core.default.use_template_debitor_contact_number: true !!! note - Using template debitor only works if price requests without customer number are enabled for the `RemotePriceProvider`. - They can be enabled/disabled in the Configuration Settings of the shop (Price group). - - While disabled, an exception is thrown in the `remotePriceProvider` and fallback is used. - - ``` - //use the template debitor only if the $customerNumber and $contactNumber are empty! - if (empty($customerNumber) && empty($contactNumber)) { - if (!$this->configResolver->getParameter('price_requests_without_customerno', 'siso_price')) { - throw new PriceCalculationFailedException('No price request is sent without customer number.'); - } - - if ($this->useTemplateDebitorCustomerNumber) { - $customerNumber = $fallbackCustomerNumber; - } - ``` - -## Request to the ERP/Web.Connector - -The Web.Connector service uses the data from `PriceRequest` to build the ERP Request object. -The ERP request contains header information such as the customer number and an optional contact number and, if applicable, the delivery address. -Information about requested products is also part of the request. - -``` xml - - 469 - - - 10000 - - ... -``` - -??? note "Delivery information (required for calculating shipping costs)" - - ``` xml - - - - - - - - - - - Melanie Bourne - - - - - - Färberstraße 14 - - - Berlin - 12345 - - - - DE - - - - - - - - - - - - - - - - - - - - <MiddleName/> - <SesExtension/> - </Person> - <SesExtension> - <status>sameAsInvoice</status> - <store/> - </SesExtension> - </DeliveryParty> - </Delivery> - ``` - -??? note "Information about the requested products" - - ``` xml - <OrderLine> - <LineItem> - <ID/> - <SalesOrderID/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <TotalTaxAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <Price> - <PriceAmount/> - <BaseQuantity/> - </Price> - <Item> - <Name/> - <SellersItemIdentification> - <ID>SE0102</ID> - <ExtendedID/> - </SellersItemIdentification> - <BuyersItemIdentification> - <ID/> - </BuyersItemIdentification> - </Item> - <SesExtension/> - </LineItem> - <SesExtension/> - </OrderLine> - ``` - -## Response from the ERP/Web.Connector - -The ERP provides a price response using the UBL format. -The whole ERP response object is returned back to `RemotePriceProvider` and is used for price calculation. - -For each requested product an `orderLine` in the response can be provided. -If ERP doesn't recognize the requested product, it might not return any information about this product at all. -It is also possible that ERP returns more order lines than requested. - -This might have several reasons: - -- additional costs such as shipping costs, costs for payment on delivery and others -- discounts -- additional products, that are MUST HAVE, if some special products were requested, or e.g. if the price totals of ordered products is over a specific amount - -#### VAT - -If the ERP System does not provide information about VAT, the VAT can be determined in the shop. -In that case the shop uses [VatService](../pricing/price_api/localvatservice.md) to get the `vatPercent` by the `vatCode`. - -### SesExtension Fields - -|SesExtension Field|Type| -|--- |--- | -|`LineType`|int| -|`CostType`|string| -|`StockNumeric`|int| -|`OnStock`|bool| -|`VatCode`|string| -|`VatPercent`|float| -|`PriceIsIncVat`|bool| - -##### LineType - -The tag `LineType` controls whether the returned `orderLine` is a product or a cost. - -|LineType|Description| -|--- |--- | -|1|cost| -|2|product| - -##### CostType - -The tag `CostType` indicates which kind of cost is returned. - -|CostType|Description| -|--- |--- | -|`shipping`|indicates shipping costs| -|`charge`|indicates other (not shipping) costs| -|`discount`|indicates discounts| - -##### StockNumeric - -The tag `StockNumeric` indicates how many items are in stock. - -##### OnStock - -The tag `OnStock` indicates if the item is available in stock. - -##### VatCode - -The tag `VatCode` indicates the code for VAT rate, e.g. 'download' or 'food'. - -##### VatPercent - -The tag `VatPercent` indicates the VAT in %. - -##### PriceIsIncVat - -The tag `PriceIsIncVat` indicates, if the returned price includes VAT or not. - -!!! note - - Either `VatCode` or `VatPercent` must be returned from ERP. - -??? note "Example OrderLine" + Using a template debitor only works if price requests without customer number are enabled for the `RemotePriceProvider`. + They can be enabled/disabled in the [shop configuration settings](../../guide/basket/basket.md). - ``` xml - <OrderLine> - <LineItem> - <ID>123456789</ID> - <SalesOrderID/> - <UUID/> - <Note/> - <LineStatusCode/> - <Quantity>1</Quantity> - <LineExtensionAmount/> - <MinimumQuantity/> - <MaximumQuantity/> - <MinimumBackorderQuantity/> - <MaximumBackorderQuantity/> - <InspectionMethodCode/> - <PartialDeliveryIndicator/> - <BackOrderAllowedIndicator/> - <AccountingCostCode/> - <AccountingCost/> - <Delivery> - <MaximumQuantity/> - <LatestDeliveryDate/> - </Delivery> - <Price> - <PriceAmount>7.90</PriceAmount> - </Price> - <TotalTaxAmount/> - <Item singleElementArrays="Description"> - <SellersItemIdentification> - <ID>SE0103</ID> - <ExtendedID/> - </SellersItemIdentification> - <Name>DHL Standard</Name> - <Description>Shipping costs DHL Standard</Description> - </Item> - <SesExtension> - <LineType>1</LineType> - <CostType>shipping</CostType> - <StockNumeric/> - <OnStock/> - <VatCode/> - <VatPercent>19</VatPercent> - <PriceIsIncVat>1</PriceIsIncVat> - </SesExtension> - </LineItem> - </OrderLine> - ``` + When this setting is disabled, an exception is thrown in the `remotePriceProvider` and fallback is used. diff --git a/docs/guide/file_management.md b/docs/guide/file_management.md deleted file mode 100644 index 32efb2fa42..0000000000 --- a/docs/guide/file_management.md +++ /dev/null @@ -1,205 +0,0 @@ -# File Management - -## Handling binary files - -[[= product_name =]] supports multiple binary file handling mechanisms by means of an `IOHandler` interface. This feature is used by the [BinaryFile](../api/field_type_reference.md#binaryfile-field-type), [Media](../api/field_type_reference.md#media-field-type) and [Image](../api/field_type_reference.md#image-field-type) Field Types. - -### Native IO handler - -The IO API is organized around two types of handlers, both used by the IOService: - -- `eZ\Publish\IO\IOMetadataHandler`: Stores and reads metadata (validity, size, etc.) -- `eZ\Publish\IO\IOBinarydataHandler`: Stores and reads the actual binary data - -IO handlers can be configured using semantic configuration and are configurable per SiteAccess. -This is the default configuration: - -``` yaml -ezplatform: - system: - default: - io: - metadata_handler: default - binarydata_handler: default -``` - -Metadata and binary data handlers are configured under `ez_io`. Below is what the configuration looks like for the default handlers. It declares a metadata handler and a binary data handler, both labeled `default`. Both handlers are of type `flysystem`, and use the same Flysystem adapter, labeled `default` as well. - -``` yaml -ez_io: - metadata_handlers: - default: - flysystem: - adapter: default - binarydata_handlers: - default: - flysystem: - adapter: default -``` - -The 'default' Flysystem adapter's directory is based on your site settings, and will automatically be set to `%webroot_dir%/$var_dir$/$storage_dir$` (for example: `/path/to/ezplatform/public/var/site/storage`). - -!!! note - - When Legacy Bridge is enabled, the path will be automatically set to - `%ezpublish_legacy.root_dir%/$var_dir$/$storage_dir$` instead. - -#### Configure the permissions of generated files - -``` yaml -ezplatform: - system: - default: - io: - permissions: - files: 0750 #default is 0644 - directories: 0640 #default is 0755 -``` - -Both `files` and `directories` rules are optional. - -Default values are 0644 for files and 0755 for directories. - -!!! note - - Make sure to configure permissions using a number and *not* a string. If you write "0644" it will *not* be interpreted by PHP as an octal number, and unexpected permissions will be applied. - -!!! note - - When using the NFS [adapter](file_management.md#adapter), configure file permissions under the `oneup_flysystem` key instead, as follows: - - ``` yaml - oneup_flysystem: - adapters: - nfs_adapter: - local: - permissions: - file: - public: 0750 - dir: - public: 0640 - ``` - -### The native Flysystem handler - -[league/flysystem](http://flysystem.thephpleague.com/) (along with [FlysystemBundle](https://github.com/1up-lab/OneupFlysystemBundle/)) is an abstract file handling library. - -[[= product_name =]] uses it as the default way to read and write content in form of binary files. Flysystem can use the `local` filesystem (this is the default and officially supported configuration), but is also able to read/write to `sftp`, `zip` or cloud filesystems (`azure`, `rackspace`, `S3`). - -#### Handler options - -##### Adapter - -The adapter is the "driver" used by Flysystem to read/write files. Adapters can be declared using `oneup_flysystem` as follows: - -``` yaml -oneup_flysystem: - adapters: - default: - local: - directory: /path/to/directory -``` - -The way to configure other adapters can be found in the [bundle's online documentation](https://github.com/1up-lab/OneupFlysystemBundle/blob/master/Resources/doc/index.md#step3-configure-your-filesystems). Note that we do not use the filesystem configuration described in this documentation, only the adapters. - -### The DFS Cluster handler - -For clustering use we provide a custom metadata handler that stores metadata about your assets in the database. This is done as it is faster than accessing the remote NFS or S3 instance in order to read metadata. For further reading on setting this up, see [Clustering](clustering.md). - -## Binary and Media download - -Unlike image files, files stored in BinaryFile or Media Fields may be restricted to certain User Roles. As such, they are not publicly downloadable from disk, and are instead served by Symfony, using a custom route that runs the necessary checks. This route is automatically generated as the `url` property for those Field values. - -### The `content/download` route - -The route follows this pattern: `/content/download/{contentId}/{fieldIdentifier}/{filename}`. For example: `/content/download/68/file/My-file.pdf.` - -It also accepts optional query parameters: - -- `version`: the version number that the file must be downloaded for. Requires the `versionread` permission. If not specified, the published version is used. -- `inLanguage`: The language the file should be downloaded in. If not specified, the most prioritized language for the SiteAccess will be used. - -The [ez\_render\_field](twig_functions_reference.md#ez_render_field) Twig helper will by default generate a working link. - -### Download link generation - -To generate a direct download link for the `File` Field Type you have to create -a Route Reference with the `ez_route` helper, passing `content` and `File` Field identifier as parameters. -Optional parameter `inLanguage` may be used to specify File content translation. - -```twig - {% set routeReference = ez_route( 'ez_content_download', {'content': content, 'fieldIdentifier': 'file', 'inLanguage': content.prioritizedFieldLanguageCode } ) %} - <a href="{{ ez_path( routeReference ) }}">Download</a> -``` - -### REST API: `uri` property - -The `uri` property of Binary Fields in REST contains a valid download URL, of the same format as the Public API, prefixed with the same host as the REST Request. - -For [more information about REST API see the documentation](../api/rest_api_guide). - -## URL handling - -### IO URL decoration - -By default, images and binary files that are referenced by the content will be served from the same server as the application, for example `/var/site/storage/images/3/6/4/6/6463-1-eng-GB/kidding.png`. -This is the default semantic configuration: - -``` yaml -ezplatform: - system: - default: - io: - url_prefix: '$var_dir$/$storage_dir$' -``` - -`$var_dir$` and `$storage_dir$` are dynamic, [SiteAccess-aware settings](configuration.md#dynamic-settings-injection), and will be replaced by their values in the execution context. - -### Using a static server for images - -One common use case is to use an optimized nginx to serve images in an optimized way. The example image -above could be made available as `http://static.example.com/var/site/storage/images/3/6/4/6/6463-1-eng-GB/kidding.png` -by setting up a separate server that maps the `/path/to/ezplatform/public/var` directory. -The configuration would be as follows: - -``` yaml -ezplatform: - system: - default: - io: - url_prefix: 'http://static.example.com/$var_dir$/$storage_dir$' -``` - -!!! caution - - For security reasons, do not map `/path/to/ezplatform/public/` as - Document Root of the static server. - Map the `/var/` directory directly to `/path/to/ezplatform/public/var` instead. - -### Internals - -Any `BinaryFile` returned by the public API is prefixed with the value of this setting, internally stored as `ezsettings.scope.io.url_prefix`. - -#### `io.url_prefix` dynamic container setting - -Default value: `$var_dir$/$storage_dir$` -Example: `/var/site/storage` - -Used to configure the default URL decorator service (`ezpublish.core.io.default_url_decorator`), used by all binary data handlers to generate the URI of loaded files. It is always interpreted as an absolute URI, meaning that unless it contains a scheme (http://, ftp://), it will be prepended with a '/'. - -This setting is SiteAccess-aware. - -#### Services - -##### URL decorators - -A UrlDecorator decorates and undecorates a given string (URL). It has two mirror methods: `decorate` and `undecorate`. - -Two implementations are provided: `Prefix`, and `AbsolutePrefix`. They both add a prefix to a URL, but `AbsolutePrefix` will ensure that unless the prefix is an external URL, the result will be prepended with /. - -Three UrlDecorator services are introduced: - -- `ezpublish.core.io.prefix_url_decorator` used by the binary data handlers to decorate all URIs sent out by the API. Uses `AbsolutePrefix`. -- `ezpublish.core.io.image_fieldtype.legacy_url_decorator` used via the UrlRedecorator by various legacy elements (Converter, Storage Gateway, etc.) to generate its internal storage format for URIs. Uses a `Prefix`, not an `AbsolutePrefix`, meaning that no leading / is added. - -In addition, a UrlRedecorator service, `ezpublish.core.io.image_fieldtype.legacy_url_redecorator`, uses both decorators above to convert URIs between what is used on the new stack, and what format legacy expects (relative urls from the ezpublish root). diff --git a/docs/guide/file_management/binary_media_download.md b/docs/guide/file_management/binary_media_download.md new file mode 100644 index 0000000000..3280ed19f5 --- /dev/null +++ b/docs/guide/file_management/binary_media_download.md @@ -0,0 +1,37 @@ +--- +description: Create route to to enable binary and media files download. +--- + +# Binary and Media download + +You can restrict files stored in BinaryFile or Media Fields to certain user Roles. +These files are not publicly downloadable from disk, and are instead served by Symfony, using a custom route that runs the necessary checks. +This route is automatically generated as the `url` property for those Field values. + +## `content/download` route + +You have to create a route using the `download` route name. + +It accepts optional query parameters: + +- `version`: The content version number that the file is downloaded for. Requires the `content / read` permission for a published version and additionally `content / versionread` permission for an unpublished version. If not specified, uses the published version. +- `inLanguage`: The language the file should be downloaded in. If not specified, the most prioritized language for the SiteAccess is used. + +The [`ibexa_render_field`](../content_rendering/twig_function_reference/field_twig_functions.md#ibexa_render_field) Twig helper by default generates a working link. + +## Download link generation + +To generate a direct download link for the `File` Field Type you have to create +a [RouteReference](../content_rendering/urls_and_routes.md#routereference) with the `ibexa_route` helper, passing `content` and `File` Field identifier as parameters. +Optional parameter `inLanguage` may be used to specify File content translation. + +```html+twig +{% set routeReference = ibexa_route( 'ibexa.content.download', {'content': content, 'fieldIdentifier': 'file', 'inLanguage': content.prioritizedFieldLanguageCode } ) %} +<a href="{{ ibexa_path( routeReference ) }}">Download</a> +``` + +## REST API: `uri` property + +The `uri` property of Binary Fields in REST contains a valid download URL, of the same format as the Public API, prefixed with the same host as the REST Request. + +For [more information about REST API see the documentation](../../api/rest_api_guide). diff --git a/docs/guide/file_management/file_management.md b/docs/guide/file_management/file_management.md new file mode 100644 index 0000000000..5e49408982 --- /dev/null +++ b/docs/guide/file_management/file_management.md @@ -0,0 +1,144 @@ +--- +description: Configurations and management of binary files. +--- + +# File management + +## Access binary files + +To access binary files from the PHP API, use the `Ibexa\Core\IO\IOServiceInterface::loadBinaryFile()` method: + +```php +$file = $this->ioService->loadBinaryFile($field->value->id); +$fileContent = $this->ioService->getFileContents($file); +``` + +## Handling binary files + +[[= product_name =]] supports multiple binary file handling mechanisms by means of an `IOHandler` interface. This feature is used by the [BinaryFile](../../api/field_types_reference/binaryfilefield.md), [Media](../../api/field_types_reference/mediafield.md) and [Image](../../api/field_types_reference/imagefield.md) Field Types. + +### Native IO handler + +The IO API is organized around two types of handlers, both used by the IOService: + +- `Ibexa\Core\IO\IOMetadataHandler`: stores and reads metadata (such as validity or size) +- `Ibexa\Core\IO\IOBinarydataHandler`: stores and reads the actual binary data + +You can configure IO handlers using semantic configuration. IO handlers are configurable per SiteAccess. +See the default configuration: + +``` yaml +ibexa: + system: + default: + io: + metadata_handler: dfs + binarydata_handler: nfs +``` + +The adapter is the *driver* used by Flysystem to read/write files. Adapters are declared using `oneup_flysystem`. +Metadata and binary data handlers are configured under `ibexa_io`. See below the configuration for the default handlers. It declares a metadata handler and a binary data handler, both labeled `default`. Both handlers are of type `flysystem`, and use the same Flysystem adapter, labeled `default` as well. + +``` yaml +ibexa_io: + binarydata_handlers: + nfs: + flysystem: + adapter: nfs_adapter + metadata_handlers: + dfs: + legacy_dfs_cluster: + connection: doctrine.dbal.dfs_connection +``` + +The `nfs_adapter`'s directory is based on your site settings, and is automatically set to `$var_dir$/$storage_dir$` (for example, `/path/to/ibexa/public/var/site/storage`). + +#### Permissions of generated files + +``` yaml +ibexa: + system: + default: + io: + permissions: + files: 0750 #default is 0644 + directories: 0640 #default is 0755 +``` + +Both `files` and `directories` are optional. + +Default values: + +- 0644 for files +- 0755 for directories + +!!! note + + Make sure to configure permissions using a number and **not** a string. + "0644" is **not** interpreted by PHP as an octal number, and unexpected permissions can be applied. + +!!! note + + As SiteAccess configuration Flysystem's native Local NFS adapter is not supported, the following + configuration should be used: + + ``` yaml + oneup_flysystem: + adapters: + nfs_adapter: + custom: + service: ibexa.io.nfs.adapter.site_access_aware + ``` + + +### Native Flysystem handler + +[[= product_name =]] uses it as the default way to read and write content in form of binary files. Flysystem can use the `local` filesystem, but is also able to read/write to `sftp`, `zip` or cloud filesystems (`azure`, `rackspace`, `S3`). +[league/flysystem](http://flysystem.thephpleague.com/) (along with [FlysystemBundle](https://github.com/1up-lab/OneupFlysystemBundle/)) is an abstract file handling library. + +#### Handler options + +##### Adapter + +To be able to rely on dynamic SiteAccess-aware paths, you need to use Ibexa custom `nfs_adapter`. A basic configuration might look like the following: + +``` yaml +oneup_flysystem: + adapters: + nfs_adapter: + custom: + service: ibexa.io.nfs.adapter.site_access_aware +``` + +To learn how to configure other adapters, see the [bundle's online documentation](https://github.com/1up-lab/OneupFlysystemBundle/blob/main/doc/index.md#step3-configure-your-filesystems). + +!!! note + + Only the adapters are used here, not the filesystem configuration described in this documentation. + +### DFS Cluster handler + +For clustering, the platform provides a custom metadata handler that stores metadata about your assets in the database. +This is faster than accessing the remote NFS or S3 instance to read metadata. For further reading on setting this up, see [Clustering](../clustering.md). + +## Enabling BinaryFile Field indexing + +The indexing of all BinaryFile Fields is disabled by default. +To enable it, first, make sure you have installed Oracle Java/Open JDK 8 or higher and Apache Tika 1.20. +Next, in the `config/packages` folder create a `binary_files.yaml` file with the following configuration: + +``` yaml +ibexa_commerce_field_types: + binary_file_indexing: + enabled: true +``` + +To check what types are indexed, check the `ibexa.commerce.site_access.config.search.default.index_content` service container parameter. You can override this parameter for a specific SiteAccess by replacing `default` by its name +The following file types are indexed by default: + +``` yaml +- application/pdf +- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet +``` + +The default path to the Tika jar is specified with the `apache_tika_path` parameter in `config/packages/commerce/commerce_parameters.yaml`. diff --git a/docs/guide/file_management/handling_file_url.md b/docs/guide/file_management/handling_file_url.md new file mode 100644 index 0000000000..fc29d2cffa --- /dev/null +++ b/docs/guide/file_management/handling_file_url.md @@ -0,0 +1,69 @@ +--- +description: Manage files URL. +--- + +# File URL handling + +### IO URL decoration + +By default, images and binary files that are referenced by the content are served from the same server as the application, for example `/var/site/storage/images/3/6/4/6/6463-1-eng-GB/kidding.png`. +This is the default semantic configuration: + +``` yaml +ibexa: + system: + default: + io: + url_prefix: '$var_dir$/$storage_dir$' +``` + +`$var_dir$` and `$storage_dir$` are dynamic, [SiteAccess-aware settings](../configuration/configuration.md#dynamic-settings-injection), and are replaced by their values in the execution context. + +## Serving images with nginx + +One common use case is to use an optimized nginx to serve images in an optimized way. The previous example image +could be made available as `http://static.example.com/var/site/storage/images/3/6/4/6/6463-1-eng-GB/kidding.png` +by setting up a separate server that maps the `/path/to/ibexa/public/var` directory. +The configuration would be as follows: + +``` yaml +ibexa: + system: + default: + io: + url_prefix: 'https://static.example.com/$var_dir$/$storage_dir$' +``` + +!!! caution + + For security reasons, do not map `/path/to/ibexa/public/` as + Document Root of the static server. + Map the `/var/` directory directly to `/path/to/ibexa/public/var` instead. + +## `io.url_prefix` + +Any BinaryFile returned by the public API is prefixed with the value of this setting, internally stored as `ibexa.site_access.config.<scope>.io.url_prefix`. + +### `io.url_prefix` dynamic service container setting + +Default value: `$var_dir$/$storage_dir$` +Example: `/var/site/storage` + +You can use `io.url_prefix` to configure the default URL decorator service (`ibexa.core.io.default_url_decorator`), used by all binary data handlers to generate the URI of loaded files. It is always interpreted as an absolute URI, meaning that unless it contains a scheme (`http://`, `ftp://`), is prepended with a `/`. + +This setting is SiteAccess-aware. + +### Services + +#### URL decorators + +A `Ibexa\Core\IO\UrlDecorator` decorates and undecorates a specified string (URL). It has two mirror methods: `decorate` and `undecorate`. + +Two implementations are provided: `Prefix`, and `AbsolutePrefix`. They both add a prefix to a URL, but `AbsolutePrefix` ensures that unless the prefix is an external URL, the result is prepended with `/`. + +Three URL decorator services are introduced: + +- `Ibexa\Core\IO\UrlDecorator\AbsolutePrefix` used by the binary data handlers to decorate all URIs sent out by the API. Uses `AbsolutePrefix`. +- `Ibexa\Core\IO\UrlDecorator\Prefix` used through the `UrlRedecorator` by various legacy elements (converter, storage gateway, etc.) to generate its internal storage format for URIs. Uses a `Prefix`, not an `AbsolutePrefix`, meaning that no leading `/` is added. + +In addition, a URL redecorator service, `Ibexa\Core\IO\UrlDecorator\Prefix`, uses both previously mentioned decorators to convert URIs between what is used on the new stack, and what format legacy expects (relative URLs from the project root). \ No newline at end of file diff --git a/docs/guide/form_builder/create_custom_form_field.md b/docs/guide/form_builder/create_custom_form_field.md new file mode 100644 index 0000000000..7888e44745 --- /dev/null +++ b/docs/guide/form_builder/create_custom_form_field.md @@ -0,0 +1,98 @@ +--- +description: Extend a Form with a custom Form field to fit your particular needs. +edition: experience +--- + +# Create custom Form field + +You can extend the Form Builder by adding new Form fields or modifying existing ones. +Define new form fields in configuration. + +## Configure Form field + +For example, to create a Country Form field in the "Custom form fields" category, +provide the following configuration: + +``` yaml +[[= include_file('code_samples/forms/custom_form_field/config/packages/form_builder.yaml') =]] +``` + +Available attribute types are: + +|Type|Description| +|----|----| +|`string`|String| +|`text`|Text block| +|`integer`|Integer number| +|`url`|URL| +|`multiple`|Multiple choice| +|`select`|Dropdown| +|`checkbox`|Checkbox| +|`location`|Content Location| +|`radio`|Radio button| +|`action`|Button| +|`choices`|List of available options| + +Each type of Form field can have validators of the following types: + +- `required` +- `min_length` +- `max_length` +- `min_choices` +- `max_choices` +- `min_value` +- `max_value` +- `regex` +- `upload_size` +- `extensions` + +## Create mapper + +New types of fields require a mapper which implements the `EzSystems\EzPlatformFormBuilder\FieldType\Field\FieldMapperInterface` interface. + +To create a Country field type, implement the `FieldMapperInterface` interface in `src/FormBuilder/Field/Mapper/CountryFieldMapper.php`: + +``` php +[[= include_file('code_samples/forms/custom_form_field/src/FormBuilder/Field/Mapper/CountryFieldMapper.php') =]] +``` + +Then, register the mapper as a service: + +``` yaml +[[= include_file('code_samples/forms/custom_form_field/config/custom_services.yaml', 0, 7) =]] +``` + +Now you can go to Back Office and build a new form. +You should be able to see the new section in the list of available fields: + +![Custom form fields](../img/extending_form_builder_custom_form_fields.png) + +And a new Country Form field: + +![Country field](../img/extending_form_builder_country_field.png) + +## Modify existing Form fields + +Field or field attribute definition can be modified by subscribing to one of the following events: + +- `ezplatform.form_builder.field.<FIELD_ID>` +- `ezplatform.form_builder.field.<FIELD_ID>.<ATTRIBUTE_ID>` + +The following example adds a `custom` string attribute to `single_line` field definition. + +``` php +[[= include_file('code_samples/forms/custom_form_field/src/EventSubscriber/FormFieldDefinitionSubscriber.php') =]] +``` + +Register this subscriber as a service: + +``` yaml +[[= include_file('code_samples/forms/custom_form_field/config/custom_services.yaml', 0, 1) =]][[= include_file('code_samples/forms/custom_form_field/config/custom_services.yaml', 7, 11) =]] +``` + +## Access Form field definitions + +Field definitions are accessible through: + +- `\EzSystems\EzPlatformFormBuilder\Definition\FieldDefinitionFactory` in the back end +- global variable `eZ.formBuilder.config.fieldsConfig` in the front end diff --git a/docs/guide/form_builder/create_form_attribute.md b/docs/guide/form_builder/create_form_attribute.md new file mode 100644 index 0000000000..16e810b02e --- /dev/null +++ b/docs/guide/form_builder/create_form_attribute.md @@ -0,0 +1,112 @@ +--- +description: Create Form Builder form attribute +edition: experience +--- + +# Create Form Builder Form attribute + +You can create a Form attribute for new Form fields or existing ones. +To do it, you have to define a new Form attribute in the configuration. + +In the following example you can create new Form with `richtext_description` attribute that allows you to add formatted +description to the Form. + +## Configure Form attribute + +To create a `richtext_description` attribute, +provide the following configuration in `config/packages/ezplatform.yaml`: + +``` yaml +[[= include_file('code_samples/forms/custom_form_attribute/config/packages/form_attribute_config.yaml') =]] +``` + +## Create mapper + +The new Form attribute requires a `FieldAttributeTypeMapper`. Register the mapper as a service in `config/services.yaml`: + +``` yaml +[[= include_file('code_samples/forms/custom_form_attribute/config/custom_services.yaml') =]] +``` + +## Add Symfony form type + +The attribute needs to be editable for the form creator, so it needs to have a Symfony form type. +Add an `AttributeRichtextDescriptionType.php` file with the form type in the `src/FormBuilder/Form/Type/FieldAttribute` directory: + +``` php +[[= include_file('code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/FieldAttribute/AttributeRichtextDescriptionType.php') =]] +``` + +## Customize Form templates + +The template files for the front end should look as follows: + +- `templates/bundles/EzPlatformFormBuilderBundle/fields/config/form_fields.html.twig`: + +``` html+twig +[[= include_file('code_samples/templates/bundles/EzPlatformFormBuilderBundle/fields/config/form_fields.html.twig') =]] +``` + +- `templates/formtheme/formbuilder_checkbox_with_richtext_description.html.twig`: + +``` html+twig +[[= include_file('code_samples/templates/formtheme/formbuilder_checkbox_with_richtext_description.html.twig') =]] +``` + +## Add scripts + +Now you need to enable the RichText editor. Provide the required script in a new `src/Resources/public/js/formbuilder-richtext-checkbox.js` file: + +``` js +[[= include_file('code_samples/forms/custom_form_attribute/src/Resources/public/js/formbuilder-richtext-checkbox.js') =]] +``` + +Then, paste the highlighted part of the code into the `webpack.config.js` file: + +``` js hl_lines="39-42" +[[= include_file('code_samples/forms/custom_form_attribute/webpack.config.js') =]] +``` + +Clear the cache and regenerate the assets by running the following commands: + +``` bash +php bin/console cache:clear +php bin/console assets:install +yarn encore dev +``` + +## Implement Field + +Now you have to implement the Field, and make sure the value from the RichText attribute is passed on to the field form. + +Create a `src/FormBuilder/Form/Type/CheckboxWithRichtextDescriptionType.php` file. + +```php +[[= include_file('code_samples/forms/custom_form_attribute/src/FormBuilder/Form/Type/CheckboxWithRichtextDescriptionType.php') =]] +``` + +## Implement FieldMapper + +To implement a FieldMapper, create a `src/FormBuilder/FieldType/Field/Mapper/CheckboxWithRichtextDescriptionFieldMapper.php` file. + +```php +[[= include_file('code_samples/forms/custom_form_attribute/src/FormBuilder/FieldType/Field/Mapper/CheckboxWithRichtextDescriptionFieldMapper.php') =]] +``` + +Now, the attribute value can be stored in the new Form. + +## Create SubmissionConverter + +The new Field is based on checkbox, so to display the submissions of this field, you can use the `BooleanFieldSubmissionConverter`. + +Create a `src/FormBuilder/FormSubmission/Converter/RichtextDescriptionFieldSubmissionConverter.php` file. + +```php +[[= include_file('code_samples/forms/custom_form_attribute/src/FormBuilder/FormSubmission/Converter/RichtextDescriptionFieldSubmissionConverter.php') =]] +``` + +Now you can go to Back Office and build a new form. + +You should be able to see the new section in the list of available fields: + +![New form](new_form.png) \ No newline at end of file diff --git a/docs/guide/form_builder/customize_email_notifications.md b/docs/guide/form_builder/customize_email_notifications.md new file mode 100644 index 0000000000..1e9e1d7d25 --- /dev/null +++ b/docs/guide/form_builder/customize_email_notifications.md @@ -0,0 +1,47 @@ +--- +description: Adapt the form and content of emails sent out from the Form Builder. +edition: experience +--- + +# Customize email notifications + +Email is one of the Submit button options you can add to a form in the Form Builder. +Use it to configure a list of email addresses that get notifications about newly filled forms. + +![Email notification](../img/email_notification.png) + +## Override email template + +To customize the form submission email, override the `form_builder/form_submit_notification_email.html.twig` template. +It contains two blocks: `subject` and `body`. +Each of them is rendered independently and consists of three sets of parameters. + +|Parameter|Type|Description| +|---------|----|-----------| +|`content`|`eZ\Publish\API\Repository\Values\Content\Content`|Name of the form, its Content Type| +|`form`|`EzSystems\EzPlatformFormBuilder\FieldType\Model\Form`|Definition of the form| +|`data`|`EzSystems\EzPlatformFormBuilder\FieldType\Model\FormSubmission`|Sent data| + +## Configure sender details + +Some email providers require a sender address to be set, so to avoid unsent emails when using Form Builder, +it is recommended to configure `sender_address` in `config/packages/swiftmailer.yaml`. +This email acts as a sender and return address for all bounced messages. + +!!! note + + Since November 2021 the Swift Mailer is no longer supported and the integration with Symfony is deprecated in Symfony 6.0. + The Swift Mailer got replaced by the Symfony Mailer. + +Add `sender_address` entry to `config/packages/swiftmailer.yaml`: + +```yaml +swiftmailer: + url: '%env(MAILER_URL)%' + spool: { type: 'memory' } + sender_address: '%env(MAILER_SENDER_ADDRESS)%' +``` + +In the `.env` file, define a new environment variable: +`MAILER_SENDER_ADDRESS=mail@example.com` +and configure your mail server connection details in the `MAILER_URL` environmental variable. diff --git a/docs/guide/form_builder/forms.md b/docs/guide/form_builder/forms.md new file mode 100644 index 0000000000..4397c189ed --- /dev/null +++ b/docs/guide/form_builder/forms.md @@ -0,0 +1,44 @@ +--- +description: Form Builder enables creating dynamic forms to use in surveys, questionnaires, sign-up forms and others. +edition: experience +--- + +# Forms + +You can build forms consisting of different fields in the Form Builder. + +!!! tip + + To learn how to get, create, and delete form submissions by using the PHP API, + see [Managing forms](../../api/public_php_api_managing_forms.md). + +[[% include 'snippets/forms_caution.md' %]] + +## Existing Form fields + +### Captcha field + +The Captcha Form field is based on [Gregwar/CaptchaBundle](https://github.com/Gregwar/CaptchaBundle). + +![Captcha field](../img/extending_form_builder_captcha_default.png) + +You can customize the field by adding configuration to `config/packages/gregwar_captcha.yaml` under `gregwar_captcha`: + +``` yaml +gregwar_captcha: + as_url: true + width: 150 + invalid_message: Code does not match, please retry. + reload: true +``` + +The example configuration above resizes the Captcha image (line 3), changes the error message (line 4), +and enables the user to reload the code (line 5). + +![Custom captcha field](../img/extending_form_builder_captcha_result.png) + +For information about available options, see [Gregwar/CaptchaBundle's documentation.](https://github.com/Gregwar/CaptchaBundle#options) + +!!! note + + If your installation uses Varnish to manage content cache, you must modify the configuration to avoid issues with the Captcha field. For more information, see [Ensure proper captcha behavior](../cache/symfony_reverse_proxy.md#ensure-proper-captcha-behavior). diff --git a/docs/guide/forms/form_api/data_processor_events.md b/docs/guide/forms/form_api/data_processor_events.md index 9f79b2913a..00fba4cb9d 100644 --- a/docs/guide/forms/form_api/data_processor_events.md +++ b/docs/guide/forms/form_api/data_processor_events.md @@ -1,4 +1,4 @@ -# Data processor events [[% include 'snippets/commerce_badge.md' %]] +# Data processor events You can listen to two events for each data processor to extend its logic. An event is triggered before and after the execution of the data processor. diff --git a/docs/guide/forms/form_api/dataprocessors.md b/docs/guide/forms/form_api/dataprocessors.md index b0e5c18cab..36ff9866dc 100644 --- a/docs/guide/forms/form_api/dataprocessors.md +++ b/docs/guide/forms/form_api/dataprocessors.md @@ -1,4 +1,4 @@ -# Data processors [[% include 'snippets/commerce_badge.md' %]] +# Data processors Data processors are executed after a form is submitted. You can use any number of data processors per form. The configuration lists data processors that are executed in sequence, for example: @@ -69,7 +69,7 @@ The data processor sets the following fields in the User Content item: - `ses_hastopay_vat` - `ses_display_vat` -[[= product_name_com =]] provides a standard event for this handler which adds a prefix to the login name of the user. +[[= product_name =]] provides a standard event for this handler, which adds a prefix to the login name of the user. The prefix can be defined in the configuration key `data_processor_ez_user_login_prefix`. ``` xml diff --git a/docs/guide/forms/form_api/form_api.md b/docs/guide/forms/form_api/form_api.md index c82586aa0a..7f10c0a899 100644 --- a/docs/guide/forms/form_api/form_api.md +++ b/docs/guide/forms/form_api/form_api.md @@ -1,4 +1,4 @@ -# Form API [[% include 'snippets/commerce_badge.md' %]] +# Form API ## PreDataProcessor and DataProcessors @@ -16,8 +16,8 @@ Every `dataProcessor` can set an additional error message that is displayed for ## Form validators -Besides standard [Symfony validators,](http://symfony.com/doc/3.4/validation.html) -[[= product_name_com =]] offers additional validators. +Besides standard [Symfony validators,]([[= symfony_doc =]]/validation.html) +[[= product_name =]] offers additional validators. ### ZIP validator diff --git a/docs/guide/forms/form_api/predataprocessors.md b/docs/guide/forms/form_api/predataprocessors.md index 76094e7465..8a71c2f1d7 100644 --- a/docs/guide/forms/form_api/predataprocessors.md +++ b/docs/guide/forms/form_api/predataprocessors.md @@ -1,4 +1,4 @@ -# Pre-data processors [[% include 'snippets/commerce_badge.md' %]] +# Pre-data processors A pre-data processor is executed before a form is submitted. You can use one pre-data processor per form. It pre-fills the form with data. diff --git a/docs/guide/forms/form_templates.md b/docs/guide/forms/form_templates.md index a0eb1d3a79..3b99cf8155 100644 --- a/docs/guide/forms/form_templates.md +++ b/docs/guide/forms/form_templates.md @@ -1,4 +1,4 @@ -# Form templates [[% include 'snippets/commerce_badge.md' %]] +# Form templates ### Template list diff --git a/docs/guide/forms/forms.md b/docs/guide/forms/forms.md index fae67e54dc..f5b6b127a2 100644 --- a/docs/guide/forms/forms.md +++ b/docs/guide/forms/forms.md @@ -1,6 +1,6 @@ -# Forms [[% include 'snippets/commerce_badge.md' %]] +# Forms -[[= product_name_com =]] offers a unified way of handling all one-page forms. +[[= product_name =]] offers a unified way of handling all one-page forms. One-page forms have several common criteria: @@ -8,7 +8,7 @@ One-page forms have several common criteria: - After submitting some processes are executed in the backend. - After server response the user sees a confirmation page with a success or error message. -[[= product_name_com =]] uses [Symfony forms](http://symfony.com/doc/3.4/forms.html) as a part of the solution. +[[= product_name =]] uses [Symfony forms]([[= symfony_doc =]]//forms.html) as part of the solution. You can pre-fill the form with default values when it is loaded for the first time. The process that pre-fills the form is called a [pre-data processor](form_api/predataprocessors.md). diff --git a/docs/guide/forms/implement_custom_one_page_form.md b/docs/guide/forms/implement_custom_one_page_form.md deleted file mode 100644 index 40d9a7858c..0000000000 --- a/docs/guide/forms/implement_custom_one_page_form.md +++ /dev/null @@ -1,397 +0,0 @@ -# Implement custom one-page form [[% include 'snippets/commerce_badge.md' %]] - -This example shows how to implement a form for ordering the product catalog in a printed form. - -The user inputs their email address and confirms that they want to order the catalog. -An email is then sent to the shop administrator. - -## Step 1: Build the form components - -The basic form components are: - -- Form entity -- Form type -- Form template - -### Form entity - -Form entity is a simple class that holds the data. No business logic is defined here. -This is the place where you have to define: - -- form attributes -- [validation](form_api/form_api.md#form-validators) - -Every form entity has to extend `AbstractFormEntity`. - -``` php -namespace Company\Bundle\ProjectBundle\Form; - -use Silversolutions\Bundle\EshopBundle\Entities\Forms\AbstractFormEntity; -use Symfony\Component\Validator\Constraints as Assert; -use Silversolutions\Bundle\EshopBundle\Entities\Forms\Constraints as SesAssert; - -class OrderCatalog extends AbstractFormEntity -{ - /** - * @Assert\NotBlank() - * - * @var bool - */ - protected $orderCatalog; - - /** - * @Assert\NotBlank() - * @SesAssert\Email() - * - * @var string - */ - protected $email; - - /** - * @return string - */ - public function getOrderCatalog() - { - return $this->orderCatalog; - } - - /** - * @param string $orderCatalog - */ - public function setOrderCatalog($orderCatalog) - { - $this->orderCatalog = $orderCatalog; - } - - /** - * @return string - */ - public function getEmail() - { - return $this->email; - } - - /** - * @param string $email - */ - public function setEmail($email) - { - $this->email = $email; - } -} -``` - -### Form type - -Form type defines which form attributes are rendered and how. -You can define other data here, such as labels, CSS classes, data attributes, etc. - -You define this type as a service, so you can inject any logic that you need. - -``` php -namespace Company\Bundle\ProjectBundle\Form\Type; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use Silversolutions\Bundle\TranslationBundle\Services\TransService; -use Siso\Bundle\ToolsBundle\Service\CountryServiceInterface; -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraint; - -class OrderCatalogType extends AbstractType -{ - /** - * Dependency to the silversolutions translation service. - * - * @var \Silversolutions\Bundle\TranslationBundle\Services\TransService - */ - protected $transService; - - /** - * @param TransService $transService - */ - public function __construct( - TransService $transService - ) { - $this->transService = $transService; - } - - /** - * Builds the form with all fields in required type and sets fields options - * - * @param FormBuilderInterface $builder - * @param array $options - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder - ->add('email', 'text', array( - 'required' => true, - 'label' => $this->transService->translate('email') - )) - ->add('orderCatalog', 'checkbox', array( - 'required' => true, - 'label' => $this->transService->translate('orderCatalog') - )) - ; - } - - /** - * configure form options - * - * @param OptionsResolver $resolver - * @return void - * - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults(array( - 'validation_groups' => function(FormInterface $form) { - $groups = array(); - $groups[] = Constraint::DEFAULT_GROUP; - return $groups; - }, - )); - } - - /** - * Returns the name of this type. - * - * @return string The name of this type - */ - public function getName() - { - return 'company_order_catalog_type'; - } -} -``` - -Service definition: - -``` xml -<service id="company.order_catalog_type" class="Company\Bundle\ProjectBundle\Form\Type\OrderCatalogType"> - <argument type="service" id="silver_trans.translator" /> -</service> -``` - -### Form template - -Then you need to prepare template that renders the form. -See [How to render Symfony forms](https://symfony.com/doc/3.4/form/form_customization.html) for more information. - -``` html+twig -{% extends "SilversolutionsEshopBundle::pagelayout.html.twig"|st_resolve_template %} - -{% block content %} -<form action="{{ path('silversolutions_service', {'formTypeResolver': 'order_catalog'}) }}" - method="post" {{ form_enctype(form) }}> - {{ form_errors(form) }} - - <div{% if form.email.vars.errors is not empty %} class="error"{% endif %}> - {{ form_label(form.email) }} - {{ form_widget(form.email) }} - </div> - <div{% if form.orderCatalog.vars.errors is not empty %} class="error"{% endif %}> - {{ form_label(form.orderCatalog) }} - {{ form_widget(form.orderCatalog) }} - </div> - {{ form_rest(form) }} - <span>* {{ 'required fields'|st_translate }}</span> - <button type="submit" class="button right" name="order_catalog">{{ 'Order Catalog'|st_translate }}</button> -</form> -{% endblock %} -``` - -## Step 2: Prefill the form and implement the processes behind - -### Prefill the form - -You can implement a service that pre-fills the form with default data. -This uses `preDataProcessor`. - -This step is optional. - -``` php -namespace Company\Bundle\ProjectBundle\Service\DataProcessor; - -use Silversolutions\Bundle\EshopBundle\Services\Forms\DataProcessor\AbstractDataProcessor; -use Silversolutions\Bundle\EshopBundle\Services\CustomerProfileData\CustomerProfileDataServiceInterface; -use Company\Bundle\ProjectBundle\Form\OrderCatalog; - -class PreFillOrderCatalogDataProcessor extends AbstractDataProcessor -{ - const SUCCESSFUL_LAST_RESULT_KEY = 'order_catalog'; - - /** @var CustomerProfileDataServiceInterface */ - protected $customerProfileDataService; - - public function __construct( - CustomerProfileDataServiceInterface $customerProfileDataService, - ) { - $this->customerProfileDataService = $customerProfileDataService; - } - - /** - * @param NormalizedEntity $formEntity - * @param null $lastResult - * @param Response $response - * @return mixed|null - * @throws \Silversolutions\Bundle\EshopBundle\Exceptions\FormDataProcessorException - */ - public function execute(NormalizedEntity $formEntity, $lastResult = null, Response $response = null) - { - if (!$this->customerProfileDataService->isUserAnonymous()) { - - /** @var OrderCatalog $orderCatalog */ - $orderCatalog = $formEntity->getOriginalForm(); - - //prefill form with user email address - $customerProfileData = $this->customerProfileDataService->getCustomerProfileData(); - $orderCatalog->setEmail($customerProfileData->sesUser->email); - } - - return $lastResult; - } -} -``` - -Service definition: - -``` xml -<service id="company.pre_data_processor.pre_fill_order_catalog" - class="Company\Bundle\ProjectBundle\Service\DataProcessor\PreFillOrderCatalogDataProcessor"> - <argument type="service" id="ses.customer_profile_data.ez_erp" /> -</service> -``` - -### Implement the processes behind - -Implement one or more `dataProcessors` that are executed after the form is submitted. - -``` php -namespace Company\Bundle\ProjectBundle\Service\DataProcessor; - -use Silversolutions\Bundle\EshopBundle\Entities\Forms\Normalize\Entity as NormalizedEntity; -use Silversolutions\Bundle\EshopBundle\Services\Forms\DataProcessor\AbstractDataProcessor; -use Silversolutions\Bundle\TranslationBundle\Services\TransService; -use Siso\Bundle\ToolsBundle\Service\MailHelperServiceInterface; - -class OrderCatalogSendEmailDataProcessor extends AbstractDataProcessor -{ - const SUCCESSFUL_LAST_RESULT_KEY = 'mail_send'; - const SUBJECT_CONTACT_FORM = 'Order catalog e-mail'; - - /** @var TransService $translation */ - protected $translation; - - /** @var MailHelperServiceInterface */ - protected $mailService; - - /** - * contains mail values from the configuration such as mail receiver or mail sender - * - * @var array $mailValues - */ - protected $mailValues; - - /** - * @param MailHelperServiceInterface $mailService - * @param \Silversolutions\Bundle\TranslationBundle\Services\TransService $translation - */ - public function __construct( - MailHelperServiceInterface $mailService, - TransService $translation - ) { - $this->mailService = $mailService; - $this->translation = $translation; - } - - /** - * sets mail values from the configuration - * - * @param array $mailValues - */ - public function setMailValues($mailValues) - { - $this->mailValues = $mailValues; - } - - /** - * @param NormalizedEntity $formEntity - * @param array|null $lastResult - * @param Response $response - * @return mixed|null - * @throws \Silversolutions\Bundle\EshopBundle\Exceptions\FormDataProcessorException - */ - public function execute(NormalizedEntity $formEntity, $lastResult = null, Response $response = null) - { - try { - $sender = $this->mailValues['mailSender']; - $recipient = $this->mailValues['orderCatalogMailReceiver']; - - $this->mailService->sendMailWithRenderedTemplate( - $sender, - $recipient, - self::SUBJECT_CONTACT_FORM, - 'CompanyBundle:Emails:order_catalog.txt.twig', - 'CompanyBundle:Emails:order_catalog.html.twig', - array( - 'orderCatalogMailReceiver' => $recipient, - 'form' => $formEntity->originalForm, - ) - ); - - $lastResult[self::SUCCESSFUL_LAST_RESULT_KEY] = true; - - } catch (\Exception $e) { - $lastResult['_exceptions'][] = 'Error occured when sending the email.'; - } - - return $lastResult; - } -} -``` - -Service definition: - -``` xml -<service id="company.data_processor.order_catalog_send_email" - class="Company\Bundle\ProjectBundle\Service\DataProcessor\OrderCatalogSendEmailDataProcessor"> - <argument type="service" id="siso_tools.mailer_helper" /> - <argument type="service" id="silver_trans.translator" /> - <call method="setMailValues"> - <argument>$ses_swiftmailer;siso_core$</argument> - </call> -</service> -``` - -## Step 3: Configure the form - -Create form configuration to build a fully-functional one-page form: - -``` yaml -parameters: - ses_forms.configs.order_catalog: - modelClass: Company\Bundle\ProjectBundle\Form\OrderCatalog - typeService: company.order_catalog_type - template: CompanyProjectBundle:Forms:order_catalog.html.twig - invalidMessage: error_message_order_catalog - validMessage: success_order_catalog - preDataProcessor: company.pre_data_processor.pre_fill_order_catalog - dataProcessors: - - company.data_processor.order_catalog_send_email -``` - -### Form URL - -You can use any of the predefined [`FormsController::formsAction` routes](form_templates.md) to call the form, -or even define a new one. In this example use following route: - -``` html+twig -<form action="{{ path('silversolutions_service', {'formTypeResolver': 'order_catalog'}) }}" - method="post" {{ form_enctype(form) }}> -``` - -Call the form by the `/service/order_catalog` URL. diff --git a/docs/guide/forms/using_recaptcha.md b/docs/guide/forms/using_recaptcha.md index 41c996b6dc..23d16a1db2 100644 --- a/docs/guide/forms/using_recaptcha.md +++ b/docs/guide/forms/using_recaptcha.md @@ -1,4 +1,4 @@ -# Using reCAPTCHA [[% include 'snippets/commerce_badge.md' %]] +# Using reCAPTCHA The EWZRecaptchaBundle provides a reCAPTCHA form field for Symfony. It includes an additional validator. diff --git a/docs/guide/http_cache.md b/docs/guide/http_cache.md deleted file mode 100644 index 8b14ccd835..0000000000 --- a/docs/guide/http_cache.md +++ /dev/null @@ -1,899 +0,0 @@ -# HTTP cache - -[[= product_name =]] provides highly advanced caching features needed for its own content views, -taking advantage of sophisticated techniques to make Varnish and Fastly act as the view cache for the system. -This and other features allow [[= product_name =]] to be scaled up to serve high traffic websites and applications. - -This is handled by the [ezplatform-http-cache](https://github.com/ezsystems/ezplatform-http-cache) bundle, -which extends [friendsofsymfony/http-cache-bundle](https://foshttpcachebundle.readthedocs.io/en/2.8.0/), -a Symfony community bundle that in turn extends [Symfony HTTP cache](http://symfony.com/doc/5.1/http_cache.html). - -For content view responses coming from [[= product_name =]] itself, this means: - -- Cache is **[content-aware](#content-aware-http-cache)**, always kept up-to-date by invalidating using cache tags. -- Cache is **[context-aware](#context-aware-http-cache)**, to cache request for logged-in users by varying on user permissions. - -All of this works across all the supported reverse proxies: - -- Symfony HttpCache Proxy - limited to a single server, and limited performance/features -- [Varnish](https://varnish-cache.org/) -- [Fastly](https://www.fastly.com/) - Varnish-based CDN service - -You can take advantage of all these features in custom controllers as well. - -## Configuration - -### Content view configuration - -You can configure cache globally for content views in `config/packages/ezplatform.yaml`: - -``` yaml -ezplatform: - system: - my_siteaccess: - content: - # Activates HTTP cache for content - view_cache: true - # Activates expiration based HTTP cache for content (very fast) - ttl_cache: true - # Number of seconds an HTTP response cache is valid (if ttl_cache is true, and if no custom s-maxage is set) - default_ttl: 7200 -``` - -You may want to set a high default time to live (TTL) (`default_ttl`) to have a high cache hit ratio on your installation. -As the system takes care of purges, the cache shouldn't become stale with exception of grace handing in Varnish and Fastly. - -### Cache header rules - -A few redirect and error pages are served via the ContentView system, and if you set a high `default_ttl`, they could also end up being served from cache. - -To avoid this, the installation ships with configuration to match those specific situations and set a much lower TTL. -[FOSHttpCacheBundle matching rules](http://foshttpcachebundle.readthedocs.io/en/2.8.0/reference/configuration/headers.html) allow you to specify a different TTL: - -``` yaml -fos_http_cache: - cache_control: - rules: - # Make sure cacheable (fresh) responses from Ibexa Platform which are errors/redirects get lower TTL than default_ttl - - - match: - match_response: 'response.isFresh() && ( response.isServerError() || response.isClientError() || response.isRedirect() )' - headers: - overwrite: true - cache_control: - max_age: 5 - s_maxage: 20 -``` - -Similarly, by default we apply performance tuning to avoid crawlers affecting the setup too much, by caching of generic 404s and similar error pages in the following way: - -``` yaml -fos_http_cache: - cache_control: - rules: - # Example of performance tuning, force TTL on 404 pages to avoid crawlers, etc., taking too much load - # Should not be set too high, as cached 404s can cause issues for future routes, URL aliases, wildcards, etc. - - - match: - match_response: '!response.isFresh() && response.isNotFound()' - headers: - overwrite: true - cache_control: - public: true - max_age: 0 - s_maxage: 20 -``` - -### Setting time-to-live value for Page blocks - -For the Page Builder, block cache by default respects `$content.ttl_cache$` and `$content.default_ttl$` settings. -However, if the given block value has a since / till date, -it is taken into account for the TTL calculation for the block and also for the whole page. - -To overload this behavior, listen to [`BlockResponseEvents::BLOCK_RESPONSE`](../extending/extending_page.md#block-render-response), -and set priority to `-200` to adapt what Page Field Type does by default. -For example, in order to disable cache for the block, use `$event->getResponse()->setPrivate()`. - -### When to use ESI - -[Edge Side Includes](https://en.wikipedia.org/wiki/Edge_Side_Includes) (ESI) can be used -to split out the different parts of a web page into separate concerns that can be freely reused as pieces by Reverse proxy. - -However, in practice ESI means every sub request needs to start over from scratch from application perspective. -And while you can tune your system to reduce this, it always causes additional overhead: - -- When cache is cold on all or some of the sub-requests -- With Symfony Proxy (AppCache) there is always some overhead, even on warm cache (hits) -- In development environment - -It may differ depending on your system, but in general, we recommend staying below 5 ESI -requests per page and only using them for parts that are the same across the whole site or larger parts of it. - -You should not use ESI for parts that are effectively uncached, -as it will cause your reverse proxy to wait for back end and not be able to deliver cached pages directly. - -!!! note "ESI limitations with the URIElement SiteAccess matcher" - - Note that sharing ESIs across SiteAccesses when using URI matching is not possible by design, - as the URI contains the SiteAccess name encoded in its path information. - -## Using Varnish or Fastly - -As [[= product_name =]] is built on top of Symfony, it uses standard HTTP cache headers. -By default, the Symfony reverse proxy, written in PHP, is used to handle cache. -You can easily replace it with reverse proxies like Varnish or CDN like Fastly. - -This is highly recommended as they provide far better performance and more advanced features like grace handling, configurable logic through VCL and much more. - -!!! note - - Use of Varnish or Fastly is a requirement for a [Clustering](clustering.md) setup, as Symfony Proxy does not support - sharing cache between several application servers. - -### Recommended VCL base files - -For setup to work properly with your installation, you'll need to adapt one of the provided VCL files as a basis: - -- [Varnish VCL xkey example](https://github.com/ezsystems/ezplatform-http-cache/blob/2.0/docs/varnish/vcl/varnish5.vcl) -- Fastly VCL can be found in `vendor/ezsystems/ezplatform-http-cache-fastly/fastly` in Enterprise version - -!!! tip - - When you extend [FOSHttpCacheBundle](https://foshttpcachebundle.readthedocs.io/en/2.8.0/), - you can also adapt your VCL further with [FOSHttpCache documentation](http://foshttpcache.readthedocs.org/en/latest/varnish-configuration.html) - in order to use additional features. - -### Configure [[= product_name =]] - -Configuring [[= product_name =]] for Varnish or Fastly involves a few steps, starting with configuring proxy. - -#### Configuring Symfony front controller - -In a pure Symfony installation you would normally adapt front controller (`web/app.php`) -in order to configure Symfony to [work behind a load balancer or a reverse proxy](https://symfony.com/doc/5.1/deployment/proxies.html), -however in [[= product_name =]] can cover most use cases by setting supported environment variables using: - -- `APP_HTTP_CACHE`: To enable (`"1"`) or disable (`"0"`) use of Symfony HttpCache reverse proxy - - *Must* be disabled when using Varnish or Fastly. - - If not set, it is automatically disabled for `APP_ENV=dev` for local development needs, otherwise enabled. -- `TRUSTED_PROXIES`: String with trusted IP, multiple proxies can be configured with a comma, i.e. `TRUSTED_PROXIES="192.0.0.1,10.0.0.0/8"` - -!!! caution "Careful when trusting dynamic IP using TRUST_REMOTE value or similar" - - On Platform.sh, Varnish does not have a static IP, like with [AWS LB.](https://symfony.com/doc/5.1/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly) - For this `TRUSTED_PROXIES` env variable supports being set to value "TRUST_REMOTE", which is equal to: - - ```php - Request::setTrustedProxies([$request->server->get('REMOTE_ADDR')], Request::HEADER_X_FORWARDED_ALL); - ``` - - When trusting remote IP like this, make sure your application is only accessible through Varnish. - If it is accessible in other ways, you may end up trusting e.g. the IP of client browser instead, which would be a serious security issue. - - Make sure that **all** traffic always comes from the trusted proxy/load balancer, - and that there is no other way to configure it. - -See [Examples for configuring [[= product_name =]]](#examples-for-configuring-eZ-Platform) for how these variables can be set. - -#### Update YML configuration - -Secondly, you need to tell [[= product_name =]] to use an HTTP-based purge client (specifically the FosHttpCache Varnish purge client), -and specify the URL Varnish can be reached on (in `config/packages/ezplatform.yaml`): - -| Configuration | Parameter| Environment variable| Possible values| -|---------|--------|--------|----------| -| `ezplatform.http_cache.purge_type` | `purge_type` | `HTTPCACHE_PURGE_TYPE` | local, varnish, fastly | -| `ezplatform.system.<scope>.http_cache.purge_servers` | `purge_server` | `HTTPCACHE_PURGE_SERVER` | Array of URLs to proxies when using Varnish or Fastly (`https://api.fastly.com`) | -| `ezplatform.system.<scope>.http_cache.varnish_invalidate_token` | `varnish_invalidate_token` | `HTTPCACHE_VARNISH_INVALIDATE_TOKEN` | (Optional) For token-based authentication | -| `ezplatform.system.<scope>.http_cache.fastly.service_id` | `fastly_service_id` | `FASTLY_SERVICE_ID` | Service ID to authenticate with Fastly | -| `ezplatform.system.<scope>.http_cache.fastly.key` | `fastly_key` | `FASTLY_KEY` | Service key/token to authenticate with Fastly | - -If you need to set multiple purge servers configure them in the YAML configuration, -instead of parameter or environment variable as they only take single string value. - -Example configuration for Varnish as reverse proxy, assuming [front controller has been configured](#configuring-symfony-front-controller): - -``` yaml -ezplatform: - http_cache: - purge_type: varnish - - system: - # Assuming that my_siteaccess_group contains both your front-end and back-end SiteAccesses - my_siteaccess_group: - http_cache: - # Fill in your Varnish server(s) address(es). - purge_servers: [http://my.varnish.server:8081] -``` - -!!! note "Invalidating Varnish cache using tokens" - - In setups where the Varnish server IP can change (for example on platform.sh/Ibexa Cloud), - you can use token-based cache invalidation via [`ez_purge_acl`.](https://github.com/ezsystems/ezplatform-http-cache/blob/v2.1.0/docs/varnish/vcl/varnish5.vcl#L174) - - In such a case use a strong, secure hash and make sure to keep the token secret. - -#### Ensure proper Captcha behavior [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -If your installation uses Varnish and you want users to be able to configure and use Captcha in their forms, -you must enable the sending of Captcha data as a response to an Ajax request. -Otherwise, Varnish prohibits the transfer of Captcha data to the form, and users see an empty image. - -To enable sending Captcha over Ajax, modify the configuration file, for example `config/packages/ezplatform.yaml`, by adding the following code: - -``` yaml -ezplatform: - system: - default: - form_builder: - captcha: - use_ajax: <true|false> -``` - -#### Custom Captcha block [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -If you created a custom Captcha block for your site by overriding the default file (`vendor/gregwar/captcha-bundle/Resources/views/captcha.html.twig`), -you must make the following changes to the custom block template file: - -- change the name of the block to `ajax_captcha_widget` -- include the JavaScript file: - -``` js -{{ encore_entry_script_tags('ezplatform-form-builder-ajax-captcha-js', null, 'ezplatform') }} -``` - -- add a data attribute with a `fieldId` value: - -``` js -data-field-id="{{ field.id }}" -``` - -As a result, your file should be similar to [this example.](https://github.com/ezsystems/ezplatform-form-builder/blob/master/src/bundle/Resources/views/themes/standard/fields/captcha.html.twig) - -For more information about configuring Captcha fields, see [Captcha field](../extending/extending_form_builder.md#captcha-field). - -#### Using Fastly as HttpCache proxy - -[Fastly](https://www.fastly.com/) delivers Varnish as a CDN service and is supported with [[= product_name =]]. -See [Fastly documentation](https://docs.fastly.com/guides/basic-concepts/how-fastlys-cdn-service-works) to learn how it works. - -##### Configuring Fastly in YML - -``` yaml -ezplatform: - http_cache: - purge_type: fastly - - system: - # Assuming that my_siteaccess_group contains both your front-end and back-end SiteAccesses - my_siteaccess_group: - http_cache: - purge_servers: [https://api.fastly.com] - fastly: - # See below for obtaining these values - service_id: "ID" - key: "token" -``` - -##### Configuring Fastly using environment variables - -Example when using `.env` file: - -``` -SYMFONY_HTTP_CACHE="0" - -HTTPCACHE_PURGE_TYPE="fastly" -# Optional -HTTPCACHE_PURGE_SERVER="https://api.fastly.com" - -# See below for obtaining service ID and application key/token -FASTLY_SERVICE_ID="ID" -FASTLY_KEY="token" -``` - -##### Configuring Fastly on Platform.sh - -If you are using Platform.sh, it's best to configure all environment variables via [Platform.sh variables](https://docs.platform.sh/frameworks/ibexa/fastly.html). -You also need to [disable Varnish](https://docs.platform.sh/frameworks/ibexa/fastly.html#remove-varnish-configuration), -which is enabled by default in the provided configuration for Platform.sh. - -##### Obtaining Fastly service ID and API token - -The service ID can be obtained by logging in to http://fastly.com and clicking **CONFIGURE** in the top menu, -then **Show service ID** at the top left of the page. - -See [the Fastly guide](https://docs.fastly.com/guides/account-management-and-security/using-api-tokens) for -instructions on how to generate a Fastly API token. -The token needs the `purge_select` and `purge_all` scopes. - -#### Examples for configuring [[= product_name =]] - -Below you will find the most common examples for configuring the system completely by environment variables. - -Example when using `.env` file: - -``` bash -SYMFONY_HTTP_CACHE="0" -TRUSTED_PROXIES="127.0.0.1" -HTTPCACHE_PURGE_TYPE="varnish" -HTTPCACHE_PURGE_SERVER="http://varnish:80" -``` - -Example for Apache with `mod_env`: - -```apacheconfig -SetEnv SYMFONY_HTTP_CACHE 0 -SetEnv TRUSTED_PROXIES "127.0.0.1" -SetEnv HTTPCACHE_PURGE_TYPE varnish -SetEnv HTTPCACHE_PURGE_SERVER "http://varnish:80" -``` - -Example for Nginx: - -```nginx -fastcgi_param SYMFONY_HTTP_CACHE 0; -fastcgi_param TRUSTED_PROXIES "127.0.0.1"; -fastcgi_param HTTPCACHE_PURGE_TYPE varnish; -fastcgi_param HTTPCACHE_PURGE_SERVER "http://varnish:80"; -``` - -Example for Platform.sh: - -You can configure environment variables via [Platform.sh variables.](https://docs.platform.sh/frameworks/ibexa/fastly.html) - -!!! tip - - For HTTP cache, you'll most likely only use this for configuring Fastly for production and optionally staging, - allowing `variables:env:` in `.platform.app.yaml` to, for example, specify Varnish or Symfony proxy as default for dev environment. - -##### Example for Apache with Varnish - -```apacheconfig -# mysite_com.conf - -# Force front controller (public/index.php) NOT to use Symfony's built-in reverse proxy. -SetEnv APP_HTTP_CACHE 0 - -# Configure Varnish -SetEnv HTTPCACHE_PURGE_TYPE varnish -SetEnv HTTPCACHE_PURGE_SERVER "http://varnish:80" - -# Configure IP of your Varnish server to be trusted proxy -# !! Replace IP with the real one used by Varnish -SetEnv TRUSTED_PROXIES "193.22.44.22" -``` - -##### Example for Nginx with Fastly - -```nginx -# mysite_com.conf - -# Force front controller (public/index.php) NOT to use Symfony's built-in reverse proxy. -fastcgi_param APP_HTTP_CACHE 0; - -# Configure Fastly -fastcgi_param HTTPCACHE_PURGE_TYPE fastly; -fastcgi_param HTTPCACHE_PURGE_SERVER "https://api.fastly.com"; - -# See above for obtaining service ID and application key/token -fastcgi_param FASTLY_SERVICE_ID "ID" -fastcgi_param FASTLY_KEY "token" -``` - -### Understanding stale cache - -Stale cache, or grace mode in Varnish, is when cache continues to be served when expired (by means of TTL or soft purge), -or when the back-end server is not responding. - -This has several benefits for high traffic installations to reduce load to the back end. -Instead of creating several concurrent requests for the same page to the back end, -the following happens when a page has been soft purged: - -- Next request hitting the cache will trigger an asynchronous lookup to a back end. -- If cache is still within grace period, first and subsequent requests for the content are served from cache, -and don't wait for the asynchronous lookup to finish. -- The back-end lookup finishes and refreshes the cache so any subsequent requests get a fresh cache. - -By default, [[= product_name =]] always soft purges content on reverse proxies that support it (Varnish and Fastly), -with the following logic in the out-of-the-box VCL: - -- cache is within grace -- either the server is not responding, or the request comes without a session cookie (anonymous user) - -Serving grace is not always allowed by default because: - -- It is a safe default. Even if just for anonymous users, stale cache can easily confuse your team during acceptance testing. -- It means REST API, which is used by the Back Office, would serve stale data, breaking the UI. - -!!! tip "Customizing stale cache handling" - - If you want to use grace handling for logged in users as well, you can adapt the provided VCL to add condition - for opting out if the request has a cookie and the path contains REST API prefix to make sure the Back Office is not negatively affected. - - If you want to disable grace mode, you can adapt the VCL to do hard instead of soft purges, or set grace/stale time to `0s`. - -## Context-aware HTTP cache - -[[= product_name =]] allows caching requests made by logged-in users. -It is called (user) context-aware cache. - -It means that HTTP cache is unique per set of user permissions (Roles and Limitations), -and there are variations of cache shared only among users that have the exact same permissions. -So if a user is browsing a list of children Locations, -they will only see children Locations they have access to even if their rendering is served from HTTP cache. - -This is accomplished by varying on a header called `X-Context-User-Hash`, which the system populates on the request. -The [logic for this](#request-lifecycle) is accomplished in provided VCL for Varnish and Fastly. -A similar but internal logic is done in the provided enhanced Symfony Proxy (AppCache). - -#### Request lifecycle - -To expand on steps covered in [FOSHttpCacheBundle documentation on user context feature](https://foshttpcachebundle.readthedocs.io/en/2.8.0/features/user-context.html#how-it-works): - -1. A client (browser) requests URI `/foo`. -1. The caching proxy receives the request and holds it. It first sends a hash request to the application's context hash route: `/_fos_user_context_hash`. -1. The application receives the hash request. An event subscriber (`UserContextSubscriber`) aborts the request immediately after the Symfony firewall is applied. - The application calculates the hash (`HashGenerator`) and then sends a response with the hash in a custom header (`X-Context-User-Hash`). -1. The caching proxy receives the hash response, copies the hash header to the client's original request for `/foo` and restarts the modified original request. -1. If the response to `/foo` should differ per user context, the application sets a `Vary: X-Context-User-Hash` header, which will make Proxy store the variations of this cache varying on the hash value. - -The next time a request comes in from the same user, application lookup for the hash (step 3) does not happen, -as the hash lookup itself is cached by the cache proxy as described below. - -##### User Context Hash caching - -Example of response sent to reverse proxy from `/_fos_user_context_hash` with [[[= product_name =]]'s default config](#default-options-for-foshttpcachebundle-defined-in-ez-platform): - -``` -HTTP/1.1 200 OK -X-Context-User-Hash: <hash> -Content-Type: application/vnd.fos.user-context-hash -Cache-Control: public, max-age=600 -Vary: Cookie, Authorization -``` - -In the example above the response is set to be cached for 10 minutes. -It varies on the `Cookie` header in order to be able to cache it for the given user. -For cache efficiency reasons the default VCL strips any other cookie then session cookies to make this work. - -It also varies on `Authorization` to cover any possible basic auth headers in case that is used over sessions for some requests. - -!!! note "Problems with stale user hash" - - If you notice issues with stale hash usage, before you disable this cache make sure login/logout always - generates new session IDs and performs a full redirect to make sure no requests are being made using stale - user context hashes. - -!!! caution "Known limitations of the user context hash" - - If you are using URI-based SiteAccess matching on a multi-repository installation (multiple databases), - the default SiteAccess on the domain needs to point to the same repository (database), - because `/_fos_user_context_hash` is not SiteAccess-aware by default (see `ezplatform.default_router.non_siteaccess_aware_routes` parameter). - This in turn is because reverse proxy does not have knowledge about SiteAccesses - and it does not pass the whole URL in order to be able to cache the user context hash response. - - The only known workaround is to make it SiteAccess aware, and have custom VCL logic tied to your SiteAccess - matching with Varnish/Fastly, in order to send the SiteAccess prefix as URI. - -##### Default options for FOSHttpCacheBundle defined in [[= product_name =]] - -The following configuration is defined in [[= product_name =]] by default for FOSHttpCacheBundle. -Typically, you should not override these settings unless you know what you are doing. - -``` yaml -fos_http_cache: - proxy_client: - default: varnish - varnish: - http: - servers: ['$http_cache.purge_servers$'] - tag_mode: 'purgekeys' - - user_context: - enabled: true - hash_cache_ttl: 600 - # NOTE: These are also defined/used in AppCache, in Varnish VCL, and Fastly VCL - session_name_prefix: eZSESSID -``` - -#### Personalizing responses - -Here are some generic recommendations on how to approach personalized content with [[= product_name =]] / Symfony: - -1\. ESI with vary by cookie: - -Default VCL strips everything except session cookie, so this will effectively be cached "per user". -If you are on single-server setup without Varnish or Fastly, you can use the same cookie logic on the web server instead. - -This solution is low effort, and can be good enough for one fragment that is reused across whole site, -for instance in header to show user name. - -Example: - -```php - // Inside a custom controller action, or even a Content View controller - $response->setVary('Cookie'); -``` - -2\. Ajax/JS lookup to "uncached" custom Symfony Controller(s): - -This method does not consume memory in Varnish. -It can optionally be cached with custom logic: Symfony Cache on server side and/or with client side caching techniques. -This should be done as Ajax/JS lookup to avoid the uncached request slowing down the whole delivery of Vanish if it's done as ESI. - -This solution requires more effort depending on project requirements (traffic load, etc.). - -3\. Custom vary by logic, i.e. "X-User-Preference-Hash" inspired by X-Context-User-Hash: - -This method allows for fine-grained caching as you can explicitly vary on this in only the places that needs it. - -This solution requires more effort (controller, VCL logic and adapting your own code), see below for examples. - -!!! tip "Dealing with paywall use cases" - - If you need to handle a paywall on a per-item basis, you need to go a bit further by for instance doing a - lookup to backend for each URL where this is relevant. - - You can find an example for paywall authorization in [FOSHTTPCache documentation.](https://foshttpcache.readthedocs.io/en/latest/user-context.html#alternative-for-paywalls-authorization-request) - -##### Dos and don'ts of custom vary by logic - -Refer to [FOSHttpCacheBundle documentation on how user context hashes are generated](https://foshttpcachebundle.readthedocs.io/en/2.8.0/features/user-context.html#generating-hashes). - -[[= product_name =]] implements a custom context provider in order to make user context hash reflect the current User's Roles and Limitations. -This is needed given [[= product_name =]]'s more complex permission model compared to Symfony's. - -You can technically extend the user context hash by [implementing your own custom context provider(s)](https://foshttpcachebundle.readthedocs.io/en/2.8.0/reference/configuration/user-context.html#custom-context-providers). -However, **this is strongly discouraged** as it means increasing the amount of cache variations -stored in proxy for every single cache item, lowering cache hit ratio and increasing memory use. - -Instead, it's recommended you create your own hash header for use cases where you need it. -This way only controllers and views that really vary by your custom logic will vary on it. - -There are several ways you can do this, ranging from completely custom VCL logic and dedicated controller to respond with hash -to trusted proxy lookups, however this means additional lookups. - -##### Example for custom vary by logic - -You can extend `/_fos_user_context_hash` lookup to add another HTTP header with custom hash for your -needs, and adapt the user context hash VCL logic to use the additional header. - -To avoid overloading any application code, take advantage of Symfony's event system: - -1\. Add a Response [event listener or subscriber](https://symfony.com/doc/5.1/event_dispatcher.html) to add your own hash to `/_fos_user_context_hash`: - -```php -public function addPreferenceHash(FilterResponseEvent $event) -{ - $response = $event->getResponse(); - if ($response->headers->get('Content-Type') !== 'application/vnd.fos.user-context-hash') { - return; - } - - $response->headers->set('X-User-Preference-Hash', '<your-hash>'); -} -``` - -2\. Adapt VCL logic to pass the header to requests: - -```diff -@@ -174,6 +174,7 @@ sub ez_user_context_hash { - if (req.restarts == 0 - && (req.http.accept ~ "application/vnd.fos.user-context-hash" - || req.http.X-Context-User-Hash -+ || req.http.x-user-preference-hash - ) - ) { - return (synth(400, "Bad Request")); -@@ -263,12 +264,19 @@ sub vcl_deliver { - && resp.http.content-type ~ "application/vnd.fos.user-context-hash" - ) { - set req.http.X-Context-User-Hash = resp.http.X-Context-User-Hash; -+ set req.http.x-user-preference-hash = resp.http.x-user-preference-hash; - - return (restart); - } - - // If we get here, this is a real response that gets sent to the client. - -+ // Remove the vary on user preference hash, no need to expose this publicly. -+ if (resp.http.Vary ~ "X-User-Preference-Hash") { -+ set resp.http.Vary = regsub(resp.http.Vary, "(?i),? *X-User-Preference-Hash *", ""); -+ set resp.http.Vary = regsub(resp.http.Vary, "^, *", ""); -+ } -+ - // Remove the vary on user context hash, this is nothing public. Keep all - // other vary headers. - if (resp.http.Vary ~ "X-Context-User-Hash") { -``` - -3\. Add `Vary` in your custom controller or content view controller: - -```php -$response->setVary('X-User-Preference-Hash'); - -// If you _also_ need to vary on eZ permissions, instead use: -//$response->setVary(['X-Context-User-Hash', 'X-User-Preference-Hash']); -``` - -## Content-aware HTTP cache - -HTTP cache in [[= product_name =]] is aware of which content or entity it is connected to. -This awareness is accomplished by means of cache tagging. All supported reverse proxies are content-aware. - -!!! note "Tag header is stripped in production for security reasons" - - For security reasons this header, and other internal cache headers, - are stripped from output in production by the reverse proxy (in VCL for Varnish and Fastly). - -### Understanding cache tags - -Understanding tags is the key to making the most of `ezplatform-http-cache`. - -Tags form a secondary set of keys assigned to every cache item, on top of the "primary key" which is the URI. -Like an index in a database, a tag is typically used for anything relevant, that represents the given cache item. -Tags are used for cache invalidation. - -For example, the system tags every article response, and when the article Content Type is updated, -it tells Varnish that all articles should be considered stale -and be updated in the background when someone requests them. - -Current content tags (and when the system purges on them): - -- Content: `c<content-id>` - Purged on all smaller or larger changes to Content (including its metadata, fields and locations). -- Content Type: `ct<content-type-id>` - Used when the Content Type changes, affecting Content of its type. -- Location: `l<location-id>` - Used for clearing all cache relevant for a given Location. -- Parent Location: `pl<[parent-]location-id>` - Used for clearing all children of a Location (`pl<location-id>`), or all siblings (`pl<parent-location-id>`). -- Path: `p<location-id>` - For operations that change the tree itself, like move, remove, etc. -- Relation: `r<content-id>` - Only purged on when content updates are severe enough to also affect reverse relations. -- Relation location: `rl<location-id>` - Same as relation, but by Location ID. - -!!! note "Automatic repository prefixing of cache tags" - - As [[= product_name =]] support multi-repository (multi-database) setups that can have overlapping IDs, - the shared HTTP cache systems need to distinguish tags relevant to the different content repositories. - - This is why in multi-repository setup you can see cache tags such as `1p2`. - In this example `1` represents the index among configured repositories, meaning the second repository in the system. - - Tags are not prefixed for default repository (index "0"). - -#### Troubleshooting - Cache header too long errors - -In case of complex content, for instance Pages with many blocks, or RichText with a lot of embeds/links, -you can encounter problems with too long cache header on responses. -Because of this, necessary cache entries may not be tagged properly. -You may also see `502 Headers too long` errors, and webserver refusing to serve the page. - -Here are some options on how to solve this issue: - -###### A. Configure allowing larger headers - -Varnish configuration: - -- [http_resp_hdr_len](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-resp-hdr-len) (default 8k, change to i.e. 32k) -- [http_max_hdr](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-max-hdr) (default 64, change to i.e. 128) -- [http_resp_size](https://varnish-cache.org/docs/6.0/reference/varnishd.html#http-resp-size) (default 23k, change to i.e. 96k) -- [workspace_backend](https://varnish-cache.org/docs/6.0/reference/varnishd.html#workspace-backend) (default 64k, change to i.e. 128k) - -If you need to see these long headers in `varnishlog`, adapt the [vsl_reclen](https://varnish-cache.org/docs/6.0/reference/varnishd.html#vsl-reclen) setting. - -Nginx has a default limit of 4k/8k when buffering responses: - -- For [PHP-FPM](https://www.php.net/manual/en/install.fpm.php) setup using proxy module, configure [proxy_buffer_size](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) -- For FastCGI setup using fastcgi module, configure [fastcgi_buffer_size](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffer_size) - -Fastly has a `Surrogate-Key` header limit of 16 kB, this cannot be changed. - -Apache has a [hard](https://github.com/apache/httpd/blob/5f32ea94af5f1e7ea68d6fca58f0ac2478cc18c5/server/util_script.c#L495) [coded](https://github.com/apache/httpd/blob/7e2d26eac309b2d79e467ef586526c10e0f226f8/include/httpd.h#L299-L303) limit of 8 kB, so if you face this issue consider using Nginx instead. - -###### B. Limit tags header output by system - -1\. For inline rendering just displaying the content name, image attribute, and/or link, it would be enough to: - -- Look into how many inline (non ESI) render calls for content rendering you are doing, and see if you can organize it differently. -- Consider inlining the views not used elsewhere in the given template and [tagging the response in Twig](#response-tagging-in-twig) with "relation" tags. - - Optionally, you can set reduced cache TTL for the given view in order to reduce risk of stale cache on subtree operations affecting the inlined content. - -2\. If that is not an option, you can opt in to set a max length parameter (in bytes) and corresponding ttl (in seconds) -for cases when the limit is reached. The system will log a warning for cases where the limit is reached so you can optimize -these cases as described above when needed. - -```yaml -parameters: - # Warning, setting this means you risk losing tag information, risking stale cache. Here set below 8k: - ezplatform.http_cache.tags.header_max_length: 7900 - # In order to reduce risk of stale cache issues, you should set a lower TTL here then globally (here set as 2h) - ezplatform.http_cache.tags.header_reduced_ttl: 7200 -``` - -### Response tagging done with content view - -For content views response tagging is done automatically, and cache system outputs headers like the following: - -``` -HTTP/1.1 200 OK -Cache-Control: public, max-age=86400 -xkey: ez-all c1 ct1 l2 pl1 p1 p2 -``` - -If the given content has several Locations, you can see several `l<location-id>` and `p<location-id>` tags in the response. - -!!! note "How response tagging for ContentView is done internally" - - In `ezplatform-http-cache` there is a dedicated response listener `HttpCacheResponseSubscriber` that checks if: - - - the response has attribute `view` - - the view implements `eZ\Publish\Core\MVC\Symfony\View\CachableView` - - cache is not disabled on the individual view - - If that checks out, the response is adapted with the following: - - - `ResponseCacheConfigurator` applies SiteAccess settings for enabled/disabled cache and default TTL - - `DispatcherTagger` dispatches the built-in ResponseTaggers which generate the tags as described above. - - Further information can be found in [`ezplatform-http-cache` docs.](https://github.com/ezsystems/ezplatform-http-cache/blob/master/docs/response_taggers.md) - -### Response tagging in controllers - -For tagging needs in controllers, there are several options, here presented in recommended order: - -1\. Reusing `DispatcherTagger` to pick correct tags. - -Examples for tagging everything needed for content using the autowirable `ResponseTagger` interface: - -``` php -/** @var \EzSystems\PlatformHttpCacheBundle\ResponseTagger\ResponseTagger $responseTagger */ - -// If you have a View object you can simply call: -$responseTagger->tag($view); - -// Or if you have content / Location object only, you can instead provide content info and Location: -$responseTagger->tag($contentInfo); -$responseTagger->tag($location); -``` - -2\. Use `ContentTagInterface` API for content related tags. - -Examples for adding specific content tags using the autowireable `ContentTagInterface`: - -``` php -/** @var \EzSystems\PlatformHttpCacheBundle\Handler\ContentTagInterface $tagHandler */ - -// Example for tagging everything needed for Content: -$tagHandler->addContentTags([$content->id]); -$tagHandler->addLocationTags([$location->id]); -$tagHandler->addParentLocationTags([$location->parentLocationId]); -$tagHandler->addPathTags($location->path); -$tagHandler->addContentTypeTags([$content->getContentType()->id]); - -// Example when using ESI as also shown below using FOS tag handler (there is also a method for relation locations): -$tagHandler->addRelationTags([33, 44]); -``` - -3\. Manually add tags yourself using low-level FOS `TagHandler`. - -In PHP, FOSHttpCache exposes the `fos_http_cache.http.symfony_response_tagger` service which enables you to add tags to a response. - -The following example adds minimal tags for when ID 33 and 34 are rendered in ESI, -but parent response needs these tags to get refresh if they are deleted: - -``` php -/** @var \FOS\HttpCacheBundle\Http\SymfonyResponseTagger $responseTagger */ -$responseTagger->addTags([ContentTagInterface::RELATION_PREFIX . '33', ContentTagInterface::RELATION_PREFIX . '44']); -``` - -See [Tagging from code](https://foshttpcachebundle.readthedocs.io/en/2.8.0/features/tagging.html#tagging-and-invalidating-from-php-code) in FOSHttpCacheBundle doc. - -4\. Use deprecated `X-Location-Id` header. - -For custom or eZ controllers (e.g. REST) still using `X-Location-Id`, `XLocationIdResponseSubscriber` handles translating -this header to tags. It supports singular and comma-separated Location ID value(s): - -```php -/** @var \Symfony\Component\HttpFoundation\Response $response */ -$response->headers->set('X-Location-Id', 123); - -// Alternatively using several Location ID values -$response->headers->set('X-Location-Id', '123,212,42'); -``` - -!!! caution "X-Location-Id use is deprecated" - - `X-Location-Id` is deprecated and will be removed in future. - For rendering content it is advised to refactor to use Content View, - if not applicable `ContentTagInterface` or lastly manually output tags. - -### Response tagging in templates - -1\. `ez_http_tag_location()` - -For full content tagging when inline rendering, use the following: - -``` html+twig -{{ ez_http_tag_location(location) }} -``` - -2\. `ez_http_tag_relation_ids()` or `ez_http_tag_relation_location_ids()` - -When you want to reduce the amount of tags, or the inline content is rendered using ESI, a minimum set of tags can be set: - -``` html+twig -{{ ez_http_tag_relation_ids(content.id) }} - -{# Or using array for several values #} -{{ ez_http_tag_relation_ids([field1.value.destinationContentId, field2.value.destinationContentId]) }} -``` - -3\. `{{ fos_httpcache_tag(['r33', 'r44']) }}` - -As a last resort you can also use function from FOS which lets you set low level tags directly: - -``` html+twig -{{ fos_httpcache_tag('r33') }} - -{# Or using array for several values #} -{{ fos_httpcache_tag(['r33', 'r44']) }} -``` - -See [Tagging from Twig Templates](https://foshttpcachebundle.readthedocs.io/en/2.8.0/features/tagging.html#tagging-from-twig-templates) in FOSHttpCacheBundle documentation. - -### Tag purging - -#### Default purge tagging - -`ezplatform-http-cache` uses Repository API event subscribers to listen to events emitted on Repository operations, -and depending on the operation triggers expiry on a specific tag or set of tags. - -For example on the move Location event the following tags are purged: - -```php -[ - // The tree itself being moved (all children will have this tag) - ContentTagInterface::PATH_PREFIX . $event->getLocation()->id, - // old parent - ContentTagInterface::LOCATION_PREFIX . $event->getLocation()->parentLocationId, - // old siblings - ContentTagInterface::PARENT_LOCATION_PREFIX . $event->getLocation()->parentLocationId, - // new parent - ContentTagInterface::LOCATION_PREFIX . $event->getNewParentLocation()->id, - // new siblings - ContentTagInterface::PARENT_LOCATION_PREFIX . $event->getNewParentLocation()->id, -]; -``` - -All event subscribers can be found in `ezplatform-http-cache/src/EventSubscriber/CachePurge`. - -#### Custom purging from code - -While the system purges tags whenever API is used to change data, you may need to purge directly from code. -For that you can use the built-in purge client: - -```php -/** @var \EzSystems\PlatformHttpCacheBundle\PurgeClient\PurgeClientInterface $purgeClient */ - -// Example for purging by Location ID: -$purgeClient->purge([ContentTagInterface::LOCATION_PREFIX . $location->id]); - -// Example for purging all cache for instance for full re-deploy cases , usually this will trigger a expiry (soft purge): -$purgeClient->purgeAll(); -``` - -#### Purging from command line - -Example for purging by Location and by content ID: - -```bash -bin/console fos:httpcache:invalidate:tag l44 c33 -``` - -Example for purging by all cache: - -```bash -bin/console fos:httpcache:invalidate:tag ez-all -``` - -!!! tip "Purge is done on the current Repository" - - Like when purging from code, tags you purge on are prefixed to match the currently configured SiteAccess. - So make sure to specify SiteAccess argument when using this command in combination with multi-repository setup. diff --git a/docs/guide/images.md b/docs/guide/images.md deleted file mode 100644 index dc3147e775..0000000000 --- a/docs/guide/images.md +++ /dev/null @@ -1,398 +0,0 @@ -# Images - -## Introduction - -Image variations (image aliases) enable you to define and use different versions of the same image. You generate variations based on filters which modify aspects such as size and proportions, quality or effects. - -Image variations are generated with [LiipImagineBundle](https://github.com/liip/LiipImagineBundle), using the underlying [Imagine library from avalanche123](http://imagine.readthedocs.org/en/latest/). This bundle supports GD (default), Imagick or Gmagick PHP extensions, and enables you to define flexible filters in PHP. Image files are stored using the `IOService,` and are completely independent from the Image Field Type. They are generated only once and cleared on demand (e.g. on content removal). - -LiipImagineBundle only works on image blobs (no command line tool is needed). See the [bundle's documentation to learn more on that topic](http://symfony.com/doc/master/bundles/LiipImagineBundle/configuration.html). - -## Images from a DAM system - -If your installation is connected to a DAM system, you can use images directly from a DAM system in your content. - -The [specific configuration](config_connector.md#dam-configuration) will depend on the DAM system in question. - -## Configuring image variations - -Custom image variations are defined in `ezplatform.yaml` or any imported semantic configuration file. The definition is [dynamic](configuration.md#dynamic-configuration-with-the-configresolver), so it can be configured per SiteAccess and all the other scopes. - -``` yaml -# Example image variation definition - -ezplatform: - system: - my_siteaccess: - image_variations: - small: - reference: null - filters: - - { name: geometry/scaledownonly, params: [100, 160] } - medium: - reference: null - filters: - - { name: geometry/scaledownonly, params: [200, 290] } - listitem: - reference: null - filters: - - { name: geometry/scaledownonly, params: [130, 190] } - articleimage: - reference: null - filters: - - { name: geometry/scalewidth, params: [770] } -``` - -!!! note - - Each variation name **must be unique**. It may contain underscores (`_`), hyphens (`-`) or numbers, but no spaces. - -The following parameters are set for each variation: - -- `reference`: Name of a reference variation to base the variation on. If set to `null` (or `~`, which means `null` in YAML), the variation will take the original image for reference. It can be any configured variation, or a `filter_set` defined in the `liip_imagine` namespace. -- `filters`: Array of filter definitions (hashes containing `name` and `params` keys). See possible values [below](#available-filters). - -!!! caution - - If you change image variation properties manually, you need to clear persistence cache: - - `php bin/console cache:pool:clear <pool_name>` - - where `<pool_name>` is `cache.app` by default, and `cache.redis` when using Redis. - -### Built-in image variations - -A few basic image variations are included by default in [[= product_name =]] in the `default_settings.yaml` config file: - -``` yaml -ezsettings.default.image_variations: - reference: - reference: ~ - filters: - geometry/scaledownonly: [600, 600] - small: - reference: reference - filters: - geometry/scaledownonly: [100, 100] - tiny: - reference: reference - filters: - geometry/scaledownonly: [30, 30] - medium: - reference: reference - filters: - geometry/scaledownonly: [200, 200] - large: - reference: reference - filters: - geometry/scaledownonly: [300, 300] -``` - -### Post-Processors - -LiipImagineBundle supports [post-processors on image aliases](http://symfony.com/doc/master/bundles/LiipImagineBundle/post-processors.html). You can specify them in image variation configuration: - -``` yaml -ezplatform: - system: - my_siteaccess: - image_variations: - articleimage: - reference: null - filters: - - { name: geometry/scalewidth, params: [770] } - post_processors: - jpegoptim: {} -``` - -Please refer to [post-processor documentation in LiipImagineBundle](http://symfony.com/doc/master/bundles/LiipImagineBundle/post-processors.html) for details. - -## Configuration examples - -### Scaling with an [[= product_name =]] filter - -This configuration defines a `medium` image variation that is scaled to a width of 700 px. - -``` yaml -ezplatform: - system: - my_siteaccess: - image_variations: - medium: - reference: null - filters: - - geometry/scalewidth: - params: [770] -``` - -### Image quality with a liip filter - -This configuration adds a limit to the image quality using a liip filter. - -You can use both an [[= product_name =]] and a liip filter for the same image variation, in this case `medium`. - -``` yaml -ezplatform: - system: - my_siteaccess: - image_variations: - # List of variations - -liip_imagine: - driver: imagick - filter_sets: - medium: - jpeg_quality: 50 -``` - -!!! note - - Notice that the `liip_imagine` key is not placed under `image_variations`, but at the same level as `ezplatform`. - -## Rendering image variations - -### Render within a Twig template - -Image variations can be called within a Twig template by passing `alias` with the parameters: - -``` html+twig -ez_render_field( content, 'image', { parameters: { 'alias': 'medium' } } ) -``` - -## Upgrade - -### Instantiate `LiipImagineBundle` in your kernel class - -If you were using ImageMagick, install [Imagick](http://php.net/imagick) or [Gmagick](http://php.net/gmagick) PHP extensions and activate the driver in `liip_imagine `([see LiipImagineBundle configuration documentation for more information](http://symfony.com/doc/master/bundles/LiipImagineBundle/configuration.html)): - -``` yaml -liip_imagine: - # Driver can be either "imagick", "gmagick" or "gd", depending on the PHP extension you're using. - driver: imagick -``` - -GD will be used by default if no driver is specified. - -## Managing variations - -### Purging - -You can use the Liip Imagine console tool to clear generated variations. - -``` bash -$ php bin/console liip:imagine:cache:remove --filter=large -$ php bin/console liip:imagine:cache:remove -v -``` - -The first example will clear the image files for the `large` variation. The second will clear all the generated variations (be careful), and list the removed files (`-v).` - -!!! note - - The naming scheme change introduced by this feature wasn't enabled by default on 5.4.x. As part of migration you'll need to adapt to the new schema to get the benefit of this more efficient purge method. More technical information can be found on the [pull request](https://github.com/ezsystems/ezpublish-kernel/pull/1276). - -!!! caution "Code injection in image EXIF" - - EXIF metadata of an image may contain e.g. HTML, JavaScript, or PHP code. [[= product_name =]] is itself does not parse EXIF metadata, but third-party bundles need to be secured against this eventuality. Images should be treated like any other user-submitted data - make sure the metadata is properly escaped before use. - -### Resolving image URLs - -You can use LiipImagine's `liip:image:cache:resolve` script to resolve the path to image variations generated from the original image, with one or more paths as arguments. See [LiipImagineBundle documentation](http://symfony.com/doc/current/bundles/LiipImagineBundle/commands.html#resolve-cache) for more information. - -Note that paths to repository images must be relative to the `var/<site>/storage/images` directory, for example: `7/4/2/0/247-1-eng-GB/test.jpg`. - -## Filter reference - -### Available filters - -In addition to [filters exposed by LiipImagineBundle](http://symfony.com/doc/master/bundles/LiipImagineBundle/filters.html), the following are available: - -| Filter name | Parameters | Description | -|------------------------------|---------------------------------------------|----------------------------------------------------------------------------------------------------| -| geometry/scaledownonly | \[width, height\] | Generates a thumbnail that will not exceed width/height. | -| geometry/scalewidthdownonly | \[width\] | Generates a thumbnail that will not exceed width. | -| geometry/scaleheightdownonly | \[height\] | Generates a thumbnail that will not exceed height. | -| geometry/scalewidth | \[width\] | Alters image width. Proportion will be kept. | -| geometry/scaleheight | \[height\] | Alters image height. Proportion will be kept. | -| geometry/scale | \[width, height\] | Alters image size, not exceeding provided width and height. Proportion will be kept. | -| geometry/scaleexact | \[width, height\] | Alters image size to fit exactly provided width and height. Proportion will not be kept. | -| geometry/scalepercent | \[widthPercent, heightPercent\] | Scales width and height with provided percent values. Proportion will not be kept. | -| geometry/crop | \[width, height, startX, startY\] | Crops the image. Result will have provided width/height, starting at provided startX/startY | -| border | \[thickBorderX, thickBorderY, color=\#000\] | Adds a border around the image. Thickness is defined in px. Color is "\#000" by default. | -| filter/noise | \[radius=0\] | Smooths the contours of an image (`imagick`/`gmagick` only). `radius` is in px. | -| filter/swirl | \[degrees=60\] | Swirls the pixels of the center of the image (`imagick`/`gmagick` only). `degrees` defaults to 60°.| -| resize | {size: \[width, height\]} | Simple resize filter (provided by LiipImagineBundle). | -| colorspace/gray | N/A | Converts the image to grayscale. | - -LiipImagineBundle supports additional settings, it is possible to combine filters from the list above with [the ones provided in LiipImagineBundle](http://symfony.com/doc/master/bundles/LiipImagineBundle/filters.html) or custom ones. - -### Discarded filters - -The following filters exist in the Imagine library but are not used in [[= product_name =]] due to incompatibility: - -- `flatten`. Obsolete, images are automatically flattened. -- `bordercolor` -- `border/width` -- `colorspace/transparent` -- `colorspace` - -### Custom filters - -Please refer to [LiipImagineBundle documentation on custom filters](http://symfony.com/doc/master/bundles/LiipImagineBundle/filters.html#custom-filters). [Imagine library documentation](http://imagine.readthedocs.org/en/latest/) may also be useful. - -## Setting placeholder generator - -Placeholder generator enables you to download or use generated image placeholder for any missing image. It might be used when you are working on an existing database and you are not able to download uploaded images to your local development environment because of their large size - -`PlaceholderAliasGenerator::getVariation` method generates placeholder (by delegating it to the implementation of `PlaceholderProvider` interface) if original image cannot be resolved and saves it under the original path. - -In [[= product_name =]] there are two implementations of `PlaceholderProvider` interface: - -- [GenericProvider](#genericprovider) -- [RemoteProvider](#remoteprovider) - -```php -<?php - -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; - -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; - -interface PlaceholderProvider -{ - /** - * Provides a placeholder image path for a given Image FieldType value. - * - * @param \eZ\Publish\Core\FieldType\Image\Value $value - * @param array $options - * @return string Path to placeholder - */ - public function getPlaceholder(ImageValue $value, array $options = []): string; -} -``` - -### GenericProvider - -`\eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\GenericProvider` generates placeholder with basic information about original image - [example 1](#configuration-examples). - -**Generic image example:** - -![Placeholder image GenericProvider](img/placeholder_info.jpg) - -**Full page example:** - -![Placeholder GenericProvider](img/placeholder_generic_provider.png) - -|Option|Default value|Description| -|------|-------------|-----------| -|fontpath|n/a|Path to the font file (*.ttf). **This option is required.**| -|text|"IMAGE PLACEHOLDER %width%x%height%\n(%id%)"|Text which will be displayed in the image placeholder. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.| -|fontsize|20|Size of the font in the image placeholder.| -|foreground|#000000|Foreground color of the placeholder.| -|secondary|#CCCCCC|Secondary color of the placeholder.| -|background|#EEEEEE|Background color of the placeholder.| - -### RemoteProvider - -`\eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\RemoteProvider` allows you to download placeholders from: - - - remote source e.g. <http://placekitten.com> - [example 2](#configuration-examples) - - live version of a site - [example 3](#configuration-examples) - -**Full page example:** - -![Placeholder RemoteProvider - placekitten.com](img/placeholder_remote_provider.jpg) - -|Option|Default value|Description| -|------|-------------|-----------| -|url_pattern|''|URL pattern. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.| -|timeout|5|Period of time before timeout, measured in seconds.| - -### Semantic configuration - -Placeholder generation can be configured for each [`binary_handler`](file_management.md#handling-binary-files) under the `ezplatform.image_placeholder` key: - -```yaml -ezplatform: - # ... - image_placeholder: - <BINARY_HANDLER_NAME>: - provider: <PROVIDER TYPE> - options: <CONFIGURATION> -``` - -If there is no configuration assigned to [`binary_handler`](file_management/#handling-binary-files) name, the placeholder generation will be disabled. - -##### Configuration examples: - -**Example 1 - placeholders with basic information about original image** - -```yaml -ezplatform: - # ... - image_placeholder: - default: - provider: generic - options: - fontpath: '%kernel.project_dir%/src/Resources/font/font.ttf' - background: '#EEEEEE' - foreground: '#FF0000' - text: 'MISSING IMAGE %%width%%x%%height%%' -``` - -**Example 2 - placeholders from remote source** - -```yaml -ezplatform: - # ... - image_placeholder: - default: - provider: remote - options: - url_pattern: 'https://placekitten.com/%%width%%/%%height%%' -``` - -**Example 3 - placeholders from live version of a site** - -```yaml -ezplatform: - # ... - image_placeholder: - default: - provider: remote - options: - url_pattern: 'http://example.com/var/site/storage/%%id%%' -``` - -## Resizing images - -You can resize all original images of a chosen Content Type using the `ezplatform:images:resize-original` command. -You need to provide the command with: - -- identifier of the image Content Type -- identifier of the Field you want to affect -- name of the image variation to apply to the images - -`ezplatform:images:resize-original <Content Type identifier> <Field identifier> -f <variation name>` - -For example: - -`ezplatform:images:resize-original photo image -f small_image` - -Additionally you can provide two parameters: - -- `iteration-count` is the number of images to be recreated in a single iteration, to reduce memory use. Default is `25`. -- `user` is the identifier of a User with proper permission who will perform the operation (`read`, `versionread`, `edit` and `publish`). Default is `admin`. - -!!! caution - - This command publishes a new version of each Content item it modifies. - -## Reusing images - -You can store images in the media library as independent Content items of a generic Image Content Type to reuse them across the system. -It is achieved by uploading images to an ImageAsset Field Type. - -For an ImageAsset Field to be reused you have to publish it. Only then is notification triggered stating image has been published under the Location and can now be reused. -After establishing media library you can create object Relations between the main Content item and the image Content item being used by it. - -To learn more about ImageAsset Field Type and its customization see [Field Type Reference](../api/field_type_reference.md#imageasset-field-type). diff --git a/docs/guide/images/config_connector.md b/docs/guide/images/config_connector.md new file mode 100644 index 0000000000..620222991e --- /dev/null +++ b/docs/guide/images/config_connector.md @@ -0,0 +1,83 @@ +--- +description: Configure a Digital Asset Management connector. +--- + +# Add Image Asset from Digital Asset Management + +With the Digital Asset Management (DAM) system connector you can use assets such as images directly from the DAM in your content. + +## DAM configuration + +You can configure a connection with a Digital Asset Management (DAM) system. + +``` yaml +ezplatform: + system: + default: + content: + dam: [ dam_name ] +``` + +The configuration for each connector depends on the requirements of the specific DAM system. + +You can create your own connectors, or use the provided example DAM connector for [Unsplash](https://unsplash.com/). + +To add the Unsplash connector to your system add the `ezsystems/ezplatform-connector-unsplash` bundle to your installation. + +## Add Image Asset in Page Builder [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +To add Image Assets directly in the Page Builder, you can do it by using the Embed block. +The example below shows how to add images from [Unsplash](https://unsplash.com/). + +First, in `templates/embed/`, create a custom template `dam.html.twig`: + +``` html+twig +{% set dam_image = ez_field_value(content, 'image') %} +{% if dam_image.source is not null %} + {% set transformation = ibexa_platform_dam_image_transformation(dam_image.source, '770px') %} + {% set asset = ibexa_platform_asset(dam_image.destinationContentId, dam_image.source, transformation) %} + {% set image_uri = asset.assetUri.path %} + <img src="{{ image_uri }}"> +{% endif %} +``` + +The `770px` parameter in the template above is used to render the DAM image. It is the `unsplash` specific image variation and must be defined separately. + +Next, in `config/packages/ezplatform.yaml`, set the `dam.html.twig` template for the `embed` view type that is matched for the Content Type, which you created for DAM images. +For more information about displaying content, see [Content rendering](../../guide/content_rendering/render_content/render_content.md). + +``` yaml + ezplatform: + system: + site: + content_view: + embed: + image_dam: + template: embed/dam.html.twig + match: + Identifier\ContentType: <dam_image_content_type_identifier> +``` + +In the `config/packages/ezplatform.yaml` add the following configuration: + +``` yaml +dam_unsplash: + application_id: <your_application_access_key> + utm_source: <your_utm_source_name> + variations: + 770px: + fm: jpg + q: 80 + w: 770 + fit: max +``` + +You can customize the parameters according to your needs. +For more information about supported parameters, see the [Unsplash documentation](https://unsplash.com/documentation#dynamically-resizable-images). + +In the Back Office, go to **Admin** > **Content Types**. +In the **Content** group, create a Content Type for DAM images, which includes the ImageAsset Field. + +Now, when you use the Embed block in the Page Builder, you should see a DAM Image. + +For more information about block customization (defined templates, variations), see [Create custom block](../../tutorials/enterprise_beginner/4_create_a_custom_block.md). diff --git a/docs/guide/images/extending_image_editor.md b/docs/guide/images/extending_image_editor.md new file mode 100644 index 0000000000..8044165b40 --- /dev/null +++ b/docs/guide/images/extending_image_editor.md @@ -0,0 +1,87 @@ +--- +description: Add new functionalities to the image editor. +--- + +# Extend Image Editor + +With the Image Editor, users can do basic image modifications. +You can configure the Image Editor's [default appearance or behavior](image_editor.md). +You can also extend it by adding custom features. + +The following example shows how to extend the Image Editor +by adding a button that draws a dot at a random location on the image. + +## Create the JavaScript component file + +In `assets/random_dot/`, create the `random-dot.js` file with the following code of the React component: + +``` js +[[= include_file('code_samples/back_office/image_editor/assets/random_dot/random-dot-stem.js') =]] +``` + +The code does not perform any action yet, you add the action in the following steps. + +## Add configuration + +Depending on whether you [modified the default settings](image_editor.md#configuration), and where you did it, +in `config/packages` either modify the `ezplatform.yaml` file, or create an +`image_editor.yaml` file by adding settings similar to the following example: + +``` yaml +[[= include_file('code_samples/back_office/image_editor/config/packages/image_editor.yaml', 0, 9) =]][[= include_file('code_samples/back_office/image_editor/config/packages/image_editor.yaml', 36, 39) =]] +``` + +## Add entry to the Webpack configuration + +Once you create and configure the React component, you must add an entry to [the Webpack configuration](../../tutorials/platform_beginner/3_customize_the_front_page.md#configuring-webpack). +In the root directory of your project, modify the `webpack.config.js` file by adding the following code: + +``` js +[[= include_file('code_samples/back_office/image_editor/config/webpack.config.js', 39, 44) =]] +``` +At this point you should be able to see a new button in the Image Editor's UI. + +!!! tip + + Before you restart [[= product_name =]], run `php bin/console cache:clear` and `yarn encore <dev|prod>` to regenerate the assets. + +## Expand the React component + +The button that you created above does not initiate any action yet. +You must modify the JavaScript component to add a function to the button. + +### Contexts + +When you create a React-based extension of the Image Editor, you can use a number of contexts that have the following functions: + +- CanvasContext - stores a canvas that displays the image, on which you can modify the image +- ImageHistoryContext - stores the image history used by the Undo/Redo feature +- AdditionalDataContext - stores additional data that is attached to the image, for example, focal point coordinates +- TechnicalCanvasContext - stores a canvas, which you can use to draw elements that help modify the image, for example, a crop area or a grid, without interrupting with the actual image + +The last context is not used in this example. + +### Draw a dot + +Modify the `random-dot.js` file by creating a function that uses the canvas context to draw a random dot on the image: + +``` js +[[= include_file('code_samples/back_office/image_editor/assets/random_dot/random-dot.js', 24, 41) =]] +``` + +### Store changes in history + +Create another function that uses the history context to store changes, so that users can undo their edits: + +``` js +[[= include_file('code_samples/back_office/image_editor/assets/random_dot/random-dot.js', 15, 24) =]] +``` + +<details class="tip"> +<summary>Complete component code</summary> +``` js +[[= include_file('code_samples/back_office/image_editor/assets/random_dot/random-dot.js') =]] +``` +</details> + +At this point you should be able to draw a random dot by clicking a button in the Image Editor's UI. diff --git a/docs/guide/images/image_editor.md b/docs/guide/images/image_editor.md new file mode 100644 index 0000000000..4700d6a105 --- /dev/null +++ b/docs/guide/images/image_editor.md @@ -0,0 +1,61 @@ +--- +description: Configure image editor to crop, flip, and modify images. +--- + +# Configure Image Editor + +When a Content item contains Fields of the [ezimage](../../api/field_types_reference/imagefield.md) or [ezimageasset](../../api/field_types_reference/imageassetfield.md) type, users can perform basic image editing functions with the Image Editor. +For more information, see the [user documentation]([[= user_doc =]]/editing_images/). + +!!! note + + The Image Editor does not support images that come from a Digital Asset Management (DAM) system. + +!!! note + + If you intend to modify images in formats other than JPEG in image editor, + consider [adding a library to optimize them](images.md#image-optimization). + +## Configuration + +You can modify the default settings to change the appearance or behavior of the Image Editor. +You can also expand the default set of parameters to create buttons that may be required by custom features +that you add by extending the Image Editor, for example, to enable changes to the color palette of an image. + +To do this, modify the `config/packages/ezplatform.yaml` file, or create a separate +YAML file in the `config/packages` folder, and add a settings tree similar to +the following example. +The settings tree can contain one or more action groups. +You can control the order of actions within a group by setting the `priority` parameter. +You can also toggle the visibility of actions within the user interface. +Image Editor settings are [SiteAccess-aware](../configuration/config_dynamic.md). + +The following example sets the aspect ratio values and label names for buttons used by the Crop feature. + +``` yaml +[[= include_file('code_samples/back_office/image_editor/config/packages/image_editor.yaml', 0, 36) =]] +``` + +### Image quality + +You can configure the quality of the images modified in the Image Editor with the following configuration. + +The setting accepts values between 0 and 1, which corresponds to the compression level, with 0 being the strongest compression. +The default quality is 0.92: + +``` yaml +[[= include_file('code_samples/back_office/image_editor/config/packages/image_editor.yaml', 0, 4) =]] [[= include_file('code_samples/back_office/image_editor/config/packages/image_editor.yaml', 39, 40) =]] +``` + +### Additional information + +Each image can be accompanied by additional information that is not visible to the user. +By default, additional information stores the coordinates of the [focal point]([[= user_doc =]]/editing_images/#focal-point), +but you can use this extension point to pass various parameters of custom features +that you add by extending the Image Editor. + +To modify the value of additional information programmatically, you can set a value of the `Image` field by using the PHP API, for example: + +``` php +[[= include_file('code_samples/back_office/image_editor/src/AdditionalInformation.php', 2, 17) =]] +``` diff --git a/docs/guide/images/images.md b/docs/guide/images/images.md new file mode 100644 index 0000000000..3bcc10e879 --- /dev/null +++ b/docs/guide/images/images.md @@ -0,0 +1,307 @@ +--- +description: Manage image assets by using DAM systems, configuring image variations, optimizing and using placeholders. +--- + +# Images + +Images are an integral part of any website. +They can serve as decoration and convey information. + +In [[= product_name =]], you can reuse them, normalize their file names, generate +different size variations, resize images programmatically, or even define +placeholders for missing ones. + +## Images from DAM systems + +If your installation is connected to a DAM system, you can use images directly +from a DAM system in your content. + +Specific [DAM configuration](config_connector.md#dam-configuration) depends on +the system that the installation uses. + +## Reuse images + +You can store images in the media library as independent Content items of +a generic Image [Content Type](../content_model.md#content-types) to reuse them across the system. +You do this by uploading images to an [ImageAsset](../../api/field_types_reference/imageassetfield.md) Field Type. + +For an ImageAsset field to be reused, you must publish it. +Only then is notification triggered, which states that an image has been published +under the Location and can now be reused. +After you establish a media library, you can create [Relations](../content_management.md#content-relations) between the +image Content item and the main Content item that uses it. + +## Normalizing image file names + +If you use image files with unprintable UTF-8 characters in file names, you may +come across a problem with images not displaying. +Run the following command to normalize image file names: + +``` bash +php bin/console ibexa:images:normalize-paths +``` + +Next, clear the cache: + +```bash +php bin/console cache:clear +``` + +and run the following: + +```bash +php bin/console liip:imagine:cache:remove +``` + +## Configuring image variations + +With image variations (image aliases) you can define and use different versions +of the same image. +You generate variations based on filters that modify aspects such as size +and proportions, quality or effects. + +Image variations are generated with [LiipImagineBundle](https://github.com/liip/LiipImagineBundle), by using the underlying +[Imagine library from avalanche123](http://imagine.readthedocs.org/en/latest/).  +The LiipImagineBundle bundle supports GD (default), Imagick or Gmagick PHP +extensions, and enables you to define flexible filters in PHP.  +Image files are stored by using the `IOService,` and are completely independent +from the Image Field Type. +They are generated only once and cleared on demand, for example, on content removal). + +LiipImagineBundle only works on image blobs, so no command line tool is needed. +For more information, see the [bundle's documentation](https://symfony.com/doc/current/bundles/LiipImagineBundle/configuration.html). + +!!! caution "Code injection in image EXIF" + + EXIF metadata of an image may contain for example, HTML, JavaScript, + or PHP code.  + [[= product_name =]] is itself does not parse EXIF metadata, but third-party + bundles must be secured against this eventuality. + Images must be treated like any other user-submitted data - make sure that + metadata is properly escaped before use. + +### Image URL resolution + +You can use LiipImagine's `liip:imagine:cache:resolve` command to resolve the path +to image variations that are generated from the original image, with one or more +paths as arguments. +Paths to repository images must be relative to the `var/<site>/storage/images` +directory, for example: `7/4/2/0/247-1-eng-GB/test.jpg`. +For more information, see [LiipImagineBundle documentation](https://symfony.com/bundles/LiipImagineBundle/current/commands.html#resolve-cache). + +## Resizing images + +You can resize all original images of a chosen Content Type with the following +command. + +``` bash +php bin/console ibexa:images:resize-original <Field identifier> <Content Type identifier> -f <variation name> +``` + +You must provide the command with: + +- identifier of the image Content Type +- identifier of the Field that you want to affect +- name of the image variation to apply to the images + +For example: + +``` bash +php bin/console ibexa:images:resize-original image photo -f small_image +``` + +You can also pass two additional parameters: + +- `iteration-count` is the number of images to be recreated in a single iteration, + to reduce memory use. + The default value is `25`. +- `user` is the identifier of a User with proper permission who will perform + the operation (`read`, `versionread`, `edit` and `publish`). + The default value is `admin`. + +!!! caution + + The `resize-original` command publishes a new version of each Content item it modifies. + +## Generating placeholder images + +With a placeholder generator you can download or generate placeholder images for +any missing image. +It proves useful when you are working on an existing database and are unable to +download uploaded images to your local development environment, due to, for +example, a large size of files. + +If the original image cannot be resolved, the `PlaceholderAliasGenerator::getVariation` +method generates a placeholder by delegating it to the implementation of the +`PlaceholderProvider` interface, and saves it under the original path. + +In [[= product_name =]], there are two implementations of the `PlaceholderProvider` interface: + +- [GenericProvider](#genericprovider) +- [RemoteProvider](#remoteprovider) + +``` php +[[= include_file('code_samples/back_office/images/src/PlaceholderProvider.php') =]] +``` + +### GenericProvider + +The [`GenericProvider`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider.php) package generates placeholders +with basic information about the original image (see [example 1](#configuration-examples)). + +![Placeholder image GenericProvider](../img/placeholder_info.jpg "Example of a generic placeholder image") + +![Placeholder GenericProvider](../img/placeholder_generic_provider.png "Generic placeholder images on a page") + +|Option|Default value|Description|Required?| +|------|-------------|-----------|-| +|fontpath|n/a|Path to the font file (*.ttf).|Yes| +|text|"IMAGE PLACEHOLDER %width%x%height%\n(%id%)"|Text which will be displayed in the image placeholder. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.| | +|fontsize|20|Size of the font in the image placeholder.| | +|foreground|#000000|Foreground color of the placeholder.| | +|secondary|#CCCCCC|Secondary color of the placeholder.| | +|background|#EEEEEE|Background color of the placeholder.| | + +### RemoteProvider + +With the [`RemoteProvider`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/RemoteProvider.php) you can download +placeholders from: + + - remote sources, for example, <http://placekitten.com> (see [example 2](#configuration-examples)) + - live version of a site (see [example 3](#configuration-examples)) + +![Placeholder RemoteProvider - placekitten.com](../img/placeholder_remote_provider.jpg "Remote placeholder images on a page") + +|Option|Default value|Description| +|------|-------------|-----------| +|url_pattern|''|URL pattern. %width%, %height%, %id% in it will be replaced with width, height and ID of the original image.| +|timeout|5|Period of time before timeout, measured in seconds.| + +### Semantic configuration + +Placeholder generation can be configured for each [`binary_handler`](../file_management/file_management.md#handling-binary-files) under the +`ibexa.image_placeholder` key: + +```yaml +ezplatform: + # ... + image_placeholder: + <BINARY_HANDLER_NAME>: + provider: <PROVIDER TYPE> + options: <CONFIGURATION> +``` + +If there is no configuration assigned to the `binary_handler`, the placeholder +generation is disabled. + +##### Configuration examples: + +**Example 1 - placeholders with basic information about original image** + +```yaml +[[= include_file('code_samples/back_office/images/config/packages/images_basic.yaml') =]] +``` + +**Example 2 - placeholders from remote source** + +```yaml +[[= include_file('code_samples/back_office/images/config/packages/images_remote.yaml') =]] +``` + +**Example 3 - placeholders from live version of a site** + +```yaml +[[= include_file('code_samples/back_office/images/config/packages/images_live.yaml') =]] +``` + +## Support for SVG images + +You cannot store SVG images in [[= product_name =]] by using the Image or +ImageAsset Field Type. +However, you can work things around by relying on the File Field Type and implementing +a custom extension that lets you display and download files in your templates. + +!!! caution + + SVG images may contain JavaScript, so they may introduce XSS or other security + vulnerabilities. + Make sure end users are not allowed to upload SVG images, and be restrictive + about which editors are allowed to do so. + +First, enable adding SVG files to content by removing them from the blacklist +of allowed MIME types. + +To do it, overwrite `ezsettings.default.io.file_storage.file_type_blacklist` defined in `EzPublishCoreBundle/Resources/config/default_settings.yml` so that `svg` is removed from the blacklist. +You can do it per SiteAccess or SiteAccess group by using [SiteAccess-aware configuration](siteaccess_aware_configuration.md). + +Then, add a download route to the `config/routes.yaml` file: + +```yaml +[[= include_file('code_samples/back_office/images/config/routes.yaml') =]] +``` + +It points to a custom controller that handles the downloading of the SVG file. +The controller's definition (that you place in the `config/services.yaml` file under `services` key) and implementation are as follows: + +```yaml +[[= include_file('code_samples/back_office/images/config/services.yaml') =]] +``` + +```php +[[= include_file('code_samples/back_office/images/src/SvgController.php') =]] +``` + +To be able to use a proper link in your templates, you also need a dedicated Twig extension: + +```php +[[= include_file('code_samples/back_office/images/src/SvgExtension.php') =]] +``` + +Now you can load SVG files in your templates by using generated links and a newly created Twig helper: + +```twig +[[= include_file('code_samples/back_office/images/templates/svg_helper.html.twig') =]] +``` + +## Image optimization + +JPEG images are optimized using the ImageMagic library, which is available out of the box. + +If you use other formats, such a PNG, SVG, GIF or WEBP, and you use the Image Editor, +to prevent images increasing in size when you modify them in the editor, +you need to install additional image handling libraries. + +|Image format|Library| +|---|---| +|JPEG|JpegOptim| +|PNG|Either Optipng or Pngquant 2| +|SVG|SVGO 1| +|GIF|Gifsicle| +|WEBP|cwebp| + +Install these libraries using your package manager, for example: + +``` bash +sudo apt-get install optipng +``` + +## Embedding images in Rich Text + +The [RichText](../../api/field_types_reference/richtextfield.md) field allows you to embed other +Content items within the field. + +Content items that are identified as images are rendered in the Rich Text Field +by using a dedicated template. + +You can determine Content Types that will be treated as images and rendered. +You do this by overriding the `ezplatform.content_view.image_embed_content_types_identifiers` parameter, for example: + +``` yaml +[[= include_file('code_samples/back_office/images/config/default_settings.yaml', 0, 2) =]] +``` + +You can set the template that is used when rendering embedded images in the `ezplatform.default_view_templates.content.embed_image` container parameter: + +``` yaml +[[= include_file('code_samples/back_office/images/config/default_settings.yaml', 0, 1) =]] [[= include_file('code_samples/back_office/images/config/default_settings.yaml', 2, 3) =]] +``` diff --git a/docs/guide/img/admin_panel_segment.png b/docs/guide/img/admin_panel_segment.png index 8a40565e65..657d76dcc9 100644 Binary files a/docs/guide/img/admin_panel_segment.png and b/docs/guide/img/admin_panel_segment.png differ diff --git a/docs/guide/img/architecture.png b/docs/guide/img/architecture.png new file mode 100644 index 0000000000..5c64f587be Binary files /dev/null and b/docs/guide/img/architecture.png differ diff --git a/docs/guide/img/base_configuration.png b/docs/guide/img/base_configuration.png deleted file mode 100644 index 82dd797919..0000000000 Binary files a/docs/guide/img/base_configuration.png and /dev/null differ diff --git a/docs/guide/img/basket_1.jpg b/docs/guide/img/basket_1.jpg deleted file mode 100644 index 128dbeae2b..0000000000 Binary files a/docs/guide/img/basket_1.jpg and /dev/null differ diff --git a/docs/guide/img/basketpreview_1.png b/docs/guide/img/basketpreview_1.png deleted file mode 100644 index 0a98fcc4c4..0000000000 Binary files a/docs/guide/img/basketpreview_1.png and /dev/null differ diff --git a/docs/guide/img/basketpreview_2.png b/docs/guide/img/basketpreview_2.png deleted file mode 100644 index 100b2bbc3d..0000000000 Binary files a/docs/guide/img/basketpreview_2.png and /dev/null differ diff --git a/docs/guide/img/class_model.png b/docs/guide/img/class_model.png deleted file mode 100644 index b52253ff30..0000000000 Binary files a/docs/guide/img/class_model.png and /dev/null differ diff --git a/docs/guide/img/config_content_structure.png b/docs/guide/img/config_content_structure.png index 0f16f84f04..1634fac796 100644 Binary files a/docs/guide/img/config_content_structure.png and b/docs/guide/img/config_content_structure.png differ diff --git a/docs/guide/img/configuration.png b/docs/guide/img/configuration.png deleted file mode 100644 index efeec31900..0000000000 Binary files a/docs/guide/img/configuration.png and /dev/null differ diff --git a/docs/guide/img/configure_erp.png b/docs/guide/img/configure_erp.png deleted file mode 100644 index 2ca777bf4d..0000000000 Binary files a/docs/guide/img/configure_erp.png and /dev/null differ diff --git a/docs/guide/img/content_cache_1.png b/docs/guide/img/content_cache_1.png deleted file mode 100644 index af3bbbf2e7..0000000000 Binary files a/docs/guide/img/content_cache_1.png and /dev/null differ diff --git a/docs/guide/img/content_model_diagram.png b/docs/guide/img/content_model_diagram.png index 4bd68a81e7..7f4fcea76f 100644 Binary files a/docs/guide/img/content_model_diagram.png and b/docs/guide/img/content_model_diagram.png differ diff --git a/docs/guide/img/content_model_item_diagram.png b/docs/guide/img/content_model_item_diagram.png index 9572ddd923..58c9c63a84 100644 Binary files a/docs/guide/img/content_model_item_diagram.png and b/docs/guide/img/content_model_item_diagram.png differ diff --git a/docs/guide/img/content_model_type_diagram.png b/docs/guide/img/content_model_type_diagram.png index 3956257c4a..432cf8dbc4 100644 Binary files a/docs/guide/img/content_model_type_diagram.png and b/docs/guide/img/content_model_type_diagram.png differ diff --git a/docs/guide/img/content_query_field_definition.png b/docs/guide/img/content_query_field_definition.png new file mode 100644 index 0000000000..55086a4a63 Binary files /dev/null and b/docs/guide/img/content_query_field_definition.png differ diff --git a/docs/guide/img/create_erp_message_1.jpg b/docs/guide/img/create_erp_message_1.jpg deleted file mode 100644 index 6dbddd585d..0000000000 Binary files a/docs/guide/img/create_erp_message_1.jpg and /dev/null differ diff --git a/docs/guide/img/customers_1.png b/docs/guide/img/customers_1.png deleted file mode 100644 index 37715c3e7b..0000000000 Binary files a/docs/guide/img/customers_1.png and /dev/null differ diff --git a/docs/guide/img/diagram_source/config_content_structure.xml b/docs/guide/img/diagram_source/config_content_structure.xml index c34dc24439..319d2b6c7e 100644 --- a/docs/guide/img/diagram_source/config_content_structure.xml +++ b/docs/guide/img/diagram_source/config_content_structure.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="4ea72595-dc03-ce1c-249a-60aaa57b6e98" name="Page-1">7Vtdb5swFP01eZwU44SQxzbrx6RVmtSHPbvgEFSDM8f56H79DNgBbBqtxI6WuVGlwrG5dnzuuVxfyAgu8sMDQ+vVE00wGQXj5DCCX0dBACZBMCr/xslbjYSRBFKWJbJTAzxnv7EExxLdZgnedDpySgnP1l0wpkWBY97BEGN03+22pKQ76hql2ACeY0RM9GeW8FWNRsGswR9xlq7UyCCc1y0vKH5NGd0WcrxRAJfVp27OkbIlv+hmhRK6b0HwbgQXjFJeH+WHBSbl2qplq6+7f6f1OG+GC/43F0hadohssZpxNS/+ptYCF8lNuaTirKCFAG9XPCfiDIjDJS34PcozUhL9iMkO8yxGskHyClTHBSWUVUbhuPoIHJEsLQQWiwlj0Xi74Yy+4r6u9cxwYlDXfFlwXELhmpjmmLM30WXfcDiVC79q0acwhgni2a5rHklXSo/mjiP8oJkYOBhLt4+kGenzR46VhQ3dshjLi9qcnLYz0cxwxFLMDTPioPWdG6givJ98eCb5V0ZIOJAPEJy2Y4+QSQ8hIeFSPh1mwl9bqhq+bCqh3YgOIFwfmkZxlMr/lZUXBSzEVSVHEhfzetH7CqweVMGaZ3B84F136CpXussyI0SDDMnvMCujBrmRDXmWJOUwt/tVxvHzGsXlmHtxqxFYFVxxuWBNTCgN4MNHnfAdepXztXx01uOjurrb7thxgRN8R/+5AAHsLi0YD5WgZmjmTIJzzyiJhjIyPW3HHiMAXCoqPuACM0Q+o2IPvzMzKoaOomLQR/hHk1KVe4ZXqMnj2p8bJo14a1OVoW8shbZuZpqhuUOSZr6RFDkiyamUzDzwpoz9BG/+0RuM7hbVuZykit5n3X2ifolc4O4DwLkp4NVpZq65+tDChWEIuNOMmqM3LB1ro9ZZ0g3ZZCl4L7IBTyObQSO4YGg7u+J3baLRdzH2MmvdkE3RmGXAux2Wdv1TDJhOu0s/MRUDp64UM/VNMXreq5faBifQ7srmIPBuL6pvc6yx5K6yCgJzM/q5zenXyEWSgXOfPlydaPTEVy+8DM6gHRa/z34gcW0sGQmyNZYc1tlUdmiGNpMtP0KbQWN0wdAGvXuCoD3DtvXuiWHHpmbM2sATTjLkqWAMCnsKA30eZEcw3hUGtP288ZbV0MJAAB0qpu/9oE+WBryt4PClOgDNksG3HKX+bnKmsF8hbR+auQps3lUGtA398LcedUMOK57Q98fU9lhyWGWDZsHgO02pt3FN/aZBF0jLhSZ2EjZx2vwkoKau+d0FvPsD</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T11:31:19.985Z" agent="5.0 (Macintosh)" etag="edmkYi18_lEtuAdlLdjd" version="14.9.6" type="device"><diagram id="4ea72595-dc03-ce1c-249a-60aaa57b6e98" name="Page-1">7Zxdb5swFIZ/TS5XYRNIcplmbTdplSb1YtceOIBqMAOnSffrZ8CED9NspTZhknORkGM4GPvxe8wxycLexaeHDKXhI/UxWUDLPy3szwsIwRK6/KOwvFaWVW0IssgXOzWGp+g3FkZLWA+Rj/POjoxSwqK0a/RokmCPdWwoy+ixu9ueku5ZUxRgyfDkISJbf0Q+CyvrGq4a+xccBWF9ZuBuqpKfyHsOMnpIxPkW0N6Xr6o4RrUvcaF5iHx6bJnsu4W9yyhl1VZ82mFStG3dbNVx92+Unuud4YT9ywGwOuAFkQOua1zWi73WbYETf1s0Kf+W0IQbb0MWE/4N8M09Tdg9iiNSdPSRZs/FsSjJRZHoWVDvuqOEZqVb2ypf3I5IFCTc5vEqY154m7OMPuOhXUuX9JB5hdOQMU4DdOwtf+MXWLwVO+Q3AaUBwSiN8huPxmWBl5e73u+ruvJNUVsH3rbrKy7bkttRNC32O+SIVn3ANMYs446tY8OLI9yELVRqW4YJYtFLlzcksA3O7s5n+E4jXhNoiSG2Fm7E+DrzVHvIy1YSB7X7/7KfZc8NQ1mAmeSGb7SuuTGVcA2DZmsGzVAh96Y7EgoAL/tRR8VygAqXMNHuHTzcXwdaF3zKS13hXWwBNz01hXwrEJ+llzxFSW3b8QOLfhNFvGrt0pa5OnVt7kHK8Il1yezKlSB3HxHSM0k694IzFvGYsxUFceT7xWluj2HE8FOKSqCPPMJyWxlTsF9SOPfRUFwYPl0cD2+QVo+D1nBZDQyXvtq1R0aHxgvorY0g6RckYHf7F1hjJannaKVNkjaGi+m5WI/FwrnsRx0WAAxwoStUPeAEZ4iYUDWnUOUMK1BrwLiaQhUcYu+9mlTfirmz75OZatQZgI/GLikIqlQp16ByBVRcVdOcnqONRlJWhpQrkLLWRIpWUZHvlbbFfIDgfMaTjj6bDSPXY1XFPGQ9LBETzEMAUHFzZDTjvZqx6Q31scleyRHQpxl1HQ0qU6JyXkBTjkrfkUpU4FvhBZjwMnV4kQgCE8YXJatERjTeG196mQ11N7p9RypFQ1462qE4RcVYNqIx9Zx05XQ7fimLhu3oEg3HiMYMUh79FZnRN7L6VpsBNNmxOeQ8lKGibxUQQDk9ZnIec8l5nEGYZFKq4kkBIxofTXr0U+Gj72Q1rhEreXjAoPLRpIcyVDQuv9S3SnJ8kZEx8WXqpMd6wvhim9X9a8SX3pOHqp6glvyo1Aw5UfqI/QgZwZh8QtqnZyBLOgSvGsEwWdIZPA4k/VZibJYU2hoVY+gBe4PKf4OK8xdHKlGR06hfYxSYdMcVootjDytEG9+VruhicqQzSKeP/+1W35HGNTjbPEI4g3S6OlQ0rrzYchL1Gw2oCS5XyKXDYYFo0bscoHdEcOFfm1+yV9Q0fxdg3/0B</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/content_model_diagram.xml b/docs/guide/img/diagram_source/content_model_diagram.xml index 9b38c1c373..4101d33c9e 100644 --- a/docs/guide/img/diagram_source/content_model_diagram.xml +++ b/docs/guide/img/diagram_source/content_model_diagram.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7V1bc6M2FP41flwPNwF+TLKbdnemnZ0mM+0+dYiRbc1icDGO7f76SiAw6AgH2wLsmnhnYwQION+5fhLKyHxa7n6JvdXit8jHwcjQ/N3I/DwyDN0yjBH7p/n7rMV2ecM8Jj4/6NDwQv7FvFHjrRvi43XlwCSKgoSsqo3TKAzxNKm0eXEcbauHzaKgetWVN8eg4WXqBbD1T+Ini6zVNZxD+6+YzBf5lXV7ku1586Y/53G0Cfn1RoY5S3+y3Usv74s/6Hrh+dG21GR+GZlPcRQl2bfl7gkHTLa52LLznmv2Fvcd4zBpdIKdnfHuBRuc33J6Y8k+F0b6OJidoI/Mx+2CJPhl5U3Z3i2Fn7YtkmXAd6+TOPqJn6IgitOzTddhH7pnRoJA2h6FCdcAejfmoxeQeUg34ky+j/wGcZzgXe1T6oXsqE7iaImTeE8P4ScYiIt7nz81394ewDXytkUJWFPjjR5XqHnR90Go9AuXq1zGFhDx796Sfnt48sI3HMcekDh90uSYWMMoxIJEeVMuvADPWA9MaoSq9QNvXhLfZxeRonjAWZPAkm7zW9T5dgnO5/RHDVymLsDlQLh0SwKXoQAtR2IQdsCE6ZP3Ckz2Pxtmpo8MrU9c8A/MKdHHxPFhP/0257/TftYrL8zbvvr0YDIj9Pj03AeGF+2fH0pvtnx0qTm9mbx1UJ+j6jPpUH1coD7j8bgFhLiS3SxGThWjIvKXPXJLEOWXKmH0TDDNEAztdb/Cl8W/umhW4CWEQf+NfWSBs9hThUWB7CeC7C2JfegS4VsqhG8A4b8yYzC0gIQKZY8ayJ5nZhLZPyP26UL2Mt/UmuzNi2MbDwxpTh0mn9aptNkOHa12WYQSot7DJllE8bph6GoV7wLV/vA2u7Q1Ayb3uaNLM1DFeU1b0F6pGy0Khj7cqCRNfSVJ0LED7cGggNS7dKCS7C5zb9ci9s6UvVs/NlETtwytPnQBr/YHmS6yzGQIXOXtLgA3YYb+GPn7/73QgZV1KnQdCH0oXRuUrpYJQWqrdDUhl3iZUVwlXatrVQkjQyLh1uhaEwEZf/WzSGHrgzkAsAwBLIk5oLbMARY4nFqnjQO7XgeYUQUMSYKM5baEmKRuYZxblfweADtqYbLhEFlWoASwgdA+DyRJ6tZaVgDrIzkZcDlKeZS/UZCEcWBbUsW2NTBkwZrmstStLhHjvu70akaBgA3NqgpYkgzoedRWXcBYsICpYWfu3g5MwQ4kzspGLdmBcY924HZpB4qGeWqJ/jexgUEg7bcR03ZI1DWypv8lCxq5tKm3IonHniuasdxwQwGjd+il2UdByWVXBuMQ4A67GJu4JoVzmjpeW4XCQVZCStcNSaKQfzhd5h+Q1hgSeaklCRhJYqPZFkaQz7g5FqMEi5b+APgUYKT3mMdDBuNbtAhpy8uS0IsPOB3BSWJLk7ZwgsTFZWH/Kqlyq1eq3ILEQ0GVQ2nffWixeqTK836lVPnL3g9xG/naTfOuAC4ZUd5WKoAgmTEQ5acC1iVRjiCvMeTXTUDqkChHkBkZiHIpSD0S5Uj1HIdr5GtQn0Q5glTAQJTL7aBHohypfjXzJuygS6IcXf6uX6dEeZ6ml2nywIvneM06nZIk3ceoiIEtP0nrOmXLESQmBra8SRLSJVuOILMxZPNSS+qPLc+NsYTRjREZXXCwqEeu3IYkxjePveWpUeFswwGnYzg1nfuvBKgGuSYO/Qe2tApzWoG3XpNpfaBHRwSXQZovq0Il8oh3JPmLiX9spWey7R+sm7Gm883vOCb0qZin/KwVrgv7YBkXQdz0CaJNPM2PsuUQfMC/5m0xDryEvFcvKRM7v8L3iKTpXv7igMB8WGI2kd0qP+sAHujIcKsdIX1S7ShhSWECOkq1oHjsZoohS5FPVAwIeFNVoVjG+0wzTJqy8QamGp+0saY5Fd1Brt5Md9JOVCoUzcVTgXNY3T6VzNL0qpI5ZyqZJbwAZulCRwqVDGbET1QjcHo70iUZri/vcjsMFaIj0SWDfnpbHKoNM2MAD7T4MlILj41jfF7u5mzxsPEsiLbThRcnYy8Mo4QqfxT+rTevFJui4JMYT1nnDE5WMqtJgEUsZEtLSaBQUUk6MAFW7JyPybOpx+zLEyJLQGZypidE9gcdqfOEDkyYjyHKfZoSOItYq5fjrH55vCzHxsx59BYbJzW2erJGiCtMiR0p1Ajjw9io0e4830vamCN2y2ESIWeMqjhJ2GVDa4m/d2TTsO84UOpVLGTLpRgSKIC3PQsLBfXt/URKUxxcOTdSgo4U+kVYmOZrG/l4RkLCFHg9eMSqDdpVGzRl4x6tLaw5FA5lLGxhGQnpzM2WCgf3tMJBaZpZ8DUTwyzxNfk+NbxMvxyMXTcP7VR/Wqsj6v2pe1rlcWcR0hYqh2Iq6KmIOnVzSltA9FjlQCPUkv0KZ9EQIyt+GSAkW+ksn52rfPXgoWaoxEghX5HNkpPWDK4KLBrMQxw8YjHwL75P0tgjWh90pNAjwnmPxxBVmfZcPZxC5eaKoz3npiygI4Vw1ixvO1R9QkQTLEw6ibWlos9tMH589QEtjGJ2a20sbedKqj7UGhgNKvD7jWimMKvCPZcFAx2J5Z86F5hbcm2OP/hCYehcq2aUrmy+uaJ3MOnm4Q85ZeAe/lqW+eU/</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T11:42:14.178Z" agent="5.0 (Macintosh)" etag="3DSAxhEB9SIhtaex2ywm" version="14.9.6" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7V1tk5s2EP41/hgPbwL88e6SS5OZdDq9zLT91CFGtjXByAUuvuuvrwSSDZLsw7YwcqPzzNlIIAH7aPfZXSQm/sP65WORbFZfcAqzieekLxP//cTz3MALyRcteW1KIl6wLFDKdtoXPKF/ISt0WOkzSmHZ2bHCOKvQpls4x3kO51WnLCkKvO3utsBZt9dNsoRSwdM8yeTSP1BarZrS2Iv25b9AtFzxnt1w1tR8S+bflwV+zll/E89f1H9N9TrhbbELLVdJiretIv/DxH8oMK6aX+uXB5jRe8tvW3Pc44Ha3XkXMK96HcDk8iPJniE/5frEqld+M+rLgfQAd+Lfb1eogk+bZE5rt0T8pGxVrTNWXVYF/g4fcIaL+mg/juiH1CxwXjFJk179+yRDy5xsFM19rOsfkzXKKGa2uPhOzyLJS34ofi7qPldVRVDgAf+O/CMXRv/RHcrpEuNlBpMNKqdzvK4r5mW96+OiaZj8ZE0D777dOLtah26jLGudv+u7Dx49X/nWsrv9AxYVfGkVsVv9EeI1rArSq8NqPcDE/srvPtve7kHm8bJVC2C+wwoTBuzlru29cMkPJl+1rANJ1L8ma/Lr7iHJv8GiSCTJk8uqjok3xzkU7hgr4sLN4IK2QG8RIsPrjhWvUZrSTpRo2uPNUcBGEhfZbonrsf4bEU4aYOK7AkwiGSZuoICJpwElkUIhhBkVYop+dOAR/vNM1dQ9Rck7JvA7qpTJlcNiX09+Ldl33U65SXJe9iklO6MFIvvXx95RnJD22a7kZNt7t4rrk+GlFrZGwnZ2RdjGEmyn0+kAyGDgttg4ERtRFxs7pte2fANBg3fVwsYjgoQRes7X1w28jO8cYjU7nEiEKP1GPwfEPgoB6hKeDw75xJrEPhPEHihUgquQe6BD7p4k9690/HtOhnKNYgcqsQs3lTkB5ghdguVOywwhdpUlGEzs/sUMhpn/2nPMq3dlLWha4YLNS8NDBG5z91ytcFH2JCh6oSaLEtCPQWATBsOQUPOvqWE82XvmlqV2rTQT56FQZe2W5KiHI9othQv2FVXZ0Bbrp1YjksCvabEUzktjT4bmKIbIdQD5XdcMzPQwDs85TDoko/A7mq8aOmspx8iU45q6wped2XucvlpNcbamuKr0XEl6NkxlEDjEMFXgy+AYKkzly/mZy0a1TcUdkbTrdCUNPIWkB0vF+UCS9ae0sfaha9WBKerA9QSQKNQBGEodyLEFlq4lhTZjaxpQvC5QgIJUBPFASFHEC2heo5tQtUAxAyiCRlGl9lXsUwtQbJL0tsChcE0GY59yDEMddLwcHZxlWnCcBA7h2bFQEeEa6uGKQA46XOaaHHJAmE0xxf8YNEnqOUFXoApy6XIWqDsQEciBiAMhZzveRxrvvjDeFcYgBAONd8+O9+HHe3zN8a7poYheD3XyMnp7lU33SnDsfVwHEUk61YqQEWdOZFwl9NLwgro3z0Rm5CSTmsjuMiFNz1L2/IxnSS3ML4R51NeshTpgLsdQldkR6+IYwmKja7JYOehq3V+DsOEJIflIwXj8obAhR1tvLsbagoNT/x2Aza3Cwx3RAZZDrJ/xinAJ52mNSOcWImZCRKFBZkNBRA6uXkYmbfr2iKSDUdO3gRws3aVvZalbQjGSOghGTN/ydpXp26fXNIdDeCU2J6cDJqrk7VDEE8iBWJu8vRWgXDN5C+SYrPVeTQbHFZO3QI7u2uStUeAYMXkLdD9X+kaU+/jcAAP9Ep3RbzBmUhfIAU6b1DVLD4yY1AW6l3qyeuAEPXDNZC+4fA2fayd7uUfcTvVmSbGEJW10jqq6jgZcbyrja8dAO59zzUwwkMOxNhNslCkcMRMM5Pit9aUNwgYYMRPMlU8LGzcWrv2fJ/nAiHngUI7Wfk7oWlIOkcs2txAxFCJ953hrwUgPPwvm6R1dppiaiCwpSzQ/TCvBEZk1aOJLFJM7cg9fUPUnleA0qI+k23/RZqaOyzZ/gwUiV0Xt0rhLNHQAd0jqJe9+sl+tGKad5ZtlHLyR4ONlBcySCv2AnQ5Vsmc9/IZR7eTw2eJCyDcQiWtz7uyoPYKkhry42xBwZ92GKuoKVVJDNRR3l90PnSrf8ER0yqjri1ci3OK1gadPvANWQPH5zpk6TtQBMIjdfgCuG7l9VDdCZlCKjUJ64LhdpEdnIj0Q1k4JXKEhjUiXPcAHIhtYn45y4U/z+H78c9hrUZG6iqd53KGSZ6HsDErIkDVeGySrhCbs369flvRtCNNFhrfzVVJU0yTPcUXGHc7/dmVOJ4ZeOkso9QBAigo4p41TedNAmelK7wQCJwJCtVC+Ag86IkeR7ABqtpBvjWpzJWiIOQKBAI/ZmeYIhG80pM8cRbLreAxWzLBowdSOdbltxrXbuC3m1GZJXHUbAkuR3Ljn+gOi/yo1pBGW3pssySHNJWlSDTETwhKm8+wjiKagCxFFgtFzBkolR6rZpJYyjUuZ3C4gVEsWewo8SCbvLEBoiHlZznRVzuSLTxqcy5mkhjQaJzlYxddXT+EC5YgO5dKaJWPMUtjVQb4q4z/Yi6usH2+cUQqF9VSV07QG8uM5IRrD4drFsGee34ph87rb8rjasWrD4tLhoRkZpxq1g0DVb9Ti0wIBliuNACvBkd/NCDsVVtGhqWUDwOqYI0+4ypp+5Qts2ZIpbEkCh+qdFXx+oPb3JVoX3jy2JNBn1eQRpQsf6wBEj2lC1iwZZZYicS2D3mYpeKMhjWZJnpN0DFY6WbjFVB9MCdGcWHwg41wGLTWkEVMHXrtnI0HmcBtBwygnug0UCIp7POJmPLXJcUFPzXAldv77VmJFJAgMhogeoUHLbcZ9llZ4BDY+Nz0hNSSGhPTZIa7TDrrc1iAZY5B8p+tbxaoJuZoWgyKbBabTW/e4onbkC04h3eM/</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/content_model_item_diagram.xml b/docs/guide/img/diagram_source/content_model_item_diagram.xml index 4b550cb7b8..4cf7655750 100644 --- a/docs/guide/img/diagram_source/content_model_item_diagram.xml +++ b/docs/guide/img/diagram_source/content_model_item_diagram.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7Zldk5owFIZ/DZe7QxJEvVS7btuZ9sbO9DpKhMwGQmP86q9vEoKIibs7U9jObkVnhPdAPt4ngRMM0Cw/PApcZt94QlgAw+QQoE8BhCCCMNDfMDlWSjyyQipoYk9qhAX9TawYWnVLE7JpnSg5Z5KWbXHFi4KsZEvDQvB9+7Q1Z+1aS5wSR1isMHPVnzSRWaWO4LDRPxOaZnXNIB5XkSVePaWCbwtbXwDR2mxVOMd1WbajmwwnfH8moYcAzQTnstrLDzPCtLe1bdV18yvRU7sFKeSrLoirK3aYbUndZNMweazNMN0h+gIQoOk+o5IsSrzS0b3Cr7RM5syGN1LwJzLjjAtzNRoN9UdF1pQxr84LaUeAag2aYkbTQh2Iyt+p2yXbyx0RkhzOJNvFR8JzIsVRnVJHx9ZuOxzBwB7vG7gwtlp2BhaFVsR2QKWnshtT1Y711e8xciz+oiITJcXAMVv1SD7naMELcmGmlWrfVsonovSp9oeqMT2xgZwmia7Gi7CBHHqYmGPbSGCPz1jOzdYNq9M9oGYVu6wGHlSwA1KRQ+o7zknFaoaLJREC90CMkbX8OLxGLi+AegI2dID9OJYW2ES7qRy48XqWFww9vEBPvEYOr/v7+9s98EVG0PO86gnR2J1SVPYyjeoH/DtlFF0wGrqMop4Y1cOjs6ztWg5m73QXqdt8oD++ZO8U6cLgcdQ22JMIABS7DkddOAwchydbmal+3ubBBSbYngfIc6+KBz3NA/gfzgME3nIeuIuXAMZMm5HQXcvp+NdWr1qnekLcWeN0Hma8O0XVXmp/TSnLS0Ej8JarA3cbA0cXCwblwS22ydJDutFvDTKiXxPgkkqs+8XXOjPcKmCqhdgkH1W1yoiq5nZrlOy0UGmm77X64Qfca2+8cRcDzl2DTXlyvOWIL+Uf6C3zj4ED6ZbHe2dSm1HkeTb2tTQG7tr43b3COMMSms3B9/eMTq/4/kUe7y62vvKsUMoip6ryG6frnHx55rgbTuqweQNvYmd/c6CHPw==</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:00:53.938Z" agent="5.0 (Macintosh)" etag="J16adPDQPCy4MYMyHwPN" version="14.9.6" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7Zldk5owFIZ/DZfrQKKol2rXbTvT3tiZXmchQmYDoSGu2l/fkxAUDNvZTmXXnUFnFN4D+TjvY0iih1fZ4UGSIv0mYso95McHD3/yEArGKIQvrRwrZVoLiWSxvegsbNhvakXfqjsW07J1oRKCK1a0xUjkOY1USyNSin37sq3g7VoLklBH2ESEu+pPFqu0UmdoetY/U5akdc1BOK8ijyR6SqTY5bY+D+GteVXhjNRl2Y6WKYnFviHhew+vpBCqOsoOK8p1buu0VfetX4ie2i1prl51g/XlmfAdrZtsGqaOdTJMd6i+IfDwcp8yRTcFiXR0D/aDlqqM23CppHiiK8GFNHfj2VS/IbIVubJOQ614SThLcjiRVR5NfE0yxjUzeyGfdCtIXta3ip00daZKAQVoghfwAR3TH/qCcpQIkXBKClaOIpGZQFSaS9fbqmA4tEVP0LJZuO2tr88Z5432BzhYId1eN7U2289UKnpoSDbVD1RkVEmo1a+jc2u7/VkEE3u+P0OGQqulDcCwb0ViwU5OZZ/NhQPrb7fX2LH6C0Qgi34YOKZDj9TfnM1FTi+SZaXa1wjyREFf6vww+G0tbCBjcayr6UTpDJvfwYzjFZw3vFqb1zuydAVGTmNgzUjoMjLpQARdgZCxQ8h3ktGKkRXJH6mUpAdSON2qgZP/5WTmchLgnkCZOqD8OBYWlIV2ETI/cHKTnCC/g5OgJ05mDiej0Wh41twsG6hjPtITGnN3CGGql2GjnmAObPwTG+MLNqYuG+Oe2KixvNqq5KW1h32i3OjS44SRs6g6Ra5h9HzcNrpjwhng0HV6fA2nA8fpxU6l0M9hHLiRcQC1xwHc8YwIJz2NA2gYB95tHMDBW44D7uaEh0KuTYnZc8vx8NdO744t9YBwZw3U6w7j4SkKR4n9NqWUBckvNZ32zqJ14K40nOiSg0lxcEs+L4h9VuoNypTqHUnwXhHdNbHVi6EdeAaNJGbeW1XrVbBctgbkrkaCbDJQqwP+b4T/ax+D4TXwd3deliI+Diulm3kKXsyG8VvOhicOHMMq+pbYuNjVH3fMkPraiAvcnbgPt1HbwME3rxew+aB4nP67eY+FtLvL8lWkMMfwNxmDygdEbhKRrjXW/DqIwOn532UTa/yFj+//AA==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/content_model_type_diagram.xml b/docs/guide/img/diagram_source/content_model_type_diagram.xml index ad5adec2c3..e98b7d5a9b 100644 --- a/docs/guide/img/diagram_source/content_model_type_diagram.xml +++ b/docs/guide/img/diagram_source/content_model_type_diagram.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7VhNc5swEP01HJMBySb20Xbjtof20GSmZ8XIoIlAVJZru7++kli+5YxngmmaCRxAb/X53mpX4OFVevwsSZ58ExHlHvKjo4c/eQgFExTqh0FOBRLOJgUQSxZBpRp4YH8ogD6gexbRXauiEoIrlrfBjcgyulEtjEgpDu1qW8Hbo+Ykpj3gYUN4H/3JIpUU6Azd1fgXyuKkHDkI54XliWyeYyn2GYznIby1V2FOSdkXLHSXkEgcGhC+9/BKCqGKt/S4otxwW9JWtFufsVbzljRTFzUAoX4TvqfllO3E1Kkkwy6HmgaBh5eHhCn6kJONsR60/BpLVMrBvFNSPNOV4ELa1nh2Z25t2TLOnbjIFHiAng1eEs7iTBdkwe8SJkilosezqwwq7rRPUpFSJU+6StlgDnSDOwYzKB9qcVEIWNIQFvsAEnCouOq7JlW/AK9ujic9ir+TVL8tViR7olKSHuN6peolWjOR0Q6jAJXkcbo1PRjWmHbrBcApiyIziFPFWmffIYstwxQDKDfkXNtrGLnQtCPXvC9XMHHIhQZQ686xIUJuyIzY75ZM4a+92aZLo9YNEL8wQUkvk8rart9ieNp+djnJSuxrpCuzLdP1bduF0Uv3D1X1ZJu1G7CdTIl+uM9L7lNllTHcZ9Zzn9vb2ysoBE72v2qEg45G2BGRryRROXRDozWj+oSA/MdTTl+X/85ls0qvThqMnsztSpyVpS3LANzjDvehY38EDvInQ5CPeuQ/ms2AfM6yAbmfXsD9empuF/eV5drcY1dsuhr3+NW5DRKDPVNn6mZn2TaGYJofiwzVyXqLvUqE3F2Yut693mPuNdQ/3JeBzp5ABz7XXEvaNxpGkf8Pw6jjmPrIFH//AbTL+qgB1HG6K8LbW6F9NGcfN47Nh8lbyD+funpR7QfbJMXJ5CNxGR3H3Ge4f0Jfiuj07knv7rJxSQ96pH98ul7w6TqZ9kUa6NNVF+tfwdbW+N+O7/8C</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:09:30.520Z" agent="5.0 (Macintosh)" etag="V6fTWllI6RZ6t79KWqs1" version="14.9.6" type="device"><diagram id="60d8250f-f1a4-fc91-e579-d172dbe7004c" name="Page-1">7VnLcpswFP0alvGA5OfSduO0i3bRZKZrBWTQRCAq5Nju11cSwoAld9IJkCwwM0Y6V+9z0L0CD27T0wNHefKdRZh6wI9OHvziARBMwVzeFHIukUUFxJxEplANPJI/2IC+QQ8kwkWroGCMCpK3wZBlGQ5FC0Ocs2O72J7Rdq85irEFPIaI2ugvEomkRJdgUeNfMYmTqudgviotzyh8iTk7ZKY/D8C9/pXmFFVtmYkWCYrYsQHBew9uOWOiTKWnLaZqbatlK+vtblgv4+Y4E2+qYHh5RfSAqyHrgYlztRh6OlhVCDy4OSZE4Mcchcp6lPRLLBEpNeZCcPaCt4wyrmvD5UJd0rJnmTBMy17hBlESZzLDy3XU9h1KCVWaOTL+okaBsqKqyg5c95kIIVUAZnAt/+TE1J8qUExixmKKUU6KSchSbQgLXXS3LxuWSdP0DGyajZvZ+ipPKG2MP4DBFqjx2ktrVvsVc4FPDcgs9QNmKRZc9upX1pWh3TwWwdLkj7XIwNxgSUNg0DcgMsKOL23X5MqE4dfN9dSi+gdKZWq9Rdkz5hxZzMtpiX/Rm7EMX62YgSpyKd6rFtQSEfl4rQ2ckihSnTjVVOvNd8jGokvmG3Tt9O8D5dSBTMDsSiYrWybB1CET0IFKFo4NYU4ViRF5bclj/vugtqmNUsmdIXytNmU5c8xru0zF5q7bKXKUVdi3SBYmeyLL67prpRPZvikqB9ss3YD1YCp0lO1nlO3Fmw8h26Ul28lk0oMyjLhHbfyfNmBwpQ3o8Hw9SaPquqGNHcEyIgT+0znH74t3bkU1F524yP2QMKcd1tz78lq6AraLpQva4RXtc8eWEDh4n3bBO7B4f1LPP/ApyTqkffaZabfovewmPdALXTt+b/TCd0cqxs3rE2Im7gpNqDIEs/xUxhtXMcz6IBLGizcGIqOk3i+pIXcMYJ+GK0+hj0odB8J9qWf0Q1Zo6n+gH3IcqZ6IoKMH6oHYQT2Q49BR+oeR2R6YHfSRXXUTWwD/dnhhuYWfJEzKAHUMLoYJLqZD7hbQPoZuWHQeee1+rxiW18DidXz19IlfPU1ntjg6evUks/WnO21rfB+F938B</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/ez_platform_architecture.xml b/docs/guide/img/diagram_source/ez_platform_architecture.xml deleted file mode 100644 index 0bfca6fff4..0000000000 --- a/docs/guide/img/diagram_source/ez_platform_architecture.xml +++ /dev/null @@ -1 +0,0 @@ -<mxfile modified="2019-05-22T11:00:12.367Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" etag="0qAborFBFtyjqFJ3i27w" version="10.6.7" type="device"><diagram id="483213f3-c85c-a72a-08e8-804d8fc675d3" name="Page-1">7V1pl5rK1v41Weu+H5LFKPBRQBAFBEEZvtzFPMgggwL++he6tdOtdNJJ7KTvOTHntFIUULWfPdWuqs0nmEpbtrT2oZC7XvIJAtz2E0x/giAQJpD+ayjpHkuwybkgKCP3XOlrgRKdvHMhcC49RK5XvahY53lSR/uXhU6eZZ5TvyizyjJvXlbz8+TlU/dW4N0UKI6V3JZqkVuHj6U4hH0tn3tREF6eDE6IxzO25eyCMj9k5+d9gmD/4fN4OrUu9zp3tAotN2+eFcGzTzBV5nn9+CttKS8ZaHsh2+N1zCtnn9pdeln9lgtQ9PGKo5Uczn2nLCf0zq2ruwtFHvrkDVeBn2CyCaPaU/aWM5xteh7oy8I6Tc6nq7rMdx6VJ3n5cDUMADiEOv0ZP0qS0fI8q89sAE4ux+eng+fjZ9cxD5++vMxrq47yrC8G+sPb7p8pcvTK2mufFZ3JwXp56tVl11e5nJ2coTmzLoKej5uvjPBUJ3zGBJcy68x7wdOtv9K//3GGYBwOBLiB4xM0SeozBR44/kKESXEYuIR8xmJfiybB+fvhWvtSMHXTKOvvsuEup/oW2dfV+7LHp12K78oLODb8u+WFr+VXvHAHUCH8Jaj4LaZPOD/HFLkHpuCrmN5Q/mdAnquq1F9zFlvgPwrzCeobBWytMouq8PHAq50v//cqvmOs8Gfl/x0EGYRvQUex9wIdugF9PVPUvmQqcUM5dGcK+yBqgcAthb+Wj2jYMY36UvP+OgzwleiBI7IHjsGA3QMG+Hv69CsAF7kaTnyuHig17SuA6L69FTojP5T9Sc2zqx6nD6o4fxfE15I2hjD4XoJ268B45qAO+z6WeZJ4ZfWkbcsLMGoTBX0d1Uv3iVX3juZ9gUJBcjJFboH6Wv5GoN5DC46AAwHvJX6TG3Ckg51EzpMe/I80l/7v36IKr7GAgd9pkbAbLJQu7bs4NJA5PNRV6n74coNG37v6WyTP8sy7ova5yEqiYHDNnZ5mXl9ODrSK+mHW9HwijVx3eMwoxl+54DXQ7gkORLwEB5qgN+DAY1oMugc4+A045KGKMq/qdRPA58EgMv8aHQWPDLlG5eIuQy7iVi4G1fTvVEkINDIygt6J69FbJ1nq7XVU1V7m3DsE8VMQvCUE8R4ovDXmcBcUbn1kxbNKJ/wIAPyWEQr8BuqPRQfuQn3khvrc6iNQ/jexPkJ8n/jEe9H+1j29M+UBf+IS6Ejc46n84ygddET1jyod4h6kv3V4/jTp30OTjAy2xvwYELgHSW8dmX8ASRHwD5L0wv3/MJJeq9wRkuLvRtLbaDiTV0MMe3ze6eMNPt9ZLU+udAiIjKjlsYDNPSziZMQXfAoSOI8ATax0IFBmV/uxGOe/DjDwWpyI3wnYbfjzEu50o+Onsfj2ANDnM62HAPeZ3NDj/ClwHf5GxsLfNxNYiszfcsbrM4xfr6vzcpiL/5lrZ1kQZd63qvZlD1R4JRx/y6q/k22wN7hfY/Em9B5sc+v5qgMxvk+h0ut5w7IfKgwcs8+jrH5oCUp+QulBjA91Xp1p+EyqE8+vR2S6zge8q16WoyxQhwP683X46d2HINgLJCbQ5AYJZNQe3gGIW6eNzp26fGBsgCan/F8FC4HwS0kZmcgdGyJepoZ+BR/s9cn7H9Wv39Chb5iFHFXDj1r3zervByc+v6n579j7c1/OMacf7c5Ntbu37MftzM8vrfl5k3Qj3HeQvJvxLHY7J/JecXnsNjj8VTP+21Xi0wzJN1TimMm6h8uJ3UYsP5JKzJPyr078qxPfTSfeBKQw6MvbtCJ6D39kLGR9X4x7Xzz7dVl4QnmYzfgm9o/PewvzfNAezq2sNxHf0zpv6ebHZvy3hA2hd2L728UrTNIpXVV76V9vAAXRl97ASCjhveYvsduJnIf1d1Ji1X5epg/Q/G+tKnqxPnn4XOrdcc4ZvVoVOxlZCvZuM/4XKf3WtIaXudNhI81A58Sqqsh5XRO9lXDAE8iXTTXQtwTAc19s0rml5XfY+1JWej0vRkfvxc3H6Hd+gjSEtJ7pvSuorhdGVvmhdLzzRV9B+O59ruN4tVUGXn1znwcwnzr9NnxfDwo/WaYN92jjLjtCehv2VKe8VJLy5iEyvKmeLaMdjcmWP2DEPrKc38VBhL+81MdjkV34vWR7bGL9FexnA+bfAvffDiV4WZf5tGD3FkgQfB8giTco6afdg0N/XasKn4j0PYwSy/YSKa+i8+a5G2D4qwp2Xtd5OoLcYwA/P9RJPyCknvZjAmOo9+3dD01P22DYN/ol9/3I8b64Vm3ZVuVVT7/+2yul6L/gFTP16Lr28O83CzSEvFTcI0uULvt9XsSf4TtwwdgGlr9c8Ae4AL2ehfidXDAWcvt3cQH0MbgARq51ATgS/RnlA+gOfPB69OeHIpgPjttVWIP+hXjcn3YDkFvQUXRYPeVGZc+Cj1zb++kDPvdfPoxcjb5RYIwl3mlbD3EbGblPUPsqkP6P4IjfxxQIdMUUI7uJRido7hAsI14fAvwaSzDKX4a4m/swtr0Mex+GAIG3+Q8P9jhKH3KjfN/4P7gN5FO+k+f7Ch4zFbzmH1jV/tEv8KN2wIp8eOT0UgpcSvrfgw/Qc+TjIcTss+ATREVbcrVugCUb5NP+IyqbcLYJ+l+rWf+HDqmpMJQvbGXBDT9ChhS2s81wbiDZ5b/elpLHsif5wM3HDSiq0qmXHOaAHTALqssAWdEdflyWxaSKuIjXQcKfNgLtwi5cDhfXtgYe0IOdoJ6eGuki6E+S2/6zt7S0rey+fUuyc7KlEHPtIDHw+VnDM5nQ0PqHmLQMiFN5RvIKUKErNdmZrLxhSDlZKJq6QwWTXe8qnmr1LejwVFPpTOvoc9waur4Sd8lM3q6RTABda3vsViFZFx1VUiC95HYcmZg+9tRfn9ysuUUsLUQhBbK07ypZHVTZ5G1hV6hKxxe0oTJerNtlZ7H9BVJnz30fbzSPYAt41s7n8gn3CGkxkBGfaKv1vOUw+phFCJB0sy0nrPBt17MaSaVGKVSrHVfuNBoRBaTc8VU8Byr8JAbCoa9BnHjYGviWJ2BugufLSoTKposIZNWByAQt5oFQ4YujDfFcBAedGNo0GMvWcU0IGspoTK6HTX+f5iSU+EnKKklYG3rgzU8KR3s0YEs+QocGdlLDbui8vQhDq4OXGNKCCDcTjCSgjzC1msqdb/peFsWnQJrnJ4yzTT7ZtMTJBH0yW1mHLdwWrRYWvXiTmKyo+xUBz+eAYZbzmukmqVRaDomvpPAYufh2Rg+t6kDZWCzwpESFrZms2G4DtwmtGTa/P6ZuRa53fSV6sU9rgNm1zj5MCXGDHUPdsMMiTTwqMSiaFwzODJGluOsS59js5V3Z5sYD7aYdqyKQKgckcPJTQWs9CuIDa2NoDYip+jQGjJitYFCKgoO4FtbBIT2YYbN1hDDrb2AaC65qHn7aBidE0YT1UShIJ0saseM9oQoNnc4Rd6LlVFnPapXHU6T0QkYHoTbur1pvBTHEoWl1IisRgw9USTtsLQjVwG2p6sbyfoJblmH2h004cIs+gK4V/Z8NALeIy3ddKUAnL+/VG9Mz4j5Z6wekYpECOBwylsf01pB60RU8sK7TZWbSwrzbx3LB8utkbyUVMtshw91telEte4RJzt3XNabxteUQhI0uGMMQ59Uyxo+Nd8QLPhR3WtdLB+9vtf5CKopo8gCY5npb7FmmsHPcyVdhHcCssDyQlIH1goAIBCm7ruVCne/G3MYSDG0FK3ihBruJaJaK7FiiPq8kNjh6y7ghGbPYHXHzOIloZ9U0bjiPlM0GWu9kY8pjUf/cTgZO5UlTDZDUoF1CRiRpdtXMtiZoms6mNt2rSpHd5UpNZctcXcIYLcSZwwQKtd9oSjBTt+xCIgPFa306PHCalfPw6Rged5uFIQUlK05JWqs1Tk70bUfPTJlbaLEMdfuGpr1khfrFnMC4cG2GcqFxOgMLdqMZBc24hSKwYKVMCYxhXAknBBPcu3XBUjEvMycHgbjlMmt5ggQj3GpSj7LcFJXzAd4pxRddFatHTZMMhQ1ZGOERvl4yhSucllTKxkqIwVYaG8HRAnRuAEDa6ps2ORjFxKE8ytg1dqabRAPs7U0+ARp7JW+mu+1UA9eJo87deZagJoLnTWc7ckbNew3MRGt9M1dXINKGe3ja+kVQtiuBHZRswU1PyTIhcpnZeFi3CM2eUbg4oLHlIV8k21RgT1RhTLdkPNcFWjDEjFZNMoyFTT2I8J4MRfbBxPZC4tuo3ckrRTaICQVMBYEDzCzW+V3VhWgvRkzbHcX+62QduHleOEh/B2bl2Wn/tfHoB7VPpgcYm/cuEzOfdItdnTpWQ0m9GQuBaILOM6jrXGgqH6YFLbOcgYa1XVW9uWOc1D9K/TetUWA6jbbtju1tw6ZMW1dcwdxp0QlYvQd6fg8pS+XIQ99q0uV1w2mPZi/n/mAeBmrtNM+XN2HAHnisE0+pMbSl/98yY3Tbf7MqHu6KgtEkDCsIabdApOUKX01Zindye29WZW42JNiwewYT2GIxSHe43cHbIy3B6iylnBM47VtKWrjiuTRE2Wtug3eD1duq/Z/BZm70o4gb3s5dGFagDS0FExLc7Ta1W4bhfKjHOEotCbaMEnDWzKRZN1P8vb7oT6jrWjX4VFMqNIPgOY8LWXby/GpqlgsaBjB/wxKwyZ2MikOlrBwM3JpPTq3P+vmE39JLTejY3EbUWX+Xdb1Urd7k+Kyph3uETEPGyLONOFBlu+DmHDtT1xOvlHTQV8vEmkteK51mbnkcfAcC3JmeG5PbhHs06oNOGXRcBuiI4eVWEPPNfA1gCHa0dqopAGtTm7ghTWzrFBUkFVgiJ2C+8MPNFvHbYC7qDreJtby/D0mmWmtooYnSorYUVWM22J7tvguTuEwDK7Y6YTB9cwYwdlkSbQ+dINp7l6DhOvRo7zQ4Gsx0QjBZuD0iUZ2rC4/imoPwyIh7H1mVBztUJ2SRrWi6txMKJ622xdzKi6k9cIUw6HGczXaIIJn1lJsBU23rYGtyJllh6RP5wNnOwPitsQuzDZOScyXVyME8zSV+yu5it57pVIJn2966ZOucBNvVjOP5Xkx1zMN9Y5UlfBYQjioPzZqZjHEwpd6ET/dycOQZg3jkGMZgKBGL1qJPiY5HiysXCHilmRHm8Kis92mXmDRpMoewoqI8kglamnlyJDkhF3XOYzc16OzNZH1c7HB2Kw5SOndRcb3U5SWoRJJm52rXAvPI64XL20x1iBvasz49PNtcCyA0j1j80YVaMG6ayCe5UGQPzoTjNjHJnR4eyXmB9ea42uUhIacVpiMdn4fRYKs1i0d7kpKGi2ZxS1dowyxtq57jXu+/GuKK3XpLN5/SpyWjyG7MCxu+poRZHSViNPDTqmtELogTpW3pqc6Xc2uQBAJELT3lkikS6dPjUbSjVg36E6SUHHQJWKhIN2lMnlcW01TWwHjwNwIbZ8KeqkypKVs1xheFOmkIwyXLDXsqV1NKbbxVqPe+Eh3M6sKKUioZOJtSlJUhzQXOSkJllnLcrok1BdqEw7ijFVGDnO4IhIK5kJGKaZxZUAoeEO4Ymmud2OBrer8FJ+kMEzPNXbOGxS3YMl2v63rup50rpXGHNOygH2UOZNTdQdzVEEieToQ1JyCHEVu9NmdTPptHiU5QbqRi+j6ZxSbdS7dgF4hqJtKhyFOarjcJuJ2iRKdOEsbgYFJJaHxrsq15zDVjjpO4K2dsMqPEDdWb6RRCxHhwzGdSDlZsKReLcIoFHYft3W5u2+yRwX0qTmkZZC26IKyGbPUjQGxFW/H0mVWIvrifqqdVzQQWo+Z6xNieeur1chnovbVguZkFuvlhISuiQx+KIHSOody0Srtci2U+X8uBhahGLIO9OsVzThWmg3NlqpOuifh1GR4Rg4TtZhaHRKytC35g+cOmFPSF4AnoIoF9uBdIxOJiiub0Ig2CbClCLAdJXq/XyaJsjivsxLVLzCVSwpkxxzSl1VmHuDg90de5B5xMKLcQ0NGBCLYkJNEPUgmBqhwHJtXfvgNkROajbRc6GAMIhMkpMEIMDh0he7SPTbYp0D8olmZqRNey0QXUbLFCHCX22H68RvLZWgQIg1o69DqbHo05Nie4WLfKXe2CWkRW4WbJTKVNNeu4rR1rXYlSticpa+HYhrDtk0zLyut1tl9SM2uL7+pjkkk2MbUngADALjn1sL1x4Kihs6gc++Yux2VysGlUKc7UBS7ReLJw+9On41Y4neDB/ApJPp/E0p7lQjcCPdYGmW3G5gXgFMPADJbFQseEpN5V8aJuSTMze26uBx3VD6lClxjUnrJH1ygxmQCgQFpTP2wqa6IhxWAaNhponHwgxoLS2Ei6oxXmSp2v+zOwdmJUd9n/Akxo204UKtH9mRK78xVCLNzQ9SqYR+kp5K8oPWaWArVuE07EMHOvq5KJgewBHIya3+QuZGk6nuoiydW9rQZbnAUXU29RH+t2Bc6QyWQwpITfOUs8TB5sSmgNylkBjopQAieGxNmQs7C9eowKXneAA6Dg6wZBlMH8TY5s0mt4Ego9VVVgvQOzoCR9debIJMBPnMEdydXZRj3G06CaR5ntA3BkU9xwdTXb5KVMUP46MTvjtNGQCMoMAw8Bpyl5lrG03cz0rcXMg8HQLN0mmNp7QtnkFja1Bp0yZzFoGBuKuxk/JVasRm89lZoSRKMk4sBVRBQQaOPnaXfQWu446QhKnHmDkVwSCsFYUTdTlajwoXSy83fQYiaSaH7sXbNSVtKEACBZzU4JR6xZrTK24glI8VqH9tjaB2mUVa2p0bSUmwMShK/ddtZMpmt7sq8nzu6wW4fhNl0sIegY9DpOWsdUZR92SA0DUJA7csrjxFFXWB1SlxLWGskKt/reHTeuzqQicKDzHe9pkSrlqlbxkU7ySGsJqFBsYEU+SFTFtr6YAJDhdGah8HtjzlFGeqIsfoIcHMtgHFXf7gxrcEuXLr2AjtH0xB6p3nWcbhdWbM8WQticcFnitDrtOYBYF0gUz5yZHq0cZZ836ARp5xsNwMIGz0F+sAfwKfMPdiUxGEfuYZi2BgdYWrNVWGIQLKXzcrXu6vmBt0N+L2aOKDe+KXStiWsxcNyd7MHf6McBZJmynGps0P2iSUkoKvLe6QGPZk4roresIMW01oRx3BZpdNq2TO4aSFScZBEaNMqhCZl4q/Ue5XAjmSvoWIV0YLPbrurjTjtNVoVBJwuUL52TxfZO5M4lAsjAI/0wiyzgNBWApiIIVrZEETiRxQ61qjhjQN8VLeckAoyp7yt9L3bL4KDF4Kl3rgGRqukHtgEyANpDdVTIReRRLjHDT0EmI26dtykptv7g4BEbhffZHGi2yBIwVjLiyOCy6+aIWO9h1NzWYm5saomPXHqQ9tZVkLqWGgSYu2ltNTIxCVYNq5bFoJuzdjFoLZBW01O99UNhrmw9CmkmHYgVg1scFMahH5GzpyjbTEAObtRSYQOyQSiVM4SqhMHq6OESi+KnU+W7vZ0VZ5O9EYvVMlmAwcnYK5Gf66dJobjV0bEKwVQOqe/Nti6jm2Is86yM2UvTcg17rzS9Z2vB+aGzmK2mGWYOCD5lrmcR2E7DWEMVu5HnJSDLpzKaB53sbHdymHbkzu4dtgymGDPTZn2nuVbWojwOlOY09IHVEwtYEGEab9rj8bDaYoZAaEYOd+qUTKq4ywLBQuoVuwg1P6cG9w4ADYUBME9HcTOBuPW89943RrwQDjjn8jKd1nubEYKTSvCDhjJm3YoB9rxwatyB6tkuiiXND5yGrERrxdktBQ2jRjAhoHJFzAWGNbFhxMbFLdtozhEJhN6EMb1Yl96SEqQs5pFNyU1zmlnt1rqc7/P5HENMg54NClEn58f1ymJiuLezLUYTYnda7Ixm359sTTtTSq4r6RlWHg3bXg1KDLY9aianDz6uCq0eHUmzkCLZCmSTDLyZlWksUuzEqoq6Mu6K/TQ6zBgV30QuuLHmvTMc4kp8ANgNIm9N9zSt2uK4tmMAJSR/jvLxcd7ZJawFIJ8bdC0yG11qJFpf7it7HXrwEBqdTpXNdrVeopTBcUOU9l32Bz1t/Hke1B6Z5YDvMPEFAm/I3nK72vD5lMTVDLSf5I0TWmX9xcqyc17p/45sYb2ahqYoiqTIH5huuAPhwav9P+BYyqKxLav3yOoIAmNzjleU/33rR702qvXz2eG3Mfz+gp6PJK+M+h4OU0nfzhD+uIzzirc+yBLUq5WjT8c/ugT1+j7A1ZqEV5ag9kBa3bNq5z3fb24ujF6x1+MNf3Z5KwjcTnBS/HlNI+0dvSTfPyxbvVrT+oZpz8kriyOUrrIuqdOrq5Syv3Db/4F509+5KP5qZ9Vo5pOxSfN77DABgTekT/pj5uS917lemRN4bMXymB2/RwqEpyQX/3OUf75uoPGq+j5gYDcJgG5XCozb9nuIAfiWBaf/XjB+s2S8Yd3GM0frbApurMdzpN5KzotXBT7zqi6/r32qnrxl91R5ODCen/lJF+ysjx99kivu/CB+GXrFHZPr1IhvdcxutoNdLw283+YgEHzDuOkPe++v8VlfRrfPD7ofYqmLTH4Q7sHQl3mHYBD/Oe55ei/V0/gAfRP3/LBfD900+Av6Yy0j7jwUAD/UUPRVNvwoHIe8VDPI9erCtw8kX+KKXLvgd+K44b0mYw1+c7uge/Pb2Cjhx7ITPPg738lN8EqmgYdXCD3cRPOGwjfnzPs0nn/gWaKR8hg5P5HP4zd0musHtL0r+eDa/YYUCx95wP2qfvkBh/Yq7SB268+O5VK7R85n8Km139ibKs2lB4653ldMHoYkG9UwW9/WXlb13DDyYp/XSz793dj6nAuuBvxPIZTvpby/x5YV8GKjn7/34RXYz7lVBtzpsjd8Y29z+h+H895RtGtsR19nMPZKDwT/dWxLOcc01oNAEeXcQJUzIMc+j71j5eHFsfJtdst/6Dsm0Cu1O/oWvLF8H/eYsBkHZeS9H9+yx2/eOjT6vry15+Rp2nvi13b85TaiV0z5n+eR3/ROBvQ6x9JYuGk0BP5+bPKGGNQ/9yUZN4CMye1li/fL5MDfGJ39IiJ3z/k9bNf+fGn9cL7vJPq6ux7CX9t4qeOEnrOLsuBz5H+uQ++zf0iSz04PzPBkr93nZf25sarPVZ2Xnvs5yh5qlRe14JWfvUtKwVee+mt7F39UAQ19/DO9/tW8dL+W3e4mseMrg6pb+vzwSOtPJrN7yrf1Lbkec5LuMdc4LtZvednI3yQPv2N0dP0KDhTBRnb0j3lrF0f3HZT+nfb5/039cEevfoJcM8pYLlgY/61qZMw7+BsRfyVgfP3ylp9N7nd9n/dL7vcK6h9qUu9atp9sydNMMfBlAmOfxmaLv07wPRx1n35mJvmDcNdTIrhf5K7r+/x27vpQs2x3WvD5QXjkek4OxCZffnZW7mYBHXx9q3vNBF8/6anRb24bCn+579zcK6z7lmV9f73q3+BVXy/vRC8vkvhjPjU2NuL661P/WZ/6xtZdJPj+HvWw9y0fUPuqcQZ5EnLXG2r8Pw==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/full_content_export.xml b/docs/guide/img/diagram_source/full_content_export.xml index a0478c3fc8..660d380575 100644 --- a/docs/guide/img/diagram_source/full_content_export.xml +++ b/docs/guide/img/diagram_source/full_content_export.xml @@ -1 +1 @@ -<mxfile modified="2019-05-15T10:46:34.415Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" version="10.6.7" etag="ywZ0FOL3pC7ZEzaXUGpA" type="device"><diagram id="b9c71eab-6556-00f1-fea0-a5488a9d87dd" name="Page-1">7Vpbc+o2EP41zOQ8HMaSL8AjpEn60mmmOTO9vHSELcAT26KyCJBfX8mWfJHl4FAlIZ3wgO1dWZf9Vrur9Y7c6/RwR9F28wuJcDKCTnQYuT+NIAQOcPhFUI4lxfdnJWFN40g2qgkP8TNWb0rqLo5w3mrICElYvG0TQ5JlOGQtGqKU7NvNViRpj7pFa9whPIQo6VJ/jyO2KalTOKnpP+N4vVEjg0Cub4nCxzUlu0yON4LuqviV7BSpvuRC8w2KyL5Bcm9G7jUlhJV36eEaJ0K2Smzle7c93GreFGdsyAuwfOEJJTu5dPwXf75PEFsRmspJsqMSTLE0LF4GI3ex38QMP2xRKLh7rgqctmFpItkrkjGJLfDUs+yr4MdJck0SQou+3RXwEVcAd5EzSh6xiSN6aNBvix+nd5ctJfGEKcOHBkmK4Q6TFDN65E0k15WISI315OO+hh8o2qYBvaIhqXHrquNa6vxGCt4MgtsBYQSDhMn1tgAI/tkRxfieF7Kd8wbA2x5qJr9bswIKJvbLXEwE3qIojbPxkZBwQ0iOxxlmahjOLUcq33tX1KMZjDxoQr3ivCXq8CTs0Hkj2L0O7NycMVwgHqdbwtdA3xUKx5lCPzRBUXHeEIrA+7gd6Bt2oCZ4nEVz4Vn4U0YyTlxEKN9USDSk3iuLnOxoKLtT6OOo5Ym60mks3zesXtEo5iY7fmr7L5NI5Aj3JC4UrXLZbeH7gSbVcu7yraZDOdHRVOuHIbrGrNNPAVC16kGYBe+OGbgszDxoCTO9o7fDbGITMw4VPf7BH5yxrx7/fBWeMmgr19cyyhcC8QRYgljvaCDEHAl0bDTbigb58Pkq31lrTNnjufozPa0/r3OQbQeIZ9MlxCYHWHEsOLpZW0YAmmKOrkqBmQVHB7oBf0eC4Y4+VQJsbMcwQXkehz2bEIxhcxM646lXEe4xjflUsRBmHeCrkxV8SazNvTm9qL0JlDtQOOqByGCXGbzL3gRTxzjf3mlp7eHE7l4GpmNPvzMwal9XkSp9/O7oCukGXp9CDlK/C3P/QLO1rnOu+3dOdGTP/4PumceqAb+5nTj+tcmAVxwLBtzTvVww0IIDGxbcagz1GRX/E8a9KtJ8K733wSKYeya9rzgW9H6iG4qBkQvUDco5eq8k9oFnh9aemF3Unvjog8IZe8KY9rQTirb9/sy3HIdeGvZ6DtM9F/tThtXWKVGLLL0TkWin/cxuJGrIxFq1zh9yrHSnw4yzjvFZtvl98qfNPXhhiZpPmD99ZQJ14PmrF7wLAQrqtkSPyQcDBU50ZBGp14X8rz0pO2136fW6y8/oHK3h7WvO0R3oHM/Be8Bp4b9lRrQICfyfEPe0L/nuuXk5T0+A6QlYe4irKfeWAKgP8yVtqQhX4JsQ/DPFIUlTrhNccCQbuXN8EN+P+Q3dZY2v/MtOT7S3b35FqQhssmUuLmucYYoY5oGdU3+olgNBh8c8gtU/1KkiAx7mMJMOq3BJRg3N2EqSUBKvM7ER+JSE6i5E0BSHKJlLRhpHkRjGGLfVkZ2qMGl+Mk/QEieLqqqnWZRS1vVYid10ZwImhuBtYtgzEwvRmws6yncFhV7d//rw40JhGgDLy8UPFjDT7Aw0YRYYMNOPZ2dh1s2GXLkCM0bj9VrUjTixkHKGxFt7Qh9XCXcWX2D2pXR1pwH8YWBCG2B2MyFX/rcCwhwXxrW2t9zCo4LTRjhnhOL8C9/e1KVe3mdIXQITvjY+urrd/MKVJ/C9u/nxojN1tDrAsJJcXernOoHno6hb/SdK/rYx/8fPW/7/BIvbv7VQAd6Ox+NeF90XJLw0H1m215lPih6x0M4dFRdGhFCTwig5v90IR+OgMMR5XgAg2gye05fS93goXzNqhpSQb6puPCMnxB/rquUyAq5Lw92bfwE=</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T12:18:49.409Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="Fe1XjYnDVKcSIWpZfeCR" version="14.9.6" type="device"><diagram id="b9c71eab-6556-00f1-fea0-a5488a9d87dd" name="Page-1">7ZtLc6s2FIB/jWdyF9cjiYftZZzEaRedZpo709tVRwEFMwHkCjl28usrgcRD4JhQ0pDEXtj4SEhC59M5Rw8m1kW8v2Z4s/6N+iSaIODvJ9blBCFoI1f8SMlTLplpQcBCX2UqBbfhM1FCoKTb0CdpLSOnNOLhpi70aJIQj9dkmDG6q2e7p1G91g0OSENw6+GoKf0z9Pk6l87RrJT/QsJgrWuG7iJPucPeQ8DoNlH1TZB1n33y5BjrstSDpmvs011FZF1NrAtGKc+v4v0FiWTf6m7L71sdSC3azUjCu9yA8hsecbRVj/7rHdljIbr8eaOayJ90t2QPRuStcGItd+uQk9sN9mTqToAgZGseRyr5niZcaRba+r8qC6j/FzSiLCvbWmUfJV/hOIwkOzvKHmQbcJLqIuiWZTWuORc0IMc6F1/iAeWXzJBOA0qDiOBNmE49GmcJXpplXd3nBYtLVbSDlrrwMIpq7bFn0BLylDP6QNpSmp2t+v+RME72FZHq/GtCY8KZqB+oVEtxoMaJrf7uSuiglq0rwGkZVpwHRcGlrsWFUne76q2G6ifIjbjq5pri3X+2VCd8TzOdik4H0N7sy0RxFfAMAakXmUHoZIX9OEymT5R6a0pTMk0I19VMcn3p+740bZdLACzURluRMgRt6ChuCLwRbnYDN2G8OclIC+MNFc/AvjQCANiXzmUbAkXKAAi49vtZHKfF4hgKJ4l/Lv23+JfQRAiXPk7XBQGGtt9FazWqDikk1dXLIaXQJ34t6GiqqKIDp0UFWsZIhHn4WA9V2vSiarihYTbKNB+gToDjGqrN267uqsYORwqaG+VwzALCG+VklBRP3Qkc92uCA8cFjo0GAscs6O3AmQ0JjlAVe/optTd19N+/Ph5UaqaSd3LNN4+EsxkciDOzoI6cCRzwUyXbRmZIu7dXh1AltnmJfSGeH4f41XHSCOKdDhOsbnR3D34WdUVB1Bb/NrmGiwGCH9icajfU6G3ZY6HFimHyIpymoXfAHMEpqpojMJ3bheCGsFA0lTAVA+d9rdc00OgtV9VKzUdlpaD2zhomM0LuHEa5/4uVgnPQ2t6DzTLyo9mwVg22rT8c9s2tQ6BJczEovgNzVFiufWhUfJwxMLKQEBquzwJ9Q0JwpKDhYkLYXIk4+dN+/tQ2Ix+3o0OFQzjUQYP7kwnoaQI+4KxQT4E+mwXosII4tAWYmXa7Y0iNTPvexwJotX316X3NOixGZR3eey7fwzq0bowNM1GrB6SLArPPNEsbG4DmlpfVF8Bjfm6o1SRj3mUfmac18i+Gnae1bNx9Cme5uji/slt3XIuUN15+subdfKUJWi9X+YX23qrWaGRL2x9w7+2Vm28d12nGTdBIaEGmaTenzZ1pgUcKGhCX183KX7usB+ohlH0whBo3YCMOmAaDzjECJqtjwNQHug4T+v+2lmyE7vCE3dDLSMZJSKvvdopt7luYm3fDYaebfPAIpT7YmMvSDU607Ax+k33/zIhQcizYFH1Hk4l1TvbyKJy4YNukclCyeq8W37GXahC/OJahd3KXyp+AJIRhTgRKoDx5p6pDQETHMunFCo8d2BRhMG8bVDq2VlFlNRBXIhyFQSJHpmiVHEtLGVSHHo7OVUIc+r6spnVyUU4/9BCsHgOM8B2JlsW57Eqgr05mj33Udp9gmH4ezlpmGLOWQTwDh8dr5/PEsDEazpCk/Ob32x8jZaUjG8dOlb4HO0PgYthc1IaL24KLuYbSC5fm4u2ZJXHhLAwCeRYYhFLBCZZ3yae/j4T3PnE0Po4avhs63ThCQ3DUXK49c75l9KQkc22ltxNeFmcpdbhSThlJT2iNEK2Z+YJMy/4SbENriCNbVnPp88yWaF1f/XgxigLGmzReobTyZRkLuLaD/eb7M/KlmU0ovsnzRnw/ouzybyNSRKvpdHowMDsUIL7UHn+BfBs12xPjByKB2TL5w6ns1CgzxeCPK+nZAfY8ImkQCpB5OrfpNN5GN96gY5jyljVqp+0VoR6L1OJv+aJjPv0q3ya1rv4F</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/incremental_content_export.xml b/docs/guide/img/diagram_source/incremental_content_export.xml index 3528f4ee90..35c9b54d09 100644 --- a/docs/guide/img/diagram_source/incremental_content_export.xml +++ b/docs/guide/img/diagram_source/incremental_content_export.xml @@ -1 +1 @@ -<mxfile modified="2019-05-15T10:50:58.782Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" version="10.6.7" etag="I1kZu8R3fY3HJyR2S62D" type="device"><diagram id="b9c71eab-6556-00f1-fea0-a5488a9d87dd" name="Page-1">7VpLc+smFP41nkkX1yPQw/bSTpN005lM05k+Nh0sYVsTSagIJ/H99QUJ9EAoVnyJ43SShWPOQQjOdx4fmIl7nb7cUZTvfiURTibQiV4m7s8TCIEDHP5PSA6VxFeCLY0j2akRPMTfsXpSSvdxhItOR0ZIwuK8KwxJluGQdWSIUvLc7bYhSfetOdrinuAhRElf+kccsV0lncNZI/8Fx9udejMIFpVmjcLHLSX7TL5vAt1N+VepU6TGkgstdigizy2RezNxrykhrPqWvlzjRNhWma167nZAW8+b4oyNeQBWDzyhZC+Xjv/m7fsEsQ2hqZwkOyjDlEvD4mEwcVfPu5jhhxyFQvvMXYHLdixNpHpDMiaxBZ5qy7FKfZwk1yQhtBzb3QAfcQdwVwWj5BGbNGKElvy2/OPy/rKlJZ4wZfilJZJmuMMkxYweeBepdSUi0mM92Xxu4AdKtmtBr2RIety2HrixOv8iDW8Gwe2BMIFBwuR6OwAE/+6JUnwrStsueQfg5S+Nkn/bshIKJuJlKSYCb1GUxtn0QEi4I6TA0wwz9Rqurd5UPXdW1KMFjDxoQr3WvCfq8Cjs0Hkn2L0e7DydMVwiHqc54WugZ4XCcebQD01Q1Jp3hCLwPi4CfUMEaobHWbQUlYW3MpJx4SpCxa5GomX1QVsUZE9DOZxCH0edStS3Tmv5vmH1SkYxT9nxU7d+mUwi33BP4tLR6pLdNb4faFat5i6faheUIwPNtXEYolvMeuOUANWrHoVZcHbMwGVh5kFLmOkDvR9mM5uYcajo4U/ecKa+av71JjwlaavW10nKFwLxDFiCWB9oJMQcCXRodctFh2L8fFXtbDymGvFU/5kf95+3FchuAcSL+RpiUwGsNRYK3aJrIwBNnKPvUmBhodCBPuHvWTDc06fagK1wDBNUFHE4EIRgCttB6EznXi24xzTmU8XCmA3BVzsr+JpZ27E5v6jYBKocKBx1IjK6ZAZniU0wd4zzHZyW1h/O7MYyMG17houB0fv6jlT74zdHd0g38IYccpT7XVj5B1qudZ1Ty79zZCB79R/09zxWE/jN7czxr00JvNZYSOCeXuWCkRkc2MjgVjnUZ3T8T8h7FdN8L7/3wSpYeia/rzUW/H6mJ4qRzAXqCeUUv1cW+8C9QycmFhcVEx+9UTghJozHnnaoaLfuL3zLPPTSsNfPMN1TsT+WWG3tEjVm6R1hor3+C7tM1HASazU7f8i20p2PS846xifl5vOcn7Zj8MIOaj7h+ekbD1BH7r8GwbsQoKCeS3ROPhoocGQgi0i9jfK/dafsdMulN1guP2NxtIa3rxVHd2RxPAXvEbuFHzsZ0RgS+D8h7mm/5Lunnst5+gGYfgBrD3E1ZcMVgHXzq7yNOwFX4CeuW2YlXjHjRAQ6IcWIYU7Y+DydfR5VDafURTjBVbP+cXrgzkAtXg/eI+BMhpncVDEiSQza9EmKUBJvM+HrfAbCO1eCF8UhSpZSkcZRJF5jpGYNeVOXSNq/iidojZNVfXGnfe+kurpjhZ4BzS3BbORVg5kFguaCnn9dQeEIGWHxhtuRxUS4RIGlK18eaiNQev26g30IoQnCwAChviE7CcL++ceVyyEMUCqMla2LvBWGRY4yFYYhSXOOR7klc8hGZO8dLvMs16S8kCj0cbaNM1x7BTd9E9TtAb88ZPBkWK89jj/OQ6AND+kfqFz5IsjjrOArqHN71E7mXFl+cOtnKClhJRQXXxAPHoLq15RM95Reozc/BHH/pOLKExDf3fxuxtecHiSHCGv7NTTBdQLPR1GfOYgrhHnMP/H3nH8+wfLrP3oWuZ1Op0c4wpoOcBrjfOQ1wN58UvQoclWxp+IfI8K0ScIZMXR+u3kQ5kBhiIuihEH0GT2nL9cfqH++lt0MR0y+icKccMbEm80t6IpRN1fN3Zv/AA==</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T12:23:39.553Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="j5W15h7HZm_UD0YfwHZS" version="14.9.6" type="device"><diagram id="b9c71eab-6556-00f1-fea0-a5488a9d87dd" name="Page-1">7ZtRc6M2EIB/jWfSh/NIAmP7MY7jtA+duWk60+tTRwHZ1gQQBTl27tdXAgmDkM+EIw1OkgcCKyGE9tvVrpBHzk10uEtxsv2dBSQcIRAcRs5yhBB0kSf+SclzIZlqwSalgap0FNzT70QJgZLuaECyWkXOWMhpUhf6LI6Jz2synKZsX6+2ZmH9qQnekIbg3sdhU/oXDfi2kM7Q9Cj/ldDNVj8ZevOi5AH7j5uU7WL1vBFy1vlfURxh3ZZ60WyLA7aviJzbkXOTMsaLs+hwQ0I5tnrYivtWJ0rLfqck5m1uQMUNTzjcqVf/7YEcsBAtv31VXeTPeljyFyPyVjhyFvst5eQ+wb4s3QsQhGzLo1AVr1nMlWahq69VW0Bd37CQpXnbzir/k3IahjW5O4WOkGc8ZY/EViJbWuGIhpK2PUsfZa9xnOmHsl2a93HLueAHTZxrcRBDIg+yQjbeMLYJCU5oNvZZlBf4WV51tS4aFqeq6QlaqMabg63G/4mknBwqIjX4d4RFhKeiNaBKHcWBshNXXe6P0EEt21aA0zKsON+UDR91LU6Uuu2qdxqqHyEv5GrQaor3/t0xXfAly3UqhhBANzkcC8XZhucIyFGWFcQIr3AQ0Xj8zJi/ZSwj45hw/ZhRMfr6vkHQtlwA4CAbbcs5WrrowmlDZ3FD4JVwcxu4CefNSU4ajRIm3iEdBAIAuMvJ0oZAWXLBCHju23mcicXjGAoncXAt529xFbNYCBcBzrYlAYa230IHdapOKSTTj5cmpdAnQS3oaKqoooOJRQValpIQc/pUD1VselFP+MpobmWaD1AnYOIZqi36ru6qxg5nGpoZ7XCcbghvtJNTUr51K3C8jwkOHBY4LuoJHLOh1wNn2ic4QlXp8zepvfFEX/59eVCpTKUY5NrcPBDOprAnzsyGWnImcMDPlWqJrJC1768OoY7YFi12hXh2HuIXx0lvwmqnxO483e2Dn3ldURDZ4t8m13DeQ/ADm6l2Q43+Ln0qtVhxTH6Is4z6J9wRHKOqOwLjmVsKvpKUiq6SVMXAxVjrNY23TGfa6bbqpWaD8lJQz84aJjNCbh1Gef+Ll4IzYO3vyW4Z9dG0X68GbesPp+dmqwk0aS6N4gswrcLx3FNWcTk2MLCQEBpTnwO6hoTgTEP9xYSwuRLxOZ92m09dM/LxWk6osI8Jtdfg/tMFdHQBF5gV6hTovXmAliuXfXqAqem3W4bUyPTvXTyAVttHT+9r3mE+KO/w1rl8B+9g/TDWT6JWD0jnJWbvKUsbGoDmJy+nK4Dn5rm+VpOMvMs9k6c16s/7zdMsH+7exWS5urm+da1fesuSV15+cmbt5koTtE5T5Qf69lb1RgNb2r7Ab28v/PjWcp1m2AQNhBZkunYzbW5NCzzTUI+4vCwrf+myHqiHUO7JEGrYgA04YOoNuokRMDktA6Yu0LVI6H9uLdkI3eEndn0vIxk7IZ2un1Nc87uF+fGuP+x0ly1bKLMEx8eNjX1sq7yCv4iy6zhXGeUiWEXATwnmRMAhugp2SVBcgLwsICEpLst9die2XZbieqcN+xExLrdZjA6cVchYjbKVCId0E0uzE52QhrKQETP1cXitCiIaBPIx1szhmFto+6ru8QvxAwkX5abrShSvtl0P3STbZw/QsBA4bbl7cwpOG2PrzcKwgfoVkkDGjNO1UCanTKKZEWVVw0OnJSrndpBe6L5Pkx5ko8ez0GOul3Sip7lQe+UIejwcST3FD1lS8URVNyQGKREo5CsmgK3lHLYl+WwjSiIxp2vwSLyhMSmBFFq/KL/2seFsTP5g0g5O1AeczfXeq4l0bTTOxBuUM2tQnUpFYX4Qio9xmBPFUpJ90jVAuqbmlnfbnvcfhbY/RVdz+fTKlXTd3f5pR8vuFFXw6JeqO8aHDvDcCQ6aIaP8+U1CxZF8T8TxCeWn/5i+czUej88Ehw/piWDW2p9gjgIXNfsT4UfpobNdKv9xJoc2DEVKhsAft/dyOLDvE8mEUIOs07pPn1Y3OKsrLeoHq90TW7jaYblbXB5/MlkkcsffpTq3/wE=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/locations_visibility.xml b/docs/guide/img/diagram_source/locations_visibility.xml index b6e862dcc3..7602fe2812 100644 --- a/docs/guide/img/diagram_source/locations_visibility.xml +++ b/docs/guide/img/diagram_source/locations_visibility.xml @@ -1 +1 @@ -<mxfile modified="2019-04-04T09:46:46.055Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" version="10.6.0" etag="UroK0BZr162YMdKACEwo" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">5VfBjpswEP0arhXYQOCYkM320Eor5dD26AUHrDU4cpyE9OtrsE2wIavtqptWKofE8+wZe+aNn8CDWd0+crSvvrICUw/4RevBtQcAhKn87YCLAgBIFFByUigouAJb8hNr0NfokRT4YC0UjFFB9jaYs6bBubAwxDk728t2jNq77lGJJ8A2R3SKfiOFqBSagMUV/4xJWZmdg1gn/Izyl5KzY6P38wDc9Y+arpGJpRM9VKhg5xEEHzyYccaEGtVthmlXWlM25be5MTucm+NGvMUBKocToked+heWI0FYI9GlB2Iqw6yeuRyV3UifWlxMpWQ4SYo0VueKCLzdo7ybOcu2kFglaiqtQA53rBFb7TfYivgg7GxCacYo431c6PsJiDKJHwRnL3huposwwjf9M5xxXANdlhPmArcjSNfkEbMaC36RS/QsiDU/un8Dw9d51A0aqkaNYNyQ7r9yiHzlQA40DfOUhBNKMplol4tcI3At/4I/TsyYCJcoh5hNvE6jWWLMzEcS49vEDPYdiAnAhJlp1Zti2emPtBrWYLvSuCXiuxz7n+JFpO0fve2DWNtPmBN5MtzVzu+wRp5SOYEwMIDySkFqANftZqkP7MhzbHWaQLzEwtIDXFj6OKVjVO5optwG45hKMTnZqjrHgd7hiZG+zw3bkcM2dGhU2Wivscy5gZLAvs+L1A6kajAJ1LfEkPabusQoxbyk3k9Ao2AVL8O5ezrMfOA9DUNXQMHd7ulw2tcV9L/gATqNP2R+Dx7Sv6uXQQpsvUySm3rZg+vWsi6/o6XDa+pITI0U/CNqGkJXTcH71DSMHTVNnLO8W02leX33Vcuv3xfw4Rc=</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T12:27:17.119Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="4KE0xUTARtUtRHvTAuQF" version="14.9.6" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7VdLj5swEP41XCMwjyTHPLeHVloph7ZHLzhgxeDIOK/++g7YJhjIarVKs1u1HBLPZ8/YnvmYTzj+Ij8/CbzPvvGEMAe5ydnxlw5CXoAi+KuQi0LGBkgFTfSiK7Chv4gGXY0eaEJKa6HknEm6t8GYFwWJpYVhIfjJXrblzN51j1PSAzYxZn30O01kptAJGl/xL4SmmdnZi6Zq5gXHu1TwQ6H3c5C/rR81nWMTS1+0zHDCTy3IXzn+QnAu1Sg/LwircmvSpvzWN2abcwtSyLc4+MrhiNlBX/0rj7GkvAB05qCIQZj5i4BRWo30qeXFZArCQVHAmJ8yKslmj+Nq5gS8ACyTOQPLg+GWF3Kj/Vxjq8J7gbYXnHFRx/XX9aPxNc4pq6h04mJX7Y+L0oTgB1HvmEkJ5EChD6cO4b7VT7WgHKWcp4zgPS1HMc/ribisl663KjAMdegQzU1wyljrPK4bLMMl4KUUfEeGZvq51+U4EiHJuQXpWjwRnhMpYH9Xz6JI80K/N57hyanFQg1lLQIaN6x5nzaRr7WHgS7/MBWCHhUWkL7qLrBGEkic692dEB0CdAnyiQkRjOer5XyIEM3MPQjh2oRo7AcQwkM9RvSrXSSzqt+CVfCC2BUmZyp/VNUcReNQ2z9r2wU9UPYzERRORoSuOqRKXJQTCjwDKK8pmhqg6/ZhpLBIe6vepdn++ppJLFIirSZMEkuU+pxo1TwcqLnBBGHQwY+2lA0RQe/wzGn9khvKhR3K+R0uqdtor7a2dANNPLuZjad2IJWDXqCal82130RV0yaHdezfVa21N5lFg02qmblDkwqCrmqhhzWp5rSvy9b/+v/B+vudF73J+CPqP/1YkfKmyBapyeSmSNXg8mxZl79OwJoPspaCmf77SSQs8LsSht4nYUHUkbBJ5yzvljAwr195avn1U9pf/QY=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/node_visibility_hide.xml b/docs/guide/img/diagram_source/node_visibility_hide.xml index 841c3ff1e8..c2b0957e14 100644 --- a/docs/guide/img/diagram_source/node_visibility_hide.xml +++ b/docs/guide/img/diagram_source/node_visibility_hide.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7VxLk5s4EP41vk4hJAQcZybJ5rJVqZqq7O4Rg8amgsGF8diTX7/CSBi1cIIxCKYcXwa1nnxftx7dYhb4eXP8Kw+267+ziCUL24qOC/xpYduI2JT/KSXvlcTFuBKs8jgShc6Cl/gnE0JLSPdxxHZKwSLLkiLeqsIwS1MWFoosyPPsoBZ7zRK1122wYprgJQwSXfpPHBXrSurZ7ln+lcWrtewZUb/KWQbhj1We7VPR38LGr6dflb0JZFviRXfrIMoODRH+vMDPeZYV1dPm+MySElsJW1Xvy4Xcetw5S4suFQQtb0GyF6/+Pd7FS45DNbziXULC63H0eeLpsI4L9rINwjLnwBWAy9bFJuEpxB9fs7R4EfXqdMUwImU6TpLnLMnyU7vYQU/0sZTvijz7wdpyyhYa8i+nH5eLsbO8YMeL749qVLm2smzDivydFxEVbCqIEIqKJDGHBu1CtG4wLqsFQtFWdctnsPmDwLsdezIS9k2sIRezwt5Ssa/TBrB3JtD7C0hez8kA2GN7OuzpnWMP55waaAPYu3c+3yMyHfbenWPvqdBjag56/86hh0utSezlnr4B/tc4ilhqbJtjWZ7thG3Y1zmjYq9AT4hB6NFH0PskWLLkqT44ySJpljKzi4FRamyNGp2TNHosD7NnMBo8sGNc/MufrQfqOiL93ylt8R1Wlf7G8piPjJXYWaUs5aOsKtkESUFVy+eTpBDAatXAWKSdmQHQfPDZPg9FKXG8KYJ8xUQp3E5HA26nBW4py1kSFPGbOog2DkQP37KYD+9shw5Y+jGgsRq8qNU8M8OGPKQ0hFxfbah6Za2hk0rUr91NS/Tz+XVa0p262fCECVF5snA/nrD0btQ80dF40s/yV1pz0zJdYJmYDGuZrs44mdQyKSnnI+z6GFmcfl/lH6EHx6O2RT2fOg5ySV+z9R/qNnh3qpYhj7bljqAquuuhr6ooajLkPE2n1QYwT8PltrP9I9AQAg0NSKru07iS1Ho1d22krubEv7Ca96Pb0+l2p6QbEXU1xX3p1s449nh0626UnnQ3qVaXAep6YBnwhl0F/LkpArB77PdVBGD3mIynCLpPp7fdY29Uu5egNPn2puQbbMeJ25NuBOyejGj3uh/pOrq7UzcbmhDkyRlofibWaDxJDZi332NEz4YLnE4mw5q27nQaxN/XHftp/X0U7l8NhtbkfHS/iu9NCL7urJGKby15SzThnT0tc/60Kp92+y1f2bNczzFnKa808p1WS5E5Bmcpk8G4ltsXXckKNiXo6XK3vR+qKLyrYZIq3WPyx65+RZYD4homo30ttzsUsqwzO3fBBdwNGOVCd1Pc1U6MgM2Ayfhey22PP5PWNSuMUbJuPddPH4xtunrafAhyq9WBEDN+BRd65/uGY13gQB4xHCuHOLT/Z9ZEedZA8VjXMxaPxboP4kqDHjQeqxhnS7ztitnSkHHaJiKyLplFRBbrTpO+ytIxIvvbyRrPTh8Gism6LmhovJgsvvXuzIAxWYVwp4VwMjPCqTVQVFY7/4wXncE3X8IxEZVVVIF+BFUAtt87LkuB7Y8Yl8U3X7IZLi6rEN5y9UrOB7Mh3AE7896RWQfY/oiRWXzrBZwL5M2aKAqJcrh6Nn6oH21wyh4xUIt7XaSJgt2aRYK4HtH1qfgiwC/r9F1UYUPeeAR1uOAS7vO3mo4GW2ES7HZx2OKTavigKl+T/Mja/gAsYmAeCC5jnadHDBqCHpALNHJ8g/dGsW1ZYPeLAfugH/hN+G+0S16wuVgeto+U8vyhGnFvHbzOO9eqdrqaQUWcu9rByR71XZW1+wnjHcGkIqlOcP0uQsGORRtf8FOghr9aiIIkXqUl6ZykciP2VPqg4zBIHkXGJo6isptWX/npe6PT1GW1uccV93n1IjeF5UA4or5Z1VAg0qJAcNfUwcPNk+f/IVERd/5HHfjz/w==</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:33:35.178Z" agent="5.0 (Macintosh)" etag="SEJDRCzlAIYyCLC_fwll" version="14.9.6" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7ZxLc6M4EMc/ja8phMTrGOcxc9mqqUrVPo7YKDYVjFyAY2c//QojYdTCszYxWE7wIQMtIQH9oyVa/2GCH1a7H1m4Xv7BIppMbCvaTfDjxLYRsV3+T2n5qCyeNCyyOBKVDoaX+F8qjJawbuKI5krFgrGkiNeqcc7SlM4LxRZmGduq1V5Zova6DhdUM7zMw0S3/hVHxbKy+rZ3sP+k8WIpe0ZuUJXMwvnbImObVPQ3sfHr/lcVr0LZlrjQfBlGbNsw4acJfsgYK6qt1e6BJuW9lbetOu75SGl93hlNi1MOwNUB72GyEZf+Z5zHM34fqtMrPuQt4cfxu893pttlXNCXdTgvS7YcAG5bFquE7yG++crS4kUcZ8n9ysOIiP0HlrBs3y5+3v+E/TlcxUnJzJZlb2X/YZrLJtgm2/e4LApOge3ge/6HX1j5p6yQ3y0YWyQ0XMf53Zyt9gXzfF/1+bVqmG+Kph17KhuPk6R5Psi/d6fcnhcZe6NtJfpNFvf9nWYF3TVM4qb/oGxFi4z3b4lS2xUAiAcESSC2DdyEadkgTR4WCsAXdcsHJ/MN4ed2n5OefA58DBkYfW6pPq/3B/C5Mz7nV/E5tq/nc3f0uRGxvXbwAD73Rp9fxeeIXM/n/ujzq/jcV12O3eFcHowuN2IKN6TP5btxw+k/4yii6bedtlsWeXQe23xel1zE54rLCRnQ5Uhzee/PeRLOaDKt0xjypqYspeYTcZ3BflAkbA0JnYU0ui+TYAe3NfxPd3Hxd+n7O9dzxP4/+32LvzFU+79oFvMzo5lghN+q7KM6yCZIGqqjAj4YCQM87GpQKIgf83cuu5/UKZEizBZU1BKZMRopmUKdiYbPnRafS1tGk7CI39X8YhsIoodfLObnewhCDphfYsBSdTXiqGbCDzbkI6Uh5AVqQ9U90Brac1lf9mmo6snF81A1mx9zYcGEqLBYuBssWF5YDYvbGyx6VvLMuNaMUR6IUZjcfozydOyIUdjZLimHB+wFGFmcwUCFEKE7x3dty/UD13GQR7oGsOCuboN3p6KOfLettAde9YxqV14VVm8KyZZh0zUMSTBswinYyZEQgYYQaOiCZOl52zPJqmd4no3UGR4Jjszwboc5X2fOM4o5RNQZFu7KnJZssPtjTs8bd2SuyZs6KrueD0Zl//YH5cB4GkEExEFXGkEExKQ/GvWMducIiP0vFwGlI5rQ+UZBB15WideROQQiIOkxAuop9fOYM5sfc1lBEBbnQsMlsXqDRWI4rr8MnHn1QDJ+SNmUrSfjL7L+crs+H2b9xYXvcANKaOQgMD7oQz/o/hWdruet5YNuzXhLbsI7m84yvrUot/LNms8lWaaXfFtIiDd9emyFpC7pYTQYUnTToqI9FZJwVTo7neXrEZG+EXGh5nZIRPQU8RhHTITEAev5Q6p6WlS6CiTWgYqRgT4DBcyTDcmAnn0d3ywGeLMgYJI5pI6nRbU7Dg5GBgaYVhoSks8mJUexV0v+vC0nKt8kDEmKenC5uavcywOLkT3KveQpfoMMumG0+NaF9F6eP5jeC+vZ1DND21fXe7Wpa2Q9Q8Dz7CEUXx4xQvGF9VxwV2InN6v4ao2GZqlftaxgV/2N54GG+tN84c9Kpb+25ktm0hTqzBK/utaFVF9aIqI/zQP+tOb6m6q+ZNLOZB5BFOys+3JBFOxR94U/ran+0rqvtv8BICOjIdQ54L21s/LLAVGwR+UX/qze2myCDKbFhbQ4/EFt/FA3duAI2qMQDHfSTUdhvqSRoOdmQDIEGgIW65yuEy3YkN8fJSfomeeb7L1mooHMPAnzPJ63JO4bifoqAS6/VGePKJ2GEgaBAsGpzcmjFQYNwXTtEZa4k8OPRrV1WSH/zQkHoB/4db//QVzqqY/Wh+0jpT7fqM6484Nw3npGK/vtrMNlq5H9M8de1HWmpskz+8uSSJrVNVNdilnQXdEGDfyWRWM5UZjCJF6kJXnca+UbwrRcJ4znYXIvClZxFJXdtC6t7j+YsQ/ix1ZT4WrrNTA9SuYZghmwcF5r+BvskhZ24ST+hDVRvnv4GmrFzOGTs/jpPw==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/node_visibility_hide_invisible.xml b/docs/guide/img/diagram_source/node_visibility_hide_invisible.xml index 150fb13aa9..516b7963b7 100644 --- a/docs/guide/img/diagram_source/node_visibility_hide_invisible.xml +++ b/docs/guide/img/diagram_source/node_visibility_hide_invisible.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7VxLc6M4EP41vqbQEzgm2Zmdy1ZNVar2cSRGsanB4AKcOPvrVxgJoxZMbDCYWceXQa0n/XWrpeabLMjjZv97FmzXf6ShiBfYCfcL8tsCY0Qxl/+UkvdKwj1aCVZZFKpGR8FT9K9QQkdJd1EocqNhkaZxEW1N4TJNErEsDFmQZemb2ewljc1Zt8FKWIKnZRDb0r+isFhXUg+7R/k3Ea3WembE/armOVj+WGXpLlHzLTB5Ofyq6k2gx1Ivmq+DMH1riMiXBXnM0rSonjb7RxGXutVqq/p97ait152JpDilg17HaxDv1Lv/GeXRs1REtb7iXetEdpTql4WHt3VUiKdtsCxr3qQFSNm62MSyhOTjS5oUT6pfXa4gRrQsR3H8mMZpdhiXMPTA70t5XmTpD9FWU47QkH89/KRcrV1khdh3KgDVapXmKtKNKLJ32UR1cLUGlKUiXX5r4K5E6wbkXMkCZWmreuSjtuWDUniH8pGl/G9RGIpkMt07jofZsk33dc2Iuufc1D12JtQ9vnXD966ofNJp+M6zHInHcrKH50w+rcqnfLcVWZRmds10nvLCQ5+1eoqumXCXqpGZAizaG6xgUyo9ec63twMVd64IFeuE6nqOc9UQw6iJBuETosF/7jjO0VNuwzP4FbFwb/uoRUG0p3RC3Xuf0X5QCJkULN8Cy9Z6Et6Xl2xZStJEmJoW+6j4Wz47d9xlqvzPoezIDbEqf5f4ypWJUndOKUvkKqtOmCItqHr5ckVKALtVCxMhuMvn6S5bKlGdPQiylSj0SfdkQBoKZy0K17JMxEERvZrLaENBzfA9jeTEx9MdAkcGAoCs3kj1at7m4UAUGQMh1zcHqtRgDXQwivq1T7ITvcTedtIB3qyB8hxqAuWQnkB5BADFRwPKTjKc6dBN53SBcxI6xDlpC+an75YTOSem5aZEXJ8gh1DqmxaA0B3zOHa453PGkEv7eq5/V48hpzPtDHm8rXYEY7GzIn2NxTCUAZs1mZ09gM0aRt2TIXfBQAgMdEFY7XzLmbDWQd3FyAzq1O8I6qcAzloApzMDnDtmUCV9AbfuP3g8wO2cTU/Am2CbwYC7HggG3pBYwH8FUwC+T/y+pgB8n9DxTMHOCfX2feJd0PfdFsDZzABn4GRO3Z6AM+D7dETft9NO5wHeAd6sgeIQKCbNs/FD/WCDWzZ1xoPNzlCdAFsY5GsRKuA+xnA2eFGQl2V9gyocyBsPIDuNZQG03GWvNRwNtJZxkOfRsiUn1chBVbkmzTfAvwCKBLgHgmHs5O2RgIFgBqQDRqnf4L3RbFs2yH+yYB/MA+kRH1gXcj5oD8dHRnv5UK24tw2el51rNTvbzKAhzt3soJZJ33yZ9XUIhvfL7R56JjMJbpMNCrEv2vDSqWa17zfz1UoUxNEqKUGXIJUHsYcyBx0tg/heVWyiMCynac2VHzhLh63LaUuPG+nz6kUGfY6AVyEdvhsGRFsMCJ6a+mS4dY70ZpkfGO7aE1KeKG5zg9v5DochO2BC1g21s0C3ZfgEpu6mVH5/Fs1tfgSFu9SUPBrazaP5pDzZUCF6Rag+INl8+hUACxBBpiTh0G4Szk0SouBhYFIsukk5N3IQM1Q/JcWG2pf4zy3rnPgyJVg6ZfE/4UPp+1czmU/nlt+pxxnKh8LeZHwoNpRm0wHerIEi+lYzlA9F8GR8KGZnIM506NH4ULSFDqEddjaYYz4FHwp7s+BDscHEmSF8qNbNem6cCOue3vdLHoEs2PH4UOxi9JjL8qFoCwmGzu1TOwIk4958KOv6Mx4ngl2MBLOYiA9FW+gx8zMF+BmnLx8KA98fkQ/FhtJjRuJD6Qu5ATifGeDgYN6bDoXg/3AZ0fV78WrOPJjPDScEgWIX2qMvR4CSxeOfuaiaH/+WCPnyHw==</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:29:05.992Z" agent="5.0 (Macintosh)" etag="2Nk7XACx7hUuYmTxhfqn" version="14.9.6" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7Zxbc6o6FMc/ja8dQi7AY+1l75c9s2c6cy6PVFJlisQBrPZ8+hMkUZLArlLB1NKHFhYhiaxfVsLKv07g3XL7IwtXi18sosnEdaLtBN5PXBcgl/A/peW9snjSMM/iSBQ6GJ7i/6gwOsK6jiOaKwULxpIiXqnGGUtTOisUW5hlbKMWe2GJ2uoqnFPD8DQLE9P6dxwVi8rqu97B/pPG84VsGZCguvIczl7nGVunor2JC192P9XlZSjrEh80X4QR29RM8GEC7zLGiupoub2jSfls5WOr7ntsubrvd0bT4pgbZD/ewmQtPvtfcR4/8wdR9a94l8+E38gfPz+ZbhZxQZ9W4ay8suEEcNuiWCb8DPDDF5YWT+I+R55XLgZInN+xhGW7euHj7kfYH8NlnJTQbFj2WrYfprmsgq2zXYuLouAYuBje8l/8k5W/ygL5zZyxeULDVZzfzNhyd2GW74o+vlQV80NRNXansvI4Ser9Af4tmXJ7XmTslTZdMZ+yePBvNCvotmYST/0HZUtaZLx9ZytHhHjyYoQAeb6p8SZMixpqRNhCQfh8X/PBy/xAOLrF6cBw+s84imj6bX3uOOge3zf5fH/lDD4nRPW56wzoc3cc6JcZ6P4FnQ5bB7rzzGsiCW9s+pzxo3l5lK9XNItZZl75tpAgb/pw3wjJ/koPs8GeiCEgQZ0hCZels9PnfDUi0jcixLkgIrgVkTFQDLqEwEilAJIBKSB/DhTOITJ8UwYGigTkggx446vDJcY90laRCA3oc39cRX6NwOBcEJLAgMT0dhrdlsk5fpaylKoeptu4+Kf07g3xsDj/d3fu8ImnOv/NueI9o5mggD+q7L26yUVAGqq7At4jYdBvuxgUCsRt/s5l85NDpqYIszkVxeSrAo2UHKZJRc3ruMHr0pbRJCziNzXz2YSCaOE3i3mPD68uQFuXQo2m6vOIu+qpSL0iBJSKgBeoFVUPwahoR+b+Yx8Fq+xiZ1jtJshiWnwHqbQ4sCMtPtRoIb3RYqZLTwxt9TDlaWEKoisIU6gBPGAVeJ6LyjkCegEEDkQoUDEE4Ab7xHWIHxCMgYe6xrDgZl8Hb06FHfik6WoPxJrJ3q7EKrR+LSiboiG0DEpt7tRXYkdz52kVAa2iM7Jl5pRPZGu/0PNcoC70UNCy0PtC1OEG6pBV1BFHXWjBrtQZiQi3P+rMJHVH6urEqXMz8XxtbvavYGom9vOoRUEYdOVRi4IQ9cejmRHvHAWhf31R0GugDltFHdbeW5HXkTqsRUHUYxQ0M/CnUWc3QRbTQnRaMB+otR/QjR19BkVOf+yYmfsj2InCfEEjQc+XAckSaJC2WYe7LrT0ivz+KDFz/QYls3X2tmeihswsCfM8njUk7muJ+ioBLkWV7ojScShBLVAAfWlz9GwFtYr0dG0LS9zJ4Xut2KoskP+hw4HWji5E/QBx4HxQXq8fKOX5QdXjzgPhtP2MRvabWde3rUb2Pwqjmqth1x0GQzyhL/nOF0dlS+qeqam1LOi2aIJG7hCKabi+nShMYRLP05I87rXyDWFa7hPGszC5FReWcRSVzTRure4k6rsg3rabqu+2XgLTVjJP2DjXcxRyIVdjFzWwqy/iu+yJyg2tUXM7sObW1WfLAcX1yG0a+aNCpm+FjKvrIwfUWSMzJz4O9CEGOtQ3UIZ0enfd9CiLqvozjCxKnw2GVE6jduX0KK63BxGALojIB7LqMY5YAokmwR1Sdo3aZdej9H7IqcS5IAPtMuxv6vOhXiwUlw8pqpYqtXFqsDwu6OuHISGRWdZReX9OBYNM49T3pJFdefF9PZ9V3rv+YMp7/Fkttd0EWUwLlPmKzyrvoRTR9q+8x2Yu9cTQdu3Ke9QgN5WhyxLwXDKE8t71rVDe40+ro69Aed84d9qlOTVygl2lOVD/97f+lPf4bBroq1TeowalM7JLRQi0f3HsrLw38hD9aU7x2ZTOk++lvEcNGmjbeNTFEV2V964WBXtU3uPPaqCvW3kvs4QKdcQq6rTX1s7Ce6B/3UGPQbCTePprvrbaBQvQacFnmjLPJ7Xnp4cvbK2KH74VFz78Dw==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/node_visibility_unhide1.xml b/docs/guide/img/diagram_source/node_visibility_unhide1.xml index d913a140ba..69f8ff1ccd 100644 --- a/docs/guide/img/diagram_source/node_visibility_unhide1.xml +++ b/docs/guide/img/diagram_source/node_visibility_unhide1.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7VxLk5s4EP41vk6hJ3CcmU02l61K7VTt48iAxqaCwQV4bO+vX2EkDC3I2NhgEuJLUOtJf90tqfkyC/K83v+eepvVH0kgogW2gv2C/LbAGFHM5T+F5FBKuENLwTINA9XoJHgJ/xNKaCnpNgxE1miYJ0mUh5um0E/iWPh5Q+alabJrNntLouasG28pDMGL70Wm9O8wyFel1MH2Sf5FhMuVnhlxt6x59fxvyzTZxmq+BSZvx19Zvfb0WOpFs5UXJLuaiHxakOc0SfLyab1/FlGhW622st/njtpq3amI83M66HW8e9FWvftfYRa+SkWU68sPWieyo1S/LDztVmEuXjaeX9TspAVI2SpfR7KE5ONbEucvql9VLiFGtCiHUfScREl6HJcw9MQfC3mWp8k30VZTjFCTfz7+pFytXaS52HcqAFVqleYqkrXI04NsojrYWgPKUpEu72q4K9GqBjlXMk9Z2rIa+aRt+aAU3qF8NHPlc95UPrZGVD6eufJt547KJ93K55Gc6Ok1lU/LvHrVnx8OEIgq3Y8BB525L3DrjspnhvK/hEEg4ju6gmU5mPltaFQ1A6LBaBMNwkdEg8/dFfgdlW93usIsDJ+CLZnSEXXvdOreej2YsSjbbkQaJuk9o9QbD1zWCpauGXHPGBUs1wDL1HocPBZXYVmKk1g0NS32Yf6PfLYeuM1U+d9j2ZIRsCx/lfjKlYlCd1Yhi+Uqy06YIi0oe7lyRUoAu5ULEwG4cWfJNvWVqLrje+lS5Po4ejYgNYWzFoVrWSoiLw/fm8toQ0HN8DUJ5cSnAxoCZwQCgCzfSPWq37nhQBQ1BkK22xyoVIMx0NEoqtc+y070EnvbSQd4kwbKsWgTKIv0BMohACg+GFBmKuBCh647pw2ck9BrnJO2YH5+tBzJOTEtghKxXYIsQqnbtACEHpjDscUdlzOGbNrXc92Hagw5XdPOkMPbagcwFjN10ddYGoZyRbAmk7MHEKzhrns25DYYCIGBbgirmRS5ENZqU7cxam7q1O3Y1M8BnLUATicGOLeamyrpC7hx/8HDAW6mXXoCXge7uRlw2wGbgXPNXsB/BFMAvk/cvqYAfJ/Q4UzBTAL19n3i3ND37RbA2cQAZ+BkTu2egDPg+3RA3zfzTJcB3gHepIHiECgmzbP2Q/1ggyGbWsPBZmaozoAt8LKVCBRwH2M4GbwoSMSyvpsqHMgZDiAzjWUA5G/T9wqOGlp+5GVZ6LfkpGo5qDLXpFkB+AdAkQD3QHAbOzs8EjAQzIB0wCj16x1qzTZFg+w7C3bBPJDE8IF1IeuD9nB81GgvH8oV97bBy7JzrWZnmhk0xKmbHQz2qO+ubLAEhruCaUOqIfeneBdeZOCXi33ehphONqvIX89YK5EXhcu4gF3CVBzFnoosdOh70aOqWIdBUEzTmi0/couOwctqS5A3Eujlq1z1JQ58kEDaNmomRFtMCJ6b+uS4dZZ0tl/iMIzbI1KTKDaUP6svcRgSAkYkx9DvkGNmYfgEJu/GVL6Zk/n1GfSSKDUmdYZ2U2c+AstbF0qPX7PNfKBC9I5QmfmOmbOcwNFqTJ4N7ebZHP3GOjnKLBwD7vajYtHNu5mFG4C9fkwWDTXv6b/2+ks2kDHB0jfPn4TypC9Y9Xw9nVoKpxrnWsoTdkajPLFrmTQd4E0aKKKvLddSnggejfLEzBTDhQ49GOWJtjAetMNOBnPMx6A8YWcSlCd2NTfmGspTa7CeGu3BuIj3/VhHINF1uHw7uxkD5raUJ9rCc6FT+5qOAI+4N+XJuP4MR3tgN+O5LEaiPNEWBsz0TAGS1vpSnjDw/QEpT+xaBsxAlCd9IW8AzicGODiY92Y8IfifWAZ0/V7UmQsP5lPDCUGg2I1i9O04TrJ4+nsTZfPTH/Ugn/4H</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:35:53.332Z" agent="5.0 (Macintosh)" etag="i5y6kX6pc1XfWmf7fPjJ" version="14.9.6" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7Zxbc6o6FMc/ja8dcgN8rL3s/XJm9pzOnMsjlVSZInEAqz2f/gRJkCSwq1QwtfjQwgKSwPplJaz8dYLuVrsfabBe/sFCGk+gE+4m6H4CIcDQ5f8Ky3tp8aRhkUahOOlgeIr+o8LoCOsmCmmmnJgzFufRWjXOWZLQea7YgjRlW/W0Fxarta6DBTUMT/MgNq1/R2G+LK0+9A72nzRaLGXNwJ2WR56D+esiZZtE1DeB6GX/KQ+vAlmWuNFsGYRsWzOhhwm6SxnLy63V7o7GxbOVj6287rHlaNXulCb5MRfIdrwF8Ubc+19RFj3zB1G2L3+Xz4RfyB8/35ltl1FOn9bBvDiy5QRw2zJfxXwP8M0XluRP4jpH7pcuBljs37GYpfty0eP+I+yPwSqKC2i2LH0t6g+STBbBNum+xmWecwwgQbf8D7+z4k9xQnazYGwR02AdZTdzttofmGf7Ux9fyoL5piiawJksPIrjenuAf+vOuD3LU/ZKm46YT1k8+Dea5nRXM4mn/oOyFc1TXr+zkz1CPHnRQ4Dc39Z4E6ZlDTVX2AJB+KIq+eBlviEc3eJ0MDr9Ik53XdXp0BnQ6XB0+mV6un9Bp6N2p7sxr2j2nPKtRV7d6ojBQAG/8vkQGOCx718m4DsXdDoxnP4zCkOajF2/ao/j4Hty30RBdeQMFBCsUoDcASlwx65vxVxvUKd7rV3/m/p8mI6OtakexgP63G/1ufP8bsb8bLOmacTScTSo2oO92cN9Y2CojvQwJxgUkqkBientJLwtUmh8L2EJVT1Md1H+T+HdG9cjYv/f/b7DR5py/xfnireMpoIC/qjS9/IiiIE0lFdNeYuEQb/sYlAoELf5O5PVTw75lDxIF1ScJmf7NFQyjSYVNa+TBq9LW0rjII/e1PxkEwqihl8s4i0+vH0AbSKKNJrK+xFX1ROGekEYKAUBb6oWVD4Eo6A9mdVtHwWrbGJnWO0myGJafAertDioIy0+0mhxe6PFTGqeGNrqYcrTwhTCVxCmcAN4wCrwPIiLMQJ5UwQchPFUxRCAG+K70HH9qUsI8HDXGDa9qcrg1amwA99tOtoDsWZGtiuxCq1fC8qmaIgsg1IbO/WZ2NHceVpBQCvojGyZid8T2aomeh4E6kQPT1smel+IOtJAHbaKOtdRJ1qoK3VGIgL2R52ZZ+5IXZ04dWx2PV8bm/0rGJpd+3nUoiCaduVRi4II98ejmQLvHAWRf31R0GugjlhFHdHeW7HXkTqiRUHcYxQ0U+6nUWc3QRbT4uq0EN5Rax/QjR19BMVOf+yYmfsj2AmDbElDQc+XAckSaLC2Oke6TrT0gvz+KDFz/QYl8036VjFRQ2YeB1kWzRsS97VEfZkAl9JHOKJ0HEpICxRAn9ocPVohrSA9XdvCEndy8F47bV2ckP2mwVOtHl0u+gHiwPngfL18oJzPN8oWd+4Ip61nNLLfzLq+bDWyf+LYC7rO1AyRZH9ZEklzDZ8/6RsNYgOinO7yJmzkGqEYiOsLisIUxNEiKdjjfiveEWbFSmE0D+JbcWAVhWFRTePi6l5Kvg/jbeup+nrrJUBtZfMEjYy2dA4kljV6cQO9+jS+y6qoXNIaNTIDa2SgPl4OKILH0HD6qJEZQCMDdUnkgHJo/Bs59Dd1+jAdHelLKEM63UxKj8IoG4VR+mgwpFgat4ulP4IkWBXOTp6z9YhI34gAfEFEzLTuqKe/yBRCe1UYUlmN25XV+zjhHALDN0VgoLHCuSAD7Urrb+rzod4cFJcPqZuWQrRxDml5XNAnCENCIjNWo7j+nCIFmaepLztju1LfVTmfFddDfzBxPfmsXNpugiymBcmExGfF9UjqZPsX1xMzWXpiaLt2cT1uUJTK0GUJeNAdQlwPfSvE9eTTAugrENc3jp12yUqNpF9X9Q3Sv+HW37IxOZvM+SrF9bhBzIztEgoC7VuMncX1Rh6iP1kpOZuYefK9xPW4QeZsG4/6dzS6iuuhFgV7FNeTz8qcr1tcL7OECnWuVdRpr62dtfVA/0WDHoNgJ33013xttQsWoNNCzjRknk9Nz3cPv5xann74eVr08D8=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/node_visibility_unhide2.xml b/docs/guide/img/diagram_source/node_visibility_unhide2.xml index 44ac24b49d..b3a48ce250 100644 --- a/docs/guide/img/diagram_source/node_visibility_unhide2.xml +++ b/docs/guide/img/diagram_source/node_visibility_unhide2.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7VxLc6M4EP41vqbQAwmOSXZm57JVU5uqfRyJkW1qMLgAP7K/foV5GLVgbMuAmXJ8CWqJFvTX3ZL6czwjr+vD74m3Wf0R+yKcYcs/zMhvM4wRxUz+ySUfhYQ5tBAsk8AvB50Eb8F/ohRapXQb+CJVBmZxHGbBRhXO4ygS80yReUkS79VhizhUZ914S6EJ3uZeqEv/DvxsVUgdzE/ybyJYrqqZEXOLnndv/mOZxNuonG+GyeL4KbrXXqWrfNF05fnxviEiX2bkNYnjrLhaH15FmNu2Mltx39eO3vq5ExFll9xAyjfaeeFWVI98fLDsozKGiPzn3KayFcWRFL74XroSuQYkG6tsHZaXxa3C14x7ehxUv6R0HhGvRZZ8yCH7k5Xt0jSrhoErWSJCLwt2qnqvBHtZq6tn+B4HcmJslY5JaamndEubWaqKNN4mc1He1TTbGUUO0JN5yVJkmh550Xjrk+gISgdAznmA5ttkV8PRQGseemkazFWMFnGUlQGHqGynWRL/qL0c/wIoEqYaH1FDFG0CFJHLYJT29T4awzb5gPQnD+yCeWBQnvEuZJ0ZD/UjZby8KJ7Y2Afdq5JEq9vpbgYdcepuB61MoLdc7HYAXsIHyx7VTA3k/hQ74YUafpk4ZG2IvcZhnJwy/yIIQyDywmAZ5bBLmISUv+xEkgVyLX0uO9aB7+fTvOxXQSbeNt48n3Mvdw5Sdlwrj8nLavGJY7t8yBKwXLs4XOsiFYYgdZBqw9FwIdriQtjq9hYFnp9hgTQs/grS4F2+nBZMYSh3Ol0mg8m8tk+L/Rpwya2IjV7YM9WwbfbkGhryr8dPHZ03GR/DvI114zstxoeLtJHxsWb8b9ItRTSa7S3Lwfa8zfZ1z5C2t1Tb1+0xbE8e3PEJvqPxaafjW+9SEwvztP+eyKtlfpVuNyIJ4kTvGS9SFsx37dZIqXpGzFI1MmOAZRuD5a1zo0fv6eZxoEL0jlCxTqjuFzh3XWIcsLViI4KhVzGUuLFOgfIQgQFX+1Gx0AsWD7bTUkxfn+XHML1+Tv9c669ZQMYEq6pKXFd5bVhaHILsH3ltPTFul+1/j20Ls7L9XeIrnyw/lh/P19IuyUdxU34eLQXFXS52KwG8bdZemikKHjPlgFXULmbKiXcyJZxaT7VhMC3hYAcpihB3VUX9lXBsvWxwnZ90gDdpoEh1bKlPTMQMKIIJAIoNBpReYrgyoJvByUFwEnpLcNotmOOJYY4ZzZMS4S5BlnQAV/UAhJ5sh2GLOS6zbcSpaeS6T7UOOZ3qZ8hhbb0DOIteEzF1FsVRbkjWdHL+AJK1KVlXF3lrVxqs3m7r1ZYrYa0XdY6RuqhTt2NRvwRw1gK4PTHAEVUXVWIKuHb8geX7HgHXKzaGgDfBVhcDxh2wGDi3rAX8V3AFyNO4pq4AYp9ArrhHV9ArQsaxT5weY99pAZxNDHCwMaeQEr0UbwRCnw4Y+kZfnblyYz41nBAEyu4pR1NrMKCYvjF/LP6Jg5rUmMQr0ze6D1UOZHAXOyL3x/Td6IM5vnNH45tzeY9ZjIVZakw2j3WzeZ/Ea0tSg98mGROqM1zfZ1wBsLSvG45IBrJuMvAhiVm4GxgVi2528CF2YhRsBsYk+6rj7GfSMlxhRgXrVsZtWsxsdQJr1hWqI/Fk6goc1uhNmVkOisgDMrNcrytc5ycd4E0aKMfqiZnlzmjMLO+PbOubmWUtxEwVsJPBnOMxmFlOJ8HMcr1oYuosBsxsa7KeGjujndRNiTrOgaLhmFneG1HXLzPLWug4NrWqP7N6Yma1889w9AzvjY4bi5llLUTd9FwBxL4xM8tA7A/IzPJbmbqBmNnqRK4AXv/EwEQAt8HO3JiatUHsD0jN8gv+af7mnfnUgGIQKFNqFibp/qhZ2Tz9tEUx/PT7IeTL/w==</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:37:05.441Z" agent="5.0 (Macintosh)" etag="J9UMweQ010vGrkrvm7zj" version="14.9.6" type="device"><diagram id="0ade7833-b749-31ec-2401-9a8ba7d19c55" name="Page-1">7Zxbc6sqFMc/TV47Cgr62PSy98uZ2XM6cy6PNtLEqZGMmiY9n/6gohEwbeJWQlP70K2Ay8v6sYDFf3cG79b7H2mwWf1BQxLPgBXuZ/B+BoDtAMT+KUreqxJcFyzTKOSNDgVP0X+EF1q8dBuFJBMa5pTGebQRCxc0ScgiF8qCNKU7sdkLjcW7boIlUQqeFkGslv4dhfmqKvUAPpT/JNFyVd/ZRn5V8xwsXpcp3Sb8fjMAX8qfqnod1Lb4i2arIKS7VhF8mMG7lNK8Olrv70hcfNv6s1XXPR6pbZ47JUl+ygWQv9FbEG9J/cjlg+Xv9ccgSXhbfFN2ltCEFc7DIFuRwoLNTlb5OuaHLzTJH4N1FBdu39H0tTAUJBmveqLbdFEYXeU5cyRw4S37xZ6t+FU0yG6WlC5jEmyi7GZB12XFIiubPr5UhtkhN+2COTeuvjX/ECQU/My/wQ9C1yRPmS1rd/Cuy12yajm2LktJHOTRm0hHwCFbNuaaO/yiEXsSYPEO4TjcDu8OLrJEE1n5ZfhVbXd9YsiT7ORBuiS5YocdtN76UFTCcAQM73MwFtv0rcGgRckiDrIsWqhs8I5uO+w8y1P62vQucEF6SuP8rSzTUYJIJMB2eqLkQskQPI0l5uTgvdVsUzTIPnhgX7qPHJE+Qdy2Pmkv27eF9uygeuLeHcE/K0J2st/Ners3TOyfEkYlV0MZ2ZPZlxiDeLQ4Wt+phc+f5I0EsQJRTvZ5FzZ3NKbpYex9ieJYKgriaJkU7DG/EVY+fyNpHrHZzC2vWEdhWNxmvltFOXnaBCVLOzZ3Y2XlbKUM49YRMA+AGDjEF+9K9h/SWeMjhU5YzzZb9Dod9ALrOKgCGR9hYCsY/BVl0TP7rkowiWM2zT3mLXlElVwju64mhc1DH8ufS8aaFrnF89jeLZormLdrBnA6kMdLoDrd63C6PEPr5XSgOP0n64kk+bY+tyzn3r3v8nlTM4TPLdHnzbkOn8Opo1+ko0NwQac7Rzu69cwsobgY2Z9TdrQsjrLthqQRTdWabwuJg+cP952QNDUjjAYNETogcXtDEqwLZyfP2WZCZGxEbOeCiKCjiEyBQusUwpOWCkgjBGpKVogT1iEwfFMENI0V1gUZULOv08pBy8pBcHmTi9ThcjXPOM0hTYwL8gRBJyR1IvW87bqWh8k+yv8pvHuDsMvP/y3PLYD4+S/GFXuyIpNYUsA+VfpeXVTksXhBdZUP/LpAvszslHZW3352yNNUWd+ZkLAzJPXd2KlnpX1T38CzBUM29kVDw6W+XTXneR6sZhNkMC2wTkg0uRDYjxYIoEQLGo0WNVl6ZmhrhykshSnoXEGYcjvAA0aBB5BTjBEQ+9C2GIW+iKFt37geAhbyfOS6Nnb6xjD/prHBbifCbnuoq3YEYtVUb19iBVq/FpRdY6djGJTS2NlXfdNsGDY8j7Zt7KoZ5TPZaiZ6GNjiRM/xj0z0vhB1qIM61yjqbEecaMG+1Cl5CHk/ekDq1BR1T+raxIljM8KeNDZ7VzA0Y/N5lNUPfl8epSgIZQXagDyq+fDeURB61xcFvQ7qkFHUSctWRxZanQqdLQVBZ8Qg2EsS/TWXrWbBYsu0uAMNmY41Gi1IXbZOeg8deg8s5ep1CruQuvKbtmc0bM8geSWnUeOD1BXZ1NG1dHTvgk7vr9mZNuWq59GzKSePBjpVO+i4amcSdpmDCJJVwToR+UTTM8URQyBR/meQRtEPOi76mYRfOgOFnCnTycBxFdA39bmelYUjTTJ1inrqnNg0OJgeGOS0kk5IfldMMym/OjLodSKnnRStM3qGJEWxvOncV/mFpQ3JEZVfWE2Knger2QQZTItnDaT8wp425RceTkdzpcov1CF3qEOXIeBhoEP5hR0jlF9YzQX3JXb2ZZVfnWOnWZoHJSvYV4ODsWRoPOUXHkyDc5XKL9ShtEFmbWMjayDll5KIGE/0gAdT2sy+l/ILdWhwTONRioK9lV9IioIjKr/w74pwrlv5VacJBeqwUdS50rq1t/TLlaLgiNIvfMIfPbyWdatZtCCZlr7SL3nMHE76xU4Pf5e1an7447fw4X8=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/organising_templates.xml b/docs/guide/img/diagram_source/organising_templates.xml index 18df3a97cc..96b7130d0d 100644 --- a/docs/guide/img/diagram_source/organising_templates.xml +++ b/docs/guide/img/diagram_source/organising_templates.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="483213f3-c85c-a72a-08e8-804d8fc675d3" name="Page-1">7VpNj9sgEP01ua6CHTvJcTfdtpdKlSK1Z9ae2GiJiQj56q8vxBAbcLdt1khtnFxixvDA83ijYexRvFgfP3G8Kb+wHOgoGufHUfxhFEVoEqXyT1lOtWUax7Wh4CTXnRrDkvwAbRxr647ksLU6CsaoIBvbmLGqgkxYNsw5O9jdVozas25wAZ5hmWHqW7+TXJS1dRZNG/tnIEVpZkbpvL7zgrPXgrNdpecbRfHq/Ktvr7HB0g+6LXHODi1T/DyKF5wxUV+tjwugyrfGbfW4j7+4e1k3h0r8yYCoHrDHdAdmxed1iZPxBVT5o3KpbFWsksanUqypbCF5WfeG3HNnswB0eSy5XYCtQfCT7HJo/JpoZ5QtlxobB4oF2dvwWNNbXOAuM3xlRE4cjfVWnGiYk900AFu24xnoMW03vQ0TOTAC8wKEByMvWo/cmM4cdPMR3zgfsyB8uDD98THx+PhGQEYXlxQBR2EzsRWcvcKCUcYbplaEUseEKSkq2cwkQSDtT3vggshQ9KhvrEmeq2meDiURsNzgTM15kIFX2s6hBtRixwqeVUIHU5Satl6kpkqhw/FvN0c3e7Fu/mbruGpp7xKLmjd4SN6pC9cz/7pO5ranUXylUBycJJhQ0oERhJDt2bQfglyY/giaegSpLIbiE9uJB0XEgziQYqCBzWUz8SMbmgYKbbOhKSeyfT27UjkuzjSYdOZDY2gShiEXpz+GjHxbFK12qscwo9mkWxmt3RMHCmYIeUTcuFYS29dofK1YHKB5OLG899D/33E0DcSRB9QjSX4lAKuAQ+GerM26ldJO1lCo+OYXBG5cO05m3NdJ1MPpUTp+sQD2oGEHKBfjD+P5qCMdSALJZXBlASflRe6p5NrcGYWrcSK/NHDjJDn5MXLLYleTFK6+hvwyQXf9JqUqpr1weVWICxnDi3puqjbpyBJCVauNgIarqN7CXriqTse7tnvNoHF82pElhJLL4JJqNzi5h/2rD6ThSmyDewXnVQ16IylcaafjNdy9amDodI5Bs46EAPVzDpLN5hOemsbmO6n4+Sc=</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-18T07:20:39.404Z" agent="5.0 (Macintosh)" version="14.9.7" etag="xDkmcGj6hYaKj0xZYVvf" type="device"><diagram id="483213f3-c85c-a72a-08e8-804d8fc675d3" name="Page-1">7ZtBc6IwFMc/jVfHgIAeq9t2LzuzM87snlOIkGkgToii++k3gaCQ0HbXIeohPVjyCI+Q9/P/yAMn/jo/vjK4y37QBJGJN0uOE//bxPPA3AvFP2k5NZYojBpDynCiOl0MG/wHKeNMWfc4QWWvI6eUcLzrG2NaFCjmPRtkjFb9bltK+mfdwRQZhk0MiWn9jROeNdaFF13s3xFOs/bMIFw2e95g/J4yui/U+Saev63/mt05bH2pCy0zmNCqY/KfJ/6aUcqbrfy4RkTObTttzXEvH+w9j5uhgv/LAV5zwAGSPWpHXI+Ln9q5QEXyJKdUtApaCOMq4zkRLSA2t7TgLzDHRAa6ouxdHguLUu3a0D2LpZ+McxE6L/CfxIcYjfyQHcppSmlKENzhchrTvN4Rl3XXl23jWGwq14G36jpXY5yZF63mASW9MKspeEU0R5wJx7PqEtxAuck6cW1tDBHI8aEPB1SMpWd35zP8pFiMxJup78NcuTn1m62Dsp4kdUw3Vp+78TQ3HLIUccON2Ohc8sVUgzAMhe+gsA/FwgoUupvxoJgbUPzCSOisTgZHR97HoeSMvqM1JZRdcNliQjQTJDgtRDMWEUPCvjogxrEQ5Se1I8dJIk+zqjLM0WYHa4oqkYKErRZdlNShVyg0aQWEBhr3Q/RDKuW1ouOnXA6D46vmF9TqatEFtEfFJwgEI+iCHhSnE1/oxLIfbuBfKRSan8CaUISOkttTAkA/vOE4lOhuxqMkMiiRN9UEnuieTyUNU17h1GWXW2cXHaTATC8gspRfFk457qAcXj/giyuVQ/cTWZOOpcPkDpjM7WCi+xkPk1bIOpxs97KHSyk3TinzYWXogOtbyigAGAw4rbCvFUE/4GB2rVhojpb2xGKMQqgD5X9BiSyBYjgakRSzOgql6hPkli13zDGLYaXoLluArSRjlkaddtjXDm2hOlZhzPAzonSYBVR0QMqtk4ubVjmCftC9gXvSwJJcuALpA5Q5gF6fuHYBC+w9cgNmkdSRYp8UbbkK9EclV5Ni75kLMEunw+X0kMjE8sbEVsrPWupSzw1Tj75UmQ/cqtp6gtsKiFOUuyrKaLnHXpF94CUgVz19hOopCAduVW3JhVvZPkL5VC97Xl0Vs/ewxb0b9BD109FIsVdpH3g/yNVP759lIq0gshi4KwXjVERE8/KCe0PQ5VcE/vNf</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/request_lifecycle_concept.xml b/docs/guide/img/diagram_source/request_lifecycle_concept.xml new file mode 100644 index 0000000000..f8483894a4 --- /dev/null +++ b/docs/guide/img/diagram_source/request_lifecycle_concept.xml @@ -0,0 +1 @@ +<mxfile host="Electron" modified="2021-08-11T11:42:34.035Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="ZDMeXYPsGaWWX9bFYF-J" version="14.6.13" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7Vxbd9o4EP41eUwPvpvHQNJs0rRpk7TdPO1RjIIVhEVtuUB//UpYBiy5IAyYcHlJrJEsWzPzzU0yZ1a7P7qOwSD8TDoQn5mNzujMujwzTcNouOwfp4wzim8JQjdGHTFoRnhEf6AgNgQ1RR2YFAZSQjBFgyIxIFEEA1qggTgmw+KwV4KLTx2ALlQIjwHAKvUn6tBQrML0ZvR/IOqG+ZMNt5n19EE+WKwkCUGHDOdI1tWZ1Y4JodlVf9SGmDMv58vPm/FPfNdzr2+/Jb/A99anpy8/zrPJPq5yy3QJMYxo5alNZPX/ux/Hn9Dwx+td9HT32Q7OTTub+zfAqWBYOwQoeiAphbFYNx3nzIxJGnUgn9A4s1rDEFH4OAAB7x0y9WG0kPax6H5FGN+zXkS56jQY5TeMKWKiucCoGzEaJfwWIFoBWx17ptXqgCScPiQkMfpDIgqwmCTpQRqEoqHJGcFB/nw4mtMLwalrSPqQxmM2JO91hNCF1lu5Og9nOmTYghbO6Y/pCiIQetudzj2TDbsQ4llBC0xFUgvE0yiK4g1SOhbYBCkljERiGpIuiQC+I1wMmcgYo8Uwg7dh1LngGGTtF0yCXkb6iHAuDNYS430uGhqT3hRmfIIpZvhgDF4gboGg1528aJtgwsR9GZEI8qk6DLdiLbOXu5pRW3CE6L98rg+OaD2Lx/Dry1H+Trwxnr4g4/3cTbz5PN83u23SGstaZqysZQlJ4wAuAJ3QEAriLhTzvfnxG+ylaatzb9wHpjO4wjfnVjaOM2ahzsYQA4p+Fw1emf6JW78SxNYx1fWpgRO6bnqSCmcvKu6atzBLJjJMaaKMM8pEEzhM11MdIY6CkC9EwchyNdtrFK2mqktVK5ekEGRVTcuHkNfXBC5RIk/SRqNeJfJ1lGivVeQgDW2pAXU1DW1TEw1CRc/ZEpuGX7R19SHEk2OThq+FkKX22m3oGX6mqWA8N2zAByRrgnGRB5wD4yMLOS+CACbseY3PgKkOirprxqlzWGRyaXUxSJJcOxcDbQ3lXSEWdSWLaJfEomZJLGpvIBQtFYuaNJws5H5aSEvTQjr1hKKW+Ze0a1XTZik20tEybZsyXGoo+jjuM5Uen2PUg6yHJ9hHZ7ksa9eWy1XkcsG1llc7spJPEMJEFUpI+i9pslwgCS93RN0WoZT0c05ntKeJZTs3a+O9X+S93VB575ew3t8W672T0zgQp+FoOg23HqdhGs2ilZHrF9rxsDSRLdcvtuw01NTzQRimNtPrmGC8dkn2nTsM2f9PJbAzh9FUZPL94Y4RLjACybH6caesGl6rWAw1M3wuc9zH4VByF/JccC7LHErBncy8yzuu0/iaDmVWp/EM2yqobo11GjkHceStoarJjKuZzNRXpzHUfOdYsbjvwV1TxWLpJlZNFQEWk31wijDym9VgZBsSjGQ8bi68W7TtNx/ewV8pTKgCFaaYA37JhApY0IdJNwYsnWwNYIzYW/BN8mLf11nH8l35EcxhYewqgJi26wggSoVhlAgjGZAogYcvDVsuDdQZZZdKQ43mjtV97Hso52u6D90N6DXdh21Jqu5VdR7SRI4pTbRl56EGWLwmwGT2A8Eh62ilCHcmiaiLGd9bLzG76tKJgF3Qn5yzmvxlY8Utx7l75pbUQes1dmohlNeggzThNePJQchpraeBeI0hTzTWKUln5We7Nq5LLsb1Va5vq/pcynS1tnasHmaPEpRFlnCph/Hq8TByydLTPPS29DSGZ7q1ehi10tkWJkn2KMJ/HGtR2tu5BzFO2cu7Eod5ci4VU5Eqac82nYtu9cuoK3+RS79ygFQ1f/Eb9eYvJRs2R4eR9QIw791EYNogsWsKwTy5RuzJtl57q0V2Lk1DCyZ/32rRe47vNBa+l+ksHM8usjfYLGbVc55Z6HfoIYZcM/BLsteaQwy1/nMJX0HKIvNjD88VYe0+HlRTqeMNz3ePHVPNlk5nRfZhg2FRWWdp8LH6Nz07PSsihefKpzjaAYy0ya2Uo+o8K1IORzVbPn1ht6/FWF00GrrHRd7HJ3ZGU0KRKz1GF47yRL78QUudcEQvcHRpBfDjzfjxLmp8ebocfSs5CCHixwcGCBZrHPr+oFzdrTVeKZXIqZp4KLFKnkDPm8dFINz6YTr5EEPVE6nyRK6mWduU0ToVEw8lgDB0t3PrKibKqt20NoSRpubPqmwKI6ePtPfQjSzyDsshopvzbnhTyqsKEVv6rZemvLu1OYi04EXaue1dD25Gt9HYMdw/3teSE9nHCpFKbsSo7EQicpvy04iTnjd2mb8liIOcMpczlKr1Lg6t2lvBk6t8XudUw5PpmsVcprlutqm3e5XXfP+Kc3e98b67ZHdMrjkXx6+6O8aas191zIbPfhvTuvof</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/request_lifecycle_events.xml b/docs/guide/img/diagram_source/request_lifecycle_events.xml new file mode 100644 index 0000000000..a0e49c4e0f --- /dev/null +++ b/docs/guide/img/diagram_source/request_lifecycle_events.xml @@ -0,0 +1 @@ +<mxfile host="Electron" modified="2021-08-11T11:50:42.391Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="RpFdj4Wq9h85QVeF8wJu" version="14.6.13" type="device"><diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">7V1bd5u6Ev41Wefsh2YhbjaPufaWdnU3TXO6X7KIkW12MLiAk7i//ggbDNYoBmQJy4mbhwZBhD3zae4jHRlnk+f3sTsdf4k8HBzpmvd8ZJwf6TpCmk3+y0bmy5G+kQ+MYt/LHyoHrv0/OB/U8tGZ7+Fk7cE0ioLUn64PDqIwxIN0bcyN4+hp/bFhFKy/deqOMBi4HrgBHL31vXScfwu9V45/wP5oXLwZ2c7yzsQtHs6/STJ2veipMmRcHBlncRSly98mz2c4yIhX0OX24/w2uHqw33/6O/nt3px+/vH157vlZJdt/mT1FWIcptxT//s7xOdXhvHhJvr07cQY4gvbyv9Ee3SDWU6vI90OyEtOhxF5F6G3O1jesH/Psi96ehbNYh/H5NZX/FQOk99G2f8POA5xcBzj3zOcpMVk5FMt51s+lNMznRdMiqNZ6OHsgyJy+2nsp/h6unzzE4ElGRunkyC/nTzgdJCxRiMXo8BNkvz3FYOyi6EfBGdREMWLNxhDK/sh44F7j4NTd/AwWry0+sjiX/7VK+Paefaz+ti1bMjZ9YjjFD9XQJiz5T2OJjiN5+SR/G4BsHlxmV8/lXhFdj42rmDVzMfcfImMVjOXMCC/5EhogQoLoOJ7wU/tgxt6gR+OAA8J9afZr4Q+bhDgIBrF7oRQbYpjn3wsHNP3vpU36lg+9J9xIVuQRE4Y65wwGYwwu2SEDRjxIU2nnxdrbMMi0uopKouC5joFkc4god4lCR1AwmtCmJPBABO5oWtfXCJLWHBuJZIy+vhE55wE/igkYxPf87KJJIoMSmb0d01nBFXJ92iWLkirFXrALzRAgiduSEj2jShZPxxGFUXhl1piOXYfFyP/PRu7fphNi+O/xHIsjaYS2YX6lGCxG4p4oy+NXwjw6xwP3VmQLum75wvCYinRbheEDgh8Qi7jjLpL83IwJoYxIPM4mtzPknoSuzlpB4SSCx3aPc0pw8ViCKE+g+TyMG0Akt/EGT3c5FWAurd7KW+yKHzrB97Ajb3XQeTdSw5of594Ez8kQzcf/5NZLbk5fuUnKQ73nuL9ncNah8oQ0BSH3kkWjyBX90E0eFgnIn720/9lpvexlV/9qtw5f86t8sXFvLgIvUs/+6D5FfkmlSmyy1/Ve+Uki6tilnZcSoj7nnv0GyMCqRuPcLrhOTMHKfbW4i+Q6RWmWgyeFmMxDtzUf1yP2rAYnb/hW+QvAhSF5+ZQusimsLL84vlfVcMk1ESIAqdDT7SkDJhogbvV194CitBsaAvFF2C1Bqpj3RKKq1q8OErBheay7nDCxaQcXgt1DBdo8mwHl0KOVaSYdtyzNgoywSJIMajUcZhXshhdQwXabltBpR3XVeEmxQSz5xxbnPyknXrar5HNTxgdFKQp2hkgPKaPYImBmmqhwilXBI3vKNliOtxotGtnkgzHInZegeMvVnxjLUpdAeO/OE3neYzfnaURGYridByNotANrqIsPrfMBURhWqQCchDTEKdxXaQO7CzymMbRwyoViOi00QvpoTAKM48og811/l3KD3dRjr6gPztVn40xXoBQO3b0vr0Gn1U4lxfgxSPRcJjgzciloWvTcpRXwdp6M4FM4OPOK49NswcSKWsE+plfoze7RFq5yko4x8XSqtczZss1iHa3/Kikp21wLr93Vs1Eu19+0nxr1SwmRewb3VpZIUV0lY7hNUUXY6qO7W1DsP+kgqlgKAUXhGiPit85o2PMWsdggTF8QaJGahhPFSCADAE3EGCyoXPHSLSfLj9EpwgMaN45mjB5wJhKNgx6b8X4qFU7+YKotaI1pdCIelRtG6+fqtdNJBuJ/QMS11MN9dlOtQSjQZcM88cNzfqpJMPRhEGRLYvQ8Z/p7D7wk/Fx4qfYzWs9D6Xo9XEIOqdhFIDdWS26Kc1nXzek6+zoQlihI2NX3lqxUOqllVpunUFlOQz+nBudbmdMJVtawYR7WVC+KsgyTmD1MmcR9DbyqfPSLqOJ/Oi0tMuUFbVpYbSsB/TrI+Si5YZa4sCiU/B0mobXpwMTtQ44NxM6xXtKWC5nFCtnWAEkEa15A/LXcRQE2YMHk6hepFH2MdJYXWHdmkSyAkq788PUKial49Arz39bCYUaSihhQmT7mNNrqWsu5GnVeH6OzPH59T9Xf0+C24sf54PJYPBJsaCTQ2e9EKIzGdxg7LpgzWTFnUSotEd/cfOgzGqVGe3fI2a3S7faDHbonlVMFO3iGQ9mqR+FG1ipUMOzxTIPOvV4LBhPOwM2X+mWxjidxWHWVeQeZZ1FyTQKEwyJvT99iVQdCrIRgyUagyVAtIrjyfZRrFejiJvG3C3FYu4AVrwtRmAmUBQjWRFbMIb1lkt1d+j2NIW4IqW6dCSkFK1bG6N95Yp1LRg5fLvFum3SHoooGqtxuqTfchXusFiXLh1AvNW6cCY6yLH7BQjjXD+Jp1cas3L637vdCgYxt0hi7gUjzzqFjjn+c5cFihdf/gTSV7wzkInBS3fiBxldrmYD38vcEcLrJFrdL8SkITPcS8d7EWOvnk53NbGggwwY8ub6Nku2bNu2CWeSbH0XrzvYFRXjQXw5xb7Y1FDg8BdzwVXSuJqrO7Vuw/jUwfvcpy64wlyoNaxtfb+Wom3Qy4e3xNeiM4hNa3w7XIYwJHm2tPgyI/t05gfe3u8shXa/L6YNY20/F+mq17L1KDJYm1l2S2MFarwUkc3Fqq6XzYoVhTn0yuW17016JrPj6LotuvWPw1Sutz+omkTyx5X9uKUgUy3AGdSuE0jYjnEIbNorqA4RSt5+B4WINozCVeyEoyxlTIAU758io2uV2bUQ3e73Lq1HcE0l1XcNs8P+erftDo3dDFspyULvZ4N6vJIFxOjNhltqCFv7ojsFOdSS5Fbner2lFrro1j/k8KZg6bYc1Nt6w5aWL5KrtwSHzbmcgpZ4bwddRRAJ5R3vHi/Akmq6xUtrSwp85HVLqvknkwrhQvxLOJ6pqPI7FM02iXis877fNGMrzU7swdhdt3p5Lzr+meX9TeMjPbXcVSCAHGFC1mm4NVJ3oekejJmWdcnaNcHuGzmADDms+LUsl3RTO4xwFQQL0cvGxDsCwNkEL9B1UFDtFVTj46WkoUaB8zQE6w212sJAh6LFe4ABUAeWvHD5JoUsXMAM/dBPxneHk0i3ECROl91hTHSwjrcQgQ6i9Sd+6KYHF4gHGLq2cw0jOuX7Kj2gjVGF2k5oXW2V51i8Kg/MRCvPLj0gJulFp5DfELqNhuhWzL9/peh+DLWbxHuyxh8ufs5/XVwan3u3DJ8SboX1ovpt0OtddB0EeJhKVIo22IysYSEU3erGoxSZhIVuV3UDxH0kadPDOKWRFLond/kRvnT84q66s9Ie0tpitcmzaE2nCYXRGhr72c4eP+ZTBrXz7qSPHrxFtKGbbRiR3dtDPvSaihFaQwjjA7StrwhJA/xKzpt1uixYZRL4cDiAwFSIYws7HIAxlbjoFxMJh7MBOFEAWCfurBDGVJJRAMv+7oKFxN1LBcZMWnVqtMGytaKZ+UVjgVy7k4x+4X1S9n/vGeXR6hzjndlwsOoqxZMpWer4o0e+vT/0WZtQlabzdzzEhEyD/YQ+auwDSmMAgk7gJpWSNyxWg9eg8ZFtX6Aa44JVQ7w5FOS5yXhlRbZjWeNNkKtxoU3BCUXUHDjXXthB5fLSfGxYQkf6AEvmASSbIjuKoNKi6lRM3j1JbadmItmoZOUXD6hknae9KTqmCizpjdp5S+N79M7y9ESyYdkuu/lmYNlUh6uV27Go/R65Dzzo0edjyttOnA3LdnGrRrDcDmB8qOaH5aZwaS0qe2qhkuqBAL1ljXU4nXDUulbi7YJorxCWjbcC2BQtUQSXBl0DrfFqcdq4hDPJBma7dt43A8ymatxRC5h0rEfj1uN0zS2YSTYwYYA0fnHj930Iu/VYLVndll60azd9M2udcdbvRkiqsthpLcTtSwIt1LUzqbcLCDNyjBCaFNCKFGR7n08RdtMCBaT+G3OblkxN9zUXxm19g2i/XJ6Us48S3iq2wd1dJZiEWOErkPCs8zw2IlORJU8HC5HVcwRJeN2kZpK95mG4cH8Xurn7kk/xYS4By1aB2nqrqTFXAFKRpQ73UeI90BFsUwhm4l7q5DKOsla18nGyJsdfIg9nT/wf</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/server_setup.xml b/docs/guide/img/diagram_source/server_setup.xml index 4dce7d836a..c7080d6a59 100644 --- a/docs/guide/img/diagram_source/server_setup.xml +++ b/docs/guide/img/diagram_source/server_setup.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="bf64c1a7-49a9-5ec2-fcb1-fb6d2ebd0733" name="Page-1">7VtNd+I2FP01LIfjb8wyIclkkXbSctq0qznCFqAztkVtJZD59ZVs+UuSsSE2MJyQLOBJlq17n570ruSROQt3X2OwWf+GfRiMDM3fjcy7kWHolmGM2L/mv2cWx+WGVYx8Xqk0zNFPyI0at74iHya1igTjgKBN3ejhKIIeqdlAHONtvdoSB/W7bsAKSoa5BwLZ+oJ8ss6srjEp7Y8Qrdb5nXVnmpUsgPdjFePXiN9vZJjL9JMVhyBvi3c0WQMfbysm835kzmKMSfYt3M1gwLDNYcuue2goLZ47hhHpcoHFe/QGgleYP3L6YOQ9ByPtDmQX6CPzdrtGBM43wGOlW0o/ta1JGPDihMT4B5zhAMfp1aaTfmjJEgVBxb602R+z44hwD5jyn5Vq2Yfa5Z7xzr7BmMBdxcR7+hXiEJL4nVbhpabDUedeafKf25Jiw+K2dYXeKbcB7lWrouUSWfqFg6sG2rDbgaZXUAeH7SCDZJN5/RLtGDEitrZ+69xYKjYe0k8/cNp2Hc7CqSt4ugo43T7gtK4PTkuAUz8dnLpxfXCaApzaCeHUeo6qAoKaY9nAHxrBYjzvGd8TBYKTHhCcXgWAevuIHgrAvud1cfbWbUB7owCwKOljwp6ezwOdcwHYpwea7vk8sMOU8hEA3Qn7UwFYlPQB4KQOoHU6/MxrwM82zoZfhwX3LzCAJ+2rmKEAdK9iDtbOB6CutyNYCADMlXyQrFM0tZakulhCC5gWyAVgAYNnnCCCcETLPIogpJVuGXLIA8GTUGGBCcFhpcJNgFasgGBGI34lAYroI+R6C3tCwKsUjdPObFi/wt2K6UJjvFwiD459QMACJDApvn0PUYS+6zzdfwAhChg/jzB4g+zudVmAZieSLqCln6EyL1vyEUfhI7bdg48MHKa0peNPbZUPFSUDhKlCaTnFKBt4pWY707uZoRyFeckAE+VJEewQ6Y+IUxGOoAynv2B/lx+ieqLVMWq0ujKr+TxbZdV0e2C1Qw79yepRrFr5zH56VhXKyPyPJ4lY2hFyIIvcJGEr8hIi3w+aZMAy0GrCHO7Ic3ixWknr8YdXbGEckfbaNX5seT5X6fxGH1mbLP198iNrs+fjp8OaHEb+DdtELEEvQqMuLBU6Akihit//ocYv2ljT9dzyL+NhbKfbY6nhGcaIdonxerd3cUtAvIJ5WMlM0K/tacrgV9C1VatZbothAAh6g7X7qSDnd3jGiD5cGRuFxbSopyf4NfYgv6jkTW5H0P5MoZ0MAamdlP+i091cQlaqXl5ePofs3pBa6CunGLOyFDa7mT3ejwwnYJQsYvptRdLxklmSDYhq7Dn/vbIt9RSWL1v+fDe0SoTjELCbpVdraXmS4stKdWOzKy8W78IqN9+lrZUQhh7wWFjhzVEYshbrd6HmrDt1c7XTn55a8VRd3DRQZPNThaeaPWTzpmpbmvOV01VzXdrgopnZv0EcoWR9dZQPEI9MxRpcFY/EaeQollWazSfLg2zin5Flla5UsNwe9ac06Kd0C3F//u3pzzbHOL712dO3v+76aery3U9xXsxOPz15n6gLW9104V7WPB028Ct5iheAJEFeY3ayD6kiOdHGllUYWG6ijzXXbMpNMu7z45F7ZdBqtsK7cSnZilZXcortoYPTFeEwnG4Pl690kGvbXEMm73BnmUzcmrMcnMReiA807rEe6gOGcL5U14fzgQ7i7il8wDJNIWBY7sF+UA0P+WL9QnzDEraPraPlDLEhazDfyFvuR+L6uJcwzcuquYk2tpvnlW5u4lyWmwgrBevYEGLrLQ316CaHKaHDhRBDF0KIYX3INy47ghQbux9dYZjDzS6WcUbX6Db+L2sJIY5/Q4zunce/eGp9wGmiwwHD05N8IYRKa8JjCTXaGuqR0GbxkSvVXsFEmevnJ6kqpmMVbUkEuwMEyLIWiHzZOIfUu3CU7FNGGgTyX0kwG1ixMDu8caDSy6wetG+r2ymi9JwGoTDBn5hderupzPGFvTLxtx3X8lEMPX6YZAsTIvCZerhr2DOJ/GpJA9AKOjrvOyg2tVXvG/VxiNDqkg12fX2r+TjuflhlJahXOBUnx1VwGj3AqXj34/eHOb1qbl5whGncWxOk+j732sQ3nCaDbQrTn+Vb0dnkWL56bt7/Dw==</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T12:54:55.820Z" agent="5.0 (Macintosh)" etag="nK8-tb6GgoK2AEMDZwuL" version="14.9.6" type="device"><diagram id="bf64c1a7-49a9-5ec2-fcb1-fb6d2ebd0733" name="Page-1">7Vxtl5o4FP41fqyHvID4UR1tP8xuu+vZnd1PPRmIyikSFzLV9tdvgPAeRnRAcao9p6M34ULyPDe5uTdhgGbbw0ef7Da/MZu6A6jZhwF6GEAIMDTEn1DyI5aMEsHad2xZKRMsnZ9UCjUpfXFsGhQqcsZc7uyKQot5HrV4QUZ8n+2L1VbMLd51R9a0IlhaxK1Knxybb2KpCUeZ/BN11pvkzsAYxyXPxPq29tmLJ+83gGgVfeLiLUl0yYYGG2KzfU6E5gM08xnj8bftYUbdsG+TbouvW9SUps/tU483uQDLFn0n7gtNHjl6MP4j6YyoOTS8AAzQdL9xOF3uiBWW7gX8QrbhW1cWr5jHJZ5j+XNBto4bEmHP/G+hauIFSU324keKNpwLaKGOJuI/8bThf2GFYLhmbO1SsnOCocW2UYEVRFUXq1ix+CpV63CaVy6boFU7RfbTd+pzesiJZCd9pGxLuS9Ua7IUGVKLJDSSP/cZOyCWsk2OGWMpI5KQ61RzBor4InFRYwT14xiJK4Rt0OP4kGAXG8zKOYSYTgPus290xlzmR6rQIvpcEzvHdXPPg0fT+cO0A0x1vYhpapQ5UE0FpmYbmOI7pp1gikuYgsthCuAd004wRSVMtQtiqrU8P/YexslceDO4AxjT4fWV4XakgHHUAozjO4ptoQiOD7Bdodi2r1pBcQV0ItrTGxQXCzwCqAt3dnw9WzR+NVvsDkXzerbYwNc5ed3YA6wAApZgqIJVomQWlbSO4qiIIr4ciOjdgji7MIg6vBqIDaID9/H0LEtULTS6QtH81VDszkPVrociAMdhTEO+oaNpk2ATQaodgU8HU2OCVcYgYXXJM3W/sMDhDvNEmSV6kIpK07DnHIu4j6UKz4xzgV5WYeI667CAs5BL7IW7jiceIYmwh09IZJVUuWjMLmzX9rAOMwFDtlo5Fh3ahJNnEtAg/fZ163jO1yaTRBw8BlD+zrVViz63FjquhqT0Ch8NBR91vQU+tj059GTwWABzYkyVw1xS0vXkkGYELjGstL1m6guMs8kcQyWMSUnXjtpFYWwwyZ8xO3jMo/2fAPrDurkm/pldcMuABW6ZVWolfl6eWshsgVoNIpx3at0utXDiWV6eWorg+fKPxwq7REN4AyrlO0yKKsCXSbN1bNuty5ll817qGUoPMllc1C2CMnyux582HBNTL1BDr7qXqp0JsIUJLUnM3anRT2ro6HrUaLASpp49CTdrZXinsxKo+qsJdsYr2Imu8n/8I4QftKEGQCL5N4RyqBtGIvhCfUc0KaTUVTFuNk1w4q9pMqLHImoXNrBVGZCDWFctLqXMpy7hzndauJ8Kd3mHL8wRD5dNS6W1bTnlHkQdJy/KyFPVU8pHoZKeuAcqeiISpo1uxstq4uLp6ek+ZPVlyCrPZml8+xJjVjUfMpvMPs0H0HBDNjz7BZIY/72E2zSjLv0QRDCJftSAtjtEfZGUi29r+TdSE+yI14qisP6bFG3p1iJWOOJKjYMY//KNhDh+6KL4zf3Re6NLfr9zowPl7QCKEOVYYXSohRAlUu2EPNFQcA3DCtb7NhLX3eJv4ntOsGlJ2w2YxLF5qL9u1PmTEFIst1WTUNltOcseVCH7uz3c7eGaKSx0RXtQ5T5Os4dxDbuWnx//bMUa6m4we/z810M7qvpvCeOqIejR5z0ZQjmXi5vlcltZnTTY/pqLqFguCQLHGhw7naWCKw2jaEOMU0EYRQFDzUSDmihKTMDkwBzsPe75uIrsy77EVbRiuD/dw3JyYKV0xgno3UVWGmQ3j/FTzaDTGDsamQXG1rG139zsCRFrt8SdSkRYOkAJQHdEbJALvQQRMUKloRObt0nG/ECZhAZ6QlBc2u2Hzw5BlxXhzgiaaG4nN/J2qobJElzgqjbU66f5G+Kq0S+ulrxHfO5gqoMjilrk6ml5vO4GUwhKgynEt0/Qfo+l6d64t3qdqLvJHsMr8vN2iAb65VaWR0JYnmwbj4Tls+EdztoNTkv9okzrCasqi5VzWQWPKWqRVfXpr8bpXbg75AOGmpUSKBMmhyeOZKVj2QPhJB+2jKXEs6vCJRUsZ4Irr8dRaxLLmZ6KKd3jm9dIfDU43a8K9OMWEsG42e74aEswF1jRnyy8dLrLeX+pPOcSHjsLYTs+teS+5T0N+DXhLO4m1jT8oD9UmJ8vaZsA5Z0Aih2DqlettHFWCTcJ2zR9fU798cceBalr0O4aU8XZZBWmsAVMFS97+H2xFFctUY8H/NrdPqWU7zvZ/VN+pcuosx134mf2asPYWcreH4nm/wM=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/signal_slots_diagram.xml b/docs/guide/img/diagram_source/signal_slots_diagram.xml index c52134747d..6e0becb34c 100644 --- a/docs/guide/img/diagram_source/signal_slots_diagram.xml +++ b/docs/guide/img/diagram_source/signal_slots_diagram.xml @@ -1 +1 @@ -<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" version="9.3.0" editor="www.draw.io" type="device"><diagram id="adacc587-d56f-817b-fba6-5b04ec2af4b8" name="Page-1">7VpNc9owEP01HJuxLNuYI5CkvXQmMxzaHlVb2JoIi8oiQH99ZVvypwgEjJkygUPslbSS3ttd7YqM4Hy1+8rROv7OQkxHthXuRvBxZNvAsT35J5PsC4nnO4Ug4iRUnSrBgvzFSmgp6YaEOG10FIxRQdZNYcCSBAeiIUOcs22z25LR5qxrFOGOYBEg2pX+IKGIC6lvjyv5N0yiWM8MvEnR8hsFrxFnm0TNN7LhMv8UzSukdamNpjEK2bYmgk8jOOeMieJptZtjmmGrYSvGPR9oLdfNcSJOGeAXA94Q3WC94nxdYq+xyHeDs/5gBGfbmAi8WKMga91K9qUsFiuqmpeE0jmjjOdj4RK4SHIKZ6ng7BWbWtQCMBd4d3AToIRGmhxmKyz4XnZRA2xLoamszVGv24o64ClZXKMNahaQMpeoVF1BJh8UamYEbQOCHpUzzH7Lh0jkWywESyb3U8fW+7NhuuFLmnvBVHaA1npXNWotU61GrqjQ1NQuxbUZeyTRWnrhxFXLrMmf84+J3HJED+Q6R7n1DdT6PTALr+sbrjd5nNsm+MqW/uEr/WAA/Jw7xK8EZgD83KEiy2y4yCK1q9MeQEOksXzbDYwRRbccikF9UH27UONd2VXAzJs6RlfRLVfAb8hYM75HAIcMNjrzfg9BnITTLLmWbwFFaUqCJmjFABx2cusWADKZRzzCevVmTGp7dg171jKOKRLkrTmjCQg1wwsjeag8kDq2oUzZhgdYDarn0W09blMPbOkpNtzRk7NSbvo0osDlRIUojUtPKExalzn22TwC+x6IhPB9PT0Saaoc2kRSKktefDxeoXRd1MFLssuYbbEK+glQ0HEb4HhuJz5BA8VtZzgrPpmyoUvMXm6Z73/KF+sBjl0t+JULytcXzIlcKs6CvWX0lUznCxKyS5Irtq3zI6F7SweCXj8O5IDBIuEJWdO5JtG3QXTyBn+cfc82Fe/TVD5kKifkh8dM5fxz8lY8Abun5ObI2dojT6b7yI+VuMAzlbgLEiWIHqxzW6YgT0hhIl/7b8IS3CoSlAhROVFmQdICsiAxy85bEiA6VQ0rEob00IFelShWOzbllbNaoc4ALznKQSvPMdRqJsvs48YUTD5JHoZk6N+MZK3jk+Q2ySHhMlUnLNOxxanIewukJI7bsYPOpVcrd7jQRPyHZlIPwIBW0q2BFpSJ/4HG+lWmdwXfdVpJlt1lxbkWK91fPu6Dlat6UpswYLg/vhpj3d9aHkm6RiKIJbz3x1uf1/42bMY/A22Tfi415Gv1y36RHVf/PgGf/gE=</diagram></mxfile> \ No newline at end of file +<mxfile host="www.draw.io" modified="2021-08-16T13:16:04.663Z" agent="5.0 (Macintosh)" etag="4ZAp6e48cB9ZjwqlSIAi" version="14.9.6" type="device"><diagram id="adacc587-d56f-817b-fba6-5b04ec2af4b8" name="Page-1">7ZpRc6IwEMc/jY/nAAG0j9rWu5eb64wPd/eYQoRMI+FCrPY+/SWQoEBsvYqIjnbG4gaWsP8fu0lgAO6Xm68MpvF3GiIycKxwMwAPA8exXccX/6TlrbCMtCFiOFQ7bQ1z/Bcpo6WsKxyirLIjp5RwnFaNAU0SFPCKDTJG19XdFpRUz5rCCDUM8wCSpvUnDnlcWMfOaGv/hnAU6zPb/l3R8gyDl4jRVaLON3DAIv8UzUuofakLzWIY0vWOCTwOwD2jlBdby809IjK2OmzFcbM9rWW/GUr4IQeMiwNeIVkh3eO8X/xNxyK/GiT3twdguo4xR/MUBrJ1LdQXtpgviWpe0ITP4BITKfyashfpCyaZaprTFcuPjDkXUjoemIgv0Tv5JXfIhhGlEUEwxdkwoMu8IcjyXWeLwrHYVK49Z6qdY0LuKaEs7zOYzdyRDYQ944y+IFNL3h11lVYzbCqSr4hxtNkxqTB+RXSJOBOdsVSrYykvCnlX/Vxv+bF9ZYt32AEaBaiYjUrXW93EhpLOLKNjkNEnXAYghYnYjuS2tslLr2js/1lR3fAly+9GoYsFrHSzbdReJtrNoJCs7l2Yqyc9lqeKevLTH87c0TMKn02cuaPp48P0BJy5H2I2NlA2bgEycJ25olTqHQ3FET/EdWAuuypieXJZy1TRga7uTdfOdC0F60BXr8OiMO26KKgxY1nKe1UkKgw1wJw8ivGiawKzbGkDvPMVCv+KEsq76aEn0nZZLEY3bTvVtsuCoWff74mLknAiJ9jiV0BgluGgJ3oeVLc5ZBHSEStMKKwsBTSF2Qm8Zwi8tjFEIMev1QUEkxrqDE8U5wV3z/SxrmeWB04dtDuhr/vxqn5AzU8RgYafHI3yog+jxT6elhBmcZkpivtKr7c4FwWT7VwlTQC876dFmkxLGHWaCMFphj4uKjBLi1XBBd5IvGpo9T9PHV4vgOtVFPK9RrkABs7qaeFT5cI0wTgmAYgosLdfMjBDMPK04XduKH8+IYZFVxFTVbqZNaTPJ8jFLknu2LH6L/huLvF6lUqA304qce3OCtMB84/Pctk2lY2x5Hgk/y6KV//G63G8HjCn+ojXSxs79QQW22lp1P3BeKtFWExP7P5vBc/2TSt4cxwlkOxdxqvxKEYn3ESgzmQJTVBtCq1MkIgTSYwFEjJdTuVYBweQTFTDEoch2TfI264tWPUs3Zi+n+kGaGFQZ9eG3Yb1HdNN0caTRPvuxtfV8wXGZ+NL+7jxVecrxExMWjGVPtYo4/neHCqL6+1BsE9jx1bQHA+rM1rb7pDO5irEnFB+CfjsPhTza6ycc/7bAhNubUjvNIFwTwVE86WHawDiSpNHHRTb8NDzZKQ0X6N4wFkKeRALWa+Pl7M/ZW+BFwdUS40Bl7t2Fk/Fz+1LncW0b/vmLHj8Bw==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/spi_cache.xml b/docs/guide/img/diagram_source/spi_cache.xml index db29cf9859..2b5abecc13 100644 --- a/docs/guide/img/diagram_source/spi_cache.xml +++ b/docs/guide/img/diagram_source/spi_cache.xml @@ -1 +1 @@ -<mxfile modified="2019-08-30T07:19:50.436Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36" version="11.2.4" etag="WYI221xnA1HH1EB6F7t6" type="device" pages="1"><diagram id="6a56a1c8-b199-1328-c7f2-c56da846c7cf" name="Page-1">7VnbbuM2EP0aA+1DA0mUZOfRdrPbBdLCqB/6uKAlWiJCiS5J2U6/vqREXckkaldyU2SdALZneBmeMzwc0Quwza6fGTylv9IYkYXnxNcF+Hnhea7veQv178TPlWW59CtDwnCsG7WGPf4LaaOjrQWOEe81FJQSgU99Y0TzHEWiZ4OM0Uu/2ZGS/qwnmCDDsI8gMa1/4FiklXXlLVv7LwgnaT2zG95XngOMnhJGi1zPt/DAsXxV7gzWY+mF8hTG9NIxgYcF2DJKRfUpu24RUdjWsFX9Pr3gbeJmKBdjOugFnSEpUB1xGZd4rrEoV4NUe3cBNpcUC7Q/wUh5L5J9aUtFRrSbC0afGszkcjZmRDrIM2ICXTsmHeFnRDMk2LNsor0NWjqbvHv9/dJyA+o2aYeXpiPU+ZA0Y7eYyA8aFjtE4f8RIv+WCLnB2xA1ie7IBceQpyVcjg2aLSWUSUtOc9l5Q+ABkR3lWGCaS3MkUULSv1HoYLlnHwcNDlQImnUarAlOlENQRQUtBMG5nKWWDhUE1E2awWW8JxV6dk2UxN3R4xFH6C6GAh4gR7z59DXDOf6qaDxiQurY5b5fLdXfNPSCEPToDYCF38Ck1/cmYHfEBkB5vFay27L27xKe04JFqJ9VKO5ptYlRB4PAkuK1jSECBT73Fd4GjJ5hR7GMrqUA9HfYcrBxqtB1p67iDsdZDcRsMI6ALEHCGKdkqVn0KOI8C28hESpTaRlR1CRr+GehTpxN57hqTWGi38u+/ATzHvl1QzXoT7w8yteygRueri+PcqgND2dUxkLgs9x32itXdhj2kLZqasNcraY2f4s0D3bw0Q2g6xi61PVMId1+PyFW5tZubN28BhMoNzBSZFtwJZ6e84jOZVHnTgqp46y8ILJB2ngUnR37p/Kl7bpSlJKkv+uoLHXPN/Pgejckwn+biN8+LBHhDYkwS5nf0UnVFlSNo8WL/TNRvOgYlSzmlGWQvCyMWprfltfAJq/rJ1i5W9em4LLa4VxlEk1w1Otkl9BXBbe7+NtqbT/P7Hloy9cZUtJzRqZkMEFKrkxtgFGK/gv434McAEvtO5scuLYi6rYyPAeE4fIuuCGII8qM93W6vaIyM9Dhu7fMaLPUeEQJjFR8e3nKqZsx+dCVJ/LUeA8KP0Zy5uAkuKHCu7ZruBBmCtDy0N0VB6KObme9+1I+uOW8yMrnpaqR/TyWSxcjLli6PGmTcR0yvErJcByraaw50GaJYyFr8vNhZUqZe29hapKrrnuDqT43H5OCweax7J25CKnH6BDywxeF2FHi8eMH5cN/m4/lXHyYBdOOIY65QHmkTpb9911SV2GWhwgXzMWLWYN93yfNIf8KI+FchIy48jHv3kxexj4FT40d8MY8U7j+NGWs/Nr+KFrdhbe/PIOHvwE=</diagram></mxfile> \ No newline at end of file +<mxfile modified="2022-02-14T10:46:11.175Z" host="app.diagrams.net" agent="5.0 (Macintosh)" etag="XubE38vakg3yBPNUfVvb" version="16.5.6" type="device"><diagram id="6a56a1c8-b199-1328-c7f2-c56da846c7cf" name="Page-1">7VrbkuI2EP0aqjYPoWzLNvAIzLC7VbMpKjzkcUvYwlaNbLGWDEy+PpItX6UJJMMtG5gqxu5utS7nqLstMwDz5PA5g9v4Gw0RGThWeBiAp4HjjMa++JaCt1IAJuNSEGU4LEV2I1jhP5ESWkqa4xCxjiGnlHC87QoDmqYo4B0ZzDK675ptKOn2uoUR0gSrABJd+gcOeVxKx86okX9BOIqrnm1/UmrWMHiNMpqnqr+BAzbFp1QnsPKlJspiGNJ9SwSeB2CeUcrLq+QwR0QubbVsZbvFO9p63BlK+SkN1IR2kOSoGnExLv5WrUUxGyTt7QGY7WPM0WoLA6ndC/CFLOYJUWrGM/par5mYzmxDU76ACSaSCnuavUrvMGVKtaJ5VviKORfgOh6Yii8xXvklDdgwojQiCG4xGwY0KRQBK0wXm9KxuFSuPWfWdq5mYenLolZqhzKODi2RWqbPiCaIZ8K1pbQ1ZIrSzkTd7xuCgMombpGjbggVKaPadwOMuFDYmHHyHzj9S5zca8Jke8dxqre8JWYfQhYXmFkmfOaU0ExIUpqKxjMC14gsKcMc01SIA7FKSOhncnWwiF4vPYM15Vwg0RhMCY6kglPJB5pzglPRSxVE5SCgMqmdi/Fu5dCTQyRj/ZBuNjhAwxByuIYMsfrqe4JT/P1+uCTuMSHVGopI/GyJv7GQn4FmwAcdmnnAwDNPp5nrnIFlJ0QDlIZTmQgb9vyHdz+rum9tMRR2UrgOVAsIz7DfK1mGCOR41038JnRUD0uKxegaHoBuuBn1okg5dNWonYj7fsa99NLzw2EWIa75KahST/ok9jgG8viEKzCKoqraMf6PXBYis1YV04j8SP0v2rItTDsMrAyl019ZUeEJzli2vz0c81LKnneoGA6BbyIOKQMxubbNcXE5p0r8kZR5H1tEC2qLhTuygZYy2ppzZFW3S8+xHu1qWXuXgTMkVaARdp4zmdcc6wXtigcP++PQdhZOfqqlLx9ORMw1QHErSnQpYFnuk/dkokCtuQAFbOeKHHCPc+C3BweuzwH/ihzQa+vf0VYWu1T6KSP+Ojs5Mb2fhVQePJ7LPFMum77CUt2oZjkTdbbE1HqhEQ46jcyZ6m/zWnuWH+V8n8t9rr+3J+6A87dJfY51Iue9M3B+rMc9GMTofx3qbgM7MDzgXSzU2aYi/ScsXm+TtYA/GnpXBPOEAvaSxcuRAH8HO/o2NHDta+5ovYJ9QREM5PhWooKRLyQcC6WRKBTOn9RPjfZ3wIXbRHfXu2JSt01vXXyYSCCL+m6Zr4msEq3p8mtxIJOyPCkOQUojc+knps5N53y9U+T2eiuRdubbPy9OcBjKbozca9j5Xoi5hyLiHKQZ61nDnhhIcpZXCRONJF1aPNC/Mvq9kGGIGJfiQuWjxYVPXyVYGwHFLw8qXJsK7nEqjC5FBf3JYIkyhhlHaSBLiNUjNtw+NgDDGYENLkUJ/fniER1uGR2842TwL8WFEw7K9ZclOiX+yUngTwIbcE45JbDd8zwgitvmV17lW9zml3Tg+S8=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/subtree_usability_notes_1.xml b/docs/guide/img/diagram_source/subtree_usability_notes_1.xml index f76507183b..a0d3819708 100644 --- a/docs/guide/img/diagram_source/subtree_usability_notes_1.xml +++ b/docs/guide/img/diagram_source/subtree_usability_notes_1.xml @@ -1 +1 @@ -<mxfile modified="2019-07-18T11:44:46.670Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" etag="YShK-N-hf3cLP7eM9q1W" version="10.9.8" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7Vrfb9s2EP5rDGwPCShRPx9ju00LtE23AGn3NNASIxOWRY2mbGt//Y4SZYuSmrqxnW2NHwyRR+ruyPv43VHwCE+W21tB8vlHHtN0ZKN4O8LTkW1blu3DQ0nKWuKGYS1IBIv1pL3gnv1NtRBpacFiujImSs5TyXJTGPEso5E0ZEQIvjGnPfLUtJqThPYE9xFJ+9IvLJbzWhq4aC9/R1kybyxbSI8sSTNZC1ZzEvNNS4TfjPBEcC7r1nI7oanavGZf6vfefmN055igmTzkBXm3yH5/KHznrix/E8WXD+/L4EpHZ03SQi9YOyvLZgdoFt+ojYRexjMQjudymULPgibdMvkV2ki3/1Dta9/V3em2NTYtW53PVLAllVRoWX81eoE0NgKk13ZLObwtSpiw2Yelicq8FZFGJmhKJFubYSUaHclO3c7CZ87AExtpJGOMrpHl+W7o+56LPYxrDRrWlhtehwG2HQRTHQ/7tmlgxQsRUa2zHaSuGeSaej1kKpJEJFT2FEGESNmalqsJq/YMaLS2bS+qUPIDiAmORMz/INBGBHZ6jw6lc2woD7PTOHywX6ExHxq1BydFjeUNwMZLIXTjXFADPt5fhaLE8YxEi0TwIouvIp5yIIobFd1k9ovtwgrAaXAFddq/qo4CGTB9Jq8eyZKlZf0maCbLvBrE2IFnBIFkQEA2yuimN7p3BFqJflYOK82DHlcmV1UKUwYtJ9/2tUwZJCpR4TRiOaQ2rRW2tVZsGgNxtUGNtHPUJN1K83wJCh6QWTVBbQUpJK99qoZJypIM2hEcP0W+4zUVkkG+u9EDSxbH6uWxxh2Ycccjd6rWx9J0UoeiOdgrKfiCdoRV0GisHVCreqvjMJ3s9vwT7Hk9eK+XM5DRtEj5SLejp8ihAXdDR91D1yIPCw+wR/dwtonCoNAfRv5Qin2VyP9Iibyg/aRo97vVgttD+1CqPB/YwwvYay0PFHI7EYxkF8ifFvJep3oJ+wSPXhLztvOzV8SBWRFbgffMithxn1b0r19u7KHbzavkryldreBQXurUc9WpnVsmHqhTnZekMXzsxf7J/fvvc5x5b9h9tTua4zz/SI47zE7n69/Rt/hPD+tbEf5J39/N83RRvLNvP6yvhjKdQTaPJDK5xsRBi2Wey0uNwVkjmHC+mMGvRUSzAXIyOeu77DTIFwO8cjhNbeZM0vu83qCNIHmPgE5BK6iTqpuE1jpF/sApsk7AKoOIudwHGpiSxSWHnjiHeoFJgW4f7N6ZUugg2JtTdEG7ImV2wfupr76dGiXwDyL38+HdGsD7T3Xz7Xxfs/3wuVUhelrRS958h2Npv+ryvxNojIITBdo9V/mPBh1+ZvkP3f0fEerp+79z4Df/AA==</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T13:18:51.511Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="M5JItriJsAshlAm9MXc2" version="14.9.6" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7Vrbbts4EP0aA92HGNTdeoztJi3QbrobIO0+LRiJkQjLokpRvuzX71CibFFWUiexXQjQQ2xyOBpedM7hkPHImi03txxn8VcWkmRkonAzsuYj0zRs04UvadlWFs/1KkPEaaic9oZ7+h9RRqSsBQ1JrjkKxhJBM90YsDQlgdBsmHO21t2eWKL3muGIHBjuA5wcWr/TUMSVdeKgvf0ToVFc92wg1bLEtbMy5DEO2bphsj6OrBlnTFSl5WZGErl49bpUz90807obGCepOOYBcbdI/34oPPtuu/2LF9+/fN5OrtTLWOGkUBNWgxXbegVIGl7LhYRaylIwTmOxTKBmQJFsqPgBZaTK/8jy2HNUdb5ptM23jco3wumSCMKV7Yml4gYvaSKd1owv5CBwmqume1bwQA4oFgLevOlY1/ABc5Uf0iEfR4xFCcEZzccBW5YNQV663jxVgaGoQjvmtBlcTRYdLqlaZRJqKFELfEsYTIFDYLTeY6OGRtyARW3jJMGCrnRsYQXRaBdu18M3RmEkJlJ0siw0RobrOb7nuY7lWlYVQXHLcPyxP7FMG4Gr7VqeqXeQl4uoYjaR0u4GOXpcF+mBBOYREQeBACZ423DLpEPe9IBCY9n2phKqr4Dt5J2wHdB2HNo0GOzivhtP9nvxdFw/9YCPHpev+UOhGsFJoWu4Hdh1E3h105CuNAy7Pwu5OUwfcbCIOCvS8CpgCQPJvJZvN3r8YDowAxgHahb+gEf2D0MpUt9lJxKCnb3Ihqu83IBleMPONodR5hS2WV5iK6AZbMwq6qjiRbszMJeTqq0tjgqyEToxOYER4EdFEGuKC8GqMZXNOKFRCuUAKCO3jumKcEFht75WDUsahvLhqcIKdONMR85czo8myaxavloRcsHZgrSM5UKT8Mh9aUfmMyvHs2IhV4BsRi/JxY7PZmu7UDRsyIlhdOiJ2aJrUzo0ZX81F7rSj95w4SvBYsB/j/DvteS+rv9iOz0f/P0+w/+BwJ6NOcXpQIIekWBi6DkS8g9I4F+SBKY95PIXyOUnei5vTNw35vK283Kg3342NLsOh71R1TnJcyDzkGH3UlzbJ2a7I8M2L6mu1nBTchF11U9WB+/zzepa35af+qak1U/r7vjdNx9/Pqxuuf8v+XwXZ8mi+GTefllddW30p5DMOkqelaloaZsxtniEv4ZMNpufFdVfymenoHUI3/E6uo6pIPcZLmm05jh7tUL+Xg6+Qh5RKwmpt+oGS70OlhonUMdORPb6/DXDiyE76FV24E500XUP4e+eKTnohH/Nq57iH+SdDgzoFQPq/0jWDPC9ozaA8zHA6GDAkB6fOj1uXbyanv/W9Bi9HOiSlw/dgDIHQF0eUBaanAhQzrnOW6hzwG88b0F1/7uhyn3/6yvr4/8=</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/subtree_usability_notes_2.xml b/docs/guide/img/diagram_source/subtree_usability_notes_2.xml index 37143da0a4..631f3ad1a1 100644 --- a/docs/guide/img/diagram_source/subtree_usability_notes_2.xml +++ b/docs/guide/img/diagram_source/subtree_usability_notes_2.xml @@ -1 +1 @@ -<mxfile modified="2019-07-18T09:42:10.468Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" etag="-i_j0YBKQYLsYBN_gYYS" version="10.9.8" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7VdRb5swEP41kbaHTIAhSR+bpE0rde22SO3j5IALVgxmjknCfv3O2CQYSNdF7Z76kGB/tu+O7747mQGapfuFwHnylUeEDTwn2g/QfOB5gR/AvwJKDXhjVwOxoJGGGsCS/iYGdAxa0IhsrI2ScyZpboMhzzISSgvDQvCdve2ZM9trjmPSAZYhZl30iUYy0egkcI74DaFxUnt2HbOS4nqzATYJjviuAaGrAZoJzqUepfsZYYq7mhd97vrE6iEwQTL5mgPyYZ39eCzG/kNZfhfF091tORlOtJUtZoV5YROsLGsGSBZdKiJhlvEMwGkiUwYzF4bdIExcJLJ4NSEtCE+JFCVs2B3ZrMlMGkTWmCAMS7q1s4FNUuODuYOHb5xCJJ5j9IfcsT5i5OeOAtvEhhciJOZUk722ISewDfmObUhiERPZMQTU4bKxLVcbNi8E3PZjAjbmYKAt1rMGp0eoyvw/qMAd9chgxCAV01wQSw6jX4US7HSFw3UseJFFw5AzLgboUmUrXn3yAogYyIFQnNb4s5oo0UAdZnL4jFPKSn0SLOM0rxYR8uEZQmIoEbCUkV1n9RgIjGLzrAJWlnsjrlxuqgajHLp+vu9amVNoI6LSXUhzaDzGKtCqDdvOAK4IqtFW6Uiyl3a9CAIR4FW1QVGBC8l1TNUyZjTOYBxCOUEYaLolQlLoRpdmIaVRpA5PjY7ATTAdBHP1fpSxmU5FXagbKfiatMAqaSQyAai3ujZ5mM8OnN8D53pxaV6np98YSMVI9i8W+6EanVYR+XreaAYu6ukG7WJrFn6jNM5Q/sWH8rWVRwLtCwuKsw/Nv6nmUauhj1FX8847af7+cbsQFz/J7UOSs3Vx4y3utkP/pOSNgp5xaAvI5qghnXPFVjtc1cCM8/UKfg2RrXqEZ+vxr8rr1UKPZl4vwV1CJVnmmqAd3Hg74jqhjx4VnZaM02qT3qQjmXGPYtz3UoyL3uSa2LoTvoqS/3RN9D2b8Qv05dyLImolb9yK5q0uim0/h5DPvCrC9Pgxorcfv+jQ1R8=</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T13:19:33.367Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="eFHgz6prW56X64wDNx59" version="14.9.6" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7VfJbtswEP0aAe3BgVYvx8iJnQBp09ZAcgxoiZEIU6JKUV769R2KlLXZSZw2LQLkYJl8HA5HM+8NKMOZJts5R1n8hYWYGrYZbg3nwrBty7WH8CeRnUJGw5ECIk5CbVQDC/ILa9DUaEFCnLcMBWNUkKwNBixNcSBaGOKcbdpmj4y2T81QhHvAIkC0j96TUMQKHXtmjV9hEsXVyZapVxJUGWsgj1HINg3IuTScKWdMqFGynWIqk1flRe2bHVndB8ZxKl6yQdyu0h93xci93e2+8+L+5no3HoyVlzWihX5hHazYVRnAaXguEwmzlKUA+rFIKMwsGD6yVMxQQqgs74bxldyL0lwvLVjBA+knFgIKZnvOOTwgRPmQBvlZxFhEMcpIfhawpFwI8tJ09qgcw1C79my/6VzHaPYzoZODw1ZxdV7mmCVYcHBsbuqSVhWNG9WsMI4pEmTdpgTSzIr27vYnfGMEIrFNrQLH0pzXGrCGXttFXqZJ72qWsOvI9NqOXLPtSCAeYdFzBPVDu4ZZJg3yJwLunqMD1u5goDxWs0ZOa6ik3wlUtIYHuDikUAo/JOsWJ4c/C6kaf4mCVcRZkYaDgFHGDUkvk0fLT7YHEUMcZnPwGbbUm2EU6f/yEEmpg6fIhUFedibp3nKzbd/LBYH+w0uuBCSDjqW9Gorn3cMALl+qQjuaE3gr2kLjGCJAS014x0eFYCqmchlREqUwDkACEIbjrzEXBNrYuV5ISBjKzb6uPRzj+YZ3Id+PUDpV6asUngvOVrgDlonGoQ7gOeXvxfnGneCo+GUG8PZJ+Vd0r1p0RXfPVfNGe7CsA/3B7siv2QoaYnmFFibvWQt3GJoQ4gSlHyp4RypwOyoYOz0VTN5IBF/v1nM+ecDXt3FGV8WVPb9ZD9yjGvgzelZe8qwkaIlNGVst4ddgZnP5KI+fZexBDh3g2supu4mJwIsMlTzawK37ZFL+30vZCYw0O4y0xz1Gjg4w0norRlrOx3X5H1yXXbtd9olz9toLs9Nh0KgTzd+6MHfP2Yf8yiszTOsvQ2Vef187l78B</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/img/diagram_source/~$ganising_templates.xml b/docs/guide/img/diagram_source/~$ganising_templates.xml new file mode 100644 index 0000000000..b106c07191 Binary files /dev/null and b/docs/guide/img/diagram_source/~$ganising_templates.xml differ diff --git a/docs/guide/img/ecommerce_jobs.png b/docs/guide/img/ecommerce_jobs.png deleted file mode 100644 index 97f19ee16a..0000000000 Binary files a/docs/guide/img/ecommerce_jobs.png and /dev/null differ diff --git a/docs/guide/img/email_notification.png b/docs/guide/img/email_notification.png new file mode 100644 index 0000000000..70b4564e40 Binary files /dev/null and b/docs/guide/img/email_notification.png differ diff --git a/docs/guide/img/erp_request_log.png b/docs/guide/img/erp_request_log.png index a1d273fe84..478c835e95 100644 Binary files a/docs/guide/img/erp_request_log.png and b/docs/guide/img/erp_request_log.png differ diff --git a/docs/extending/img/extending_form_builder_captcha_default.png b/docs/guide/img/extending_form_builder_captcha_default.png similarity index 100% rename from docs/extending/img/extending_form_builder_captcha_default.png rename to docs/guide/img/extending_form_builder_captcha_default.png diff --git a/docs/extending/img/extending_form_builder_captcha_result.png b/docs/guide/img/extending_form_builder_captcha_result.png similarity index 100% rename from docs/extending/img/extending_form_builder_captcha_result.png rename to docs/guide/img/extending_form_builder_captcha_result.png diff --git a/docs/guide/img/extending_form_builder_country_field.png b/docs/guide/img/extending_form_builder_country_field.png new file mode 100644 index 0000000000..162fa514eb Binary files /dev/null and b/docs/guide/img/extending_form_builder_country_field.png differ diff --git a/docs/guide/img/extending_form_builder_custom_form_fields.png b/docs/guide/img/extending_form_builder_custom_form_fields.png new file mode 100644 index 0000000000..750f56c1e8 Binary files /dev/null and b/docs/guide/img/extending_form_builder_custom_form_fields.png differ diff --git a/docs/guide/img/ez_platform_architecture.png b/docs/guide/img/ez_platform_architecture.png deleted file mode 100644 index cb919d77af..0000000000 Binary files a/docs/guide/img/ez_platform_architecture.png and /dev/null differ diff --git a/docs/guide/img/full_content_export.png b/docs/guide/img/full_content_export.png new file mode 100644 index 0000000000..d3a17159a6 Binary files /dev/null and b/docs/guide/img/full_content_export.png differ diff --git a/docs/guide/img/incremental_content_export.png b/docs/guide/img/incremental_content_export.png new file mode 100644 index 0000000000..451b318948 Binary files /dev/null and b/docs/guide/img/incremental_content_export.png differ diff --git a/docs/guide/img/locations_visibility.png b/docs/guide/img/locations_visibility.png index cfcd70c410..3d4985e54e 100644 Binary files a/docs/guide/img/locations_visibility.png and b/docs/guide/img/locations_visibility.png differ diff --git a/docs/guide/img/log_in_via_google.png b/docs/guide/img/log_in_via_google.png new file mode 100644 index 0000000000..7f3cc0e0df Binary files /dev/null and b/docs/guide/img/log_in_via_google.png differ diff --git a/docs/guide/img/lost_orders.png b/docs/guide/img/lost_orders.png deleted file mode 100644 index edda5f920e..0000000000 Binary files a/docs/guide/img/lost_orders.png and /dev/null differ diff --git a/docs/guide/img/lost_orders_2.png b/docs/guide/img/lost_orders_2.png deleted file mode 100644 index 0b2c0bc75c..0000000000 Binary files a/docs/guide/img/lost_orders_2.png and /dev/null differ diff --git a/docs/guide/img/message_instatiation_model.png b/docs/guide/img/message_instatiation_model.png deleted file mode 100644 index b52253ff30..0000000000 Binary files a/docs/guide/img/message_instatiation_model.png and /dev/null differ diff --git a/docs/guide/img/new_form.png b/docs/guide/img/new_form.png new file mode 100644 index 0000000000..e89d69cb16 Binary files /dev/null and b/docs/guide/img/new_form.png differ diff --git a/docs/guide/img/node_visibility_hide.png b/docs/guide/img/node_visibility_hide.png index 9e1dd28dfa..6ec88fa187 100644 Binary files a/docs/guide/img/node_visibility_hide.png and b/docs/guide/img/node_visibility_hide.png differ diff --git a/docs/guide/img/node_visibility_hide_invisible.png b/docs/guide/img/node_visibility_hide_invisible.png index 926e1827cc..9a2d238c1f 100644 Binary files a/docs/guide/img/node_visibility_hide_invisible.png and b/docs/guide/img/node_visibility_hide_invisible.png differ diff --git a/docs/guide/img/node_visibility_unhide1.png b/docs/guide/img/node_visibility_unhide1.png index 90c892d14e..8fb9eef135 100644 Binary files a/docs/guide/img/node_visibility_unhide1.png and b/docs/guide/img/node_visibility_unhide1.png differ diff --git a/docs/guide/img/node_visibility_unhide2.png b/docs/guide/img/node_visibility_unhide2.png index 7a4209fb30..83106bbe9b 100644 Binary files a/docs/guide/img/node_visibility_unhide2.png and b/docs/guide/img/node_visibility_unhide2.png differ diff --git a/docs/guide/img/orderhistory.png b/docs/guide/img/orderhistory.png new file mode 100644 index 0000000000..19d5924a14 Binary files /dev/null and b/docs/guide/img/orderhistory.png differ diff --git a/docs/guide/img/orderhistory_1.jpg b/docs/guide/img/orderhistory_1.jpg deleted file mode 100644 index 614cf69b7e..0000000000 Binary files a/docs/guide/img/orderhistory_1.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_2.jpg b/docs/guide/img/orderhistory_2.jpg deleted file mode 100644 index 342a152050..0000000000 Binary files a/docs/guide/img/orderhistory_2.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_3.jpg b/docs/guide/img/orderhistory_3.jpg deleted file mode 100644 index e7b0eb9dd3..0000000000 Binary files a/docs/guide/img/orderhistory_3.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_4.jpg b/docs/guide/img/orderhistory_4.jpg deleted file mode 100644 index 9f2ffb5bc2..0000000000 Binary files a/docs/guide/img/orderhistory_4.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_5.jpg b/docs/guide/img/orderhistory_5.jpg deleted file mode 100644 index 16bd0b93e6..0000000000 Binary files a/docs/guide/img/orderhistory_5.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_6.jpg b/docs/guide/img/orderhistory_6.jpg deleted file mode 100644 index ad641f8164..0000000000 Binary files a/docs/guide/img/orderhistory_6.jpg and /dev/null differ diff --git a/docs/guide/img/orderhistory_detail.png b/docs/guide/img/orderhistory_detail.png new file mode 100644 index 0000000000..e92e36ecb3 Binary files /dev/null and b/docs/guide/img/orderhistory_detail.png differ diff --git a/docs/guide/img/orderhistory_invoice.png b/docs/guide/img/orderhistory_invoice.png new file mode 100644 index 0000000000..e8e19b7392 Binary files /dev/null and b/docs/guide/img/orderhistory_invoice.png differ diff --git a/docs/guide/img/organising_templates.png b/docs/guide/img/organising_templates.png index fea8173c7d..c2083a5f27 100644 Binary files a/docs/guide/img/organising_templates.png and b/docs/guide/img/organising_templates.png differ diff --git a/docs/guide/img/page_block_help_message.png b/docs/guide/img/page_block_help_message.png new file mode 100644 index 0000000000..9bf85e38c5 Binary files /dev/null and b/docs/guide/img/page_block_help_message.png differ diff --git a/docs/guide/img/request_lifecycle_concept.png b/docs/guide/img/request_lifecycle_concept.png new file mode 100644 index 0000000000..7895c1246a Binary files /dev/null and b/docs/guide/img/request_lifecycle_concept.png differ diff --git a/docs/guide/img/request_lifecycle_events.png b/docs/guide/img/request_lifecycle_events.png new file mode 100644 index 0000000000..c2dbdb34da Binary files /dev/null and b/docs/guide/img/request_lifecycle_events.png differ diff --git a/docs/guide/img/schedule_block_example.png b/docs/guide/img/schedule_block_example.png deleted file mode 100644 index c511080798..0000000000 Binary files a/docs/guide/img/schedule_block_example.png and /dev/null differ diff --git a/docs/guide/img/search_19.png b/docs/guide/img/search_19.png deleted file mode 100644 index 01fb997775..0000000000 Binary files a/docs/guide/img/search_19.png and /dev/null differ diff --git a/docs/guide/img/search_20.png b/docs/guide/img/search_20.png deleted file mode 100644 index 195ff5a599..0000000000 Binary files a/docs/guide/img/search_20.png and /dev/null differ diff --git a/docs/guide/img/server_setup.png b/docs/guide/img/server_setup.png index 6083ea69a5..4d295e6074 100644 Binary files a/docs/guide/img/server_setup.png and b/docs/guide/img/server_setup.png differ diff --git a/docs/guide/img/shop_configuration_settings.png b/docs/guide/img/shop_configuration_settings.png new file mode 100644 index 0000000000..f7c729b3bf Binary files /dev/null and b/docs/guide/img/shop_configuration_settings.png differ diff --git a/docs/guide/img/signal_slots_diagram.png b/docs/guide/img/signal_slots_diagram.png new file mode 100644 index 0000000000..5a18958c3d Binary files /dev/null and b/docs/guide/img/signal_slots_diagram.png differ diff --git a/docs/guide/img/site_factory_icon.png b/docs/guide/img/site_factory_icon.png deleted file mode 100644 index c5d7812042..0000000000 Binary files a/docs/guide/img/site_factory_icon.png and /dev/null differ diff --git a/docs/guide/img/spi_cache.png b/docs/guide/img/spi_cache.png index 1e5f6145e2..be959de3d5 100644 Binary files a/docs/guide/img/spi_cache.png and b/docs/guide/img/spi_cache.png differ diff --git a/docs/guide/img/subtree_usability_notes_1.png b/docs/guide/img/subtree_usability_notes_1.png index 71106ce686..c3b8b03993 100644 Binary files a/docs/guide/img/subtree_usability_notes_1.png and b/docs/guide/img/subtree_usability_notes_1.png differ diff --git a/docs/guide/img/subtree_usability_notes_2.png b/docs/guide/img/subtree_usability_notes_2.png index fec0b18d5f..5920db8027 100644 Binary files a/docs/guide/img/subtree_usability_notes_2.png and b/docs/guide/img/subtree_usability_notes_2.png differ diff --git a/docs/guide/img/web_connector.png b/docs/guide/img/web_connector.png deleted file mode 100644 index a48c508372..0000000000 Binary files a/docs/guide/img/web_connector.png and /dev/null differ diff --git a/docs/guide/img/workflow_custom_diagram.png b/docs/guide/img/workflow_custom_diagram.png new file mode 100644 index 0000000000..6570bd458d Binary files /dev/null and b/docs/guide/img/workflow_custom_diagram.png differ diff --git a/docs/guide/img/workflow_notification.png b/docs/guide/img/workflow_notification.png index d71d362f02..ef9031cb12 100644 Binary files a/docs/guide/img/workflow_notification.png and b/docs/guide/img/workflow_notification.png differ diff --git a/docs/guide/img/workflow_panel.png b/docs/guide/img/workflow_panel.png deleted file mode 100644 index aee655141d..0000000000 Binary files a/docs/guide/img/workflow_panel.png and /dev/null differ diff --git a/docs/guide/img/workflow_reviewers.png b/docs/guide/img/workflow_reviewers.png deleted file mode 100644 index 8eb5ee9ae5..0000000000 Binary files a/docs/guide/img/workflow_reviewers.png and /dev/null differ diff --git a/docs/guide/internationalization.md b/docs/guide/internationalization.md index efbe4b3ea8..f037e55fac 100644 --- a/docs/guide/internationalization.md +++ b/docs/guide/internationalization.md @@ -1,3 +1,7 @@ +--- +description: You can create multiple language versions (translations) of content and serve different language versions of your site with the help of SiteAccesses. +--- + # Languages ## Language versions @@ -15,14 +19,14 @@ publishing the draft will not overwrite later modifications. ### Adding available languages -The multilanguage system operates based on a global translation list that contains all languages available in the installation. Languages can be [added to this list from the Admin Panel](https://doc.ezplatform.com/projects/userguide/en/latest/creating_content_advanced/#languages) in the Back Office. After adding a language be sure to dump all assets to the file system: +The multilanguage system operates based on a global translation list that contains all languages available in the installation. Languages can be [added to this list from the Admin Panel](https://doc.ibexa.co/projects/userguide/en/latest/translating_content/) in the Back Office. After adding a language be sure to dump all assets to the file system: ``` yarn encore <environment> -# OR php bin/console ezplatform:encore:compile +# OR php bin/console ibexa:encore:compile ``` -**The new language must then be added to the [SiteAccess](siteaccess.md) configuration**. Once this is done, any user with proper permissions can create Content item versions in these languages in the user interface. +**The new language must then be added to the [SiteAccess](multisite/multisite.md) configuration**. Once this is done, any user with proper permissions can create Content item versions in these languages in the user interface. ### Translatable and untranslatable Fields @@ -47,7 +51,7 @@ you can [use SiteAccesses](#using-siteaccesses-for-handling-translations). Depending on the URI used to access the website, a different site will open, with a language set in configuration settings. All Content items will then be displayed in this language. -For details, see [Multi-language SiteAccesses](multi_language_siteaccesses.md). +For details, see [Multi-language SiteAccesses](multisite/set_up_translation_siteaccess.md). ### Explicit translation SiteAccesses @@ -168,7 +172,7 @@ ezplatform: If the `translation_siteaccesses` setting is not provided, implicit *related SiteAccesses* will be used instead. SiteAccesses are considered *related* if they share: - The same Repository -- The same root `location_id` (see [Multisite](multisite.md)) +- The same root `location_id` (see [Multisite](multisite/multisite.md)) ### Fallback languages and missing translations diff --git a/docs/guide/job_system.md b/docs/guide/job_system.md deleted file mode 100644 index 6b89415b78..0000000000 --- a/docs/guide/job_system.md +++ /dev/null @@ -1,61 +0,0 @@ -# Job system [[% include 'snippets/commerce_badge.md' %]] - -The job system enables automating tasks implemented as a command. - -It is based on the [JMSJobQueueBundle.](https://github.com/schmittjoh/JMSJobQueueBundle) - -You can see an overview of the last jobs in the Back Office by clicking **Control Center** > **e-commerce jobs**. - -Expand a running job to see more details of its state. -You can see the output of the command. - -The list is automatically updated every 10 seconds. - -![](img/ecommerce_jobs.png) - -You can [add your own jobs](#configure-jobs-for-the-backend) to the menu. -The user can then click **Start** and the job is executed. - -## Starting jobs - -The job system requires executing the `jms-job-queue:run` command (see [Required crontab tasks](shop_configuration/required_crontab_tasks.md)). - -``` bash -php bin/console jms-job-queue:run --env=prod -``` - -A job can be started using the Back Office (and a configuration of tasks offered) or using the PHP API. - -See [bundle documentation](http://jmsyst.com/bundles/JMSJobQueueBundle) for more details about the PHP API. - -## Configuring jobs for the Back Office - -You can configure project-specific jobs which are displayed in the Back Office. - -You can define the Symfony command (parameter `command`) and the parameters. - -If your job requires additional tasks, you can add these tasks in the `depending_jobs` parameter. -These jobs are started after the main job has been executed successfully. - -``` yaml -parameters: - siso_control_center.default.add_jobs: - import_solr: - label: "Import from eZ" - desc: "Starts import from a remote system" - command: "silver:import" - params: - - tmp - depending_jobs: - index_solr: - label: "Index econtent tmp cores" - command: "silversolutions:indexecontent-job" -``` - -## Removing old jobs - -To remove old jobs, run the following command: - -``` bash -bin/console jms-job-queue:clean-up --env=prod --per-call=10 --max-retention="1 min" -``` diff --git a/docs/guide/limitation_reference.md b/docs/guide/limitation_reference.md index b75fb88537..7548b2a76e 100644 --- a/docs/guide/limitation_reference.md +++ b/docs/guide/limitation_reference.md @@ -1,3 +1,7 @@ +--- +description: Limitations let you fine-tune the permission system by specifying limits to Roles granted to users. +--- + # Limitation reference ## Blocking Limitation @@ -308,7 +312,7 @@ A Limitation to specify if the User has access to content within a specific Sect |------|------|------| |`<Session_id>`|`<Session_name>`|All valid session IDs can be set as value(s)| -## Segment Group Limitation +## Segment Group Limitation [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] A Limitation to specify whether the User has access Segments within a specific Segment Group. @@ -368,7 +372,27 @@ A Limitation to specify if the User has access to content within a specific Subt ### Usage notes -For more information on how to restrict User's access to part of the Subtree follow [the example in the Admin management section](admin_panel.md#restrict-editing-to-part-of-the-tree). +For more information on how to restrict User's access to part of the Subtree +follow [the example in the Admin management section](permission_use_cases.md#restrict-editing-to-part-of-the-tree). + +## Version Lock Limitation + +A Limitation to specify whether the User can perform actions, for example, edit +or unlock, on Content items that are in a workflow. + +| | | +|-----------------|------------------------------------------------------------------------------------------------| +| Identifier | `VersionLock` | +| Value Class | `Ibexa\Workflow\Security\Limitation\VersionLockLimitation.php` | +| Type Class | `Ibexa\Workflow\Security\Limitation\VersionLockLimitationType.php` | +| Role Limitation | yes | + +### Possible values + +| Value | UI value | Description | +|------|------|------| +| `userId` | "Assigned only" | Users can perform actions only on Content items that are assigned to them or not assigned to anybody. | +| `null` | "none" | Users can perform actions on all drafts, regardless of the assignments or whether drafts are locked or not. | ## Workflow Stage Limitation diff --git a/docs/guide/limitations.md b/docs/guide/limitations.md index 658a135431..3c9796e0b2 100644 --- a/docs/guide/limitations.md +++ b/docs/guide/limitations.md @@ -1,3 +1,7 @@ +--- +description: Control access to parts of the system by fine-tuning permissions with the use of Limitations. +--- + # Limitations Limitations are part of the permissions system. @@ -78,6 +82,7 @@ Beyond that the following Limitations are available: - [State](limitation_reference.md#state-limitation) - [Workflow Stage](limitation_reference.md#workflow-stage-limitation) - [Field Group](limitation_reference.md#field-group-limitation) +- [Version Lock](limitation_reference.md#version-lock-limitation) `content/publish`: @@ -134,6 +139,14 @@ Beyond that the following Limitations are available: - [Subtree of Location](limitation_reference.md#subtree-of-location-limitation) - [State](limitation_reference.md#state-limitation) +`content/unlock`: + +- [Owner](limitation_reference.md#owner-limitation) +- [Content Type Group](limitation_reference.md#content-type-group-limitation) +- [Subtree of Location](limitation_reference.md#subtree-of-location-limitation) +- [Language](limitation_reference.md#language-limitation) +- [Version Lock](limitation_reference.md#version-lock-limitation) + #### Section `section/assign`: diff --git a/docs/guide/logging/logfile_rotation.md b/docs/guide/logging/logfile_rotation.md new file mode 100644 index 0000000000..3fe2780aa4 --- /dev/null +++ b/docs/guide/logging/logfile_rotation.md @@ -0,0 +1,29 @@ +--- +description: Configure rotation of log files to minimize disk space usage. +--- + +# Logfile rotation + +You can set up automatic rotation of logfiles to avoid overfilling the disk space. + +The `logrotate` utility is a standard tool in Linux systems. It is run by `cron.daily` once a day at 6:25 am (on Ubuntu systems). + +To configure logfile rotation for shop logs, create a new configuration file in `/etc/logrotate.d/silver-eshop`: + +``` +<your-project>/var/log/prod.log <your-project>/var/log/silver.eshop.log <your-project>/var/log/prod-siso.eshop.erp.log { + su www-data www-data + daily + size 50M + rotate 30 + missingok + create 674 devel devel + compress +} +``` + +If the logfiles grow very quickly, you can run `logrotate` once per hour by putting it in `cron.hourly` (it will run at 17 minutes past every hour): + +``` bash +mv /etc/cron.daily/logrotate /etc/cron.hourly +``` diff --git a/docs/guide/logging/logging.md b/docs/guide/logging/logging.md index d8b479110b..e113c77075 100644 --- a/docs/guide/logging/logging.md +++ b/docs/guide/logging/logging.md @@ -1,15 +1,27 @@ -# Logging [[% include 'snippets/commerce_badge.md' %]] +--- +description: Ensure that your logs are secure and GDPR compliant by clearing them of sensitive user data. +--- -[[= product_name_com =]] uses [Monolog](https://github.com/Seldaek/monolog) to log the most important information. -The default implementation writes standard shop-related information into a single log file. -Emails and ERP communication are written into the database to make it easier to process them for administrative (HTML) presentation. +# Logging -## GDPR +[[= product_name =]] uses [Monolog](https://github.com/Seldaek/monolog) to log shop-related information. -Some logs can contain personal information which can be affected by the GDPR regulations. +By default, ERP messages and emails are stored in the database. +All other log entries are stored in `var/log/silver.eshop.log`. -By default, [[= product_name_com =]] does not log the User ID for logging search query: +All emails that are sent with `MailHelperService` are logged automatically. + +## Sensitive user data + +Some logs can contain personal information such as User ID or password. + +By default, [[= product_name =]] does not log User IDs. +You can change this behavior by modifying the following setting: ``` yaml siso_core.default.gdpr.store_user_id_in_logs: false ``` + +If the email text contains a password that should not be logged in the DB, you have to specify this password as a template parameter. + +`MailHelperService` replaces the template parameter `password` with `***`. diff --git a/docs/guide/logging/logging_api.md b/docs/guide/logging/logging_api.md deleted file mode 100644 index 0dc4e90ee7..0000000000 --- a/docs/guide/logging/logging_api.md +++ /dev/null @@ -1,169 +0,0 @@ -# Logging API [[% include 'snippets/commerce_badge.md' %]] - -Logging in [[= product_name_com =]] is based on Monolog. There is no separate logging API definition. -For the purpose of persisting logs within a database, -the Monolog API implementation has been extended by Doctrine-based classes and an interface. - -## Important service classes and interfaces of Monolog - -### Psr\Log\LoggerInterface - -`LoggerInterface` is always used as the service for logging. - -The list below contains the logging methods (in order of importance). -Use the `log()` method only if the log level of a message can only be determined at runtime. -For more information about the PSR logging standard, see [the official specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). - -``` php -public function emergency($message, array $context = array()); -public function alert($message, array $context = array()); -public function critical($message, array $context = array()); -public function error($message, array $context = array()); -public function warning($message, array $context = array()); -public function notice($message, array $context = array()); -public function info($message, array $context = array()); -public function debug($message, array $context = array()); -public function log($level, $message, array $context = array()); -``` - -### Monolog\Logger - -`Monolog\Logger` implements `LoggerInterface` but, for historical reasons, provides its own logging methods (which correspond to the interface, although are named differently). - -!!! note - - Although large parts of the shop rely on `Monolog\Logger` directly, it's recommended that all new implementations rely on the PSR `LoggerInterface` instead of explicitly referring to this class. - -## Base classes of Doctrine-based logging - -### LogRepositoryInterface - -To make database logging possible, a specific Doctrine entity class and its respective repository class must exist -for every type of log records (email log, ERP log). - -`LogRepositoryInterface` must be implemented by every repository class in order to work with the Doctrine handler. - -#### createNewLog() - -The `createNewLog()` method instantiates a new log record entity. -It takes the following parameters: - -|Parameter|Description| -|---|---| -|`$logRecord`|The record, as created by Doctrine loggers| -|`$persist`|If `true`, calls `save()` implicitly| - -#### save() - -The `save()` method persists the given log entity. -It takes the following parameters: - -|Parameter|Description| -|---|---| -|`$logEntity`|A log entity, created by `createNewLog()`| - -### AbstractOrmLogRepository - -This implementation of `LogRepositoryInterface` uses Doctrine's ORM API by deriving from `EntityRepository`. - -The class is abstract, but implements the complete `LogRepositoryInterface`. -It is intended to be extended for concrete entity implementations of `AbstractLog` and, therefore, must not be instantiated directly. - -It's recommended to extend this class for any ORM-based logging entities. - -### AbstractLog - -`AbstractLog` is the abstract class for all Log entities. - -It cannot be persisted by itself and MUST be derived by, for example, a fully-mapped Doctrine entity class. - -The attributes in the class must be considered in any DBMS mapping, as well. - -!!! note - - For any deriving class that uses Symfony's annotation-based configuration for object relational mapping, - the base attributes must be overridden and enriched with the respective ORM tags in their docblocks. - -## Monolog extension - -The classes that extend Monolog by Doctrine-based persistence do not provide a public API. -Instead, they implement the public API of Doctrine. - -### DoctrineFormatter - -`DoctrineFormatter` implements `Monolog\Formatter\FormatterInterface`. - -This is a Monolog formatter class for the Doctrine handler. -It serializes all non-scalar values, so they can be stored in database fields. - -Service definition: - -``` xml -<!-- Arguments are define here with their default values. They could be omitted --> -<service id="siso_module.logging_formatter.doctrine" class="Siso\Bundle\ToolsBundle\Service\Logging\DoctrineFormatter"> - <argument type="string">8</argument> <!-- Nesting level for recursion --> - <argument type="string">php</argument> <!-- Serialize format (php|json) --> - <argument type="string">true</argument> <!-- Convert exception traces to string instead of array --> -</service> -``` - -### DoctrineHandler - -`DoctrineHandler` extends `Monolog\Handler\AbstractProcessingHandler`. - -It is a Monolog handler class that writes log records into Doctrine entities. - -Service definition: - -``` xml -<!-- A doctrine handler must be assigned to a specific entity class and it's repository --> -<service id="siso_module.log_type.logging_handler.doctrine" class="Siso\Bundle\ToolsBundle\Service\Logging\DoctrineHandler"> - <argument type="service" id="siso_module.log_type.logging_repository.doctrine" /> <!-- The service ID of the repository class --> - <call method="setFormatter"> - <argument type="service" id="siso_module.logging_formatter.doctrine"/> <!-- The service id of the previously defined DoctrineFormatter --> - </call> -</service> -``` - -Handler injection: - -``` xml -<service id="siso_module.log_type.logger" class="%monolog.logger.class%"> - <argument>log_channel</argument> - <!-- Handlers are injected with a method call --> - <call method="pushHandler"> - <argument type="service" id="siso_module.log_type.logging_handler.doctrine"/> - </call> -</service> -``` - -### RequestDataProcessor - -`RequestDataProcessor` adds the request, session and user ID to log messages. - -Service definition: - -``` xml -<service id="siso_tools.logging_processor.request_data" class="%siso_tools.logging_processor.request_data.class%"> - <argument type="service" id="session" /> - <argument type="service" id="ezpublish.api.repository" /> -</service> -``` - -Processor injection: - -``` xml -<service id="siso_module.log_type.logger" class="%monolog.logger.class%"> - <argument>log_channel</argument> - <call method="pushHandler"> - <argument type="service" id="siso_module.log_type.logging_handler.doctrine"/> - </call> - <!-- Processors are injected with a method call --> - <call method="pushProcessor"> - <argument type="collection"> <!-- Processors must be injected as a callback (array(Object, 'methodName')) --> - <argument type="service" id="siso_tools.logging_processor.request_data" /> - <argument type="string">processRecord</argument> - </argument> - </call> -</service> -``` diff --git a/docs/guide/logging/logging_faq.md b/docs/guide/logging/logging_faq.md deleted file mode 100644 index 786171e751..0000000000 --- a/docs/guide/logging/logging_faq.md +++ /dev/null @@ -1,52 +0,0 @@ -# Logging FAQ [[% include 'snippets/commerce_badge.md' %]] - -## Which log entries are stored in the database? - -By default, ERP messages and emails are stored in the database. - -All other log entries are stored in the .log file. - -## `log` database tables cannot be found - -If you get an error that `log` database tables cannot be found, update your database schema: - -``` bash -php bin/console doctrine:schema:update [--dump-sql|--force] -``` - -## How do I invoke a simple log instruction? - -The ID for the standard logging is `silver_common.logger`. This is an instance of `Monolog\Logger`. -You can write a simple debug log in a container action in the following way: - -``` -$logger = $this->get('silver_common.logger'); -$additionalContext = array('exception' => $ex); -$logger->debug('My log message', $additionalContext); -``` - -## How do I log a newly created ERP message? - -All transmitted messages are logged automatically, because the mandatory transport layer automatically logs all messages. -But if a new transport is implemented (other than `WebConnectorMessageTransport`), -this implementation has to take care of logging the measuring point on a lower level. -See [Measuring Points in the ERP logging documentation](../erp_integration/erp_communication/erp_logging.md#logging-architecture-measuring-points) -for more information. - -## How do I log an email that is sent by the shop? - -All emails that are sent with [`MailHelperService`](../../api/commerce_api/helper_services/mailhelperservice.md) are logged automatically. - -## How do I avoid logging the password in the database? - -If the email contains a password that should not be logged in the DB, you have to specify this password as a template parameter. - -[`MailHelperService`](../../api/commerce_api/helper_services/mailhelperservice.md) replaces the template parameter `password` with `***`. - -## Where can I find the log file? - -The standard log file is located in `var/logs/silver.eshop.log` - -More precisely, Monolog's [StreamHandler](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#log-to-files-and-syslog) writes logs into files. -The path to the file is a [constructor parameter](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/StreamHandler.php#L33) of the StreamHandler. -For an example of service configuration, see [ERP Logging](../erp_integration/erp_communication/erp_logging.md#configuration). diff --git a/docs/guide/logging/tokencontroller_logging.md b/docs/guide/logging/tokencontroller_logging.md deleted file mode 100644 index 7609694d49..0000000000 --- a/docs/guide/logging/tokencontroller_logging.md +++ /dev/null @@ -1,54 +0,0 @@ -# TokenController logging [[% include 'snippets/commerce_badge.md' %]] - -If a token is processed with the [TokenController](../user_management/token.md), -it is logged in a file or database. By default, the configuration uses database logging: - -`ses_gdpr_log`: - -``` -mysql> select * from ses_gdpr_log; - -+----+---------------------+-------------------+-----------+---------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+ - -| id | log_timestamp | log_channel | log_level | log_message | log_mail | user_id | - -+----+---------------------+-------------------+-----------+---------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+ - -| 3 | 2019-02-12 16:05:08 | silver_eshop_gdpr | 600 | Called token method: Silversolutions\Bundle\EshopBundle\Services\Token\TokenServiceMethodProcessorService::enableEzUser() | dam@silversolutions.de | 381 | - -+----+---------------------+-------------------+-----------+---------------------------------------------------------------------------------------------------------------------------+--------------------------+---------+ -``` - -## Configuration - -You activate the file and database logging by adding the `pushHandler()` method call with the `gdpr_log_handler.doctrine` and/or `gdpr_log_handler.file` service as an argument: - -`src/Silversolutions/Bundle/EshopBundle/Resources/config/ses_services.xml`: - -``` xml -<service id="siso_core.gdpr_log_repository.doctrine" class="%siso_core.gdpr_log_repository.doctrine.class%"> - <factory service="Doctrine\Common\Persistence\ObjectManager" method="getRepository" /> - <argument type="string">SilversolutionsEshopBundle:GdprLog</argument> -</service> -<service id="siso_core.gdpr_log_handler.doctrine" class="%siso_tools.logging_handler.doctrine.class%"> - <call method="setLogRepository"> - <argument type="service" id="siso_core.gdpr_log_repository.doctrine" /> - </call> - <call method="setFormatter"> - <argument type="service" id="siso_tools.logging_formatter.doctrine"/> - </call> -</service> -<service id="siso_core.gdpr_log_handler.file" class="Monolog\Handler\StreamHandler"> - <argument type="string">%kernel.logs_dir%/%kernel.environment%-siso.eshop.gdpr.log</argument> -</service> -<service id="siso_core.gdpr_logger" class="Symfony\Bridge\Monolog\Logger"> - <argument type="string">silver_eshop_gdpr</argument> <!-- The logging channel --> - <call method="pushHandler"> - <argument type="service" id="siso_core.gdpr_log_handler.doctrine" /> - </call> - <!-- File logging is deactivated by default --> - <!--<call method="pushHandler"> - <argument type="service" id="siso_core.gdpr_log_handler.file" /> - </call>--> -</service> -``` diff --git a/docs/guide/multi_language_siteaccesses.md b/docs/guide/multi_language_siteaccesses.md deleted file mode 100644 index 2195f9159c..0000000000 --- a/docs/guide/multi_language_siteaccesses.md +++ /dev/null @@ -1,150 +0,0 @@ -# Multi-language SiteAccesses - -To combine translated content with multiple SiteAccesses, perform the following steps: - -1. [Create your translations](#create-a-new-translation) in the database by using the Back Office. -1. [Create at least two SiteAccesses](#create-new-siteaccesses) in `ezplatform.yaml` to deliver the right translated content at the right time. -1. [Set the correct permissions](#set-permissions-for-the-new-siteaccesses) for an anonymous user to read each SiteAccess. - -Without these three steps, your SiteAccess configuration will either not work or you will be left with duplicate content from an SEO perspective. - -## Create a new translation - -By creating a new translation, you indicate that a Content item has other language versions. -A newly defined translation can then be used according to the SiteAccess configuration. -For more details on language versions, see [Languages](internationalization.md). - -1. Log in to the Back Office. -1. In the **Admin** Panel, open the **Languages** tab. -1. Click **Create a new language** and follow the on-screen prompts. For the purpose of this procedure, configure `fre-FR` as a new language. -1. After saving the new language, refresh the assets by running: - -```bash -yarn encore <prod|dev> -#OR -php bin/console ezplatform:encore:compile -``` - -Reload the Back Office. -Now you can start translating content. - -## Create new SiteAccesses - -Now you can configure SiteAccess to match it with the newly-configured translation. -For more details, see [SiteAccess](siteaccess.md). - -The most typical setup for a site with translated content is to map the base of the domain to one language -and use the first segment of the URI to match to another. - -For example: - -- `www.mysite.com` to match to English -- `www.mysite.com/fr` to match to French - -To achieve this you need to create at least two new SiteAccesses in your `config/packages/ezplatform.yaml` file. - -The first bit of this working example lists the new SiteAccesses: `en` and `fr` and adds them both to a common `site_group` (line 8). -This group will be used for sharing settings such as API keys, cache locations, etc. - -``` yaml hl_lines="8" -siteaccess: - default_siteaccess: site - list: - - site - - en - - fr - groups: - site_group: - - site - - en - - fr -``` - -Below, in the second section of the SiteAccess block, you declare what matches to which SiteAccess. -In the example you have two matches. The simplest is `Map\Host` — when the host is `www.mysite.com`, the match is `en` (lines 11-12). -When the host and URI both match, you hit the `fr` SiteAccess, i.e., when the URI is `/fr` and the host is `www.mysite.com`. -For a full list of available matchers, see [SiteAccess matching](../guide/siteaccess_matching.md). - -``` yaml hl_lines="11 12" -siteaccess: - default_siteaccess: site - # ... - match: - Compound\LogicalAnd: - frLogicalAndMatch: - matchers: - Map\URI: { fr: true } - Map\Host: { www.mysite.com: true } - match: fr - Map\Host: - www.mysite.com: en -``` - -!!! note - - For dynamic URLs, you can replace `www.mysite.com` with `%site_domain%` - and then enter `site_domain` as a new entry in `services.yaml` at the same level as the database settings. - -Further down in `ezplatform.yaml` is the `system` section which comes with the default group named `site_group` (the same group that you modified earlier). -Add the new `translation_siteaccesses` here. After the `site_group`, you register the SiteAccess languages: - -``` yaml - system: - site_group: - api_keys: { google_maps: yourapikey } - cache_pool_name: '%cache_pool%' - var_dir: var/site - translation_siteaccesses: [fr, en] - fr: - languages: [fre-FR, eng-GB] - en: - languages: [eng-GB] - default: - content: - # templates common to both the en and fr SiteAccess -``` - -The `ezplatform.yaml` file with SiteAccesses should now be configured. -Clear the cache by running: `php bin/console cache:clear`. - -## Set permissions for the new SiteAccesses - -Now allow the Anonymous user Role to read content on the new SiteAccesses: - -1. Log in to the Back Office. -1. In the **Admin** Panel, open the **Roles** tab. -1. Click the **Anonymous** role. -1. Edit the limitations of the module `user`. -1. You should be able to see three SiteAccesses in a multi-select. Select them all and click **Update**. -1. Clear the cache by running: `php bin/console cache:clear`. - -You should now be able to reload your site in the `en` and `fr` SiteAccess. - -## Replace the `site` SiteAccess - -[[= product_name =]] ships with a pre-configured SiteAccess named `site`. -As you have now successfully introduced two new SiteAccesses, remove the `site` SiteAccess as it is no longer required. -It was not possible to remove `site` before, as you first needed to give the appropriate permissions to the new SiteAccesses (`en` and `fr`), -without which your site would not have loaded correctly. - -In `ezplatform.yaml`, set the `default_siteaccess` to `en`. -This will act as the [fallback](../guide/siteaccess/#multilanguage-sites) in case none of the matches have been hit. - -Lastly, remove `site` from the `list` and `groups` section: -The final version of the `ezplatform.yaml` should look like this: - -``` yaml -siteaccess: - default_siteaccess: en - list: - - en - - fr - groups: - site_group: - - en - - fr -``` - -Clear the cache by running: `php bin/console cache:clear`. - -Now you should be able to load your [[= product_name =]] site using the `en` and `fr` SiteAccess to display content in English and French. diff --git a/docs/guide/multisite.md b/docs/guide/multisite.md deleted file mode 100644 index a74a3bb7bc..0000000000 --- a/docs/guide/multisite.md +++ /dev/null @@ -1,186 +0,0 @@ -# Multisite - -## Introduction - -You can serve multiple different sites using one [[= product_name =]] instance and database. - -Each site will have its own content root, at a lower level than the default one (Location ID 2). -You can use one global back office for all sites, or a separate back office for each site. - -This feature is a reimplementation of the [PathPrefix](http://doc.ez.no/eZ-Publish/Technical-manual/4.x/Reference/Configuration-files/site.ini/SiteAccessSettings/PathPrefix) that existed in eZ Publish Legacy. - -## Configuring multisite - -Multisite is configured in `ezplatform.yaml`, either at [SiteAccess](siteaccess.md) or SiteAccess group level: - -``` yaml -ezplatform: - system: - site_group: - content: - tree_root: - # Root locationId. Default is top locationId - location_id: 123 - # All URL aliases starting with those prefixes will be considered - # being outside of the subtree starting at root_location. - # Default value is an empty array. - # Prefixes are not case sensitive. - excluded_uri_prefixes: [ /media, /images ] -``` - -### Configuration parameters - -The two configuration parameters, `location_id` and `excluded_uri_prefixes` are taken into account in several places, -for example in URL alias lookups for incoming web requests, and [URL alias path generation in Twig templates](templates.md#adding-links). -In addition you need to consider them when generating site paths in your layout, or other places rendering site/tree structure. - -#### `location_id` - -`content.tree_root.location_id` sets the Location ID of the content root of your site -and nothing above this level will be accessible by default. -This parameter can be filtered using the `excluded_uri_prefixes` parameter. - -#### `excluded_uri_prefixes` - -`content.tree_root.excluded_uri_prefixes` defines which URIs ignore the root limit set using `location_id`. -Add as many Location pathStrings as you like. -In the example above, the Media and Images folders will be accessible using their own URI even though they are not below `content.tree_root.location_id`. - -!!! note - - Leading slashes (`/`) are automatically trimmed internally, so they can be ignored. - -## Configuration example - -To see how multisite can be used, let's look at an example of two sites using the same [[= product_name =]] instance: a general company site and a site for a specific event. -Separate SiteAccesses are set up for the two sites: - -``` yaml -ezplatform: - siteaccess: - list: [site, event] - groups: - site_group: [site, event] - default_siteaccess: site - match: - URIElement: 1 -``` - -This is your content structure: - -![Content structure](img/config_content_structure.png "Content structure") - -You can now set the root level for `event` to only access the "Event" Location and its sub-items: - -``` yaml -ezplatform: - system: - event: - content: - tree_root: - # LocationId of "Event" - location_id: 57 -``` - -Thanks to this config, you can access `<your site>/event/Articles/Article2`, but not `<your site>/General/Articles/Article1`. - -In the next step, you need to reuse some content between sites, for example "Logos" from "Media". -You can allow the event site to access them, even though they are in a different part of the tree, via `excluded_uri_prefixes`: - -``` yaml - event: - content: - tree_root: - location_id: 57 - excluded_uri_prefixes: [ /media/images/logos ] -``` - -At this point, using the `event` SiteAccess, you gain access to `<your site>/event/Media/Images/Logos`, -despite the fact that it is not a sub-item of the "Event" Location. - -### Setting the Index Page - -The Index Page is the page shown when the root index `/` is accessed. -You can configure the Index Page separately for each SiteAccess. Place the parameter `index_page` in your `ezplatform.yaml` file, under the correct SiteAccess: - -``` yaml -ezplatform: - system: - event: - # The page to show when accessing IndexPage (/) - index_page: /EventFrontPage -``` - -If not specified, the `index_page` is the configured content root. - -## Multisite with multiple Repositories - -Multisite can be configured to use one or multiple Repositories. -With multiple Repositories, each site can have a separate Repository. -To configure multisite with multiple Repositories, [configure the repositories](config_repository.md) and perform [multi-Repository setup](persistence_cache.md#multi-repository-setup). - -### Limitations when using with multisite URI matching with multi-Repository setup - -!!! caution - - Only one Repository (database) can be used per domain. - This does not prohibit using [different Repositories](persistence_cache.md#multi-repository-setup) on different subdomains. - However, when using URI matching for multisite setup, all SiteAccesses sharing domain also need to share Repository. - For example: - - - `ibexa.co` domain can use `ibexa_repo` - - `doc.ibexa.co` domain can use `doc_repo` - - But the following configuration would be invalid: - - - `ibexa.co` domain can use `ibexa_repo` - - `ibexa.co/doc` **cannot** use `doc_repo`, as it is under the same domain. - - Invalid configuration will cause problems for different parts of the system, - for example back-end UI, REST interface and other non-SiteAccess-aware Symfony routes - such as `/_fos_user_context_hash` used by [HTTP cache](http_cache.md). - -## Different designs for multiple sites - -[[= product_name =]] does not apply a [Legacy template fallback](https://doc.ez.no/display/EZP/Legacy+template+fallback) like eZ Publish did. -You can, however, have different designs in your multisite installation if you organize the view configuration with the use of SiteAccesses. - -Looking back at the [previous example](#configuration-example), you can apply different designs to the two sites, but use some common templates. -To do this, organize your templates in the following folder structure: - -![Organising templates](img/organising_templates.png "Organising templates") - -Now you can use this view configuration (stored e.g. in a `views.yaml` file): - -``` yaml -ezplatform: - system: - default: - pagelayout: pagelayout.html.twig - content_view: - full: - article: - template: full/article.html.twig - match: - Identifier\ContentType: [article] - news: - template: full/news.html.twig - match: - Identifier\ContentType: [news] - event: - pagelayout: event/pagelayout.html.twig - content_view: - full: - article: - template: event/full/article.html.twig - match: - Identifier\ContentType: [article] -``` - -This config defines views that will be used for the `event` SiteAccess: -a separate `event/pagelayout.html.twig` and a template to be used for articles. -When no view is defined under `event`, such as in the case of the `news` Content Type, -the template defined under `default` will apply. `default` will also be used for all SiteAccesses other than `event`. - -To load the base (default) layout in templates you now need to use `{% extends no_layout == true ? view_base_layout : page_layout %}`. -(See [Template inheritance and sub-requests](content_rendering.md#template-inheritance-and-sub-requests) for more information). diff --git a/docs/guide/multisite/injecting_siteaccess.md b/docs/guide/multisite/injecting_siteaccess.md new file mode 100644 index 0000000000..96311386c0 --- /dev/null +++ b/docs/guide/multisite/injecting_siteaccess.md @@ -0,0 +1,43 @@ +--- +description: Inject the SiteAccess service to get SiteAccess information in your custom PHP code. +--- + +# Injecting SiteAccess + +The [service container](../../api/public_php_api.md#service-container) exposes the SiteAccess through the `@ezpublish.siteaccess_service` service, which fulfills the `\eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface` contract. +This means you can inject it into any custom service constructor, type hinting that contract. +You can get the current SiteAccess from that service by calling the `SiteAccessServiceInterface::getCurrent` method. + +For example, define a service which depends on the Repository's ContentService and the SiteAccessService. + +``` yaml +services: + App\MyService: + arguments: ['@ezpublish.siteaccess_service'] +``` + +``` php +declare(strict_types=1); + +namespace App; + +use eZ\Publish\API\Repository\ContentService; +use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface; + +class MyService +{ + /** @var \eZ\Publish\API\Repository\ContentService */ + private $contentService; + + /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface */ + private $siteAccessService; + + public function __construct( + SiteAccessServiceInterface $siteAccessService, + ContentService $contentService + ) { + $this->siteAccessService = $siteAccessService; + $this->contentService = $contentService; + } +} +``` diff --git a/docs/guide/multisite/multisite.md b/docs/guide/multisite/multisite.md new file mode 100644 index 0000000000..e981029d86 --- /dev/null +++ b/docs/guide/multisite/multisite.md @@ -0,0 +1,33 @@ +--- +description: Multisite enables hosting multiple websites with different content, templates and configuration using one Repository. +--- + +# Multisite + +Multisite configuration is done using SiteAccesses. + +A SiteAccess is a set of configuration settings that the application uses when you access the site through a specific address. +When the user visits the site, the system analyzes the URI and compares it to rules specified in the configuration. +If it finds a set of fitting rules, this SiteAccess is used. + +You can create a multisite setup with SiteAccesses covering different use cases, for example: + +- [different language versions of the webpage](set_up_translation_siteaccess.md) +- [a special sub-site for a campaign or event](set_up_campaign_siteaccess.md) +- additional [Back Office](multisite_configuration.md#admin-siteaccess) + +A multisite set-up enables you to configure different things per SiteAccess, for example: + +- [templates and designs](../content_rendering/design_engine/design_engine.md) +- [languages](set_up_translation_siteaccess.md) +- [tree roots](multisite_configuration.md#location-tree) +- [repositories](../configuration/config_repository.md) and [cache strategies](../persistence_cache.md#multi-repository-setup) + +Many other settings in the application are also configured per SiteAccess (also known as "SiteAccess-aware"). + +!!! tip + + When possible, always use semantic (SiteAccess-aware) configuration. + Manually editing internal settings is possible, but at your own risk, as unexpected behavior can occur. + +To quickly set up new sites with predefined site templates, use [Site Factory](site_factory.md). diff --git a/docs/guide/multisite/multisite_configuration.md b/docs/guide/multisite/multisite_configuration.md new file mode 100644 index 0000000000..1315970b76 --- /dev/null +++ b/docs/guide/multisite/multisite_configuration.md @@ -0,0 +1,207 @@ +--- +description: Configure SiteAccesses to serve different content in different layouts. +--- + +# Multisite configuration + +You can configure the available SiteAccesses under the `ezplatform.siteaccess` key. + +## SiteAccess configuration + +``` yaml +ezplatform: + siteaccess: + list: [site, event] + groups: + site_group: [site] + event_group: [event] + default_siteaccess: site + match: + URIElement: 1 +``` + +### SiteAccess groups + +`ezplatform.siteaccess.groups` defines which groups SiteAccesses belong to. + +``` yaml +ezplatform: + siteaccess: + groups: + site_group: [site] + event_group: [event] +``` + +Groups are useful when you want to use common settings for several SiteAccesses and avoid duplicating configuration. +SiteAccess groups act like regular SiteAccesses as far as configuration is concerned. +A SiteAccess can be part of several groups. SiteAccess configuration has always precedence over group configuration. + +#### `admin` SiteAccess + +The predefined `admin` SiteAccess in `admin_group` (configured in `config/packages/ezplatform_admin_ui.yaml`) serves the Back Office. +Do not remove this group. +If you need a multisite setup with multiple Back Offices, add any additional administration SiteAccesses to `admin_group`. + +In cases where the sites are on separate databases, each needs its own [repository](../configuration/config_repository.md) +(including their own storage and search connection), var dir, [cache pool](../persistence_cache.md#persistence-cache-configuration), +and ideally also separate Varnish/Fastly configuration. + +!!! caution + + Different SiteAccesses can only have different `var_dir` if they also have different repositories. + +### Default SiteAccess + +The `default_siteaccess` setting identifies which SiteAccess is used by default when no other SiteAccess matches. + +``` yaml +ezplatform: + siteaccess: + default_siteaccess: site +``` + +### SiteAccess matching + +The `match` setting defines the rule or set of rules by which SiteAccesses are matched. +See [SiteAccess matching](siteaccess_matching.md) for more information. + +``` yaml +ezplatform: + siteaccess: + match: + URIElement: 1 +``` + +### SiteAccess name + +To create a better editorial experience, you can replace the SiteAccess code in the Back Office +with a human-readable name of the website, for example `Company site` or `Summer Sale`. + +You can also translate SiteAccess names. Displayed names depend on the current Back Office language. + +To define translations or SiteAccess names, place them in YAML file with correct language code, +for example `translations/ezplatform_siteaccess.en.yaml`: + +``` yaml +en: Company site +fr: Company site France +``` + +## Scope + +All SiteAccess-aware configuration is resolved depending on scope. + +The available scopes are: + +1. `global` +2. SiteAccess +3. SiteAccess group +4. `default` + +`global` overrides all other scopes. +If `global` is not defined, the configuration then tries to match a SiteAccess, and then a SiteAccess group. +Finally, if no other scope is matched, `default` is applied. + +In short: if you want a match that always applies, regardless of SiteAccesses, use `global`. +To define a fallback, use `default`. + +``` yaml +ezplatform: + system: + global: + # If set, this value is used regardless of any other configuration + site: + # This is used for the 'site' SiteAccess + site_group: + # This is overwritten by the SiteAccess above, since the SiteAccess has precedence + default: + # This value is only used if there is no setting for global scope, SiteAccess or SiteAccess group +``` + +`global` and `default` scopes include the `admin` SiteAccess, which is responsible for the Back Office. +For example, the following configuration defines both the front template for articles +and the template used in the Back Office, unless you configure other templates for a specific SiteAccess or SiteAccess group: + +``` yaml +ezplatform: + system: + default: + content_view: + full: + article: + template: full/article.html.twig + match: + Identifier\ContentType: [article] +``` + +### SiteAccesses and Page Builder [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +To define which SiteAccesses are available in the submenu in Page Builder, use the following configuration: + +``` yaml +ezplatform: + system: + admin: + page_builder: + siteaccess_list: [site, de, fr, no] + de: + page_builder: + siteaccess_list: [site, de] +``` + +If you are using multiple domains, list all domains for an admin SiteAccess under `siteaccess_hosts`: + +``` yaml +ezplatform: + system: + admin: + page_builder: + siteaccess_list: [site, de, fr, no] + siteaccess_hosts: + - my_domain.com + - another_domain.org +``` + +!!! caution "SiteAccess with separate admin domain" + + If an admin SiteAccess in your installation uses a different domain than the front SiteAccesses, + be sure to use SSL (https protocol). + Otherwise, you cannot preview content in Page Builder from the Back Office. + +#### SiteAccess switching in Page Builder + +If you need to change between SiteAccesses in Site mode, do not use any functions in the page itself (for example, a language switcher). +This may cause unexpected errors. Instead, switch between SiteAccesses using the SiteAccess bar above the page. + +## Location tree + +You can restrict SiteAccesses to different parts of the content tree. +When you do it, only the selected Location and its descendants are reachable from this SiteAccess. + +Configure this under `ezplatform.systems.<scope>.content.tree_root`, for example: + +``` yaml +ezplatform: + system: + <scope>: + content: + tree_root: + location_id: 42 + excluded_uri_prefixes: [/media, /images] + index_page: /EventFrontPage +``` + +- `location_id` defines the Location ID of the content root for the SiteAccess. +- `excluded_uri_prefixes` defines which URIs ignore the root limit set using `location_id`. +In the example above, the Media and Images folders are accessible using their own URI, +even though they are outside the Location provided in `content.tree_root.location_id`. +- `index_page` is the page shown when you access the root index `/`. + +!!! note + + Prefixes are not case sensitive. + Leading slashes (`/`) are automatically trimmed internally, so they can be ignored. + +!!! tip + + For an example of a multisite configuration, see [Set up campaign SiteAccess](set_up_campaign_siteaccess.md). diff --git a/docs/guide/multisite/set_up_campaign_siteaccess.md b/docs/guide/multisite/set_up_campaign_siteaccess.md new file mode 100644 index 0000000000..67f8ebccc2 --- /dev/null +++ b/docs/guide/multisite/set_up_campaign_siteaccess.md @@ -0,0 +1,73 @@ +--- +description: Create a special SiteAccess to host a campaign site with different content subtree. +--- + +# Set up campaign SiteAccess + +The following example shows how to set up a special `campaign` SiteAccess. +This SiteAccess serves a site devoted to a special campaign, separate from the main company website (`site` SiteAccess). + +The `campaign` site uses a different part of the content tree than the main site, but shares some media files with it. + +## Configure SiteAccesses + +First, in the SiteAccess configuration in `config/packages/ezplatform.yaml`, +add the `campaign` SiteAccess to the list under `ezplatform.siteaccess`: + +``` yaml +ezplatform: + siteaccess: + list: [site, campaign] + groups: + site_group: [site, campaign] + default_siteaccess: site + match: + Map\URI: + summer-sale: campaign + site: site +``` + +The `match` setting ensures that when a visitor accesses the `<yourdomain>/summer-sale` URI, +they see the `campaign` SiteAccess. + +## Set root folder + +Next, with the following content structure, you need to separate the "Campaign" folder as root for the new site: + +![Content structure](../img/config_content_structure.png "Content structure") + +To do it, set the root level for `campaign` to access the "Campaign" Location and its sub-items only: + +``` yaml +ezplatform: + system: + campaign: + content: + tree_root: + # LocationId of "Campaign" + location_id: 57 +``` + +Thanks to this configuration, you can access `<your site>/campaign/Articles/Article2`, +but not `<your site>/campaign/General/Articles/Article1`. + +## Reuse content + +Finally, reuse some content between sites, for example "Logos" from "Images/Media". +You can allow the `campaign` site to access them, even though they are in a different part of the tree, via `excluded_uri_prefixes`: + +``` yaml +ezplatform: + system: + campaign: + content: + tree_root: + location_id: 57 + excluded_uri_prefixes: [ /media/images/logos ] +``` + +Now, when you use the `campaign` SiteAccess, you can reach `<your site>/campaign/Media/Images/Logos`, +despite the fact that it is not a sub-item of the "Campaign" Location. + +As a next step, you can configure different [designs](../content_rendering/design_engine/design_engine.md) +for the two SiteAccesses. diff --git a/docs/guide/multisite/set_up_translation_siteaccess.md b/docs/guide/multisite/set_up_translation_siteaccess.md new file mode 100644 index 0000000000..449c771874 --- /dev/null +++ b/docs/guide/multisite/set_up_translation_siteaccess.md @@ -0,0 +1,81 @@ +--- +description: Set up SiteAccesses to hold different language versions of a site. +--- + +# Set up translation SiteAccess + +One of common uses for multisite installations is serving different language versions of a website. +To do this, set up multiple SiteAccesses, each corresponding to one language. +Proper configuration means avoiding duplicate content that could affect SEO. + +## Add a language + +First, add a new language for the whole installation. + +!!! tip + + For more details, see [Languages](../internationalization.md). + +1. In the Back Office **Admin** section, click the **Languages** tab. +1. Click **Create a new language** and provide the language name and code (examples below use French with `fre-FR`). +1. After creating the new language, refresh the assets by running: + +```bash +yarn encore <prod|dev> +``` + +## Configure SiteAccesses + +Next, configure a new SiteAccess to match the newly-configured language. + +The most typical setup for a site with translated content is to map the base of the domain to one language +and use the first segment of the URI to match to translations. + +For example: + +- `www.mysite.com` for English site +- `www.mysite.com/fr` for French site + +To achieve this you need to create a new SiteAccess in the `config/packages/ezplatform.yaml` file. +Add the `fr` SiteAccess to list of all SiteAccesses and it to the common `site_group`. +This group is used for sharing settings such as API keys, cache locations and so on. + +``` yaml +siteaccess: + list: [site, fr] + groups: + site_group: [site, fr] +``` + +Under the `system` key, add the new SiteAccess. +Indicate that they are meant for translations under `site_group.translation_siteaccesses`: + +``` yaml + system: + site_group: + # ... + translation_siteaccesses: [fr] + fr: + languages: [fre-FR, eng-GB] + site: + languages: [eng-GB] +``` + +With this configuration, the main English site displays content in English and ignores French content. +The French site displays content in French, but also in English, if it does not exist in French. + +Clear the cache by running: `php bin/console cache:clear`. + +## Set permissions + +By default, the Anonymous user Role does not have permissions for new SiteAccesses. +As a next step, allow Anonymous users to read content on the new SiteAccesses: + +1. In the Back Office **Admin** section, click the **Roles** tab. +1. Click the **Anonymous** role. +1. Edit the **Limitations** of the module `user`, select both SiteAccesses and click **Update**. +1. Clear the cache by running: `php bin/console cache:clear`. + +You can now start translating content. +When you reload the site, access a translated Content item through both SiteAccesses to see the difference, +for example: `<yourdomain>/<article-name>` and `<yourdomain>/fr/<article-name>`. diff --git a/docs/guide/multisite/site_factory.md b/docs/guide/multisite/site_factory.md new file mode 100644 index 0000000000..b464691d76 --- /dev/null +++ b/docs/guide/multisite/site_factory.md @@ -0,0 +1,223 @@ +--- +description: Site Factory allows creating multiple sites (SiteAccesses) from the Back Office. +edition: experience +--- + +# Site Factory + +Site Factory is a site management interface, integrated with the Back Office. +It enables you to configure new sites without editing [YAML-based SiteAccess configuration](multisite_configuration.md). + +!!! note + + A SiteAccess that you define for a site by following the [configuration](multisite_configuration.md) + is always treated with higher priority than a SiteAccess created by using the Site Factory. + For example, if you define a French site within a YAML file, + and then create a site that uses the `fr` path in Site Factory, matchers ignore the second site. + +Site Factory is disabled by default after installation. + +If you plan to use Site Factory, you need to enable and configure it. +To enable or disable Site Factory, follow respectively: + +- [Enable Site Factory section](#enable-site-factory) +- [Disable Site Factory section](#disable-site-factory) + +## Enable Site Factory + +To enable Site Factory, set `ez_platform_site_factory.enabled` to `true` in `config/packages/ezplatform_site_factory.yaml`. + +### Configure designs + +Next, configure Site Factory by adding empty SiteAccess groups. At least one empty group is required. +The number of empty SiteAccess groups must be equal to the number of templates that you want to have when you create the new site. + +In this example, you add two SiteAccess groups (`example_site_factory_group_1` and `example_site_factory_group_2`) +that correspond to the two templates (`site1` and `site2`) that you add in the next step. + +Add the groups in `config/packages/ezplatform.yaml`: + +``` yaml +ezplatform: + siteaccess: + list: [site] + groups: + site_group: [site] + example_site_factory_group_1: [] + example_site_factory_group_2: [] + + system: + example_site_factory_group_1: + example_site_factory_group_2: +``` + +Uncomment the SiteAccess matcher (`EzSystems\EzPlatformSiteFactory\SiteAccessMatcher`): + +``` yaml +ezplatform: + siteaccess: + match: + '@EzSystems\EzPlatformSiteFactory\SiteAccessMatcher': ~ +``` + +`ezdesign` defines templates for your sites, so add them before continuing. +Next, add the configuration for `ezdesign` on the same level as `ezplatform`: + +``` yaml +ezdesign: + design_list: + example_1: [example_1_template] + example_2: [example_2_template] +``` + +Finally, configure designs for empty SiteAccess groups: + +``` yaml +ezplatform: + system: + example_site_factory_group_1: + design: example_1 + example_site_factory_group_2: + design: example_2 +``` + +### Add site template configuration + +Add thumbnails and names for your site templates in `config/packages/ezplatform_site_factory.yaml`: + +```yaml +ez_platform_site_factory: + templates: + site1: + siteaccess_group: example_site_factory_group_1 + name: example_site_1 + thumbnail: /path/to/image/example-thumbnail_1.png + site2: + siteaccess_group: example_site_factory_group_2 + name: example_site_2 + thumbnail: /path/to/image/example-thumbnail_2.png +``` + +You can check the results of your work in the Back Office by going to the **Site** tab and selecting the **List** icon. + +There, you should be able to add a new site and choose a design for it. + +### Define domains + +To be able to see your site online, you need to define a domain for it. + +!!! caution "Define domain for production environment" + + These steps are for `dev` environment only. + If you want to define domains in production environment, you will need to configure Apache or Nginx by yourself. + +In the `.env` file change line 2 to: `COMPOSE_FILE=doc/docker/base-dev.yml:doc/docker/multihost.yml` + +Take a look into the `doc/docker/multihost.yml` file. Here you can define domains. +To add a new domain, add it in `command:` and under frontend and backend aliases as shown in the example below: + +```yaml hl_lines="3 6 11" +services: + web: + command: /bin/bash -c "cd /var/www && cp -a doc/nginx/ez_params.d /etc/nginx && bin/vhost.sh --host-name=site.example.com --host-alias='admin.example.com test.example.com' --template-file=doc/nginx/vhost.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" + networks: + frontend: + aliases: + - site.example.com + - admin.example.com + - test.example.com + backend: + aliases: + - site.example.com + - admin.example.com + - test.example.com +``` + +Next, you must define the domains in `etc/hosts`: + +`0.0.0.0 site.example.com admin.example.com test.example.com www.admin.example.com` + +Then, run `docker-compose up`: + +```bash +export COMPOSE_FILE="doc/docker/base-dev.yml:doc/docker/multihost.yml" +docker-compose up +``` + +Your sites should be now visible under: + +- `http://site.example.com:8080/` +- `http://admin.example.com:8080/` +- `http://localhost:8080/` +- `http://test.example.com:8080/` + +![Site Factory enabled](../img/site_factory_site_list.png "Site Factory enabled") + +### Define site directory + +You can adjust the place where the directory of the new site is created (Location with ID 2 by default). +To do it, go to `config/packages/ezplatform_site_factory.yaml`, and add the following parameter: + +``` yaml +ezplatform: + system: + default: + site_factory: + sites_location_id: 42 +``` + +Now, all new directories are created under "Ibexa DXP". + +### Provide access + +The Site Factory is set up, now you can provide sufficient permissions to the Users. + +Set the below Policies to allow Users to: + +- `site/view` - enter the Site Factory interface +- `site/create` - create sites +- `site/edit` - edit sites +- `site/change_status` - change status of the public accesses to `Live` or `Offline` +- `site/delete` - delete sites + +For full documentation on how Permissions work and how to set them up, see [the Permissions section](../permissions.md). + +To learn how to use Site Factory, see [User documentation.]([[= user_doc =]]/site_organization/site_factory/) + +## Disable Site Factory + +Enabled Site Factory may cause following performance issues: + +- [ConfigResolver](../configuration/config_dynamic.md#configresolver) looks for SiteAccesses in the database +- Site Factory matchers are connected to the database in search for new SiteAccesses + +You can disable Site Factory to boost ConfigResolver performance. +Keep in mind that with disabled Site Factory you are unable to add new sites or use existing ones. + +1\. In `config/packages/ezplatform_site_factory.yaml` change `enabled` to `false`. + +2\. In `config/packages/ezplatform.yaml` comment the `ezplatform.siteaccess.match: '@EzSystems\EzPlatformSiteFactory\SiteAccessMatcher': ~` if it is uncommented. + +3\. Remove separate connection to database in `config/packages/doctrine.yaml`. + +``` yaml +doctrine: + dbal: + connections: + ... + # This connection is dedicated for SiteFactory to avoid known issues + site_factory: +``` + +4\. Remove separate cache pool in `config/packages/cache.yaml`. + +``` yaml +framework: + cache: + ... + pools: + # This pool should be used only by SiteFactory bundle + site_factory_pool: +``` + +The Site Factory should be disabled. diff --git a/docs/guide/multisite/site_factory_configuration.md b/docs/guide/multisite/site_factory_configuration.md new file mode 100644 index 0000000000..8f2bd4f16a --- /dev/null +++ b/docs/guide/multisite/site_factory_configuration.md @@ -0,0 +1,113 @@ +--- +description: Configure Site Factory, including site skeletons. +edition: experience +--- + +# Site Factory configuration + +## Parent Location + +When working with the [Site Factory](site_factory.md), you can define the parent +Location for a new site in the configuration. +Each new site is created in the designated Location. + +To define a parent Location, add a new configuration key to the site template definition. +Each template is assigned to its own Location. +This can be either a Location ID (for example, `62`), or a recommended remote Location ID (for example, `1548b8cd8dd4c6b5082e566615d45e91`). + +Add the configuration key to your template: + +``` yaml hl_lines="7 12" +ez_platform_site_factory: + templates: + site1: + siteaccess_group: example_site_factory_group_1 + name: example_site_1 + thumbnail: /path/to/image/example-thumbnail_1.png + parent_location_id: 62 + site2: + siteaccess_group: example_site_factory_group_2 + name: example_site_2 + thumbnail: /path/to/image/example-thumbnail_2.png + parent_location_remote_id: 1548b8cd8dd4c6b5082e566615d45e91 +``` + +Now, you can see the path to the new site's parent Location under design selection. +If you have sufficient permissions, you can change the defined Location during site creation. +If the parent Location is not defined, you have to choose it from Universal Discovery Widget. + +## Site skeletons + +The Site skeleton enables you to copy an entire content structure of the site design to the defined Location. + +Site skeleton copying is a one-off operation, it only happens during the site creation process. +After that, you cannot copy the Site skeleton again, for example in the edit view. + +You can create as many skeletons as you need and assign them to templates. +Remember that one template can only have one Site skeleton. + +If the design does not have a defined Site skeleton, a directory of the new site is created in a standard Site Factory process. + +To define a Site skeleton, add the `site_skeleton_id` or `site_skeleton_remote_id` key to the site template definition. +This can be either a Location ID (e.g. `5966`), or a remote Location ID (e.g. `3bed95afb1f8126f06a3c464e461e1ae66`). + +``` yaml hl_lines="7 12" +ez_platform_site_factory: + templates: + site1: + siteaccess_group: example_site_factory_group_1 + name: example_site_1 + thumbnail: /path/to/image/example-thumbnail_1.png + site_skeleton_id: 5966 + site2: + siteaccess_group: example_site_factory_group_2 + name: example_site_2 + thumbnail: /path/to/image/example-thumbnail_2.png + site_skeleton_remote_id: 3bed95afb1f8126f06a3c464e461e1ae66 +``` + +Now, you can choose a design with a defined Site skeleton, and decide if you want to use its skeleton by toggling **Generate site using site skeleton**. + +## User Group skeletons + +With User Group skeletons you can define Policies and Limitations that apply to selected groups of users who can access the site. + +You can create many User Group skeletons and associate them with many templates. +One template can have many User Group skeletons assigned. + +To create a User Group skeleton, first go to **Admin** -> **Site skeletons** and add a User Group to the list of available skeletons. +Then, review the detailed information of the newly created User Group skeleton, +copy the Location ID or the Location remote ID, and add a configuration key to the site template definition: + +``` yaml +ez_platform_site_factory: + templates: + <site_name>: + # ... + user_group_skeleton_ids: [ <id_skeleton1>, <id_skeleton2>, ... ] + user_group_skeleton_remote_ids: [ <id_skeleton3>, <id_skeleton4>, ... ] +``` + +Manage the permissions associated to the User Group skeleton by [assigning Roles](https://doc.ibexa.co/projects/userguide/en/latest/site_organization/organizing_the_site/#managing-permissions). +Make sure that the Roles that you assign to the User Group skeleton do not contain Location-based Limitations. +User Group skeletons cannot contain individual User Content items either. + +User Group skeletons are retained after deleting the site. + +## Automatic update of Roles + +Role definitions can contain user/login Policies with Limitations that limit user access to certain sites. +To avoid the need to add the new SiteAccess to Limitations for all Roles, +you can decide that the Roles you select are automatically updated when the site is created, updated or deleted. + +In `config/packages/ezplatform_site_factory.yaml`, add a list of Roles which should have access to the frontend +when a site is created in Site Factory, for example: + +``` yaml +ez_platform_site_factory: + # ... + enabled: true + update_roles: [Anonymous, Administrator] +``` + +For more information about Roles and Policies, see [Permissions](../permissions.md). diff --git a/docs/guide/multisite/siteaccess_aware_configuration.md b/docs/guide/multisite/siteaccess_aware_configuration.md new file mode 100644 index 0000000000..66f3325879 --- /dev/null +++ b/docs/guide/multisite/siteaccess_aware_configuration.md @@ -0,0 +1,265 @@ +--- +description: Make sure your custom development's configuration can be used with SiteAccesses. +--- + +# SiteAccess-aware configuration + +The [Symfony Config component]([[= symfony_doc =]]/components/config.html) makes it possible to define semantic configuration, exposed to the end developer. +This configuration is validated by rules you define, e.g. validating type (string, array, integer, boolean and so on). +Usually, after it is validated and processed, this semantic configuration is then mapped to internal *key/value* parameters stored in the service container. + +[[= product_name =]] uses this for its core configuration, but adds another configuration level, the SiteAccess. +For each defined SiteAccess, you need to be able to use the same configuration tree to define SiteAccess-specific config. + +These settings then need to be mapped to SiteAccess-aware internal parameters +that you can retrieve with the [ConfigResolver](../configuration/config_dynamic.md#configresolver). +For this, internal keys need to follow the format `<namespace>.<scope>.<parameter_name>`. where: + +- `namespace` is specific to your app or bundle +- `scope` is the SiteAccess, SiteAccess group, `default` or `global` +- `parameter_name` is the actual setting *identifier* + +For more information about the ConfigResolver, namespaces and scopes, see [[[= product_name =]] configuration basics](../configuration/configuration.md). + +The example below assumes you are using an `Acme\ExampleBundle`. +Remember to register the bundle by adding it to `config/bundles.php`: + +``` php +Acme\ExampleBundle\AcmeExampleBundle::class => ['all' => true], +``` + +### Parsing semantic configuration + +To parse semantic configuration, create a `Configuration` class which extends +`eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration` +and then extend its `generateScopeBaseNode()` method: + +``` php hl_lines="17" +<?php + +namespace Acme\ExampleBundle\DependencyInjection; + +use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration as SiteAccessConfiguration; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class Configuration extends SiteAccessConfiguration +{ + public function getConfigTreeBuilder(): TreeBuilder + { + $treeBuilder = new TreeBuilder('acme_example'); + $rootNode = $treeBuilder->getRootNode(); + + // $systemNode is the root of SiteAccess-aware settings. + $systemNode = $this->generateScopeBaseNode($rootNode); + $systemNode + ->scalarNode('name')->isRequired()->end() + ->arrayNode('custom_setting') + ->children() + ->scalarNode('string')->end() + ->integerNode('number')->end() + ->booleanNode('enabled')->end() + ->end() + ->end(); + + return $treeBuilder; + } +} +``` + +!!! note + + Default name for the *SiteAccess root node* is `system`, but you can customize it. + To do this, pass the name you want to use as a second argument of `$this->generateScopeBaseNode()`. + +This enables you to use the following SiteAccess-aware configuration: + +``` yaml +acme_example: + system: + <siteaccess>: + name: name_1 + custom_setting: + number: 456 + enabled: true + <siteaccess_group>: + name: name_2 + custom_setting: + string: value + number: 123 + enabled: false +``` + +### Mapping to internal settings + +Semantic configuration must always be mapped to internal key/value settings within the service container. +You usually do it in the [service container](../../api/public_php_api.md#service-container) extension. + +``` php +<?php + +namespace Acme\ExampleBundle\DependencyInjection; + +use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; +use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class AcmeExampleExtension extends Extension +{ + public const ACME_CONFIG_DIR = __DIR__ . '/../../../config/acme'; + + /** + * @throws \Exception + */ + public function load(array $configs, ContainerBuilder $container): void + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(self::ACME_CONFIG_DIR)); + $loader->load('default_settings.yaml'); + + $processor = new ConfigurationProcessor($container, 'acme_example'); + $processor->mapConfig( + $config, + // Any kind of callable can be used here. + // It is called for each declared scope/SiteAccess. + static function ($scopeSettings, $currentScope, ContextualizerInterface $contextualizer) { + // Maps the "name" setting to "acme_example.<$currentScope>.name" container parameter + // It is then possible to retrieve this parameter through ConfigResolver in the application code: + // $helloSetting = $configResolver->getParameter( 'name', 'acme_example' ); + $contextualizer->setContextualParameter('name', $currentScope, $scopeSettings['name']); + } + ); + + // Now map "custom_setting" and ensure the key defined for "my_siteaccess" overrides the one for "my_siteaccess_group" + // It is done outside the closure as it is needed only once. + $processor->mapConfigArray('custom_setting', $config); + } +} +``` + +You can also map simple settings by calling `$processor->mapSetting()`, without having to call `$processor->mapConfig()` with a callable. + +``` php +$processor = new ConfigurationProcessor($container, 'acme_example'); +$processor->mapSetting('name', $config); +``` + +!!! caution "Important" + + Always ensure you have defined and loaded default settings. + +In `@AcmeExampleBundle/Resources/config/default_settings.yaml`: + +``` yaml +parameters: + acme_example.default.name: name_1 + acme_example.default.custom_setting: + string: ~ + number: 0 + enabled: false +``` + +#### Merging hash values between scopes + +When you define a hash as semantic config, you sometimes do not want the SiteAccess settings to replace the default or group values, +but enrich them by appending new entries. This is possible by using `$processor->mapConfigArray()`, +which you must call outside the closure (before or after), so that it is called only once. + +``` php +$processor->mapConfigArray('custom_setting', $config); +``` + +Consider the following default config in `default_settings.yaml`: + +``` yaml +parameters: + acme_example.default.custom_setting: + string: ~ + os_types: [windows] + number: 0 + enabled: false + language: php +``` + +And then this semantic configuration in `config/packages/acme.yaml`: + +``` yaml +acme_example: + system: + siteaccess_group: + custom_setting: + string: value + number: 123 + + # Assuming "siteaccess1" is part of "siteaccess_group" + siteaccess1: + custom_setting: + os_types: [linux, macos] + number: 456 + enabled: true + language: javascript +``` + +By calling `mapConfigArray()` you can get the following end configuration, +where keys defined for `custom_setting` in default/group/SiteAccess scopes are merged: + +``` yaml +parameters: + acme_example.siteaccess1.custom_setting: + string: value + os_types: [linux, macos] + number: 456 + enabled: true + language: javascript +``` + +##### Merging from second level + +In the example above, entries were merged in respect to the scope order of precedence. +However, because you defined the `os_types` key for `siteaccess1`, it completely overrode the default value, +because the merge process is done only at the first level. + +You can add another level by passing `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` +as the third argument to `$contextualizer->mapConfigArray()`: + +``` php +$contextualizer->mapConfigArray('custom_setting', $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL); +``` + +When you use `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` with the configuration above, you get the following result: + +``` yaml +parameters: + acme_example.siteaccess1.custom_setting: + string: value + os_types: [windows, linux, macos] + number: 456 + enabled: true + language: javascript +``` + +There is also another option, `ContextualizerInterface::UNIQUE`, +that ensures the array setting has unique values. It only works on normal arrays, not hashes. + +!!! note + + Merge is not recursive. Only second level merge is possible by using `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` option. + +### Dedicated mapper object + +Instead of passing a callable to `$processor->mapConfig()`, you can pass an instance of +`eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface`. + +This can be useful if you have a lot of configuration to map and do not want to pollute +your service container extension class (it is better for maintenance). + +#### Merging hash values between scopes + +You should not use `$contextualizer->mapConfigArray()` within the scope loop, like for simple values. +When using a closure/callable, you usually call it before or after `$processor->mapConfig()`. +For mapper objects, you can use a dedicated interface: `HookableConfigurationMapperInterface`, +which defines two methods: `preMap()` and `postMap()`. diff --git a/docs/guide/multisite/siteaccess_matching.md b/docs/guide/multisite/siteaccess_matching.md new file mode 100644 index 0000000000..c1219cd4c4 --- /dev/null +++ b/docs/guide/multisite/siteaccess_matching.md @@ -0,0 +1,304 @@ +--- +description: Use SiteAccess matchers to control which site is served when and to which user. +--- + +# SiteAccess matching + +To be usable, every SiteAccess must be matched by one of configured matchers. +By default, all SiteAccesses are matched using `URIElement: 1`. + +You can configure SiteAccess matchers under the `ezplatform.siteaccess.match` key: + +``` yaml +ezplatform: + siteaccess: + list: [site, event] + groups: + site_group: [site, event] + default_siteaccess: site + match: + Map\URI: + site: site + campaign: event +``` + +`ezplatform.siteaccess.match` can contain multiple matchers. + +The first matcher succeeding always wins, so be careful when using catch-all matchers like `URIElement`. In the following example, `Compound\LogicalAnd` is placed before the `Map\Host` for `my.site/corporate` to be reachable: + +```yaml +ibexa: + siteaccess: + match: + Compound\LogicalAnd: + corporate: + matchers: + Map\URI: + corporate: true + Map\Host: + my.site: true + match: corporate + Map\Host: + my.site: mysite +``` + +If the matcher class does not start with a backslash (`\`), it is relative to `eZ\Publish\MVC\SiteAccess\Matcher` +(for example, `Map\URI` refers to `eZ\Publish\MVC\SiteAccess\Matcher\Map\URI`) + +You can specify [custom matchers](#custom-matchers) by using a fully qualified class name (e.g. `\My\SiteAccess\Matcher`) +or a service identifier (e.g. `@my_matcher_service`). +In the case of a fully qualified class name, the matching configuration is passed in the constructor. +In the case of a service, it must implement `eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher`. +The matching configuration is passed to `setMatchingConfiguration()`. + +## Available SiteAccess matchers + +- [`URIElement`](#urielement) +- [`URIText`](#uritext) +- [`HostElement`](#hostelement) +- [`HostText`](#hosttext) +- [`Map\Host`](#maphost) +- [`Map\URI`](#mapuri) +- [`Map\Port`](#mapport) +- [`EzSystems\EzPlatformSiteFactory\SiteAccessMatcher`](#siteaccessmatcher) + +### `URIElement` + +Maps a URI element to a SiteAccess. + +In configuration, provide the element number you want to match (starting from 1). + +``` yaml +ezplatform: + siteaccess: + match: + URIElement: 2 +``` + +!!! note + + When you use a value > 1, the matcher concatenates the elements with `_`. + +Example URI `/my_site/company/pages` matches SiteAccess `my_site_company`. + +### `URIText` + +Matches URI using prefix and suffix sub-strings in the first URI segment. + +In configuration, provide the prefix and/or suffix (neither is required). + +``` yaml +ezplatform: + siteaccess: + match: + URIText: + prefix: main- + suffix: /company +``` + +Example URI `/main-event/company/page` matched SiteAccess `event`. + +### `HostElement` + +Maps an element in the host name to a SiteAccess. + +In configuration, provide the element number you want to match (starting from 1). + +``` yaml +ezplatform: + siteaccess: + match: + HostElement: 2 +``` + +Example host name `www.example.com` matches SiteAccess `example`. + +### `HostText` + +Matches a SiteAccess in the host name, using pre and/or post sub-strings. + +In configuration, provide the prefix and/or suffix (none are required). + +``` yaml +ezplatform: + siteaccess: + match: + HostText: + prefix: www. + suffix: .com +``` + +Example host name `www.example.com` matches SiteAccess `example`. + +### `Map\Host` + +Maps a host name to a SiteAccess. + +In configuration, provide a hash map of host/SiteAccess. + +``` yaml +ezplatform: + siteaccess: + match: + Map\Host: + www.page.com: event + adm.another-page.fr: event_admin +``` + +Example host name `www.page.com` matches SiteAccess `event`. + +!!! note + + If you encounter problems with the `Map\Host` matcher, make sure that your installation is + [properly configured to use token-based authentication](../../releases/ez_platform_v2.4.md#update-ez-enterprise-v24-to-v242). + +### `Map\URI` + +Maps a URI to a SiteAccess. + +In configuration, provide a hash map of URI/SiteAccess. + +```yaml +ezplatform: + siteaccess: + match: + Map\URI: + campaign: event + site: site +``` + +Example URI `/campaign/general/articles` matches SiteAccess `event`. + +### `Map\Port` + +Maps a port to a SiteAccess. + +In configuration, provide a hash map of Port/SiteAccess. + +``` yaml +ezplatform: + siteaccess: + match: + Map\Port: + 80: event + 8080: site +``` + +Example URL `http://my_site.com:8080/content` matches SiteAccess `site`. + +### `EzSystems\EzPlatformSiteFactory\SiteAccessMatcher` [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +Enables the use of [Site Factory](site_factory.md). +Does not take any parameters in configuration: + +``` yaml +ezplatform: + siteaccess: + match: + '@EzSystems\EzPlatformSiteFactory\SiteAccessMatcher': ~ +``` + +## Custom matchers + +Beside the built-in matchers, you can also use your own services to match SiteAcceses: + +``` yaml +ezplatform: + siteaccess: + list: [site] + groups: + site_group: [site] + default_siteaccess: site + match: + '@App\Matcher\MySiteaccessMatcher': ~ +``` + +The service must be tagged with `ezplatform.siteaccess.matcher` +and must implement `eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher` +(and `eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher` if you want to use compound logical matchers). + +## Combining SiteAccess matchers + +You can combine more than one SiteAccess matcher to match more complex situations, for example: + +- `http://example.com/en` matches `site_en` (match host example.com and the `en` URI element) +- `http://example.com/fr` matches `site_fr` (match host example.com and the `fr` URI element) +- `http://admin.example.com` matches `site_admin` (match host admin.example.com) + +To combine matchers, use compound logical matchers: + +- `Compound\LogicalAnd` +- `Compound\LogicalOr` + +Each compound matcher specifies two or more sub-matchers. +A rule applies if all the matchers combined with the logical matcher are positive. + +To get the result above, you need to combine `Map\Host` and `Map\Uri` using `LogicalAnd`. +When both the URI and host match, the SiteAccess configured with `match` is used. + +``` yaml +ezplatform: + siteaccess: + match: + Compound\LogicalAnd: + # You don't need to specify matching values (true is enough). + site_en: + matchers: + Map\URI: + en: true + Map\Host: + example.com: true + match: site_en + site_fr: + matchers: + Map\URI: + fr: true + Map\Host: + example.com: true + match: site_fr + Map\Host: + admin.example.com: site_admin +``` + +When using `Compound\LogicalAnd`, all inner matchers must match. +All matchers must implement `VersatileMatcher`. +When using `Compound\LogicalOr`, the first inner matcher succeeding wins. + +## Matching by request header + +You can define which SiteAccess to use by setting an `X-Siteaccess` header in your request. +This can be useful for REST requests. + +In such a case, `X-Siteaccess` must be the SiteAccess name (for example, `site` or `en`). + +## Matching by environment variable + +You can also define which SiteAccess to use directly by using the `EZPUBLISH_SITEACCESS` environment variable. + +This is recommended if you want to get performance gain since no matching logic is done in this case. + +You can define this environment variable directly in web server configuration: + +``` vcl +<!--Apache VirtualHost example--> +# This configuration assumes that mod_env is activated +<VirtualHost *:80> + DocumentRoot "/path/to/ezpublish5/web/folder" + ServerName example.com + ServerAlias www.example.com + SetEnv EZPUBLISH_SITEACCESS ezdemo_site +</VirtualHost> +``` + +!!! tip + + You can configure the variable by using the PHP-FPM configuration file. + See [PHP-FPM documentation](http://php.net/manual/en/install.fpm.configuration.php#example-60) for more information. + +!!! note "Precedence" + + The precedence order for SiteAccess matching is the following (the first matched wins): + + 1. Request header + 1. Environment variable + 1. Configured matchers diff --git a/docs/guide/navigation/navigation.md b/docs/guide/navigation/navigation.md index 6f1eb74cc9..9f115c2672 100644 --- a/docs/guide/navigation/navigation.md +++ b/docs/guide/navigation/navigation.md @@ -1,4 +1,4 @@ -# Navigation [[% include 'snippets/commerce_badge.md' %]] +# Navigation Navigation is generated automatically and can include one or more product catalogs and other Content items in the root Location defined in [the configuration](navigation_configuration.md). @@ -40,7 +40,7 @@ You do this by using two events that are thrown before the search query is submi The product catalog is injected into the prepared navigation. You can have multiple product catalogs on any level. -If you are using the content model data provider, place categories and products directly under the product catalog +If you are using the Repository data provider, place categories and products directly under the product catalog and then, in the catalog's Root node Field, point to the catalog's Location ID. ![](../img/navigation_6.png) diff --git a/docs/guide/navigation/navigation_configuration.md b/docs/guide/navigation/navigation_configuration.md index bd551ab1fb..db5e4e0052 100644 --- a/docs/guide/navigation/navigation_configuration.md +++ b/docs/guide/navigation/navigation_configuration.md @@ -1,4 +1,4 @@ -# Navigation configuration [[% include 'snippets/commerce_badge.md' %]] +# Navigation configuration The `navigation_ez_location_root` parameter is the entry root Location point for the whole navigation in the Back Office. This value is usually set to `2`, the Location of the content structure. diff --git a/docs/guide/navigation/navigation_templates.md b/docs/guide/navigation/navigation_templates.md index 69dc146f43..1ec4a96d87 100644 --- a/docs/guide/navigation/navigation_templates.md +++ b/docs/guide/navigation/navigation_templates.md @@ -1,4 +1,4 @@ -# Navigation templates [[% include 'snippets/commerce_badge.md' %]] +# Navigation templates ## Template list diff --git a/docs/guide/newsletter/newsletter.md b/docs/guide/newsletter/newsletter.md deleted file mode 100644 index 49d7cd5bd9..0000000000 --- a/docs/guide/newsletter/newsletter.md +++ /dev/null @@ -1,98 +0,0 @@ -# Newsletter [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] offers a newsletter interface. It allows the user to subscribe to newsletters -and see the newsletter status or update newsletter details information in their profile. - -There is no specific newsletter configured out of the box. -The standard offers only processes, templates, routes, configurations and an interface which can be used for newsletter integration. -A newsletter provider service with specific API implementation of this provider has to be implemented separately. - -The newsletter status is fetched from the newsletter provider after the user logs in, and displayed in the user's profile. -The newsletter status is stored in customer profile data together with the list of IDs of newsletter topics, so it can be rendered in the template if required. - -``` html+twig -{{ ses.profile.sesUser.subscribesNewsletter }} - -{% set dataMap = ses.profile.getDataMap.dataMap %} -{% set newsletters = dataMap.subscribed_list_ids %} -{% if newsletters|default is not empty %} - {% for newsletter in newsletters %} - {{ newsletter }} - {% endfor %} -{% endif %} -``` - -## Configuration - -General newsletter configuration is located in `newsletter.yml`: - -``` yaml -parameters: - # you can disable the newsletter module here - siso_newsletter.default.newsletter_active: true - # enable if you want to support several newsletter topics - siso_newsletter.default.support_several_newsletters: false - # if enabled, user will be unsubscribed from all newsletters (topics) at once - siso_newsletter.default.unsubscribe_globally: true - # if enabled also logged in users will see the newsletter box - siso_newsletter.default.display_newsletter_box_for_logged_in_users: true -``` - -### Default attributes - -Following attributes are offered by default. These attributes are sent to the newsletter provider if available: - -- First name -- Last name -- Gender -- Telephone Number -- Date of birth -- User language -- Last order date -- Last order amount -- Order amount total - -### Additional attributes - -There are places where you can add or modify additional user data and send it to a newsletter provider. -You can do it: - -- before a user subscribes to the newsletter. By default the user language (current locale) is sent. -- after the user updates their profile or creates an order. By default, the newsletter details are updated with latest information about: - - `last_order_date` - date when last order was made - - `last_order_amount` - order amount from last order - - `order_amount_total` - total order amount for the user - -In both cases any data from the [customer profile](../customers/customers.md) can be sent, -but you have to implement a custom event listener for that. - -Attributes that do not exist in the newsletter provider have to be created first. - -#### Additional newsletter data - -To send additional data to the newsletter provider, implement an event listener -that listens to `subscribe_newsletter_event` or `update_newsletter_event`. - -``` php -public function setCustomParameters(SubscribeNewsletterEvent $event) -{ - $customerProfileData = $event->getCustomerProfileData(); - $params = $event->getParams(); - - if ($customerProfileData instanceof CustomerProfileData && !array_key_exists('order_amount', $params)) { - $userId = $customerProfileData->sesUser->sesUserObjectId; - /** fetch the amount of all user orders */ - $orderAmount = $this->basketRepository->getUserOrdersAmount($userId); - $params['order_amount'] = $orderAmount; - } - - $event->setParams($params); -} -``` - -``` xml -<service id="project.newsletter.subscribe_newsletter_listener" class="%project.newsletter.subscribe_newsletter_listener.class%"> - <argument type="service" id="project.basket_repository"/> - <tag name="kernel.event_listener" event="subscribe_newsletter_event" method="setCustomParameters" /> -</service> -``` diff --git a/docs/guide/newsletter/newsletter_interface.md b/docs/guide/newsletter/newsletter_interface.md deleted file mode 100644 index 27be9201a3..0000000000 --- a/docs/guide/newsletter/newsletter_interface.md +++ /dev/null @@ -1,60 +0,0 @@ -# Newsletter interface [[% include 'snippets/commerce_badge.md' %]] - -`Siso\Bundle\NewsletterBundle\Api\NewsletterInterface` is the common interface for newsletter providers. - -## subscribeNewsletter/unsubscribeNewsletter - -The `subscribeNewsletter` and `unsubscribeNewsletter` methods subscribe or unsubscribe to/from a newsletter with given `params`. -You can pass the method any data that is, for example, posted from a form. -If `customerProfileData` is provided, attributes from `customerProfileData` are used to (un)subscribe to/from the newsletter. -Otherwise, the required attributes must be specified in `params`. - -Returns a list with the newsletter IDs and whether the action was successful (true/false). - -## updateNewsletter - -The `updateNewsletter` method updates newsletter details with given `params`. -The implementation of this method must dispatch `UpdateNewsletterEvent`. - -Returns a list with the newsletter IDs and whether the action was successful (true/false). - -## updateUserEmail - -The `updateUserEmail` method updates newsletter user's email address. - -Returns a list with the newsletter IDs and whether the action was successful (true/false). - -## getEmail - -The `getEmail` method returns the email address. - -If `CustomerProfileData` is provided, the email from `SesUser` is used. -If `CustomerProfileData` is not provided, the email from `params` is used. - -## doesUserExistInNewsletter - -The `doesUserExistInNewsletter` method returns true if a user exists in the newsletter provider. -This method checks if the user is known to the newsletter provider, but does not evaluate the status -(whether the user subscribes to the newsletter). - -The goal of this method is to find out whether a user exists in the database of the newsletter provider (active/inactive), -because even if the user is inactive, they probably already confirmed their email address in the past -(e.g. through the double opt-in process). - -## doesUserSubscribeNewsletter - -The `doesUserSubscribeNewsletter` method reads the user newsletter status - whether the user subscribes to the newsletter - from the provider. -If `customerProfileData` is given, attributes from `customerProfileData` are used to check the newsletter status. -Otherwise, the required attributes must be specified in `params`. - -Returns a list with the newsletter IDs and whether the action was successful (true/false). - -## doesUserSubscribeAtLeastOneNewsletter - -The `doesUserSubscribeAtLeastOneNewsletter` method returns true if the user subscribes to at least one newsletter. -No call to API is done. Instead the result of one of the methods above should be passed as parameter. - -## getSubscribedNewsletterIds - -The `getSubscribedNewsletterIds` method returns a list of newsletter IDs that a user subscribes to. -No call to API is done. Instead the result of one of the methods above should be passed as parameter. diff --git a/docs/guide/newsletter/newsletter_templates.md b/docs/guide/newsletter/newsletter_templates.md deleted file mode 100644 index 1c35dd81c3..0000000000 --- a/docs/guide/newsletter/newsletter_templates.md +++ /dev/null @@ -1,30 +0,0 @@ -# Newsletter templates [[% include 'snippets/commerce_badge.md' %]] - -### Template list - -|Path|Description| -|--- |--- | -|`Silversolutions/Bundle/EshopBundle/Resources/views/Newsletter/newsletter_box.html.twig`|Renders [the newsletter box](#newsletter-box).| -|`Silversolutions/Bundle/EshopBundle/Resources/views/Newsletter/newsletter_message.html.twig`|Renders a simple page with success/error messages after a user subscribes/unsubscribes to or from newsletter| -|`Silversolutions/Bundle/EshopBundle/Resources/views/Emails/ConfirmationMail_SubscribeNewsletter.html.twig`|HTML confirmation email that is sent to the user in the double opt-in process| -|`Silversolutions/Bundle/EshopBundle/Resources/views/Emails/ConfirmationMail_SubscribeNewsletter.txt.twig`|Text confirmation email that is sent to the user in the double opt-in process| - -### Newsletter box - -The newsletter box can be rendered as an ESI block or in the Page Builder. -The box is cached per user. - -All parameters from the block template are available in the box. - -## Rendering a newsletter form - -You can use a render statement to render the newsletter form in a template: - -``` php -{{ render( - controller( - 'SisoNewsletterBundle:Newsletter:renderNewsletterBox', - {'params' : { 'display_hr' : true }} - ) -) }} -``` diff --git a/docs/guide/order_history/displaying_custom_column.md b/docs/guide/order_history/displaying_custom_column.md deleted file mode 100644 index 633720b0b3..0000000000 --- a/docs/guide/order_history/displaying_custom_column.md +++ /dev/null @@ -1,85 +0,0 @@ -# Displaying custom column [[% include 'snippets/commerce_badge.md' %]] - -## 1. Check if there is an existing block for your field - -By default you can configure columns per document type in the [configuration](orderhistory_configuration.md). -You can display any field that has a block in `OrderHistoryBundle/Resources/views/OrderHistory/Components/fields.html.twig`. - -!!! note - - When using the block name as column identifier in the configuration, remove the suffix `_field`. - -If there is no block for your custom field, you need to create one. - -The block name usually reflects the name of the document line field, for example: - -``` xml -<CreditNoteList ses_unbounded="CreditNoteLine"> - <CreditNoteLine ses_tree="SesExtension"> - ... - <PaymentMeans> - ... - <PaymentDueDate>2007-01-01</PaymentDueDate> - </PaymentMeans> - </CreditNoteLine> -</CreditNoteList> -``` - -Block name: - -``` html+twig -{#obj is passed automatically and it is the document line#} - -{% block PaymentMeans_PaymentDueDate_field %} - {% spaceless %} - {% set field_value = obj.PaymentMeans.PaymentDueDate.value %} - {{ block( 'date_field' ) }} - {% endspaceless %} -{% endblock %} -``` - -## 2\. Create missing block - -If you did not find the corresponding block, you need to create your own. First you need to know the document structure. -Custom fields are passed to `SesExtension` if there is no standard for them. - -``` xml -<DeliveryNoteList ses_unbounded="CreditNoteLine"> - <DeliveryNoteListLine ses_tree="SesExtension"> - ... - <SesExtension> - ... - <TrackingCode>20057487546987</TrackingCode> - </SesExtension> - </DeliveryNoteListLine> -</DeliveryNoteList> -``` - -Override the corresponding template in `OrderHistoryBundle/Resources/views/OrderHistory/Components/fields.html.twig`. - -Create a new block: - -``` html+twig -{#obj will be passed automatically and it is the document line#} - -{% block SesExtension_TrackingCode_field %} - {% spaceless %} - {% if obj.SesExtension.value.TrackingCode is defined %} - {% set field_value = obj.SesExtension.value.TrackingCode %} - <a href="#">{{ field_value }}</a> - {% endif %} - {% endspaceless %} -{% endblock %} -``` - -## 3\. Add new column to the configuration - -``` yaml -siso_order_history: - #block name is specified in Components/fields.html.twig - default_list_fields: - delivery_note: - - ID_list - - IssueDate - - SesExtension_TrackingCode -``` diff --git a/docs/guide/order_history/order_history.md b/docs/guide/order_history/order_history.md new file mode 100644 index 0000000000..bea9a54af5 --- /dev/null +++ b/docs/guide/order_history/order_history.md @@ -0,0 +1,33 @@ +--- +description: Order history lets the user check the historical records of all their orders. +edition: commerce +--- + +# Order history + +The **Order History** screen enables you to see an overview and details of your orders or other documents. +You can see documents related to online purchases as well as orders placed, for example, by phone. + +Order history supports different document types: + +- order +- invoice +- delivery note +- credit memo + +Order history is available in the user menu under **Your documents**, +and through the `/orderhistory` route. + +To access order history, the User must have the `siso_policy/orderhistory_view` Policy. + +Depending on whether your system is connected to ERP, order history displays documents from ERP, +or [local documents](order_history_local_orders.md). + + +![](../img/orderhistory.png) + +Each document has a detail page, where you can see the detailed information, such as the buyer, delivery address, ordered items, status, delivery and payment information: + +![](../img/orderhistory_detail.png) + +If a product is still available in the shop, you can add it to the basket again. diff --git a/docs/guide/order_history/order_history_configuration.md b/docs/guide/order_history/order_history_configuration.md new file mode 100644 index 0000000000..48e741a46e --- /dev/null +++ b/docs/guide/order_history/order_history_configuration.md @@ -0,0 +1,118 @@ +--- +description: Configure the order history functionality to define document types and table data. +edition: commerce +--- + +# Order history configuration + +You can change a number of different parameters to configure the behavior of the **Order History** page. + +## General settings + +To configure the number of documents displayed on one page, use the following setting: + +``` yaml +siso_order_history: + list: + max_document_count_per_page: 30 +``` + +To define document types that are available for selection, list them under the `document_types` key. +The `default_document_type` setting decides which document type is displayed by default: + +``` yaml +siso_order_history: + document_types: + - invoice + - delivery_note + - credit_memo + - order + + default_document_type: invoice +``` + +You can configure the default time period for the displayed documents: + +``` yaml +siso_order_history: + date: + start: 2 years + end: 0 days + max_start: 4 years +``` + +You can configure the date format that is used in the shop +and the format in which dates are sent to ERP: + +``` yaml +parameters: + siso_order_history.default.date_format: 'd.m.Y' + + # Date format that is used for communication with ERP + siso_order_history.default.erp_date_format: 'Ymd' +``` + +## Column configuration + +To define columns that are displayed for each document type, list them under the respective key. +The column identifier is the block name from `OrderHistory/Components/fields.html.twig`, without the suffix `_field`: + +``` yaml +siso_order_history: + default_list_fields: + invoice: + - ID_list + - IssueDate + - LegalMonetaryTotal_TaxExclusiveAmount + - LegalMonetaryTotal_TaxInclusiveAmount + # ... + default_detail_fields: + invoice: + - Item_SellersItemIdentification_ID + - Item_Name + # ... +``` + +### Column sorting + +To define default sorting for columns in the list page, use the `default_list_sort` setting. + +Allowed values are: + +- `numeric-comma` - for numbers that use a comma as a decimal separator, such as currency +- `datetime` - for datetime in the `<dd.mm.YYYY HH:mm>` or `<dd.mm.YYYY>` format` +- `false` - disables column sorting + +``` yaml +siso_order_history: + default_list_sort: + invoice: + - numeric-comma + - datetime + - numeric-comma + - numeric-comma + delivery_note: + - numeric-comma + - datetime + order: + - numeric-comma + - datetime + - numeric-comma + - numeric-comma + credit_memo: + - numeric-comma + - datetime + - numeric-comma + - numeric-comma +``` + +To define column sorting for the list view, use the `default_list_sort_column` setting: + +``` yaml +siso_order_history: + default_list_sort_column: + invoice: [[1, 'desc']] + delivery_note: [[1, 'desc']] + order: [[1, 'desc']] + credit_memo: [[1, 'desc']] +``` diff --git a/docs/guide/order_history/order_history_features/orderhistory_erp_messages.md b/docs/guide/order_history/order_history_features/orderhistory_erp_messages.md deleted file mode 100644 index a824aefbdf..0000000000 --- a/docs/guide/order_history/order_history_features/orderhistory_erp_messages.md +++ /dev/null @@ -1,43 +0,0 @@ -# ERP messages [[% include 'snippets/commerce_badge.md' %]] - -Order history uses the following ERP messages: - -|`silver_erp.config.messages`|Process type in ERP|`webservice_operation`|Specifications| -|--- |--- |--- |--- | -|`invoice_detail`|`READPOSTEDSALESINVOICE`|`SV_OPENTRANS_GET_ORDERSTATUS`|`Resources/specifications/xml/request.invoiceDetail.xml`</br>`Resources/specifications/xml/response.invoiceDetail.xml`| -|`invoice_list`|`READPOSTEDSALESINVOICELIST`|`SV_OPENTRANS_GET_ORDERLIST`|`Resources/specifications/xml/request.invoiceList.xml`</br>`Resources/specifications/xml/response.invoiceList.xml`| -|`delivery_note_detail`|`READPOSTEDSALESSHIPMENT`|`SV_OPENTRANS_GET_ORDERSTATUS`|`Resources/specifications/xml/request.deliveryNoteDetail.xml`</br>`Resources/specifications/xml/response.deliveryNoteDetail.xml`| -|`delivery_note_list`|`READPOSTEDSALESSHIPMENTLIST`|`SV_OPENTRANS_GET_ORDERLIST`|`Resources/specifications/xml/request.deliveryNoteList.xml`</br>`Resources/specifications/xml/response.deliveryNoteList.xml`| -|`credit_memo_detail`|`READPOSTEDSALESCRMEMO`|`SV_OPENTRANS_GET_ORDERSTATUS`|`Resources/specifications/xml/request.creditMemoDetail.xml`</br>`Resources/specifications/xml/response.creditMemoDetail.xml`| -|`credit_memo_list`|`READPOSTEDSALESCRMEMOLIST`|`SV_OPENTRANS_GET_ORDERLIST`|`Resources/specifications/xml/request.creditMemoList.xml`</br>`Resources/specifications/xml/response.creditMemoList.xml`| -|`order_detail`|`READSALESDOCUMENT`|`SV_OPENTRANS_GET_ORDERSTATUS`|`Resources/specifications/xml/request.orderDetail.xml`</br>`Resources/specifications/xml/response.orderDetail.xml`| -|`order_list`|`READSALESDOCUMENTLIST`|`SV_OPENTRANS_GET_ORDERLIST`|`Resources/specifications/xml/request.orderList.xml`</br>`Resources/specifications/xml/response.orderList.xml`| - -## Getting an invoice - -This example fetches a invoice number from the ERP system: - -``` php -$message = $this->container->get('silver_erp.message_inquiry_service')->inquireMessage( - InvoiceDetailFactoryListener::INVOICEDETAIL -); -/** @var InvoiceDetailRequest $request */ -$request = $message->getRequestDocument(); -$request->ID->value = $invoiceNumber; -$request->CustomerNumber->value = $customerNumber; -/** @var WebConnectorMessageTransport $transport */ -$transport = $this->container->get('silver_erp.message_transport'); -$response = $transport->sendMessage($message)->getResponseDocument(); -``` - -The specification for the request (`request.invoiceDetail.xml`) reflects the fields used in the request: - -``` xml -<?xml version="1.0" encoding="UTF-8"?> -<InvoiceDetailRequest> - <ID>10000</ID> - <CustomerNumber>10000</CustomerNumber> -</InvoiceDetailRequest> -``` - -The response list is defined in `response.invoiceDetail.xml`. diff --git a/docs/guide/order_history/order_history_features/orderhistory_local_orders.md b/docs/guide/order_history/order_history_features/orderhistory_local_orders.md deleted file mode 100644 index bf1a19c818..0000000000 --- a/docs/guide/order_history/order_history_features/orderhistory_local_orders.md +++ /dev/null @@ -1,182 +0,0 @@ -# Local orders [[% include 'snippets/commerce_badge.md' %]] - -You can display user orders even if the shop is not connected to an ERP system. - -The user sees the orders stored locally in the shop. Order history shows only documents of type `orders`. - -Because the shop only has local data for orders, the available document types are different. - -The use of the local documents is enabled with the `use_local_documents` configuration parameter: - -``` yaml -siso_order_history.default.use_local_documents: false -``` - -The feature uses event listeners, so the standard logic does not change if this value is set to false. - -[[= product_name_com =]] offers an event listener which uses the `ConfigurationEvents::READ_CONFIGURATION` event. -This listener is disabled by default, so you need to activate it in your project: - -``` yaml -siso_order_history.default.read_configuration_listener_active: true -``` - -If local documents are used (parameter `use_local_documents == true`), the logic checks if the requested message is `OrderListMessage` or `OrderListMessage`. -If so, it throws an exception `UseLocalDocumentsException` that is caught by the exception event. -The system then creates the same response as is sent by ERP (the response contains the same information as if it were using ERP.) - -## UseLocalDocumentsListener - -### Listener `onRequestEvent` - -If there is no connection with ERP and local documents are used, -the listener throws an exception that stops sending the message to ERP. -When the exception is caught, another event (`MessageExceptionEvent`) is dispatched and the `onExceptionMessage` listener is executed. - -``` xml -<service id="siso_order_history.use_local_documents_listener" class="%siso_order_history.use_local_documents_listener.class%"> - ... - <tag name="kernel.event_listener" event="silver_eshop.request_message" method="onRequestEvent" /> - ... -</service> -``` - -### Listener `onExceptionMessage` - -Checks the exception that is thrown by the previous listener and creates a [`ResponseDocument`](../../erp_integration/erp_communication/erp_components/erp_component_messages/erp_component_messages.md) filled with local data (orders). -The response has the same structure as if it was returned from ERP, so no additional changes in the template are required. - -``` xml -<service id="siso_order_history.use_local_documents_listener" class="%siso_order_history.use_local_documents_listener.class%"> - ... - <tag name="kernel.event_listener" event="silver_eshop.exception_message" method="onExceptionMessage" /> - ... -</service> -``` - -`silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/EventListener/UseLocalDocumentsListener.php` - -``` php -protected function createOrderListResponseDocument(OrderListRequest $requestDocument) -{ - .... - /** @var OrderListResponse $orderListResponse */ - $orderListResponse = new OrderListResponse(); - //These values are same as in the ERP order - $orderListResponse->OrderTypeCode->value = 'SalesOrderList'; - $orderListResponse->SesExtension->Status->value = 0; - ..... -} -protected function createOrderDetailResponseDocument(OrderDetailRequest $requestDocument) -{ - .... - $responseDocument = new OrderDetailResponse(); - $responseDocument->ID->value = $basket->getBasketId(); - $issueDate = $this->dateTimeService->defaultToErpDate( - $basket->getDateLastModified()->format(\DateTime::ISO8601), - \DateTime::ISO8601 - ); - ... -} -``` - -## Generating an invoice - -For information about generating a PDF invoice from a local order, see [Local order process](../../../guide/checkout/local_orders.md). - -`SisoLocalOrderManagementBundle` handles orders without an ERP connection. - -The bundle generates: - -- local orders with an invoice -- invoice entity (special implementation to generate the invoice number) -- invoice view -- invoice PDF and attaches it to an email - -### Show invoice in order history - -Once the local order with the invoice is generated you can show it in order history: - -![](../../img/orderhistory_3.jpg) - -Local invoices are available only for the current user (nobody else has access to the invoice page). - -Order history details contain a link in the header to the invoice page: - -![](../../img/orderhistory_4.jpg) - -#### Order history list - -To show the invoice link in a new column of the table, override the configuration. - -``` yaml -siso_order_history.default.default_list_fields: - order: - # ... - - SesExtension_Invoice -``` - -A new Twig block shows the new link to the invoice if the invoice number is set. - -``` html+twig -{# Order history list #} -{% block SesExtension_Invoice_field %} - {% spaceless %} - {% if obj.SesExtension.value.InvoiceNumber|default is not empty %} - <a target="_blank" href="{{ path( 'siso_local_order_management_show_invoice', { 'invoiceNumber': obj.SesExtension.value.InvoiceNumber }, false) }}">{{ obj.SesExtension.value.InvoicePrefix }}{{ obj.SesExtension.value.InvoiceNumber }}</a> - {% endif %} - {% endspaceless %} -{% endblock %} -``` - -#### Order history detail - -To show the link to the invoice in the detail page modify the Twig template. - -#### Order history page - -Only the owner of the basket can access the `InvoiceController` controller that displays the invoice. - -``` php -if ($basket instanceof Basket) { - $userId = $basket->getUserId(); - $customerProfileData = $this->getCustomerProfileDataService()->getCustomerProfileData(); - //only display invoice if the invoice belongs to user - if ($userId == $customerProfileData->sesUser->sesUserObjectId) { - $parameters['basket'] = $basket; - - $userInvoiceNumber = $invoiceService->getUserInvoiceNumber($invoice); - $parameters['user_invoice_number'] = $userInvoiceNumber; - - return $this->render('SilversolutionsEshopBundle:Invoice:show.html.twig', $parameters); - } else { - throw new AccessDeniedException(); - } -} -``` - -### Templates - -`EshopBundle/Resources/views/Invoice/show.html.twig` template is used to generated the invoice view and the invoice PDF. - -This template does not extend any other template, so it also generates the `<html>` and `<head>` tags. - -``` html+twig -<!doctype html> -<html> -<head> - <meta charset="utf-8"> - {% block stylesheets %} - {% stylesheets 'bundles/silversolutionseshop/css/style.css' %} - <link rel="stylesheet" href="{{ asset_url }}"/> - {% endstylesheets %} - {% endblock %} -</head> - -<body> - .... -</body> -</html> -``` - -![](../../img/orderhistory_5.jpg "Example invoice page") diff --git a/docs/guide/order_history/order_history_local_orders.md b/docs/guide/order_history/order_history_local_orders.md new file mode 100644 index 0000000000..1a802a5091 --- /dev/null +++ b/docs/guide/order_history/order_history_local_orders.md @@ -0,0 +1,37 @@ +--- +edition: commerce +--- + +# Local orders + +You can display user documents even if the shop is not connected to an ERP system. +In this case, the user sees the documents [stored locally in the shop](../../guide/checkout/local_orders.md). + +The shop only stores local data for orders, so only the `order` document type is displayed. + +You can enable the use of local documents with the `use_local_documents` configuration parameter: + +``` yaml +siso_order_history.default.use_local_documents: false +``` + +## Invoices for local orders + +Invoices for local orders are available only for the current user. + +In the header of the order history details page, there is a link to the invoice page: + +![](../img/orderhistory_invoice.png) + +### Order history list + +To show the invoice link in a new column of the table, use the following configuration: + +``` yaml +siso_order_history.default.default_list_fields: + order: + # ... + - SesExtension_Invoice +``` + +The `EshopBundle/Resources/views/Invoice/show.html.twig` template renders the invoice view and the invoice PDF. diff --git a/docs/guide/order_history/order_history_templates.md b/docs/guide/order_history/order_history_templates.md new file mode 100644 index 0000000000..88ac2be2ec --- /dev/null +++ b/docs/guide/order_history/order_history_templates.md @@ -0,0 +1,23 @@ +--- +edition: commerce +--- + +# Order history templates + +## Template list + +|Path|Description| +|--- |--- | +|`OrderHistoryBundle/Resources/views/OrderHistory/list.html.twig`|Renders the document list.| +|`OrderHistoryBundle/Resources/views/OrderHistory/Components/list_table.html.twig`|Renders the document table. Included in `OrderHistory/list.html.twig`.| +|`OrderHistoryBundle/Resources/views/OrderHistory/detail.html.twig`|Renders the detail view of a single document.| +|`OrderHistoryBundle/Resources/views/OrderHistory/Components/header_default.html.twig`|Renders the header information for document detail. Included in `OrderHistory/detail.html.twig`.| +|`OrderHistoryBundle/Resources/views/OrderHistory/Components/fields.html.twig`|Contains blocks that render the content of the individual fields (defined in the configuration). Included in `OrderHistory/Components/list_table.html.twig` and `OrderHistory/detail.html.twig`.| +|`OrderHistoryBundle/Resources/views/OrderHistory/Components/user_menu.html.twig`|Renders the "Your documents" item in user menu.| + +## Custom Twig functions + +|Twig filter|Description|Usage| +|--- |--- |--- | +|`ses_erp_to_default`|Converts the ERP date into the default date format.|`{{ 'Order at:'|st_translate }} {{ response.OrderReference.IssueDate.value|ses_erp_to_default }} {{ response.OrderReference.IssueDate.value|ses_erp_to_default }}`| +|`ses_to_float`|Converts the given value to a valid float (also strings that use a comma instead of a dot).|`{{ 'VAT:'|st_translate }} {{ vat.TaxAmount.value|ses_to_float|price_format }}`| diff --git a/docs/guide/order_history/orderhistory.md b/docs/guide/order_history/orderhistory.md deleted file mode 100644 index 9080239b44..0000000000 --- a/docs/guide/order_history/orderhistory.md +++ /dev/null @@ -1,36 +0,0 @@ -# Order history [[% include 'snippets/commerce_badge.md' %]] - -Order history enables you to see an overview and details of your orders or other documents from the ERP system. -You can see documents based on online purchases as well as orders placed e.g. by a telephone call. - -On the detail page you can see the buyer, delivery address, ordered items and other details about the order, -such as status, delivery and payment selection. - -You can also use the `addToBasket` functionality and speed up the order process, for example if you often order the same items. - -Order history is available for customers who have a login and a customer number. -Usually a user gets a customer number right after placing the first order. - -The module offers a search functionality which enables searching for invoices, delivery notes, orders or credit memos. -You can configure which documents are shown first. - -![](../img/orderhistory_1.jpg) - -The user can search inside the list or sort the list by all columns by clicking the arrow in the table header. - -For each document a detail page is offered as well: - -![](../img/orderhistory_2.jpg) - -If a product is still available in the shop, a basket button is provided. - -Order history supports different document types: - -- order -- invoice -- delivery note -- credit memo - -## Permissions - -To access order history, the user must have the `siso_policy/orderhistory_view` Policy. diff --git a/docs/guide/order_history/orderhistory_api.md b/docs/guide/order_history/orderhistory_api.md deleted file mode 100644 index d916be61ea..0000000000 --- a/docs/guide/order_history/orderhistory_api.md +++ /dev/null @@ -1,62 +0,0 @@ -# Order history API [[% include 'snippets/commerce_badge.md' %]] - -## Order search form - -The order search form uses the `DatePicker` plugin for better user experience. -If you need to pass additional parameters to this plugin or adapt the form attributes, -override the form type and form validation service. - -![](../img/orderhistory_6.jpg) - -### Form type - -Form type is located in `OrderHistoryBundle/Form/Type/OrderType.php`. - -Service definition: - -``` xml -<parameter key="siso_order_history.order_type.class">Siso\Bundle\OrderHistoryBundle\Form\Type\OrderType</parameter> - -<service id="siso_order_history.order_type" class="%siso_order_history.order_type.class%" scope="prototype"> - <argument type="service" id="ezpublish.config.resolver" /> - <argument>%siso_order_history.date%</argument> -</service> -``` - -``` php -//Example: pass some data-attributes for the datepicker plugin - -$builder->add('orders_from', 'text', array( - 'label' => 'Orders from:', - 'required' => true, - 'data' => $options['data']['from_date'], - 'attr' => array( - 'class' => 'datepicker', - 'data-date-direction-start' => $maxDirectionStart, - 'data-date-direction-end' => $maxDirectionEnd, - 'data-date-caption-days' => 'F Y', - 'data-date-format' => $dateFormat - ) - ) -); -``` - -### Form validation - -Form validation is located in `OrderHistoryBundle/Services/FormValidationService.php`. - -Service definition: - -``` xml -<parameter key="siso_order_history.form_validation.class">Siso\Bundle\OrderHistoryBundle\Services\FormValidationService</parameter> - -<service id="siso_order_history.form_validation" class="%siso_order_history.form_validation.class%"> - <argument type="service" id="siso_order_history.date_time"/> - <argument type="service" id="ezpublish.config.resolver"/> -</service> -``` - -``` php -// adjust validation -public function isValid($params) { } -``` diff --git a/docs/guide/order_history/orderhistory_configuration.md b/docs/guide/order_history/orderhistory_configuration.md deleted file mode 100644 index 707c722ba8..0000000000 --- a/docs/guide/order_history/orderhistory_configuration.md +++ /dev/null @@ -1,140 +0,0 @@ -# Orderhistory configuration [[% include 'snippets/commerce_badge.md' %]] - -Order history uses semantic configuration, so it only exposes parameters that are configurable. - -However it is possible to [override this configuration per SiteAccess](overriding_semantic_configuration.md). -When an event is thrown, before the configuration is used, you can implement a listener that changes this configuration. -[[= product_name_com =]] uses this event to display [local orders](order_history_features/orderhistory_local_orders.md). -See the [Overriding semantic configuration](overriding_semantic_configuration.md) to find out how to implement a new configuration listener. - -``` yaml -siso_order_history: - list: - max_document_count: 30 - - document_types: - - invoice - - delivery_note - - credit_memo - - order - - default_document_type: invoice - - #valid values: <integer> hours or <integer> minutes and so on, the date is calculated with <now> - given time - date: - start: 2 years - end: 0 days - max_start: 4 years -``` - -To define default sorting for columns in the list page, use the `default_list_sort` setting. - -Allowed vales are: - -- `numeric-comma` - for numbers which use a comma as the decimal place like currency -- `datetime` - for datetime in the format `<dd.mm.YYYY HH:mm>` or `<dd.mm.YYYY>` -- `false` - disables sorting of a column - -``` yaml -siso_order_history: - default_list_sort: - invoice: - - numeric-comma - - datetime - - numeric-comma - - numeric-comma - delivery_note: - - numeric-comma - - datetime - order: - - numeric-comma - - datetime - - numeric-comma - - numeric-comma - credit_memo: - - numeric-comma - - datetime - - numeric-comma - - numeric-comma -``` - -To define column sorting for the list view, use the `default_list_sort_column` setting: - -``` yaml -siso_order_history: - default_list_sort_column: - #example multiple sorting - #invoice: [[0, 'desc'], [2, 'asc']] - invoice: [[1, 'desc']] - delivery_note: [[1, 'desc']] - order: [[1, 'desc']] - credit_memo: [[1, 'desc']] -``` - -You can configure which columns (per document type) you want to display. - -The column identifier is the block name from `views/OrderHistory/Components/fields.html.twig` without the suffix `_field`: - -``` yaml - default_list_fields: - invoice: - - ID_list - - IssueDate - - LegalMonetaryTotal_TaxExclusiveAmount - - LegalMonetaryTotal_TaxInclusiveAmount - delivery_note: - - ID_list - - IssueDate - order: - - ID_list - - OrderReference_IssueDate - - LegalMonetaryTotal_TaxExclusiveAmount - - LegalMonetaryTotal_TaxInclusiveAmount - credit_memo: - - ID_list - - IssueDate - - LegalMonetaryTotal_TaxExclusiveAmount - - LegalMonetaryTotal_TaxInclusiveAmount - - default_detail_fields: - invoice: - - Item_SellersItemIdentification_ID - - Item_Name - - InvoicedQuantity - - SesExtension_UnitPrice - - Price_PriceAmount - - Sum - delivery_note: - - Item_SellersItemIdentification_ID - - Item_Name - - InvoicedQuantity - - SesExtension_UnitPrice - order: - - Item_SellersItemIdentification_ID - - Item_Name - - InvoicedQuantity - - SesExtension_UnitPrice - - Price_PriceAmount - - Sum - credit_memo: - - Item_SellersItemIdentification_ID - - Item_Name - - InvoicedQuantity - - SesExtension_UnitPrice - - Price_PriceAmount - - Sum -``` - -You can configure the ERP date format and also the date format that is used in the shop: - -``` yaml -parameters: - #date format that is used and displayed in shop - #combination of these characters is allowed: Y,m,d - #accepted delimiters are: "/" , "." and "-" - #Example: d.m.Y, Y/m/d, d-m-Y... - siso_order_history.default.date_format: 'd.m.Y' - - #date format that is used for communication with ERP - siso_order_history.default.erp_date_format: 'Ymd' -``` diff --git a/docs/guide/order_history/orderhistory_templates.md b/docs/guide/order_history/orderhistory_templates.md deleted file mode 100644 index 0b3fe80db8..0000000000 --- a/docs/guide/order_history/orderhistory_templates.md +++ /dev/null @@ -1,31 +0,0 @@ -# Order history templates [[% include 'snippets/commerce_badge.md' %]] - -## Template list - -|Path|Description| -|--- |--- | -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/list.html.twig`|Renders the list view of requested documents.| -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/Components/list_table.html.twig`|Renders the table with list of requested documents. Included in `views/OrderHistory/list.html.twig`.| -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/detail.html.twig`|Renders the detail view of the requested document.| -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/Components/header_default.html.twig`|Renders the header information for document detail. Included in `views/OrderHistory/detail.html.twig`.| -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/Components/fields.html.twig`|Contains blocks that render the content of the requested field for columns (defined in the configuration). Included in `views/OrderHistory/Components/list_table.html.twig` and `views/OrderHistory/detail.html.twig`.| -|`vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views/OrderHistory/Components/user_menu.html.twig`|| - -## Custom Twig functions - -|Twig filter|Description|Usage| -|--- |--- |--- | -|`ses_erp_to_default`|Converts the ERP date into default date format.|`{{ 'Order at:'|st_translate }} {{ response.OrderReference.IssueDate.value|ses_erp_to_default }} {{ response.OrderReference.IssueDate.value|ses_erp_to_default }}`| -|`ses_to_float`|Converts the given value to a valid float (also strings that use comma instead of dot).|`{{ 'VAT:'|st_translate }} {{ vat.TaxAmount.value|ses_to_float|price_format }}`| - -## Related routes - -``` yaml -siso_order_history_list: - path: /orderhistory - defaults: { _controller: SisoOrderHistoryBundle:OrderHistory:list } - -siso_order_history_detail: - path: /orderhistory/detail/{documentType}/{id} - defaults: { _controller: SisoOrderHistoryBundle:OrderHistory:detail } -``` diff --git a/docs/guide/order_history/overriding_semantic_configuration.md b/docs/guide/order_history/overriding_semantic_configuration.md deleted file mode 100644 index 28b0cda7c1..0000000000 --- a/docs/guide/order_history/overriding_semantic_configuration.md +++ /dev/null @@ -1,83 +0,0 @@ -# Overriding semantic configuration per SiteAccess [[% include 'snippets/commerce_badge.md' %]] - -The semantic configuration (`orderhistory.yml`) is read and prepared in the `ConfigurationReaderService`. -When an event is dispatched, the service enables you to modify the configuration, for example if you need to change it per SiteAccess. - -``` php -$list = %siso_order_history.list%; -$document_types = %siso_order_history.document_types%; -$default_document_type = %siso_order_history.default_document_type%; -$date = %siso_order_history.date%; -$default_list_fields = %siso_order_history.default_list_fields%; -$default_list_sort = %siso_order_history.default_list_sort%; -$default_list_sort_column = %siso_order_history.default_list_sort_column%; -$default_detail_fields = %siso_order_history.default_detail_fields%; - -$data = array( - 'list' => $list, - 'document_types' => $document_types, - 'default_document_type' => $default_document_type, - 'date' => $this->dateTimeService->formatDateAmountToDate($date), - 'default_list_fields' => $default_list_fields, - 'default_list_sort' => $default_list_sort, - 'default_list_sort_column' => $default_list_sort_column, - 'default_detail_fields' => $default_detail_fields -); - -// ... - -/** - * Dispatch the event ConfigurationEvents::READ_CONFIGURATION - * The purpose of this event is to have a possibility to change the existing configuration depending on the siteAccess. - * - * @return void - */ -protected function getConfigurationData() -{ - /** @var ReadConfigurationEvent $event */ - $event = new ReadConfigurationEvent(); - $event->setData($this->data); - - $this->eventDispatcher->dispatch( - ConfigurationEvents::READ_CONFIGURATION, - $event - ); - - $this->data = $event->getData(); -} -``` - -You need to implement a `siso_order_history.read_configuration` listener. - -Add a configuration per SiteAccess: - -``` yaml -parameters: - siso_order_history.default.document_types: - - order - siso_order_history.default.default_document_type: order -``` - -Implement an event listener: - -``` php -public function onReadConfiguration(ReadConfigurationEvent $readConfigurationEvent) -{ - $data = $readConfigurationEvent->getData(); - - $data['document_types'] = $this->configResolver->getParameter('document_types', 'siso_order_history'); - $data['default_document_type'] = $this->configResolver->getParameter( - 'default_document_type', - 'siso_order_history' - ); - - $readConfigurationEvent->setData($data); -} -``` - -``` xml -<service id="project.read_configuration_listener" class="%project.read_configuration_listener.class%"> - <argument type="service" id="ezpublish.config.resolver"/> - <tag name="kernel.event_listener" event="siso_order_history.read_configuration" method="onReadConfiguration" /> -</service> -``` diff --git a/docs/guide/page.md b/docs/guide/page.md index fc1ee1eee3..cd8f317256 100644 --- a/docs/guide/page.md +++ b/docs/guide/page.md @@ -1,6 +1,10 @@ -# Pages in the shop [[% include 'snippets/commerce_badge.md' %]] +--- +edition: experience +--- -[[= product_name_com =]] adds the following blocks to the Page Builder: +# Pages in the shop + +[[= product_name_exp =]] adds the following blocks to the Page Builder: - Last viewed items - list of products recently viewed by the current user - Bestsellers - list of bestsellers for a given product category diff --git a/docs/guide/page/create_custom_page_block.md b/docs/guide/page/create_custom_page_block.md new file mode 100644 index 0000000000..fd39812187 --- /dev/null +++ b/docs/guide/page/create_custom_page_block.md @@ -0,0 +1,75 @@ +--- +description: Create and configure custom Page blocks to add customized content to Pages. +--- + +# Create custom Page block + +In addition to existing blocks which you can use in a Page, you can also create custom blocks. + +To do this, add block configuration in a YAML file, under the `ezplatform_page_fieldtype` key, +for example in `config/packages/ezplatform_page_builder.yaml`. + +The following example shows how to create a block that showcases an event. + +## Configure block + +First, add the following YAML configuration: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 6) =]][[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 16, 39) =]] +``` + +`event` is the internal name for the block, and `name` indicates the name under which the block is available in the interface. +You also set up the category in the Elements panel that the block appears in. +In this case, it doesn't show with the rest of the built-in blocks, but in a separate "Custom" category. +The thumbnail for the block can be one of the pre-existing icons, like in the example above, +or you can use a custom SVG file. + +A block can have multiple attributes that you edit when adding it to a Page. +In this example, you configure three attributes: name of the event, category it belongs to, +and an event Content item that you select and embed. + +For a list of all available attribute types, see [Page block attributes](page_block_attributes.md). + +Each attribute can have [validators](page_block_validators.md). The `not_blank` validators in the example ensure that the user fills in the two block fields. + +## Add block templates + +A block can have different templates that you select when adding it to a Page. + +To configure block templates, add them to block configuration: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 3) =]][[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 7, 16) =]] +``` + +Provide the templates in the indicated folder, in this case in `templates/themes/<your_theme>/blocks/event`. + +For example the `featured_template.html.twig` file can look like this: + +``` html+twig +[[= include_file('code_samples/page/custom_page_block/templates/themes/standard/blocks/event/featured_template.html.twig') =]] +``` + +The templates have access to all block attributes, as you can see above in the `name`, `category` and `event` variables. + +Priority of templates indicates the order in which they are presented in Page Builder. +The template with the greatest priority is used as the default one. + +## Add edit templates + +You can also customize the template for the block settings modal. +Do this under the `configuration_template` key: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 7) =]] +``` + +Place the edit template in `templates/themes/<your_theme>/blocks/event/config.html.twig'`: + +``` html+twig +[[= include_file('code_samples/page/custom_page_block/templates/themes/standard/blocks/event/config.html.twig') =]] +``` + +Your custom page block is now ready. +Before you can use it in Page Builder, you must [enable it in Page field settings]([[= user_doc =]]/content_management/configure_ct_field_settings/#block-display). \ No newline at end of file diff --git a/docs/guide/page/page_block_attributes.md b/docs/guide/page/page_block_attributes.md new file mode 100644 index 0000000000..b0c712c476 --- /dev/null +++ b/docs/guide/page/page_block_attributes.md @@ -0,0 +1,198 @@ +--- +description: Page blocks can contain multiple attributes, of both built-in and custom types. +--- + +# Page block attributes + +A block has attributes that the editor fills in when adding th block to a Page. +Each block can have the following properties: + +|Attribute|Description| +|----|----| +|`type`| Attribute type. | +|`name`| (Optional) The displayed name for the attribute. You can omit it, block identifier is then used as the name. | +|`value`| (Optional) The default value for the attribute. | +|`category`| (Optional) The tab where the attribute is displayed in the block edit modal. | +|`validators`| (Optional) [Validators](page_block_validators.md) checking the attribute value. | +|`options`| (Optional) Additional options, dependent on the attribute type. | + +## Block attribute types + +The following attribute types are available: + +|Type|Description|Options| +|----|----|----| +|`integer`|Integer value|-| +|`string`|String|-| +|`url`|URL|-| +|`text`|Text block|-| +|`richtext`|Rich text block (see [creating RichText block](../../extending/richtext_block.md))|-| +|`embed`|Embedded Content item|-| +|`select`|Drop-down with options to select|`choices` lists the available options</br>`multiple`, when set to true, allows selecting more than one option. +|`multiple`|Checkbox(es)|`choices` lists the available options.| +|`radio`|Radio buttons|`choices` lists the available options.| +|`locationlist`|Location selection|-| +|`contenttypelist`|List of Content Types|-| +|`schedule_events`,</br>`schedule_snapshots`,</br>`schedule_initial_items`,</br>`schedule_slots`,</br>`schedule_loaded_snapshot`|Used in the Content Scheduler block|-| +|`nested_attribute`|Defines a group of attributes in a block.|`attributes` - a list of attributes in the group. The attributes in the group are [configured](#page-block-attributes) as regular attributes. </br>`multiple`, when set to true. New groups are added dynamically with the **Add field group** button.| + +When you define attributes, you can omit most keys as long as you use simple types that do not require additional options: + +``` yaml +attributes: + first_field: text + second_field: string + third_field: integer +``` + +## Custom attribute types + +You can create custom attribute type to add to Page blocks. + +A custom attribute requires attribute type class, a mapper and a template. + +### Block attribute type + +First, create the attribute type class. + +It can extend one of the types available in `ezplatform-page-fieldtype/src/lib/Form/Type/BlockAttribute/`. +You can also use one of the [built-in Symfony types]([[= symfony_doc =]]/reference/forms/types.html), +for example `AbstractType` for any custom type or `IntegerType` for numeric types. + +To define the type, create a `src/Block/Attribute/MyStringAttributeType.php` file: + +``` php hl_lines="5 6 15" +[[= include_file('code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeType.php') =]] +``` + +Note that the attribute uses `AbstractType` (line 5) and `TextType` (line 6). +Adding `getBlockPrefix` (line 15) returns a unique prefix key for a custom template of the attribute. + +### Mapper + +At this point, the attribute type configuration is complete, but it requires a mapper. +Depending on the complexity of the type, you can use a `GenericFormTypeMapper` or create your own. + +#### Generic mapper + +For a generic mapper, add a new service definition to `config/services.yaml`: + +``` yaml +my_application.block.attribute.my_string: + class: EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Attribute\FormTypeMapper\GenericFormTypeMapper + arguments: + $formTypeClass: App\Block\Attribute\MyStringAttributeType + tags: + - { name: ezplatform.page_builder.attribute_form_type_mapper, alias: my_string } +``` + +#### Custom mapper + +To use a custom mapper, create a class that inherits from `EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Attribute\FormTypeMapper\AttributeFormTypeMapperInterface`, +for example in `src/Block/Attribute/MyStringAttributeMapper.php`: + +``` php +[[= include_file('code_samples/page/custom_attribute/src/Block/Attribute/MyStringAttributeMapper.php') =]] +``` + +Then, add a new service definition for your mapper to `config/services.yaml`: + +``` yaml +App\Block\Attribute\MyStringAttributeMapper: + tags: + - { name: ezplatform.page_builder.attribute_form_type_mapper, alias: my_string } +``` + +### Edit templates + +Next, configure a template for the attribute edit form by creating a `templates/custom_form_templates.html.twig` file: + +``` html+twig +{% block my_string_attribute_widget %} + <h2>My String</h2> + {{ form_widget(form) }} +{% endblock %} + +{# more templates here #} +``` + +Add the template to your configuration: + +``` yaml +system: + default: + page_builder_forms: + block_edit_form_templates: + - { template: custom_form_templates.html.twig, priority: 0 } +``` + +### Custom attribute configuration + +Now, you can create a block containing your custom attribute: + +``` yaml hl_lines="12-16" +[[= include_file('code_samples/page/custom_attribute/config/packages/page_blocks.yaml') =]] +``` + +### Nested attribute configuration + +The `nested_attribute` attribute is used when you want to create a group of attributes. + +First, make sure you have configured the attributes you want to use in the group. + +Next, provide the configuration. See the example: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/nested_attribute.yaml', 0,16) =]][[= include_file('code_samples/page/custom_page_block/config/packages/nested_attribute.yaml', 19,23) =]] +``` + +To set validation for each nested attribute: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/nested_attribute.yaml', 9,19) =]] +``` + +Validators can be also set on a parent attribute (group defining level), it means all validators apply to each nested attribute: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/nested_attribute.yaml', 9,16) =]] [[= include_file('code_samples/page/custom_page_block/config/packages/nested_attribute.yaml', 19,26) =]] +``` + +!!! caution "Moving attributes between groups" + + If you move an attribute between groups or add an ungrouped attribute to a group, + the block values are removed. + +## Help messages for form fields + +With the `help`, `help_attr`, and `help_html` field options, you can set help messages for fields in the Page block. + +You can set options with the following configuration: + +```yaml +ezplatform_page_fieldtype: + blocks: + block_name: + attributes: + attribute_name: + options: + help: + text: 'Some example text' + html: true|false + attr: + class: 'class1 class2' +``` + +- `help` - defines a help message which is rendered below the field. +- `help_attr` - sets the HTML attributes for the element which displays the help message. +- `help_html` - set this option to `true` to disable escaping the contents of the `help` option when rendering in the template. + +### Help message in nested attributes + +You can set the options for root or nested attribute, see the example configuration: + +```yaml +[[= include_file('code_samples/page/custom_attribute/config/packages/help_messages.yaml') =]] +``` + +![Help message](page_block_help_message.png "Help message") diff --git a/docs/guide/page/page_block_validators.md b/docs/guide/page/page_block_validators.md new file mode 100644 index 0000000000..c43b71f610 --- /dev/null +++ b/docs/guide/page/page_block_validators.md @@ -0,0 +1,67 @@ +--- +description: Set up rules for validating Page block content. +--- + +# Page block validators + +Validators check values passed to Page block attributes. +The following block validators are available: + +- `required` - checks whether the attribute is provided +- `regexp` - validates attribute according to the provided regular expression +- `not_blank` - checks whether the attribute is not left empty +- `not_blank_richtext` - checks whether a `richtext` attribute is not left empty +- `content_type` - checks whether the selected Content Types match the provided values +- `content_container` - checks whether the selected Content item is a container + +!!! note + + Do not use the `required` and `not_blank` validators for `richtext` attributes. + Instead, use `not_blank_richtext`. + +For each validator you can provide a message that displays in the Page Builder +when an attribute field does not fulfil the criteria. + +Additionally, for some validators you can provide settings in the `options` key, for example: + +``` yaml +email: + type: string + name: E-mail address + validators: + regexp: + options: + pattern: '/^\S+@\S+\.\S+$/' + message: Provide a valid e-mail address +``` + +## Custom validators + +You can create Page block attributes with custom validators. + +The following example shows how to create a validator which requires that string attributes contain only alphanumerical characters. + +First, create classes that support your intended method of validation. +For example, in `src/Validator`, create an `AlphaOnly.php` file: + +``` php +[[= include_file('code_samples/page/custom_block_validator/src/Validator/AlphaOnly.php') =]] +``` + +In `src/Validator`, create an `AlphaOnlyValidator.php` class that performs the validation. + +``` php +[[= include_file('code_samples/page/custom_block_validator/src/Validator/AlphaOnlyValidator.php') =]] +``` + +Then, in `config/packages/ezplatform_page_fieldtype.yaml` enable the new validator in Page Builder: + +``` yaml +[[= include_file('code_samples/page/custom_block_validator/config/packages/page_blocks.yaml', 0, 3) =]] +``` + +Finally, add the validator to one of your block attributes in `config/packages/ezplatform_page_fieldtype.yaml`, for example: + +``` yaml hl_lines="16-18" +[[= include_file('code_samples/page/custom_block_validator/config/packages/page_blocks.yaml', 0, 1) =]][[= include_file('code_samples/page/custom_block_validator/config/packages/page_blocks.yaml', 3, 20) =]] +``` diff --git a/docs/guide/page/page_blocks.md b/docs/guide/page/page_blocks.md new file mode 100644 index 0000000000..4d77ffcebd --- /dev/null +++ b/docs/guide/page/page_blocks.md @@ -0,0 +1,128 @@ +--- +description: Use blocks to customize the content of a Page with dynamic content. +--- + +# Page blocks + +Page blocks are configured in YAML files, under the `ezplatform_page_fieldtype` key. + +!!! caution + + Page block configuration is not SiteAccess-aware. + +!!! tip + + For information on how to create and configure new layouts for the Page, + see [Page layouts](../content_rendering/render_content/render_page.md#render-a-layout). + +## Block configuration + +Each configured block has an identifier and the following settings: + +|Setting|Description| +|---|---| +| `name` | Name of the block used in the Page Builder interface. | +| `category` | Category in the Page Builder elements menu that the block is shown in. | +| `thumbnail` | Thumbnail used in the Page Builder elements menu. | +| `views` | Available [templates for the block](#block-templates). | +| `visible` | (Optional) Toggles the block's visibility in the Page Builder elements menu. Remove the block from the layout before you publish another version of the page. | +| `configuration_template` | (Optional) Template for the block settings modal. | +| `attributes` | (Optional) List of [block attributes](page_block_attributes.md). | + +For example: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 12) =]][[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 16, 17) =]]# ... +``` + +!!! tip + + For a full example of block configuration, see [Create custom Page block](create_custom_page_block.md). + +### Overwriting existing blocks + +You can overwrite the following properties in the existing blocks: + +- `name` +- `category` +- `thumbnail` +- `views` + +## Block templates + +Page blocks can have multiple templates. +This allows you to create different styles for each block and let the editor choose them when adding the block from the UI. + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 3) =]][[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 7, 16) =]] +``` + +`priority` defines the order of block views on the block configuration screen. +The highest number shows first on the list. + +!!! tip + + Default views have a `priority` of -255. + It is good practice to keep the value between -255 and 255. + +### Block modal template + +The template for the configuration modal of built-in Page blocks is contained in +`vendor/ezsystems/ezplatform-page-builder/src/bundle/Resources/views/page_builder/block/config.html.twig`. + +You can override it by using the `configuration_template` setting: + +``` yaml +[[= include_file('code_samples/page/custom_page_block/config/packages/page_blocks.yaml', 0, 7) =]] +``` + +The template can extend the default `config.html.twig` and modify its blocks. +Blocks `basic_tab_content` and `design_tab_content` correspond to the **Basic** and **Design** tabs in the modal. + +The following example wraps all form fields for block attributes in an ordered list: + +``` html+twig +[[= include_file('code_samples/page/custom_page_block/templates/themes/standard/blocks/event/config.html.twig') =]] +``` + +## Block events + +To add functionalities to your block that go beyond the available attributes, +you can use an event listener. + +You can listen to events related to block definition and block rendering. + +The following events are available: + +- `BlockDefinitionEvents::getBlockDefinitionEventName` - dispatched when block definition is created +- `BlockDefinitionEvents::getBlockAttributeDefinitionEventName` - dispatched when block attribute definition is created +- `BlockRenderEvents::getBlockPreRenderEventName` - dispatched before a block is rendered +- `BlockRenderEvents::getBlockPostRenderEventName` - dispatched after a block is rendered + +For example, to modify a block by adding a new parameter to it, you can create the following listener: + +``` php +[[= include_file('code_samples/page/page_listener/src/Block/Listener/MyBlockListener.php') =]] +``` + +Before the block is rendered, the listener adds `my_parameter` to it with value `parameter_value`. +You can use this parameter, for example, in block template: + +``` html+twig +[[= include_file('code_samples/page/page_listener/templates/my_block.html.twig') =]] +``` + +#### Exposing content relations from blocks + +Page blocks, for example Embed block or Collection block, can embed other Content items. +Publishing a Page with such blocks creates Relations to those Content items. + +When creating a custom block with embeds, you can ensure such Relations are created using the block Relation collection event. + +The event is dispatched on content publication. +You can hook your event listener to the `BlockRelationEvents::getCollectBlockRelationsEventName` event. + +To expose relations, pass an array containing Content IDs to the `\EzSystems\EzPlatformPageFieldType\Event\CollectBlockRelationsEvent::setRelations()` method. +If embedded Content changes, old Relations are removed automatically. + +Providing Relations also invalidates HTTP cache for your block response in one of the related Content items changes. diff --git a/docs/guide/page_rendering.md b/docs/guide/page_rendering.md deleted file mode 100644 index fd11b7b87e..0000000000 --- a/docs/guide/page_rendering.md +++ /dev/null @@ -1,140 +0,0 @@ -# Page rendering [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -### Page layouts - -A Page has a customizable layout with multiple zones where you can place blocks with content. - -A clean installation has only one default layout. You can preview more layouts in the Demo bundle. - -A Page layout is composed of zones. - -##### Zone structure - -Each zone contains the following (required) parameters: - -| Name | Description | -|------------------|------------------------------| -| `zone_id` | A unique zone ID | -| `name` | Zone name | - -#### Layout configuration - -The layout is configured in YAML files: - -``` yaml -ezplatform_page_fieldtype: - layouts: - sidebar: - identifier: sidebar - name: Right sidebar - description: Main section with sidebar on the right - thumbnail: /assets/images/layouts/sidebar.png - template: layouts/sidebar.html.twig - zones: - first: - name: First zone - second: - name: Second zone -``` - -The following parameters need to be included in the settings of the configuration: - -|Parameter|Type|Description|Required| -|------|------|------|------| -|layouts|string|Layout config root|Yes| -|number|string|Unique key of the layout|Yes| -|{ID}/identifier|string|ID of the layout|Yes| -|{ID}/name|string|Name of the layout|Yes| -|{ID}/description|string|Description of the layout|Yes| -|{ID}/thumbnail|string|<path> to thumbnail image|Yes| -|{ID}/template|string|<path> to template View|Yes| -|{ID}/zones|string|Collection of zones|Yes| -|{ID}/{zone}/zone_id|string|ID of the zone|Yes| -|{ID}/{zone}/name|string|Zone name|Yes| - -#### Layout template - -A layout template will include all zones the layout contains. - -A zone is a container for blocks. Each zone must have a `data-ez-zone-id` attribute. - -The best way to display blocks in the zone is to iterate over a blocks array and render the blocks in a loop. -Each block must have the `landing-page__block block_{{ block.type }}` classes and the `data-ez-block-id="{{ block.id }}` attribute (see line 9). - -``` html+twig hl_lines="9" -<div> - <div data-ez-zone-id="{{ zones[0].id }}"> - {# If the first zone (with index [0]) contains any blocks #} - {% if zones[0].blocks %} - {# for each block #} - {% for block in zones[0].blocks %} - {# create a new layer with appropriate id #} - {# the div's class takes the type of the block that is placed in it #} - <div class="landing-page__block block_{{ block.type }}" data-ez-block-id="{{ block.id }}"> - {# render the block by using the "EzPlatformPageFieldTypeBundle\Controller\BlockController:renderAction" controller #} - {{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController:renderAction', { - 'contentId': contentInfo.id, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo, - 'languageCode': field.languageCode - })) - }} - </div> - {% endfor %} - {% endif %} - </div> - <div data-ez-zone-id="{{ zones[1].id }}"> - {# Repeat the same for the second zone, with index [1] #} - {% if zones[1].blocks %} - {% for block in zones[1].blocks %} - <div class="landing-page__block block_{{ block.type }}" data-ez-block-id="{{ block.id }}"> - {{ render_esi(controller('EzPlatformPageFieldTypeBundle\Controller\BlockController:renderAction', { - 'contentId': contentInfo.id, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo, - 'languageCode': field.languageCode - })) - }} - </div> - {% endfor %} - {% endif %} - </div> -</div> -``` - -## Block templates - -Every built-in Page block has a default template. You can [add new templates](../guide/templates.md) to blocks or override the default ones. - -### Adding new block templates - -You can add new block templates with the YAML config, for example for the Gallery block: - -``` yaml -blocks: - gallery: - views: - special: - template: blocks/gallery/special.html.twig - name: Special view -``` - -### Overriding default block templates - -To override the default block template, create a new template. -Place it in a path that mirrors the original default template from the bundle folder. -For example: -`templates/bundles/EzPlatformPageFieldTypeBundle/blocks/gallery.html.twig`. - -!!! tip - - To use a different file structure when overriding default templates, - add an import statement to the template. - - For example, in `/app/Resources/EzPlatformPageFieldTypeBundle/views/blocks/gallery.html.twig`: - - ``` html+twig - {% import 'app/Resources/views/blocks/gallery/new_default.html.twig'} - ``` - - Then, place the actual template in the imported file `app/Resources/views/blocks/gallery/new_default.html.twig`. diff --git a/docs/guide/payment/payment.md b/docs/guide/payment/payment.md index 112292cb0d..7f64c8c0ae 100644 --- a/docs/guide/payment/payment.md +++ b/docs/guide/payment/payment.md @@ -1,6 +1,11 @@ -# Payment [[% include 'snippets/commerce_badge.md' %]] +--- +description: Payments in Ibexa DXP's shop use JMSPaymentCoreBundle and allow using different payment methods. +edition: commerce +--- -Payment in [[= product_name_com =]] depends on the [JMSPaymentCoreBundle.](http://jmsyst.com/bundles/JMSPaymentCoreBundle) +# Payment + +Payment in [[= product_name =]] depends on the [JMSPaymentCoreBundle.](http://jmsyst.com/bundles/JMSPaymentCoreBundle) !!! note @@ -24,4 +29,4 @@ The respective options are registered in the checkout form and must fulfill some - The form type must define the `payment.method_form_type` tag. - The form type must define the `form.type` tag with an alias attribute that is used as text in the payment choice. -You define the compiler pass in the checkout bundle: `Siso\Bundle\CheckoutBundle\DependencyInjection\Compiler\PaymentMethodsPass` +You define the compiler pass in the checkout bundle: `Ibexa\Platform\Bundle\Commerce\Checkout\DependencyInjection\Compiler\PaymentMethodsPass` diff --git a/docs/guide/payment/payment_api.md b/docs/guide/payment/payment_api.md index f420ff7b30..f9945e450d 100644 --- a/docs/guide/payment/payment_api.md +++ b/docs/guide/payment/payment_api.md @@ -1,4 +1,9 @@ -# Payment API [[% include 'snippets/commerce_badge.md' %]] +--- +description: The PHP API exposes a PaymentServiceInterface to access and work with payments. +edition: commerce +--- + +# Payment API ## Payment bundle @@ -36,7 +41,7 @@ that was provided by the payment gateway. For this, the interface provides the ` A payment plugin (and most payment gateways) requires storing the return URLs in the extended data of the `PaymentInstruction` entity. This is because the related basket/order ID needs to be appended to the URL to determine the basket and the related payment instruction for further processing in the returning URL requests. -It is impossible to configure the URLs via the Symfony container, as the basket ID is determined dynamically. +It is impossible to configure the URLs via the [service container](../../api/public_php_api.md#service-container), as the basket ID is determined dynamically. You must implement this interface for every payment plugin, if more than the standard fields like amount are necessary for the payment process. diff --git a/docs/guide/payment/payment_troubleshooting.md b/docs/guide/payment/payment_troubleshooting.md index 8e47d50176..41d60e96f1 100644 --- a/docs/guide/payment/payment_troubleshooting.md +++ b/docs/guide/payment/payment_troubleshooting.md @@ -1,4 +1,9 @@ -# Payment troubleshooting [[% include 'snippets/commerce_badge.md' %]] +--- +description: Resolve most common issues with payments in Ibexa DXP shop. +edition: commerce +--- + +# Payment troubleshooting ## Extending the order size limit diff --git a/docs/guide/payment/paypal.md b/docs/guide/payment/paypal.md index aad91f31e7..16fa4a1070 100644 --- a/docs/guide/payment/paypal.md +++ b/docs/guide/payment/paypal.md @@ -1,15 +1,22 @@ -# PayPal Express Checkout [[% include 'snippets/commerce_badge.md' %]] +--- +description: You can configure Ibexa DXP to allow PayPal Express Checkout as one of the payment methods. +edition: commerce +--- -## Installation and configuration +# PayPal -PayPal Express Checkout payment requires the third-party [`JMSPaymentPaypalBundle`](http://jmspaymentpaypalbundle.readthedocs.io/en/stable/setup.html) library. +## Enabling PayPal Express Checkout + +PayPal Express Checkout payment requires the third-party `JMSPaymentPaypalBundle` library. +The library's fork is available at the following [location](https://github.com/ezsystems/JMSPaymentPaypalBundle/releases/tag/v2.0.0). +Make sure that you are using the latest version v2.0.0. ``` bash -php composer.phar require jms/payment-paypal-bundle -php composer.phar update -- jms/payment-paypal-bundle +php composer.phar require ezsystems/payment-paypal-bundle +php composer.phar update -- ezsystems/payment-paypal-bundle ``` -See [How to get the API credentials](#how-to-get-the-api-credentials) to learn where you can find the values for the `JMSPaymentPaypalBundle` configuration in the PayPal merchant's administration. +See [How to get the API credentials](#getting-api-credentials) to learn where you can find the values for the `JMSPaymentPaypalBundle` configuration in the PayPal merchant's administration. Additionally, you must activate `SisoPaypalPaymentBundle` in the kernel, and include the routes: @@ -25,7 +32,7 @@ _siso_paypal_payment: resource: '@SisoPaypalPaymentBundle/Resources/config/routing.yml' ``` -## How to get the API credentials +## Getting API credentials Provide your [PayPal API credentials](https://developer.paypal.com/docs/nvp-soap-api/apiCredentials/#api-certificates) in configuration: diff --git a/docs/guide/performance.md b/docs/guide/performance.md index 83210d5532..0915dcb617 100644 --- a/docs/guide/performance.md +++ b/docs/guide/performance.md @@ -1,3 +1,7 @@ +--- +description: Ensure that your Ibexa DXP installation performs well by following our set of recommendations. +--- + # Performance [[= product_name =]] can be set up to run efficiently on almost any modern configuration. @@ -38,10 +42,6 @@ In production setups: - Avoid shared filesystems for code (Docker for Mac/Win, VirtualBox/*, Vagrant, etc.), because they typically slow down the application 10x or more, compared to native Linux filesystem. - VM in itself also adds 10-30% of overhead. However when it comes to production, e.g. AWS vs barebones, it also comes down to cost and convenience factors. -!!! tip "For Development use, try eZ Launchpad" - - For a ready solution that allows you to share code between your host and the underlying running VM system without this performance hit, try [eZ Launchpad](https://ezsystems.github.io/launchpad/), made by and supported by the eZ Community. - ### Web server - Use Nginx/Apache even for development, as PHP's built-in web server (as exposed via Symfony's `server:*` commands) is only able to handle one request at a time (including JS/CSS/* asset loading, etc.). @@ -80,7 +80,7 @@ In production setups: - Use [Solr Bundle and Solr](search/solr.md) to greatly offload your database and get more stable performance on your installation. -## Executing long-running console commands +## Long-running console commands Executing long-running console commands can result in running out of memory. Two examples of such commands are a custom import command and the indexing command provided by the [Solr Bundle](search/solr.md). @@ -134,4 +134,4 @@ The things you will need to do: 2. Change the command so that the master process takes care of forking child processes in slices. 1. For execution in-order, [you may look to our platform installer code](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/PlatformInstallerBundle/src/Command/InstallPlatformCommand.php#L220) used to fork out Solr indexing after installation to avoid cache issues. - 2. For parallel execution of the slices, [see Symfony doc for further instruction](http://symfony.com/doc/5.0/components/process.html#process-signals). + 2. For parallel execution of the slices, [see Symfony doc for further instruction]([[= symfony_doc =]]/components/process.html#process-signals). diff --git a/docs/guide/permission_use_cases.md b/docs/guide/permission_use_cases.md new file mode 100644 index 0000000000..7427a093b3 --- /dev/null +++ b/docs/guide/permission_use_cases.md @@ -0,0 +1,206 @@ +--- +description: Set up permission sets for common use cases. +--- + +# Permission use cases + +Here are a few examples of sets of Policies that you can use to get some common permission configurations. + +## Enter Back Office + +To allow the User to enter the Back Office interface and view all content, set the following Policies: + +- `user/login` +- `content/read` +- `content/versionread` +- `section/view` +- `content/reverserelatedlist` + +These Policies are necessary for all other cases below that require access to the content structure. + +## Create content without publishing [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +You can use this option together with [[= product_name_exp =]]'s content review options. +Users assigned with these Policies can create content, but can't publish it. +To publish, they must send the content for review to another User with proper permissions +(for example, senior editor, proofreader, etc.). + +- `content/create` +- `content/edit` + +Use this setup with [[= product_name_exp =]] or [[= product_name_com =]] only, +as [[= product_name_content =]] does not allow the User to continue working with their content. + +## Create and publish content + +To create and publish content, users must additionally have the following Policies: + +- `content/create` +- `content/edit` +- `content/publish` + +This also lets the user copy and move content, as well as add new Locations to a Content item (but not remove them). +## Move content + +To move a Content item or a Subtree to another Location, the user must have the following Policies: + +- `content/read` - on the source Location +- `content/create` - on the target Location +## Remove content + +To send content to Trash, the User needs to have the `content/remove` Policy. +If content has more than one language, the User must have access to all the languages. +That is, the `content/remove` Policy must have either no Limitation, or a Limitation for all languages of the Content item. + +To remove an archived version of content, the User must have the `content/versionremove` Policy. + +Further manipulation of Trash requires the `content/restore` Policy to restore items from Trash, and `content/cleantrash` to completely delete all content from the Trash. + +!!! caution + + With the `content/cleantrash` Policy, the User can empty the Trash even if they do not have access to the trashed content, + for example, because it belonged to a Section that the User does not have permissions for. + +## Restrict editing to part of the tree + +If you want to let the User create or edit content, but only in one part of the content tree, use Limitations. +Three Limitations that you could use here are `Section` Limitation, `Location` Limitation and `Subtree of Location` Limitation. + +### Section Limitation + +Let's assume you have two Folders under your Home: Blog and Articles. +You can let a User create content for the blogs, but not in Articles, by adding a `Section` Limitation to +the Blog Content item. +This allows the User to publish content anywhere under this Location in the structure. +Section does not have to belong to the same Subtree of Location in the content structure, any Locations can be assigned to it. + +### Location Limitation + +If you add a `Location` Limitation and point to the same Location, the User is able to publish content directly +under the selected Location, but not anywhere deeper in its Subtree of Location. + +### Subtree of Location Limitation + +To limit the User's access to a subtree, use the `Subtree of Location` Limitation. +You do it by creating two new Roles for a User Group: + + 1. Role with a `Subtree` Limitation for the User + 1. Role with a `Location` Limitation for the Subtree + +Follow the example below to learn how to do that. + +**Cookbook**, **Dinner recipes** and **Dessert recipes** containers are not accessible in the frontend. +Edit access to them in the **Admin Panel**. + +![Subtree file structure](img/subtree_usability_notes_1.png) + +To give the vegetarian editors access only to the **Vegetarian** dinner recipes section, + create a new Role e.g. *EditorVeg*. +Next, add to it a `content/read` Policy with the `Subtree` Limitation for `Cookbook/Dinner recipes/Vegetarian`. +Assign the Role to the vegetarian editors User Group. +It allows users from that group to access the **Vegetarian** container but not **Cookbook** and **Dinner recipes**. + +To give users access to **Cookbook** and **Dinner recipes** containers, +create a new Role, for example, *EditorVegAccess*. +Next, add to it a `content/read` Policy with the `Location` Limitations **Cookbook** and **Dinner recipes**. +Assign the new Role to the vegetarian editors User Group as well. +Only then the limitations are combined with `AND`, resulting in an empty set. + +The vegetarian editors should now see the following Content Tree: + +![Limited subtree file structure](img/subtree_usability_notes_2.png) + +When a Policy has more than one Limitation, all of them have to apply, or the Policy does not work. +For example, a `Location` Limitation on Location `1/2` and `Subtree of Location` Limitation on `1/2/55` cannot work together, +because no Location can satisfy both those requirements at the same time. +To combine more than one Limitation with the *or* relation, not *and*, +you can split your Policy in two, each with one of these Limitations. + +## Manage Locations + +To add a new Location to a Content item, the Policies required for publishing content are enough. +To allow the User to remove a Location, grant them the following Policies: + +- `content/remove` +- `content/manage_locations` + +Hiding and revealing Location requires one more Policy: `content/hide`. + +## Editorial workflows + +You can control which stages in an editorial workflow the user can work with. + +Do this by adding the `WorkflowStageLimitation` to `content` Policies such as `content/edit` or `content/publish`. + +You can also control which transitions the user can pass content through. +Do this by using the `workflow/change_stage` Policy together with the `WorkflowTransitionLimitation`. + +For example, to enable the user to edit only content in the "Design" stage +and to pass it after creating design to the "Proofread stage", use following permissions: + +- `content/edit` with `WorkflowStageLimitation` set to "Design". +- `workflow/change_stage` with `WorkflowTransitionLimitation` set to `to_proofreading` + +## Multi-file upload + +Creating content through multi-file upload is treated in the same way as regular creation. +To enable upload, you need you set the following permissions: + +- `content/create` +- `content/read` +- `content/publish` + +You can control what Content items can be uploaded and where by using Limitations +on the `content/create` and `content/publish` Policies. + +A Location Limitation limits the uploading to a specific Location in the tree. +A Content Type Limitation controls the Content Types that are allowed. +For example, you can set the Location Limitation on a **Pictures** Folder, and add a Content Type Limitation +that only allows Content items of type **Image**. +This ensures that only files of type `image` can be uploaded, +and only to the **Pictures** Folder. + +## Register Users + +To allow anonymous users to register through the `/register` route, grant the `user/register` Policy to the Anonymous User Group. + +## Admin + +To access the [administration panel](admin_panel.md) in the Back Office, the User must have the `setup/administrate` Policy. +This allows the User to view the languages and Content Types. + +Additional Policies are needed for each section of the Admin. + +### System Information + +- `setup/system_info` to view the System Information tab + +### Sections + +- `section/view` to see and access the Section list +- `section/edit` to add and edit Sections +- `section/assign` to assign Sections to content + +### Languages + +- `content/translations` to add and edit languages + +### Content Types/action + +- `Content Type/create`, `Content Type/update`, `Content Type/delete` to add, modify and remove Content Types + +### Object states + +- `state/administrate` to view a list of Object states, add and edit them +- `state/assign` to assign Objects states to Content + +### Roles + +- `role/read` to view the list of Roles in Admin +- `role/create`, `role/update`, `role/assign` and `role/delete` to manage Roles + +### Users + +- `content/view` to view the list of Users + +Users are treated like other content, so to create and modify them, the User needs to have the same permissions as for managing other Content items. diff --git a/docs/guide/permissions.md b/docs/guide/permissions.md index 1b48f58f5c..d8b63284e9 100644 --- a/docs/guide/permissions.md +++ b/docs/guide/permissions.md @@ -1,3 +1,7 @@ +--- +description: Use granular permission system to grant access to various parts of the system using Roles, Policies, and Limitations. +--- + # Permissions ## Permission overview @@ -15,7 +19,8 @@ For example, a `content/publish` Policy with a `ContentType` Limitation on the " A Limitation, like a Policy, specifies what a User *can* do, not what they *can't do*. A `Section` Limitation, for example, *gives* the User access to the selected Section, not *prohibits* it. -See [Available Limitations](limitations.md#available-limitations) for further information. +See [Available Limitations](limitations.md#available-limitations) for further information +and [Permission use cases](permission_use_cases.md) for example permission setups. ### Combining Policies @@ -35,154 +40,6 @@ Instead, try to organize your content so that it can be covered with general Rol Using Groups is easier to manage and more secure. It also improves system performance. The more Role assignments and complex Policies you add for a given User, the more complex the search/load queries will be, because they always take permissions into account. -### Use Cases - -Here are a few examples of sets of Policies you can use to get some common permission configurations. - -#### Enter back end interface - -To allow the User to enter the Back Office interface and view all content, you need to set the following Policies: - -- `user/login` -- `content/read` -- `content/versionread` -- `section/view` -- `content/reverserelatedlist` - -These Policies will be necessary for all other cases below that require access to the content structure. - -#### Create and publish content - -To create and publish content, the user must additionally have the following Policies: - -- `content/create` -- `content/edit` -- `content/publish` - -This also lets the user copy and move content, as well as add new Locations to a Content item (but not remove them!). - -#### Create content without publishing - -This option can be used together with [[= product_name_exp =]]'s content review options. -Using the following Policies, the User is able to create content, but can't publish it; instead, they must send it for review to another User with proper permissions (for example, senior editor, proofreader, etc.). - -- `content/create` -- `content/edit` - -Note that without [[= product_name_exp =]] this setup should not be used, as it will not allow the User to continue working with their content. - -#### Restrict editing to part of the tree - -If you want to let the User create or edit content, but only in one part of the content tree, you need to use Limitations. -Three Limitations that could be used here are `Section` Limitation, `Location` Limitation and `Subtree of Location` Limitation. - -Let's assume you have two Folders under your Home: Blog and Articles. -You can let a User create content for the blogs, but not in Articles by adding a `Subtree of Location` Limitation on the Blog Content item. -This will allow the User to publish content anywhere under this Location in the structure. - -A `Section` Limitation can be used similarly, but a Section does not have to belong to the same Subtree of Location in the content structure, any Locations can be assigned to it. - -If you add a `Location` Limitation and point to the same Location, the User will be able to publish content directly under the selected Location, but not anywhere deeper in its Subtree of Location. - -#### Multi-file upload - -Creating content through multi-file upload is treated in the same way as regular creation. -To enable upload, you need you set the following permissions: - -- `content/create` -- `content/read` -- `content/publish` - -You can control what Content items can be uploaded and where using Limitations on the `content/create` and `content/publish` Policies. - -A Location Limitation limits uploading to a specific Location in the tree. A Content Type Limitation controls the Content Types that are allowed. -For example, you can set the Location Limitation on a **Pictures** Folder, and add a Content Type Limitation -which only allows Content items of type **Image**. This ensures that only files of type `image` can be uploaded, -and only to the **Pictures** Folder. - -#### Manage Locations - -To add a new Location to a Content item, the Policies required for publishing content are enough. -To allow the User to remove a Location, you need to grant them the following Policies: - -- `content/remove` -- `content/manage_locations` - -Hiding and revealing Location requires one more Policy: `content/hide`. - -#### Removing content - -To send content to Trash, the User needs to have the `content/remove` Policy. - -To remove an archived version of content, the User must have the `content/versionremove` Policy. - -Further manipulation of Trash requires the `content/restore` Policy to restore items from Trash, and `content/cleantrash` to completely delete all content from the Trash. - -!!! caution - - With the `content/cleantrash` Policy, the User can empty the Trash even if they do not have access to the trashed content, - e.g. because it belonged to a Section they do not have permissions for. - -#### Registering Users - -To allow anonymous users to register through the `/register` route, you need to grant the `user/register` Policy to the Anonymous User Group. - -#### Admin - -To access the Admin in the Back Office the User must have the `setup/administrate` Policy. -This will allow the User to view the languages and Content Types. - -Additional Policies are needed for each section of the Admin. - -##### System Information - -- `setup/system_info` to view the System Information tab - -##### Sections - -- `section/view` to see and access the Section list -- `section/edit` to add and edit Sections -- `section/assign` to assign Sections to content - -##### Languages - -- `content/translations` to add and edit languages - -##### Content Types/action - -- `Content Type/create`, `Content Type/update`, `Content Type/delete` to add, modify and remove Content Types - -##### Object states - -- `state/administrate` to view a list of Object states, add and edit them -- `state/assign` to assign Objects states to Content - -##### Roles - -- `role/read` to view the list of Roles in Admin -- `role/create`, `role/update`, `role/assign` and `role/delete` to manage Roles - -##### Users - -- `content/view` to view the list of Users - -Users are treated like other content, so to create and modify them the User needs to have the same permissions as for managing other Content items. - -#### Editorial workflows - -You can control which stages in an editorial workflow the user can work with. - -Do this by adding the `WorkflowStageLimitation` to `content` Policies such as `content/edit` or `content/publish`. - -You can also control which transitions the user can pass content through. -Do this by using the `workflow/change_stage` Policy together with the `WorkflowTransitionLimitation`. - -For example, to enable the user to edit only content in the "Design" stage -and to pass it after creating design to the "Proofread stage", use following permissions: - -- `content/edit` with `WorkflowStageLimitation` set to "Design". -- `workflow/change_stage` with `WorkflowTransitionLimitation` set to `to_proofreading` - ## Available Policies | Module | Function | Effect | @@ -206,6 +63,7 @@ and to pass it after creating design to the "Proofread stage", use following per |   | `pendinglist` | unused | |   | `restore` | restore content from Trash | |   | `cleantrash` | empty the Trash (even when the User does not have access to individual Content items) | +|   | `unlock` | unlock drafts locked to a user for performing actions | | `Content Type` | `update` | modify existing Content Types. Also required to create new Content Types | |   | `create` | create new Content Types. Also required to edit exiting Content Types | |   | `delete` | delete Content Types | @@ -223,11 +81,11 @@ and to pass it after creating design to the "Proofread stage", use following per |   | `install` | unused | |   | `setup` | unused | |   | `system_info` | view the System Information tab in Admin | -|`site`|`view`|view the "Sites" in the top navigation| -|   |`create`|create sites in the Site Factory| -|   |`edit`|edit sites in the Site Factory| -|   |`delete`|delete sites from the Site Factory| -|   |`change_status`|change status of the public accesses of sites to `Live` or `Offline` in the Site Factory| +|`site` <br/> [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]|`view`|view the "Sites" in the top navigation| +|   |`create`|create sites in the Site Factory</br>| +|   |`edit`|edit sites in the Site Factory</br>[[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]| +|   |`delete`|delete sites from the Site Factory</br>[[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]| +|   |`change_status`|change status of the public accesses of sites to `Live` or `Offline` in the Site Factory</br>[[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]| | `user` | `login` | log in to the application | |   | `password` | unused | |   | `preferences` | access and set user preferences | @@ -236,12 +94,12 @@ and to pass it after creating design to the "Proofread stage", use following per |   | `activation` | unused | | `workflow` | `change_stage` | change stage in the specified workflow | | `comparison` | `view` | view version comparison | -| `segment` | `read`|load Segment information| +| `segment`</br>[[% include 'snippets/commerce_badge.md' %]] | `read`|load Segment information| || `create`|create Segments| || `update`|update Segments| || `remove`|remove Segments| || `assign_to_user` |assign Segments to Users| -| `segment_group` | `read` |load Segment Group information| +| `segment_group`</br>[[% include 'snippets/commerce_badge.md' %]] | `read` |load Segment Group information| || `create` |create Segment Groups| || `update` |update Segment Groups| || `remove` |remove Segment Groups| @@ -320,14 +178,14 @@ For example, to check if content can be assigned to a Section: ``` php $hasAccess = $this->isGranted( - new Attribute( 'section', 'assign', array( 'valueObject' => $contentInfo, 'targets' => $section ) ) + new Attribute( 'section', 'assign', [ 'valueObject' => $contentInfo, 'targets' => [$section] ] ) ); ``` You can also use the permission resolver (`eZ\Publish\API\Repository\PermissionResolver`). The `canUser()` method checks if the user can perform a given action with the selected object. -For example: `canUser('content', 'edit', $content, $location );` +For example: `canUser('content', 'edit', $content, [$location] );` checks the `content/edit` permission for the provided Content item at the provided Location. ### Blocking access to controller action diff --git a/docs/guide/persistence_cache.md b/docs/guide/persistence_cache.md index 6832e77b65..80e36afcdf 100644 --- a/docs/guide/persistence_cache.md +++ b/docs/guide/persistence_cache.md @@ -1,3 +1,7 @@ +--- +description: Persistence cache caches SPI\Persistence calls used in common page loads. +--- + # Persistence cache ![SPI cache diagram](img/spi_cache.png) @@ -25,11 +29,11 @@ Persistence cache aims at caching most `SPI\Persistence` calls used in common p Notes: -- [Cache tagging](https://symfony.com/doc/5.0/components/cache/cache_invalidation.html#using-cache-tags) is used in +- [Cache tagging]([[= symfony_doc =]]/components/cache/cache_invalidation.html#using-cache-tags) is used in order to allow clearing cache by alternative indexes. For instance tree operations or changes to Content Types are examples of operations that also need to invalidate content cache by tags. -- Search is not defined as persistence and the queries themselves are not planned to be cached as they are too complex by design (full text, facets, etc.). +- Search is not defined as persistence and the queries themselves are not planned to be cached as they are too complex by design (for example, full text). Use [Solr](search/solr.md) which caches this for you to improve scale/performance, and to offload your database. For further details on which calls are cached or not, see details in the [Symfony Web Debug Toolbar](devops.md#web-debug-toolbar) @@ -45,7 +49,7 @@ To see where and how to contribute additional caches, refer to the [source code] !!! note Current implementation uses Symfony cache. It technically supports the following cache backends: - [APCu, Array, Chain, Doctrine, Filesystem, Memcached, PDO & Doctrine DBAL, Php Array, Proxy, Redis](https://symfony.com/doc/5.0/components/cache/cache_pools.html#creating-cache-pools). + [APCu, Array, Chain, Doctrine, Filesystem, Memcached, PDO & Doctrine DBAL, Php Array, Proxy, Redis]([[= symfony_doc =]]/components/cache/cache_pools.html#creating-cache-pools). [[= product_name =]] officially supports only using Filesystem for single server and Redis or Memcached for clustered setups. Use of Memcached or Redis as shared cache back end is a requirement for use in clustering setup. @@ -56,18 +60,15 @@ Filesystem adapters, for example, are **not** intended to be used over a shared The underlying cache system is exposed as an `ezpublish.cache_pool` service, and can be reused by any other service as described in the [Using Cache service](#using-cache-service) section. -### Configuration - By default, configuration uses the `cache.tagaware.filesystem` service to store cache files. -The service is defined in `app/config/cache_pool/cache.tagaware.filesystem.yml` +The service is defined in `config/packages/cache_pool/cache.tagaware.filesystem.yaml` to use [FilesystemTagAwareAdapter](https://github.com/ezsystems/ezplatform/blob/master/config/packages/cache_pool/cache.tagaware.filesystem.yaml#L8). -This service is loaded through `app/config/env/generic.php`. You can select a different cache backend and configure its parameters in the relevant file in the `cache_pool` folder. -#### Multi Repository setup +### Multi Repository setup -You can [configure multisite to work with multiple Repositories](multisite.md#multisite-with-multiple-repositories). +You can [configure multisite to work with multiple Repositories](multisite/multisite_configuration.md#location-id). Then, in `ezplatform.yaml` you can specify which cache pool you want to use on a SiteAccess or SiteAccess group level. The following example shows use in a SiteAccess group: @@ -78,7 +79,7 @@ ezplatform: # "site_group" refers to the group configured in site access site_group: # cache_pool is set to '%env(CACHE_POOL)%' - # env(CACHE_POOL) is set to 'cache.tagaware.filesystem' (a Symfony service) by default, for more examples see app/config/cache_pool/* + # env(CACHE_POOL) is set to 'cache.tagaware.filesystem' (a Symfony service) by default, for more examples see config/packages/cache_pool/* cache_service_name: '%cache_pool%' ``` @@ -86,7 +87,7 @@ ezplatform: If your installation has several Repositories *(databases)*, make sure every group of sites using different Repositories also uses a different cache pool. -#### In-Memory cache configuration +### In-Memory cache configuration Persistence cache layer caches selected objects in-memory for a short time. It avoids loading repeatedly the same data from e.g. a remote Redis instance, which can take up to 4-5ms per call due to the network latency and Redis instance load. @@ -128,7 +129,7 @@ parameters: [Redis](http://redis.io/), an in-memory data structure store, is one of the supported cache solutions for clustering. Redis is used via [Redis pecl extension](https://pecl.php.net/package/redis). -See [Redis Cache Adapter in Symfony documentation](https://symfony.com/doc/5.0/components/cache/adapters/redis_adapter.html#configure-the-connection) +See [Redis Cache Adapter in Symfony documentation]([[= symfony_doc =]]/components/cache/adapters/redis_adapter.html#configure-the-connection) for information on how to connect to Redis. #### Supported Adapters @@ -159,26 +160,26 @@ Out of the box in `config/packages/cache_pool/cache.redis.yaml` you'll find a de !!! note "Ibexa Cloud" - For Ibexa Cloud/Platform.sh: This is automatically configured in `app/config/env/platformsh.php` if you have enabled Redis as `rediscache` Platform.sh service. + For Ibexa Cloud/Platform.sh: This is automatically configured in `vendor/ezsystems/ezplatform-core/src/EzPlatformCoreBundle/bundle/DependencyInjection/EzPlatformCoreExtension.php` if you have enabled Redis as `rediscache` Platform.sh service. -For anything else, you can enable it with environment variables detected automatically by `app/config/env/generic.php`. +For anything else, you can enable it with environment variables. For instance, if you set the following environment variables `export CACHE_POOL="cache.redis" CACHE_DSN="secret@example.com:1234/13"`, it will result in config like this: ``` yaml services: cache.redis: - # NOTE: This optimized Redis Adapter is avaiable as of 2.5LTS via https://github.com/ezsystems/symfony-tools - class: Symfony\Component\Cache\Adapter\TagAware\RedisTagAwareAdapter + # NOTE: Available via https://github.com/symfony/cache + class: Symfony\Component\Cache\Adapter\RedisTagAwareAdapter parent: cache.adapter.redis tags: - name: cache.pool clearer: cache.app_clearer provider: 'redis://secret@example.com:1234/13' - # Default CACHE_NAMESPACE value, see app/config/cache_pool/cache.redis.yaml for usage with e.g. multi repo. - namespace: 'ez' + # Default CACHE_NAMESPACE value, see config/cache_pool/cache.redis.yaml for usage with e.g. multi repo. + namespace: 'ezp' ``` -See `config/packages/ezplatform.yaml` and `config/packages/cache_pool/cache.redis.yaml` for further details on `CACHE_POOL`, `CACHE_DSN` and `CACHE_NAMESPACE`. +See `.env`, `config/packages/ezplatform.yaml` and `config/packages/cache_pool/cache.redis.yaml` for further details on `CACHE_POOL`, `CACHE_DSN` and `CACHE_NAMESPACE`. !!! caution "Clearing Redis cache" @@ -198,7 +199,7 @@ With that in mind, the following configurations of Redis are possible: - Provides high availability by providing one or several slaves (ideally 2 slaves or more, e.g. minimum 3 servers), and handle failover - [Slaves are asynchronously replicated](https://redis.io/topics/sentinel#fundamental-things-to-know-about-sentinel-before-deploying), so they can't be used for reads - Typically used with a load balancer (e.g. HAproxy with occasional calls to Redis Sentinel API) in the front in order to only speak to elected master - - As of v3 you can also configure this [directly on the connection string](https://symfony.com/doc/current/components/cache/adapters/redis_adapter.html#configure-the-connection), **if** you use `Predis` instead of `php-redis` + - As of v3 you can also configure this [directly on the connection string]([[= symfony_doc =]]/components/cache/adapters/redis_adapter.html#configure-the-connection), **if** you use `Predis` instead of `php-redis` Several cloud providers have managed services that are easier to set up, handle replication and scalability for you, and might perform better. Notable services include: @@ -217,7 +218,7 @@ Several cloud providers have managed services that are easier to set up, handle [Memcached, a distributed caching solution](http://memcached.org/) is a cache solution that is supported for clustering use, as an alternative to Redis. -See [Memcached Cache Adapter in Symfony documentation](https://symfony.com/doc/5.0/components/cache/adapters/memcached_adapter.html#configure-the-connection) +See [Memcached Cache Adapter in Symfony documentation]([[= symfony_doc =]]/components/cache/adapters/memcached_adapter.html#configure-the-connection) for information on how to configure Memcached. @@ -237,9 +238,9 @@ Out of the box in `config/packages/cache_pool/cache.memcached.yaml` you'll find !!! note "Ibexa Cloud" - For Ibexa Cloud/Platform.sh: This is automatically configured in `config/env/platformsh.php` if you have enabled Memcached as `cache` Platform.sh service. + For Ibexa Cloud/Platform.sh: This is automatically configured in `vendor/ezsystems/ezplatform-core/src/EzPlatformCoreBundle/bundle/DependencyInjection/EzPlatformCoreExtension.php` if you have enabled Memcached as `cache` Platform.sh service. -For anything else, you can enable it with environment variables detected automatically by `config/env/generic.php`. +For anything else, you can enable it with environment variables detected automatically by `vendor/ezsystems/ezplatform-core/src/EzPlatformCoreBundle/bundle/DependencyInjection/EzPlatformCoreExtension.php`. For instance, if you set the following environment variables `export CACHE_POOL="cache.memcached" CACHE_DSN="user:pass@localhost?weight=33"`, it will result in config like this: ``` yaml @@ -250,7 +251,7 @@ services: - name: cache.pool clearer: cache.app_clearer provider: 'memcached://user:pass@localhost?weight=33' - # Default CACHE_NAMESPACE value, see app/config/cache_pool/cache.redis.yaml for usage with e.g. multi repo. + # Default CACHE_NAMESPACE value, see bin/config/cache_pool/cache.redis.yaml for usage with e.g. multi repo. namespace: 'ez' ``` @@ -279,7 +280,7 @@ See `config/default_parameters.yaml` and `config/cache_pool/cache.memcached.yaml > Listen on <addr>; default to INADDR\_ANY. <addr> may be specified as host:port. If you don't specify a port number, the value you specified with -p or -U is used. You may specify multiple addresses separated by comma or by using -l multiple times. This is an important option to consider as there is no other way to secure the installation. Binding to an internal or firewalled network interface is suggested. -## Using Cache Service +## Using cache service Using the internal cache service allows you to use an interface and without caring whether the system is configured to place the cache in Memcached or on File system. And as [[= product_name =]] requires that instances use a cluster-aware cache in Cluster setup, you can safely assume your cache is shared *(and invalidated)* across all web servers. @@ -295,9 +296,9 @@ And as [[= product_name =]] requires that instances use a cluster-aware cache in That is why the example of usage below starts with a unique `myApp` key. For the namespace of your own cache, you must do the same. -#### Get Cache service +#### Getting cache service -##### Via Dependency injection +##### With dependency injection In your Symfony services configuration you can simply define that you require the cache service in your configuration like so: @@ -311,9 +312,9 @@ In your Symfony services configuration you can simply define that you require th This service is an instance of `Symfony\Component\Cache\Adapter\TagAwareAdapterInterface`, which extends the `Psr\Cache\CacheItemPoolInterface` interface with tagging functionality. -##### Via Symfony Container +##### With service container -Like any other service, it is also possible to get the cache service via container like so: +Like any other service, you can also get the cache service with the [service container](../api/public_php_api.md#service-container) like so: ``` php // Getting the cache service in PHP @@ -341,19 +342,19 @@ $pool->save($cacheItem); return $myObject; ``` -For more info on usage, see [Symfony Cache's documentation](https://symfony.com/doc/5.0/components/cache.html). +For more info on usage, see [Symfony Cache's documentation]([[= symfony_doc =]]/components/cache.html). -### Clearing Persistence cache +### Clearing persistence cache -Persistence cache prefixes it's cache using "ez-". Clearing persistence cache can thus be done in the following ways: +Persistence cache prefixes it's cache using "ibx-". Clearing persistence cache can thus be done in the following ways: ``` php // To clear all cache (not recommended without a good reason) $pool->clear(); // To clear a specific cache item (check source for more examples in eZ\Publish\Core\Persistence\Cache\*) -$pool->deleteItems(["ez-content-info-$contentId"]); +$pool->deleteItems(["ibx-ci-$contentId"]); // Symfony cache is tag-based, so you can clear all cache related to a Content item like this: -$pool->invalidateTags(["content-$contentId"]); +$pool->invalidateTags(["c-$contentId"]); ``` diff --git a/docs/guide/personalization/basic_integration.md b/docs/guide/personalization/basic_integration.md new file mode 100644 index 0000000000..dd64482b54 --- /dev/null +++ b/docs/guide/personalization/basic_integration.md @@ -0,0 +1,160 @@ +--- +description: Integrate recommendation service into your website. +--- + +# Integrate Recommendation service + +To return recommendations, you must first [enable the Personalization service](enabling_personalization.md). +Then, you must integrate the service with [[= product_name =]] by activating +event tracking and embedding recommendation results into the website. + +!!! note + + Examples below use '00000' as a customer ID for creating requests. + In a real-life scenario, use the customer ID that you receive from Ibexa. + +## Tracking events + +The service primarily relies on event tracking. +For the events to be registered, every Content item or product page must call +a special tracking URL. +The simplest way of embedding the tracking URL is placing a one pixel image on every page, +just like in the case of analytical tools or visitor counters. +A code that includes an image may look like this: + +`<img href="https://event.yoochoose.net/ebl/00000/click/<user_ID>/<content_type_ID>/<content_ID>" width="1" height="1">` + +`<user_ID>` stands either for the user ID or session ID of the user who is currently +logged into your website (any URL-encoded string is allowed). + +`<content_type_ID>` stands for the [contentTypeId](../content_model.md#content-information) of the Content item or product that you want to track and recommend. + +`<content_ID>` stands for the [id](../content_model.md#content-information) of the Content item or product that you want to track and recommend. + +The following examples show how you can integrate a CLICK event: + +PHP: + +``` php +$mandator_id = '00000'; +$product_id = '123'; +$server = '//event.yoochoose.net'; +$tracking = $server.'/ebl/'.$mandator_id.'/click/'.urlencode(session_id()).'/1/'.$product_id; +echo "<img href='$tracking' width='1' height='1'>"; +``` + +JavaScript: + +``` js +var mandator_id = '00000'; +var product_id = '123'; +var server = '//event.yoochoose.net'; +var url = server + '/api/' + mandator_id + '/click/' + getSessionId() + '/1/' + product_id; +var ycimg=new Image(1,1); +ycimg.src=url; +``` + +A similar tracking image can be placed on a confirmation page that ends the payment process. + +``` php +$server = '//event.yoochoose.net'; +foreach ($just_bought_products as $product_id) { + $tracking = $server.'/ebl/'.$mandator_id.'/buy/'.urlencode(session_id()).'/1/'.$product_id; + echo "<img href='$tracking' width='1' height='1'>\n"; +} +``` + +## Embedding recommendations + +As soon as the recommendation engine collects enough events, it can generate recommendations. +The more tracking data is available, the more accurate the recommendations. +Recommendations can be fetched with the following calls, and the response is returned in JSON format. + +To return the most popular products, use: + +`https://reco.yoochoose.net/api/v2/00000/<user_ID>/landing_page.json` + +To return products that the current user is most probably interested in, use: + +`https://reco.yoochoose.net/api/v2/00000/<user_ID>/cross_sell.json?contextitems=OWNS,CLICKED` + +To return products that are most probably interesting for users interested in product 123, use: + +`https://reco.yoochoose.net/api/v2/00000/<user_ID>/cross_sell.json?contextitems=123` + +A response with two recommendations will resemble the following object: + +``` json +{ + "contextItems":[ + { + "itemId":123, + "itemType":1, + "sources":[ + "REQUEST" + ], + "viewers":0 + } + ], + "recommendationItems":[ + { + "itemId":555, + "itemType":1, + "relevance":127, + "links":{ + "clickRecommended":"//event.yoochoose.net/api/00000/clickrecommended/user/1/555?scenario=landing_page&modelid=5768", + "rendered":"//event.yoochoose.net/api/00000/rendered/user/1/555?scenario=landing_page&modelid=5768" + } + }, + { + "itemId":444, + "itemType":1, + "relevance":126, + "links":{ + "clickRecommended":"//event.yoochoose.net/api/00000/clickrecommended/user/1/444?scenario=landing_page&modelid=5768", + "rendered":"//event.yoochoose.net/api/00000/rendered/user/1/444?scenario=landing_page&modelid=5768" + } + } + ] +} +``` + +You can use the following code to make requests and parse results: + +``` php +$mandator_id = '00000'; +$license_key = '67890-1234-5678-90123-4567'; +$server = "https://reco.yoochoose.net"; +$scenario = "category_page"; +$url = $server.'/ebl/00000/'.urlencode(session_id()).'/'.urlencode($scenario).'.json'; + +$curl = curl_init(); +$request = array( + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_USERPWD => "$mandator_id:$license_key"); +curl_setopt_array($curl, $request); +$body = curl_exec($curl); +$recommendations = json_decode($body); +if ($recommendations && isset($recommendations->recommendationResponseList)) { + foreach ($recommendations->recommendationResponseList as $product) { + $product_id = $product->itemId; + # load the product and create the recommendation HTML here + echo($product->itemId); + echo(" \n"); + }; +} else { + echo("Error: ".$body); +} +curl_close($curl); +``` + +## Advanced integration + +You can configure integration at a more advanced level to track more events, +use additional parameters, apply custom scenario configurations, apply filters, +and enable additional features. + +For more information about available functionalities, see the [User Documentation]([[= user_doc =]]/personalization/personalization). + +For more information about integrating the Personalization service, see [Developer guide](developer_guide/tracking_api.md) and [Best practices](best_practices/tracking_integration.md). diff --git a/docs/guide/personalization/best_practices/recommendation_integration.md b/docs/guide/personalization/best_practices/recommendation_integration.md index 3d0a43687a..485783775a 100644 --- a/docs/guide/personalization/best_practices/recommendation_integration.md +++ b/docs/guide/personalization/best_practices/recommendation_integration.md @@ -1,8 +1,12 @@ -# Recommendation Integration +--- +description: Methods for REST call with Personalization server. +--- + +# Recommendation integration There are several ways to integrate the REST calls with the Recommendation engine and to avoid blocking web page rendering, if the communication with the Recommender is distrusted or interrupted. -## Simple Way +## Simple way The simplest way to load recommendations is to synchronously request the Recommendation Engine for recommendations as they are needed. This way is sufficient in most cases. The most important drawback is that the request time increases by the time of the recommendation request. If the network is overloaded or the Recommendation Engine is not available it can lock the request. diff --git a/docs/guide/personalization/best_practices/tracking_integration.md b/docs/guide/personalization/best_practices/tracking_integration.md index 58cbf684f9..78d77700e6 100644 --- a/docs/guide/personalization/best_practices/tracking_integration.md +++ b/docs/guide/personalization/best_practices/tracking_integration.md @@ -1,14 +1,20 @@ -# Tracking Integration +--- +description: See the methods of event tracking integration using tracking from server or from client-side. +--- -There are several ways to integrate event reporting into the webpage. The simplest way is to generate code of a tiny image and put it on the webpage (pixel tracking) where the event must be sent. +# Tracking integration -For example:  +There are several ways to integrate event reporting into the webpage. +The simplest way is to generate code of a tiny image and put it on the webpage +where the event must be sent (so-called pixel tracking). + +For example, with HTML:  ``` html <img href="https://event.yoochoose.net/ebl/00000/click/johndoe/1/100?categorypath=/a/ab/abc" width="1" height="1"> ``` -or by javascript Image tracking +or with JavaScript: ``` js <script type="text/javascript"> @@ -17,44 +23,70 @@ img.src = "https://event.yoochoose.net/ebl/00000/click/johndoe/1/100?categorypat </script> ``` -The drawback of this option is that such a call can be blocked by ad blockers or do-not-track plugins on the client side. +The drawback of this option is that such calls can be blocked by ad blockers or do-not-track plugins on the client side. -## Server side tracking +## Server-side tracking -Another option is to call the tracker from the server. The most important drawback is the increase in request time by the time of the recommendation request. If the network is overloaded or the Recommendation Engine is not available the number of requests could grow and lead to a stalled and finally crashing httpd service. There are several techniques which help to avoid it. +Another option is to call the tracker from the server. +The most important drawback is that the event request increases the general request time. +If the network is overloaded or the recommendation engine is not available, +the number of requests could grow and lead to a stalled and finally crashing HTTP service. +There are several techniques that can help you avoid it. -### Tracking in the bottom +### Tracking at the bottom -You can place the code at the very end of the generating script and flush the output buffer to the client just before sending events. The connection to the browser will usually still be open during processing, but it will be transparent for the end customer. +You can place the code at the very end of the generating script +and flush the output buffer to the client just before sending the events. +The connection to the browser might remain open for the time of processing, +but it will be transparent for the end user. ### Tracking asynchronously -If the website is implemented in a language which supports multithreading, non-blocking I/O or messaging infrastructure, it is possible to start the recommendation request just after the browser request is received and/or not wait until this process is finished. +If the website is implemented in a language that supports multithreading, non-blocking I/O or +messaging infrastructure, you can start the event request just after the browser request is received +instead of waiting for this process to finish. -## Client side tracking +## Client-side tracking -### JSONP +### Using JSONP -Another solution is to provide the proxy on the server side, which will forward script requests to the Recommender Engine. In this case the requests will be triggered from the client, when the page is already loaded and rendered. It is not possible to request the recommendation controller server directly from the javascript (over AJAX library or directly over XMLHttpRequest) because of the cross-domain restriction in most browsers. One of the possible technique to work around this limitation is [JSONP](https://en.wikipedia.org/wiki/JSONP). +Another solution is to provide a proxy on the server side, which will forward script requests to +the recommendation engine. +In this model, the requests are triggered from the client, when the page is already +loaded and rendered. +It is impossible to request the recommendation controller server directly from JavaScript +(either through the AJAX library or directly over XMLHttpRequest) because of the +cross-domain restriction in most browsers. +One possible work around this limitation is [JSONP](https://en.wikipedia.org/wiki/JSONP). ### Using a server proxy -Another option is to tunnel the JavaScript request through the proxy on the same server. The server must just forward them to the Recommender Engine. It can be a simple implemented apache proxy module, some independent daemon like “netcat” or a PHP script. +Another option is to tunnel the JavaScript request through the proxy on the same server. +The server only forwards requests to the recommendation engine. +It can be a simple implemented Apache proxy module, an independent daemon +(for example, "netcat"), or a PHP script. ## Comparison An overview of pros and cons for every technique: -| Problem | Image | Server Side | Bottom Reporting | Async. Reporting | JSON | XMLHttpRequest + Proxy | +| Problem | Pixel | Server-side | Page bottom | Async. reporting | JSONP | XMLHttpRequest + Proxy | |----|-----|-----|-----|-----|-----|------| -| Is not blocked by ad blockers or no-track plug-ins. |-| Yes | Yes | Yes |-| Yes | +| Is not blocked by ad blockers or do-not-track plug-ins. |-| Yes | Yes | Yes |-| Yes | | Works if JavaScript is disabled. | Yes | Yes | Yes | Yes |-|-| | Is compatible with frontend caching on the server. |-|-|-|-|Yes | Yes | | Does not delay page rendering. | Yes |-| Yes | Yes | Yes | Yes | -| Supports authentication for event tracking (not implemented yet) |-| Yes | Yes | Yes |-| depends | +| Supports authentication for event tracking (not implemented yet). |-| Yes | Yes | Yes |-| Yes/No | -!!! tip "What we recommend" +!!! tip "The recommended approach" - We suggest using Image/Pixel tracking for non-complex events and where every page is generated on the server side without caching logic. Some hints how to use preload image URLs with (`var img = new Image(); img.src="uri"`) or without (`<img src="uri"... />`) javascript elements are given under https://www.mediacollege.com/internet/javascript/image/preload.html. + An Ibexa-recommended solution is to use pixel tracking for non-complex events, + or where every page is generated on the server side without any caching logic. + For hints about preloading image URLs with JavaScript elements (`var img = new Image(); img.src="uri"`) + or without them (`<img src="uri"... />`), see [How to Preload Images](https://www.mediacollege.com/internet/javascript/image/preload.html). - If you are planning to implement caching mechanisms and more complex events like consume (depending on viewing time of the page), basket (which is usually an in-page event) or custom in-page events that take place on the client side, we definitely recommend a javascript implementation. We provide an example script and some instructions under [Tracking with yct.js](../developer_guide/tracking_with_yct.md) + If you plan to implement caching mechanisms and more complex events, such as, for example, + consume (depending on the viewing time of the page), basket (which is usually an in-page event) + or custom in-page events that take place on the client side, + a JavaScript implementation is strongly encouraged. + For a sample script and instructions, see [Tracking with yct.js](../developer_guide/tracking_with_yct.md). diff --git a/docs/guide/personalization/developer_guide/content_api.md b/docs/guide/personalization/developer_guide/content_api.md index 93b170e21e..ff2985ffaf 100644 --- a/docs/guide/personalization/developer_guide/content_api.md +++ b/docs/guide/personalization/developer_guide/content_api.md @@ -1,42 +1,82 @@ +--- +description: Personalization server can use external information about the items. Use HTTP methods to create, update or get items from the data store. +--- + # Content API -## Insert XML Content +Apart from the [events]([[= user_doc =]]/personalization/event_types) +collected by the recommendation client, the recommendation engine can use external information +about the products. +This information must be uploaded to the recommendation engine by the administrator of the website. -!!! note +The following information can be loaded to the recommendation solution: + +- Product price - Products cheaper than the specified threshold can be filtered out from recommendations +- Availability timeframe - Certain products are be recommended only in the specified time window +- Custom attributes - You can group recommendations and narrow down the results, for example, to non-food products or to news that are related to the end user's city + +For more information about personalization, see [Introduction](../personalization.md) and [Best practices](../best_practices/recommendation_integration.md). + +The recommendation client provides a REST interface that accepts items in XML format. +You can use the interface to post item information within the request's body into the store,  +and to display or update the items directly. + +You can use HTTP methods to create, update or retrieve items that are in the data store. + +!!! note "Authentication" + + For getting or posting content data, basic authentication is enabled by default. + Use your customer ID and license key as username and password. + If authentication is enabled for recommendation requests and you want to change this, contact support@ibexa.co. + +## GET requests + +Use the GET method to retrieve all information that is stored in the database for the given item ID: + +`GET: https://admin.yoochoose.net/api/[customerid]/item/[itemtypeid]/[itemid]` + +## POST requests - All features described in this chapter are available only for the advanced edition of the Recommender Engine. - **BASIC Authentication** is enabled by default. - Use the customerID as username and the license key as password. - The license key is displayed in the upper right of the Admin GUI ([https://admin.yoochoose.net](https://admin.yoochoose.net/)) after logging in with your registration credentials. +Use the POST request to create or update items with the given ID in the database: -The Recommender Engine needs updated information from the web presence of the customer to generate personalized recommendations for the user profile. -To get such information an event tracking process is required to collect events like clicks, purchases, consumes, etc. +`POST: https://admin.yoochoose.net/api/[customerid]/item` -In addition to events collected by the Recommender, the Recommender Engine can use external information about the products. -This information must be uploaded to the Recommender Engine by the website owner. -Here are some examples: +A body of the request must contain a valid XML document. +Once uploaded, the item is scheduled to be inserted in the database, and it is not directly available. -- **Product price** - Products cheaper than the specified threshold can be filtered out from recommendations. -- **Availability time period** - Products will be recommended only in the specified time window. -- **Custom attributes** - By using custom attributes it is possible to group recommendations and narrow the results. For example "I am interested only in non-food products" or "show me only news related to my city". +## DELETE requests -### XML Item Format +Use the DELETE method to delete all information that is related to the given item ID. -The XML format for an item import is the same for the PULL and PUSH interface. Here is an example: +`DELETE: https://admin.yoochoose.net/api/[customerid]/item/[itemtypeid]/[itemid]?lang=<language_code>` + +The item is scheduled to be removed from the database. + +## Request parameters + +The following call attributes are available: + +| Parameter name | Description | Value | +|---|---|---| +| `customerid` | Your customer ID, as defined when [enabling Personalization](../enabling_personalization.md#configuring-customer-credentials) (for example, "00000"). | alphanumeric | +| `itemid` | A unique ID of the Content item/product. Used to identify the item in the database. | integer | +| `itemtypeid` | An ID of the type of Content item/product. In most cases, the value is 1 but you might have items/products of more than one type. | integer | +| `lang` | A [language code](../../internationalization.md) of the Content item/product (for example, "ger-DE"). This parameter is optional. | string | + +### Request object format + +An XML representation of the data object used for item import can look like this: ``` xml <items version="1"> - <!-- version is mandatory and always "1" --> + <!-- Version is mandatory and must always be set to 1 --> <item id="102" type="1"> <description>the item's description</description> <price currency="EUR">122</price> - <!-- price in cents as integer value (e.g. EUR-Cent) --> <validfrom>2011-01-01T00:00:00</validfrom> - <!-- items will be recommended only in the specified time window --> <validto>2021-01-01T00:00:00</validto> <categorypaths> <categorypath>/8/4/5</categorypath> - <!-- one product can be in multiple categories --> <categorypath>/84</categorypath> <categorypath>/1/847</categorypath> </categorypaths> @@ -49,132 +89,123 @@ The XML format for an item import is the same for the PULL and PUSH interface. H </content-data> </content> <attributes> - <!-- this section should only contain values that are distinct and limited to build prefiltered models --> - <attribute key="author" value="Max Mustermann" /> + <!-- --> + <attribute key="author" value="John Doe" /> <attribute key="agency" value="dpa" /> - <!-- If no type value is given, NOMINAL (a limited list of values) is assumed --> <attribute key="vendor" value="BOSCH" /> - <!-- if NUMERIC is chosen, the value must be numeric as well. --> <attribute key="weight" value="100" type="NUMERIC" /> </attributes> </item> </items> ``` -#### Implicit vs. explicit update of category paths - -Product attributes which can be uploaded through the data import interface include the path of the category/categories that the product is located in. -The category path can be also updated over the "click" events. -If the products are regularly uploaded over the data import interface, the click event should **not** contain the category path information. -E.g. a product is clicked in the "TopSeller" section and the category path `%2FTopSeller` is sent, which is mostly undesired as it is originally located under `%2FGarden%2F`. - -Enabling both update ways for category path is possible, but it has the following side effects: - -- Every new category path attached to the "click" event will be *appended* to the list of the categories of the product. -- The Product imported will *overwrite* the collected category paths. - !!! note "XML schema definition" - The current schema can be seen under `https:// admin.yoochoose.net/api/00000/item/schema.xsd` + The current schema that is used for interpreting the XML objects can be seen [here](https://admin.yoochoose.net/api/00000/item/schema.xsd). -Following is a brief description of the attributes +The following keys and attributes used in the XML object are available: -|Name/Attribute|Description|Mandatory|Type| -|---|---|---|---| -|id|The item's id|yes|Integer| -|type|The item's type|yes|Integer| -|description|Additional information about the item|-|String| -|price|The item's price (e.g. in EUR cents)|-|Integer (see below)| -|currency|Currency used, by default EUR is assumed|-|ISO 4217| -|validfrom|Defines, together with validto, the "lifespan" of an item. If NULL or not available, the item is considered valid|-|ISO 8601 (see below)| -|validto|Defines, together with validfrom, the "lifespan" of an item. If NULL or not available, the item is considered valid|-|ISO 8601| -|categorypath|A logical (website) navigation path through which the item can be reached in the customer's system|-|String, separated with "%2F" (encoded /)| -|categoryid|The category ID. Deprecated. Use "categorypath" instead!|-|Integer| +|Key/Attribute | Description | Type | +|--- | --- | --- | +| `id` | A unique ID of the item/product. This parameter is required. | integer | +| `type` | An ID of the type of item/product. This parameter is required. | integer | +| `description`| Additional information about the item. | alphanumeric | +| `currency` | Currency used for the price. By default, prices are expressed in EUR. | ISO 4217 | +| `price` | The item's price in the currency's fractional units (for example, cents).<br/>See below for more information. | integer | +| `validfrom` | Together with `validto`, defines the lifespan of an item.<br/>If NULL or not available, the item is considered valid immediately.<br/>See below for more information. | ISO 8601 | +| `validto` | Together with `validfrom`, defines the lifespan of an item.<br/>If NULL or not available, the item is considered valid indefinitely.<br/>See below for more information. | ISO 8601 | +| `categorypath` | A logical (website) navigation path through which the end user can reach the item/product in your website.<br/>You can define multiple paths for the product.| alphanumeric, separated with "/" ("%2F") characters | -#### Empty Products +!!! caution "Encoding limitation" -All the elements and attributes except the item **type** and the item **id** are optional. -It is possible to upload a product without any additional information, e.g. if the random recommendation model is used or on-the-fly boosting and filtering of recommendations is intended to be used. -In this case the Recommendation Engine will randomly recommend the imported products even if they have to related events. -This is useful for a news agency, where new products (=news) are published very frequently. + Keys and their values can only contain letters, digits and underscore characters. + Attribute keys are case-sensitive. -#### Field Formats +##### Currency -The key attribute in the elements **attribute** and **content** must contain only letters, numbers and underscores. +If the currency does not have a fractional unit, the main unit is used, for example 12 for 12 Japanese Yen. +To check whether the currency has fractional units, see the [ISO 4217 standard](https://en.wikipedia.org/wiki/ISO_4217#cite_note-ReferenceA-6). -Price is formatted as the amount of "cents", for example 1234 for 12 Euros and 34 Cents. If the currency doesn't contain the cent part, the main currency is used, for example 12 for 12 Japanese Yen. -See [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217#cite_note-ReferenceA-6) to check if the selected currency has a "cents" part. +##### Validity -Validity Dates are formatted as specified in [XSD format](https://www.w3.org/TR/xmlschema-2/#dateTime) *without a time zone*. Time zone is the default timezone of the mandator which is used. + Items with defined validity are recommended only in the specified timeframe. + Values in the `validto` and `validfrom` attributes must follow the [XSD format](https://www.w3.org/TR/xmlschema-2/#dateTime) and do not include the time zone. +Time zone is always your time zone. -#### Attributes +##### Category path -It is also possible to define custom (numeric or nominal) attributes in the **<attributes>** section. -The default type of every attribute is "NOMINAL", i.e. the values of an attribute are treated as distinct when compared while calculating a content-based model (ADVANCED solution). -If you add another attribute in the attribute element named *type="NUMERIC"*, the recommender engine will treat the values as ranges. -This means that a size of 4 is closer to a size of 5 than to 1. -If the attribute price is of type NOMINAL, they are both just different and have no "distance-based similarity". +With the data import interface, you can upload information about the paths to categories +in which the product is located. +However, the category path can be also updated as a result of the "Click" events. +If you regularly upload product data, the "Click" event cannot contain the category path information. +Otherwise, the following negative side effects occur: -``` xml -<attribute key="size" value="4" type="NUMERIC" /> -``` +- Every new category path attached to the "Click" event is appended to a list of the categories of the product +- Imported product data overwrites the collected category paths -Another typical example is the color of an item. To insert it in the store, the following line should be a child of the <attributes> element. +For example, when a product that is originally located under `Garden` is clicked in the "Hot Sellers" section, the category path `TopSeller` is sent. -``` xml -<attribute key="color" value="green" /> -``` +#### Content items/products with no attributes -!!! caution +All the elements and attributes except the `type` and `id` are optional. +You can therefore upload a product without any additional information. +You do it, for example, when a random recommendation model is used +or you want to want to apply ad-hoc boosting and filtering of recommendations. +As a result, the recommendation engine randomly recommends the imported items/products. +This can prove useful for a news agency, where new items are published very often. - Attribute keys are **case-sensitive**. It is possible to have multiple attributes with the same name and different type. For example, the size as a number (e.g., 40.5) and as a code ("L"). +#### Custom attributes -#### Content +You can also define custom attributes under the `<attributes>` key. +This section can only contain values that are distinct and used to build pre-filtered models. -You can load any content data in the content part of the item. -The content is used only for full text analysis models, it cannot be used in `<content-data key="abstract"> <![CDATA[ ... ]]></content-data>`. +By default, it is assumed that every attribute is of type "NOMINAL", which means that +there is a limited set of values, and values of an attribute are treated as distinct +when calculating the results of a content-based model. -### Push Interface +If you have an attribute that is of type "NUMERIC", and you add another attribute of the same type, +the recommendation engine treats the two values as a range. -The Recommender provides a REST interface that accepts an item in XML-format. -The following URL describes the interface. +``` xml +<attribute key="size" value="4" type="NUMERIC" /> +``` -**`https://admin.yoochoose.net/api/[customerid]/item`** +However, if the other attribute is of type "NOMINAL", they are both treated +as different and have no "distance-based similarity". -It can be used to POST item information within the request's body into the store and to show, update or delete items directly. -The parameters that are used in the call are described in the table. +Another typical example of a custom attribute is the color of an item. +To upload the value to the data store, add the following line under the `<attributes>` key. -URL `https://admin.yoochoose.net/api/[customerid]/item/[itemtypeid]/[itemid]` is the direct link to fetch item data. +``` xml +<attribute key="color" value="green" /> +``` -|Parameter name|Description|Values| -|---|---|---| -|`customerid`|This is a reference to the account of the customer. It will be provided by YOOCHOOSE.|String| -|`itemid`|A unique identifier for the item that is used as a reference to identify the item in the database of the customer.|Numeric| -|`itemtypeid`|Describes the type of the item id. Usually it is fixed to 1 but if the customer uses more than one type of items this has to refer to the corresponding item type.|Numeric| +You can have multiple attributes with the same name and different type. +For example, `size` can be expressed as a number (40.5) or as a code ("L"). -Different HTTP-methods can be used to create, update, delete or retrieve items located in the YOOCHOOSE data store. The following table gives an overview of the different methods and their function. +## Responses -|HTTP method|Description|Values| -|---|---|---| -|`POST`|If the body contains valid xml data, the item will be persisted. The item is not directly available but scheduled to be inserted. If the XML content cannot be validated, the server will send a Bad Request status code.|202 (Accepted)</br>400 (Bad Request)| -|`GET`|This method retrieves all information that is stored in the database for the given item id. If not found, the status code 404 is returned.|200 (OK)</br>404 (Not Found)| -|`DELETE`|Deletes all information that is related to the item id that has been sent. There is no need to send a body element. The item is not deleted directly but scheduled to be removed from the data store.|202 (Accepted)</br>404 (Not Found)| +### HTTP response codes -The body of a request to import data using the above interface must contain a valid XML document. +The following HTTP response codes are used by the recommendation controller: -## Transferring Item Identifiers +|HTTP Status Code|Description| +|---|---| +|200 OK|The GET request was processed successfully.| +|202 Accepted|The POST or DELETE request was processed successfully.| +|400 Bad Request|Wrong request formatting. The XML content cannot be validated.| +|404 Not Found|The element requested by the GET or DELETE request was not found.| -### Transfer items +## Transferring item identifiers -The method transfers an item from one id to another ID. -The attributes of the old item are NOT moved or merged. -If you rely on attributes for e.g. filtering based on prices, the new item must be reimported. +You could use the data import interface to help migrate the database, +when it involves changing item IDs of items that are supported by the recommendation engine. +If you transfer items from one ID to another, you can use the events recorded for "old" item IDs +to calculate model results that present "new" IDs. -All related historical user data is rewritten to point to the new item. -The old item is wiped, including all attributes. -The authentication is based on BASIC AUTH with customerId and license-key. +Use the following method to pass the XML object: -**`POST https://import.yoochoose.net/api/[customerid]/transferitems`** +`POST: https://admin.yoochoose.net/api/[customerid]/transferitems` `Content-Type=text/xml` ``` xml @@ -185,3 +216,11 @@ The authentication is based on BASIC AUTH with customerId and license-key. </transfer> </transfers> ``` + +All related historical user data is rewritten to point to the new item. +The old item is wiped, including all attributes. + +!!! note + + The attributes of the "old" item ID are not moved or merged, and if you rely on attributes, + for example, for filtering based on prices, you must reimport the new item. diff --git a/docs/guide/personalization/developer_guide/importing_historical_user_tracking_data.md b/docs/guide/personalization/developer_guide/importing_historical_user_tracking_data.md index 8ec65892c8..df9843d538 100644 --- a/docs/guide/personalization/developer_guide/importing_historical_user_tracking_data.md +++ b/docs/guide/personalization/developer_guide/importing_historical_user_tracking_data.md @@ -1,3 +1,7 @@ +--- +description: Use historical user tracking data to build user profiles and generate better recommendations. +--- + # Importing historical user tracking data We support replaying historical user events by adding a special parameter *overridetimestamp* on the buy event to simulate the event date. diff --git a/docs/guide/personalization/developer_guide/legacy_recommendation_api.md b/docs/guide/personalization/developer_guide/legacy_recommendation_api.md deleted file mode 100644 index 6a6a61958a..0000000000 --- a/docs/guide/personalization/developer_guide/legacy_recommendation_api.md +++ /dev/null @@ -1,262 +0,0 @@ -# Legacy Recommendation API - -!!! caution - - This is a page describing the old version of the recommendation API. It is available for reference purposes only. - - Please consider using the new [Recommendation API](recommendation_api.md) instead! - -This page describes how to fetch recommendations from the Recommender System via recommendation requests. -Before recommendation can be fetched from the recommendation controller, a sufficient number of events must be collected and the model build must finish successfully. - -!!! note - - **BASIC Authentication** for fetching recommendations is enabled for some configurations (for example for Gambio Plugin) by default. - Use the customerid as username and the license key as password. - The license key is displayed in the upper right in the Admin GUI ([https://admin.yoochoose.net](https://admin.yoochoose.net/)) after logging with your registration credentials. - - If you plan to use [JSONP](https://en.wikipedia.org/wiki/JSONP), authentication must be disabled. - If it is enabled in your solution (can be easily tested with a recommendation request in a browser), please contact the eZ Recommender support (<support@yoochoose.com>) for further information and disabling. - -## Getting recommendations - -Recommendations are retrieved from the recommendation engine via RESTful requests using the GET method. -The result is a list of item IDs that can be used to call the underlying CMS or shop system in order to retrieve the necessary information for the rendering process. - -To allow the customer to retrieve different types of recommendations based on different methods (e.g. Collaborative Filtering, Content Based, Stereotype, etc.) the Recommendation System uses scenario IDs relating to a predefined set of configurations inside the system. -These configurations are a combination of methods and filters that should be applied including possible fallbacks if the requested methods do not deliver a result. - -A recommendation request looks like this: - -**`https://reco.yoochoose.net/\[solutionid\]/\[customerid\]/\[userid\]/\[scenarioid\].\[extension\]?parameter=value&\[attributename=attributevalue\]`** - -The embedded parameters `solutionid`, `clientid` and `userid` are the same as used for event tracking. Additional embedded parameters are described in the following table. - -| Parameter Name | Description | Values | -|-----|-----|------| -| `scenarioid` | The ID of the scenario used for providing recommendations. It is configured or predefined in the Administration GUI. | alphanumeric | -| `extension` | The format the server generates the response in. There are three formats supported: JSON, XML and JSONP. See the chapter [Response Handling](#response-handling) below for more information | json, xml or jsonp | - -## Basic Request Parameters - -Using additional query string parameters one can customize the recommendation request. - -|Parameter name|Description|Values| -|---|---|---| -|`numrecs`|Defines the number of recommendations that should be delivered. Keep this amount as low as possible as this increases the response time of the recommendation call.|1 to 50 (default "10")| -|`contextitems` (required for context based recommendations)|Comma-separated list of items that the user is currently viewing on the web page. All items must be from the same type. The type is defined in the scenario configuration.|comma separated list of item IDs (1 to 2147483647)| -|`itemid` (deprecated)|A single item to be used as a source for creating recommendations. This parameter is deprecated. Use `contextitems` instead.|1 to 2147483647| -|`outputtypeid` (required, if the scenario defined multiple output item types, otherwise it is optional)|Item type of the requested recommendations. This can differ from the input item type, e.g. if you want to get recommendations for pictures based on a given article the item type for pictures has to be used here. For a web shop this is probably not needed as only one type of items is tracked. Multiple item types are available only for advanced license.|numeric| -|`categorypath`|Base category path for providing recommendations. The format is the same as the category path for the event tracking. It is possible to add this parameter multiple times. The order of recommendations from the different categories is defined by the calculated relevance.|alphanumeric[/alphanumeric]*| -|`jsonpcallback` (used only for JSONP request)|Function or method name for a JSONP request. It can be a function ("callme"), or a method ("obj.callme").|legal JavaScript function call (by default "jsonpCallback")| - -An example of the recommendation request:  - -**`https://reco.yoochoose.net/ebl/0000/smith/productpage.json?contextitems=123&categorypath=%2FCamera%2FCompact&numrecs=8`** - -It fetches 8 recommendations for user Smith, who is watching the item 123 and the category *"/Camera/Compact"* from the scenario with the identifier productpage. - -!!! tip - - See the section [Advanced Request Parameter](#advanced-request-parameter) below in this document for advanced features. - - See the section [Integration best Practices](#integration-best-practices) below in this document for the implementation examples. - -## Response handling - -The recommendation request returns a list of item IDs that are JSON, JSONP or XML-formatted. -The result can be easily integrated into any webpage by using some lines of script code.  - -!!! tip - - See the [Quickstart Guide](../personalization_quickstart.md) for a simple example written in PHP. - -The recommendation result list looks like the following: - -Example JSON response: - -``` json -{ recommendationResponseList: [  - { reason: "CF_I2I (context: ITEMS(s))",  - itemType: 2,  - itemId: 1399,  - relevance: 100 }, - { reason: "POPULARITY_SHORT_BUY (context: ITEMS(s))",  - itemType: 2,  - itemId: 4711,  - relevance: 91  - } ] } -``` - -Example JSONP response: - -``` json -jsonpCallback( -{ recommendationResponseList: [ - { reason: "CF_I2I (context: ITEMS(s))", - itemType: 2, - itemId: 1399, - relevance: 100 }, - { reason: "POPULARITY_SHORT_BUY (context: ITEMS(s))", - itemType: 2, - itemId: 4711, - relevance: 91 - } ] }) -``` - -Example XML response: - -``` xml -<list> -<recommendation> - <itemId>1241769000</itemId> - <reason>POPULARITY_SHORT_BUY (context: ITEMS(s))</reason> - <relevance>148</relevance> - <itemType>1</itemType> -</recommendation> -</list> -``` - -The "reason" field string tells the user out of which model the recommendations were provided. The models are preconfigured by the Recommendation Engine. -For example "*CF\_I2I (context: ITEMS(s))*" means that the model which provides the recommendation is "Collaborative Filtering based on an Item to Item similarity" with the given context item as input. -The human readable explanation is (in this case) "Users who bought this item also bought these items". - -The "relevance" defines the similarity to the context item according to internal algorithm and scenario configuration. -A higher value means a "better" recommendation. The list of recommendations is sorted by the relevance in descending order. - -### Response Codes - -The Following HTTP response codes are used by the recommendation controller. - -|HTTP Status Code|Description| -|---|---| -|200 OK|Request was successfully processed.| -|304 Not Modified|Recommendation result was not modified since the time specified in the provided If-Modified-Since header.| -|400 Bad Request</br>414 Request-URI Too Long|The request is wrongly formatted. See response body for more information.| -|401 Unauthorized|Illegal authentication credentials.| -|403 Forbidden|Access denied.| -|404 Not Found|The requested element was not found. It can be customer ID (a.k.a. mandator ID), model ID, scenario ID etc.| -|409 Conflict|The combination of used models and given recommendation request parameters doesn't allow to provide recommendations. This could be e.g. if personalized recommendations are requested for a user who has no history at all.| -|500 Internal Server Error|Unspecified error. Please inform support if you get this error repeatedly.| - -The body of the response in case of errors contains human-readable error message. -The format of error messages can be changed and should not be used for automated processing. - -## Advanced Request Parameter - -There are some additional very special request parameters. - -###### any attribute name (used only if submodels are configured) - -Item's attribute, e.g. color, price, etc. -These are customer specific and can only be understood by the recommender system if the item attributes are imported by using the YOOCHOOSE content import APIs. -There can be multiple attributename and attributevalue pairs. - -Legacy Recommendation API and [Submodel configuration](https://doc.ezplatform.com/projects/userguide/en/master/personalization/recommendation_models.md#submodels) is required for taking an advantage from this parameter. - -**Values**: alphanumeric=alphanumeric [&alphanumeric=alphanumeric] - -###### `usecontextcategorypath` - -If set to true, the category path of the given contextitem(s) will be resolved by the recommender engine from the internal store and used as base category path. -If more than one category is returned, all categories are used for providing recommendations. -Avoid setting this parameter to true to minimize the response time. -Use the parameter categorypath to provide the category to the recommender engine during the request. - -**Values**: true or false (default "false") - -###### `recommendCategory` (to be used only in the eZ Recommendation extension) - -If passed in combination with a "categorypath" value, the "closest" category the recommended items linked with will be delivered in the response as additional field "category". - -``` -recommendationResponseList: [ { - itemId: 1007640000, - category: "09/0901/090113", ... } ... ] -``` - -This feature helps to find "better" template for articles, which are located in several categories. - -For example there is an article about football in the USA. -The article is located in both categories "/Sport/Football" and "/America/USA". -Depending on the category it is shown with a football field or the USA flag in the background. - -If this article is recommended and is clicked in the category "/Sport/Cricket" it must open with the "football" template. -If the article is clicked in the category "/America/Canada" it must open with the "USA" template. - -The category information is returned only if the article is located in several categories and the "better" category found. - -**Values**: true or false (default "false") - -## Recommendation Caching - -In most cases the response of the recommendation service can be cached for some time. -Depending on the used recommendation model and context it can dramatically reduce the number of recommendation requests and therefore the price for using the recommendation service. -Recommendation service support following HTTP headers to allow cache control on the client side: - -|Scope|||Example|Format| -|---|---|---|---|---| -|Request|`If-Modified-Since`|Allows a *304 Not Modified* to be returned if content is unchanged.|`If-Modified-Since: Sat, 29 Oct 2013 19:43:31 GMT`|"HTTP-date" format as defined by [RFC 2616](https://tools.ietf.org/html/rfc2616)| -|Response|`Last-Modified`|The last modification date of the recommendations.|`Last-Modified: Tue, 15 Nov 2013 12:45:26 GMT`|-| -||`Expires`|Gives the date/time after which the response is considered to be outdated|`Expires: Thu, 01 Dec 2013 16:00:00 GMT`|-| - -The last modification timestamp indicates a change that could influence the recommendation response. It depends on an updated recommendation calculation, an update of an item or some scenario configuration changes. The expiration timestamp is a best-effort prediction based on the model building configuration and provided context. The shortest expiration period is 5 minutes from the request time. The longest is 24 hours. In the table below several examples are illustrated: - -| Model | Context | Expiration time | Description | -|------|------|-----|-----| -| Bestselling products last 7 days | no context | 24 hours | The model with the 7 days scope is usually built once a day. It can be easily cached for 24 hours. It has no context and can therefore be cached globally for all the users of a customer. | -| Also bought products in the last month | current product | 24 hours | The model with the 30 days scope is usually built once a day. The context is always the same. It can be cached for every product. The same result can be used for all users of a customer. | -| Also consumed read articles in the last hour | current article | 30 minutes | Models with a short scope are usually built several times a day or even per hour. In this case the expiration time is set to the half of the model build frequency/period. | -| Personalized recommendation based on the user's statistic | customers click history | now | Although the statistic model is not updated within minutes, it is very likely that the context will be changed shortly (customer clicks another product and therefore the click is added to his history). The expiration time should not be much longer than the user activity on the web page. | - -In most cases you do not need to calculate the expiration time manually. The table above is provided for the orientation, how the Expires header of the HTTP response is calculated by the recommendation engine and already provided to your caching system. You just need to make sure that the Expires header is used in the configuration of your caching system instead of a static value out of your configuration. - -## Integration best Practices - -There are several ways to integrate the REST calls to the Recommendation engine and avoid the blocking of the web page rendering, if the communication with the Recommender is distrusted or interrupted. - -#### **Simple Way** - -The simplest way to load recommendations is to synchronously request the Recommendation Engine for recommendations as they are needed. -This way is sufficient in most cases. The most important drawback is that the request time increases by the time of the recommendation request. -If the network is overloaded or the Recommendation Engine is not available it can lock the request. - -#### Loading in the bottom - -You can place the code that loads the data from the eZ Recommender at the bottom of the generated document and flush the output buffer to the client just before requesting recommendations. -The browser will get a whole page to render and can display it even if the very end of the page is still loading. -Then the JavaScript code with the recommendation information loaded at the bottom of the page must fill the gaps on the page with recommendation as soon as it is completely loaded. - -#### Non-blocking loading in the background - -If the website is implemented in a language which supports multithreading or non-blocking I/O, it is possible to start the recommendation request just after the browser request is received. -The page generation and the recommendation requests will be accomplished in parallel. -By combining this idea with the previous solution and placing the recommendation results at the bottom of the page you can avoid any interruption in the processing. - -#### Loading from JavaScript using JSONP - -It is not possible to request the recommendation controller server directly from the JavaScript (over AJAX library or directly over XMLHttpRequest) because of the cross-domain restriction in most browsers. -One of the possible technique to work around this limitation is [JSONP](https://en.wikipedia.org/wiki/JSONP). - -#### Loading over proxy - -A better solution in comparison with JSONP is to provide the proxy on the server side, which will forward script requests to the Recommender system. -It can be implemented as a very simple proxy using the [mod\_proxy module](https://httpd.apache.org/docs/2.2/mod/mod_proxy.html) of Apache Webserver. -It just transfers the data and the JavaScript renders the response into HTML itself. - -An alternative approach is creating the HTML code on the server side for every target page in a sense to simplify the script on the client side. - -As a possible implementation of such a proxy following tools can be used: the apache proxy module, some independent daemon like “netcat” or a PHP script. - -#### Comparison - -An overview of pros and cons for each of the above techniques: - -|Problem|Simple Way|Bottom loading|Background loading|JSONP|XMLHttpRequest + Proxy| -|---|---|---|---|---|---| -|Is not blocked by ad blockers or no-track plug-ins|Yes|Yes|Yes|-|Yes| -|Works if JavaScript is disabled|Yes|depends|-|-|-| -|Works for server without multithreading functionality|Yes|Yes|-|Yes|Yes| -|Compatible with frontend caching on the server|-|-|-|Yes|Yes| -|Does not delay page rendering|-|depends|depends|Yes|Yes| -|Supports authentication for recommendation fetching|Yes|Yes|Yes|-|depends| diff --git a/docs/guide/personalization/developer_guide/recommendation_api.md b/docs/guide/personalization/developer_guide/recommendation_api.md index fb3c3fd5ef..884849b83b 100644 --- a/docs/guide/personalization/developer_guide/recommendation_api.md +++ b/docs/guide/personalization/developer_guide/recommendation_api.md @@ -1,86 +1,100 @@ +--- +description: Use HTTP GET request method to render recommendations. +--- + # Recommendation API -!!! note +Recommendations are retrieved from the recommendation engine with RESTful requests that rely on the HTTP GET method. +The result can a list of item IDs that can then be used to call the underlying CMS or shop system +and postload the necessary information for the rendering process. - Before recommendations can be fetched from the recommendation controller, a sufficient number of events must be collected. On a website with more than 100 clicks per day, one day of collecting user data should be sufficient for the first recommendations. They will become better over time, that is with the amount of user data collected. +For more information about Personalization, see [Introduction](../personalization.md), [Basic integration](../basic_integration.md) and [Best practices](../best_practices/recommendation_integration.md). - **BASIC Authentication** for fetching recommendations is disabled by default. If enabled, use the customerid as username and the license key as password. The license key is displayed in the upper right corner in the Admin GUI ([https://admin.yoochoose.net](https://admin.yoochoose.net/)) after logging in with your registration credentials. +!!! note "Authentication" - If you plan to use [JSONP](https://en.wikipedia.org/wiki/JSONP), authentication must be disabled. If it is enabled in your solution and you want to remove authentication for recommendation requests, please contact <support@yoochoose.com> for further information and disabling. + For fetching recommendations, authentication is disabled by default, and it must be disabled when + you use [JSONP](https://en.wikipedia.org/wiki/JSONP) for response handling. + If authentication is enabled for recommendation requests and you want to change this, contact support@ibexa.co. -Recommendations are retrieved from the Recommendation Engine via RESTful requests using the HTTP GET method. The result is at least a list of item IDs that can be used to call the underlying CMS or shop system in order to postload the necessary information for the rendering process. +## GET requests -!!! note "Rendering Data" +The request for recommendations uses the following pattern: - If a data import via the [Content API](content_api.md) has been successful, it is also possible to fetch rendering data like e.g. "title", "description" or "deeplink" from the recommendation response. +`GET https://reco.yoochoose.net/api/v2/[customerid]/[userid]/[scenarioid].[extension]?parameter=value&[attribute=attributekey]` -To allow a customer to get recommendations based on predefined configurations, so-called "scenarios" are used. Scenarios are a combination of models and filters that should be applied to recommendation results including possible fallbacks. See [8. Scenarios](https://doc.ezplatform.com/projects/userguide/en/master/personalization/scenarios.md), [6. Recommendation Models](https://doc.ezplatform.com/projects/userguide/en/master/personalization/recommendation_models.md) and [7. Filters](https://doc.ezplatform.com/projects/userguide/en/master/personalization/filters.md) for more information about scenario configuration. +## Request parameters -A recommendation request looks like this: +For the request to return recommendations, you must provide the following parameters: - GET https://reco.yoochoose.net/api/v2/[customerid]/[userid]/[scenarioid].[extension]?parameter=value&[attribute=attributekey] +|Parameter|Description|Value| +|---|---|---| +|`customerid`|Your customer ID, as defined when [enabling Personalization](../enabling_personalization.md#configuring-customer-credentials) (for example, "00000").|alphanumeric| +|`userid`|An ID of the tracked user in the website (for example, an internal customer code, a session code or a cookie for anonymous users.|alphanumeric| +|`scenarioid`|An ID of the scenario used for providing recommendations, as defined in the Back Office.|alphanumeric| +|`extension`|A format of the response (either JSON or JSONP).|`json` or `jsonp`| -The embedded parameters `customerid` and `userid` are the same as used in the [Tracking API](tracking_api.md). Additional embedded parameters are described in the following table.  +!!! caution "Parameter encoding limitations" -|Parameter Name|Description|Values| -|---|---|---| -|`scenarioid`|The ID of the scenario used for providing recommendations. It is defined in the Admin GUI.|alphanumeric| -|`extension`|The format in which the server generates the response. There are two formats supported: JSON, JSONP. See [Response Handling](#response-handling) below for more information|json or jsonp (xml is deprecated)| + All parameters must be URL-encoded (see RFC 3986) and cannot contain a backslash (`%5C`) character. -!!! caution "XML format" +### Customizing the recommendation request - XML is deprecated and will not be developed further on. It can only be used in the old api (without using `/v2/` in the recommendation URL) +You can customize the recommendation request by using additional query string parameters. +For example, you can send the following request to the recommendation engine:  - `GET https://reco.yoochoose.net/api/[customerid]/[userid]/[scenarioid].xml?parameter=value&[attribute=attributekey]` +`GET https://reco.yoochoose.net/api/v2/00000/john.doe/landing_page.json ?contextitems=123&categorypath=%2FCamera%2FCompact&attribute=title&attribute=deeplink,description&numrecs=8` -## Basic Request Parameters +The request fetches 8 recommendations for user ID `john.doe`, who is viewing item 123 +and the category `/Camera/Compact`, based on the scenario with the identifier `landing_page`. +The recommendation response uses the JSON format and should include values of `title`, `deeplink` and `description` attributes. -Using additional query string parameters you can customize the recommendation request. +You can use the following parameters to customize a request: -|Parameter Name|Example|Description|Values| +|Parameter|Example|Description|Value| |---|---|---|---| -|`numrecs`|20|Defines the number of recommendations that should be delivered. Keep this number as low as possible as this increases the response time of the recommendation call.|1 to 50 (default "10")| -|`contextitems` (required for context-based recommendations)|10,13,14 or "CLICK"|Comma-separated list of items that the user is currently viewing on the web page. All items must be of the same type. The type is defined in the scenario configuration. If a history code like "CLICKED","CONSUMED", "OWNS", "RATED" or "BASKET" is used, the user's profile is used to simulate context items (e.g. the last clicks or the last purchases).|comma-separated list of item IDs (1 to 2147483647) or comma-separated list of alphanumeric item IDs if enabled for the customer| -|`itemid` (deprecated)|10|A single item to be used as a source for creating recommendations. This parameter is deprecated. Use `contextitems` instead.|numeric| -|`outputtypeid` (required if the scenario defined multiple output item types, otherwise it is optional)|1|Required if the scenario defined multiple output item types, otherwise it is optional.|numeric (default is the first/lowest outputtype enabled in the scenario config)| -|`categorypath`|"Women/Shirts"|Category path for fetching recommendations. The format is the same as the category path in the event tracking. It is possible to add this parameter multiple times to get recommendations from multiple categories. The order of recommendations from the different categories is defined by the calculated relevance.|string[/string]* (default is `%2F`, meaning the whole website)| -|`jsonpCallback` (used only for JSONP request)|"myCallback"|Function or method name for a JSONP request. It can be a function ("callme"), or a method ("obj.callme").|legal JavaScript function call (default is "jsonpCallback")| -|`attribute`|"title" or "description"|If this parameter is used, the recommender will try to fetch the value of the given attribute.</br>E.g. `&attribute=title` will try to fetch the value of the attribute title for the item which is delivered in the recommendation response if available. It works if the content import has been done successfully.</br>Multiple attributes are allowed, e.g. `&attribute=title&attribute=description` or csv-style: `&attribute=title,description`</br>It allows pure client-based recommendations without requesting local customer data.|string| +|`numrecs`|20|Defines a number of recommendations to be delivered. The lower this value, the shorter the response time. The default value is 10. |1 to 50| +|`contextitems`|10,13,14 or "CLICKED"|A comma-separated list of items that the user is viewing on the web page. The list is required by [context-based recommendations]([[= user_doc =]]/personalization/recommendation_models). All items must be of the same type. The type is defined in the scenario configuration. If history code is used ("CLICKED","CONSUMED", "OWNS", "RATED" or "BASKET"), context items are pulled from the user profile (for example, the most recent clicks or purchases). This parameter is optional. |1 to 2147483647 (or alphanumeric if enabled)| +|`outputtypeid`|1|Required for scenarios that are defined with multiple output item types, otherwise optional. By default it is the first/lowest output type enabled in the scenario config.|numeric| +|`jsonpCallback`|"myCallback"|Function or method name (used for JSONP request only). It can be a function ("callme"), or a method ("obj.callme"). The default value is "jsonpCallback".|legal JavaScript function call| +|`attribute`|"title" or "description"|If you apply this parameter, the engine tries to fetch the value of the attribute. For example, `&attribute=title` means fetching the title for the item that is delivered in the response, if available. The fetch works if content import has been successful. You can pass multiple attributes: `&attribute=title&attribute=description` or `&attribute=title,description`. Use this to pull "pure" client-based recommendations without requesting local customer data.|string| +|`categorypath`|`/Women/Shirts`|Category path for fetching recommendations. The format is the same as the category path used in event tracking. Add this parameter multiple times to get recommendations from multiple categories. The order of recommendations from different categories is defined by the calculated relevance. The default value is `%2F`, which stands for an entire website.|string[/string]* | +|`usecontextcategorypath`| |Used in conjunction with `categorypath`. If set to true, the category path of given context item(s) is resolved by the recommendation engine from the internal store and used as base category path. If more than one category is returned, all categories are used for providing recommendations. Setting this parameter to true increases the response time. If possible, use the `categorypath` parameter to provide the category to the recommender engine during the request. The default value is false.|boolean| +|`recommendCategory`| |Used in conjunction with `categorypath`. If set to true, the neighboring category linked with the recommended items is delivered in the response as an additional field `category`. Helps find a suitable template for articles from several categories.<br/>For example, take an article about American football. The article is categorized as `Sport/Football` and `America/USA`. Depending on the category, the webpage displays a football field or an American flag in the background. If the article is recommended and clicked in the `Sport/Cricket` category, it must open with the "field" template. If clicked in the `America/Canada` category, it must open with the "flag" template. The category is returned only if the article is located in several categories and the "closer" category is found. The default value is false.|boolean| -An example of the recommendation request:  +##### Submodel parameters - `GET https://reco.yoochoose.net/ebl/0000/john.doe/detailpage.json` `?contextitems=123&categorypath=%2FCamera%2FCompact&attribute=title&attribute=deeplink,description&numrecs=8` +If your recommendation model uses submodels to group content items/products based on an attribute, you can pass the following parameters to request recommendations for a specific group. +For more information, see [Submodels]([[= user_doc =]]/personalization/recommendation_models/#submodels). -It fetches 8 recommendations for user john.doe, who is watching item 123 and the category *"/Camera/Compact"* from the scenario with the identifier detailpage. The recommendation response should also include the attribute values of the attributes `title`, `deeplink` and `description `for rendering the recommendations. +|Parameter|Example|Description|Value| +|---|---|---|---| +|attribute key|`&color=red`|Applicable if a submodel with the same name and value is configured.|string| +|`userattribute`|gender|If defined, the recommendation engine tries to find the attribute value for the current user and, if found, "prefers" recommendations that are typically followed by users with the same value of the attribute. The default value is null.|string, csv list| -## Advanced Request Parameters +## Responses -### Categorypath Parameters +The recommendation request returns information about the currently used context items and an array of recommendation objects in JSON or JSONP format. +The result can be integrated into any webpage on the client side by using script code. +To track user actions like "clickrecommended" and "rendered", use the links delivered in the response. +For more information, see inline comments below. -|Parameter Name|Description|Values| -|---|---|---| -|`usecontextcategorypath`|If set to true, the category path of the given contextitem(s) will be resolved by the recommender engine from the internal store and used as base category path. If more than one category is returned, all categories are used for providing recommendations. Avoid setting this parameter to true to minimize the response time. If possible, use the parameter categorypath to provide the category to the recommender engine during the request.|default is false| -|`recommendCategory `|If passed in combination with a `categorypath` value, the "closest" category the recommended items linked with will be delivered in the response as additional field "category".</br>This feature helps to find "better" template for articles, which are located in several categories.</br>For example there is an article about football in the USA. The article is located in both categories "Sport/Football" and "America/USA". Depending on the category it is shown with a football field or the USA flag in the webpage background.</br>If this article is recommended and is clicked in the category "Sport/Cricket" it must open with the "football" template. If the article is clicked in the category "America/Canada" it must open with the "USA" template.</br>The category information is returned only if the article is located in several categories and the "closer" category is found.|default is false| +!!! note "Previewing recommendations" -### Submodel Request Parameters + You can preview the actual responses that come from the recommendation engine and how they are rendered in the user interface. + For more information, see [Scenarios]([[= user_doc =]]/personalization/scenarios/#previewing-scenario-results). + +For more information about integrating recommendations in the web page, see [Best practices](../best_practices/recommendation_integration.md). -|Parameter Name|Description|Values| -|---|---|---| -|any attribute key (for example "color") |Used if a submodel with the same name and value is configured. See Submodel configuration for more information.</br>Example: `&color=red`|-| -|`userattribute`|If defined, the recommender tries to find the attribute value for the current user and - if available - prefers recommendations which are typically used by the current user's gender, e.g. attributename of a user like "userattribute=gender" or "userattribute=customerclass" or csv separated "userattribute=gender,customerclass"|default is null| +### Response object format -## Response handling - -The recommendation request returns information about the currently used contextitems and an array of recommendation objects in JSON or JSONP format. The result can be integrated into any webpage on the client side by using some lines of script code. Please use the delivered links in the response to track user actions like "clickrecommended" and "rendered", see comments below. - -Example JSON response: +A JSON response can look like this: ``` json { - "contextItems": [ // information about the request's contextitem(s) + "contextItems": [ // Information about the request's contextitems { - "viewers": 134, // number of users that were looking at this item - // You can for example put the information next to the currently viewed product/article as "currently viewed by 134 visitors". + "viewers": 134, // A number of users that were looking at this item + // (You could put the information on the currently viewed content item/product page as "Currently viewed by ### visitors".) "itemType" : 1, "itemId" : 10, "sources" : "REQUEST" @@ -93,21 +107,21 @@ Example JSON response: } ], "recommendationItems": [ - { // first recommendation - "relevance": 23, //level of similarity to the current item, the higher the better + { // First recommendation + "relevance": 23, // A level of similarity to the current item, the higher the better "itemType": 1, "itemId": 100175717, "origin": { - "itemIds" : [10, 11], // these are the items that the recommendations are based on (context or user history items), multiple values are possible + "itemIds" : [10, 11], // Items that the recommendations are based on (context or user history items), multiple values are possible "itemType" : 1, "source" : "REQUEST" // Possible options: REQUEST (parameter "contextitems") or CLICK, CONSUME, BUY, BASKET, RATE (user history) }, "category" : "Women/Shirts", // Provided only if category suggestion is requested "links" : { - "clickRecommended" : "//event.yoochoose.net/api/[customerID]/clickrecommended/[someuser]/[itemtype]/[itemid]?scenario=<scenario>&...", // a link which is provided if a user ID is available. It should be fired if this recommendation is clicked - "rendered" : "//event.yoochoose.net/..." // a link which should be used to tell the recommender that this recommendation was shown to the user + "clickRecommended" : "//event.yoochoose.net/api/[customerID]/clickrecommended/[someuser]/[itemtype]/[itemid]?scenario=<scenario>&...", // A link that is provided if User ID is available. Link is fired when this recommendation is clicked. + "rendered" : "//event.yoochoose.net/..." // A link used to inform the engine that this recommendation was shown to the user }, - "attributes" : [ // only values that were requested in the query string are provided + "attributes" : [ // Only values that were requested in the query string are provided { "key": "title", "value": [ "French Cuff Cotton Twill Oxford" @@ -115,7 +129,7 @@ Example JSON response: }, ... ] }, - { // second recommendation + { // Second recommendation "relevance": 22, "itemType": 1, "itemId": 100172819, @@ -131,14 +145,13 @@ Example JSON response: } ``` -Example JSONP response: +A JSONP response can look like this (same comments apply): -``` jsonp +``` json jsonpCallback({ - "contextItems": [ // information about the request's contextitem(s) + "contextItems": [ { - "viewers": 134, // number of users that were looking at this item - // You can for example put the information next to the currently viewed product/article as "currently viewed by 134 visitors". + "viewers": 134, "itemType" : 1, "itemId" : 10, "sources" : "REQUEST" @@ -151,21 +164,21 @@ jsonpCallback({ } ], "recommendationItems": [ - { // first recommendation - "relevance": 23, //level of similarity to the current item, the higher the better + { + "relevance": 23, "itemType": 1, "itemId": 100175717, "origin": { - "itemIds" : [10, 11], // these are the items that the recommendations are based on (context or user history items), multiple values are possible + "itemIds" : [10, 11], "itemType" : 1, - "source" : "REQUEST" // Possible options: REQUEST (parameter "contextitems") or CLICK, CONSUME, BUY, BASKET, RATE (user history) + "source" : "REQUEST" }, - "category" : "Women/Shirts", // Provided only if category suggestion is requested + "category" : "Women/Shirts", "links" : { - "clickRecommended" : "//event.yoochoose.net/api/[customerID]/clickrecommended/[someuser]/[itemtype]/[itemid]?scenario=<scenario>&...", // a link which is provided if a user ID is available. It should be fired if this recommendation is clicked - "rendered" : "//event.yoochoose.net/..." // a link which should be used to tell the recommender that this recommendation was shown to the user + "clickRecommended" : "//event.yoochoose.net/api/[customerID]/clickrecommended/[someuser]/[itemtype]/[itemid]?scenario=<scenario>&...", + "rendered" : "//event.yoochoose.net/..." }, - "attributes" : [ // only values that were requested in the query string are provided + "attributes" : [ { "key": "title", "value": [ "French Cuff Cotton Twill Oxford" @@ -173,7 +186,7 @@ jsonpCallback({ }, ... ] }, - { // second recommendation + { "relevance": 22, "itemType": 1, "itemId": 100172819, @@ -189,49 +202,56 @@ jsonpCallback({ }) ``` -## Response Codes +### HTTP response codes -The Following HTTP response codes are used by the recommendation controller. +The following HTTP response codes are used by the recommendation controller: |HTTP Status Code|Description| |---|---| -|200 OK|Request was successfully processed.| -|304 Not Modified|Recommendation result was not modified since the time specified in the provided If-Modified-Since header.| -|400 Bad Request</br>414 Request-URI Too Long|The request is wrongly formatted. See response body for more information.| -|401 Unauthorized|Illegal authentication credentials.| +|200 OK|Request was processed successfully.| +|304 Not Modified|Recommendation result was not modified since the time specified in the provided `If-Modified-Since` header.| +|400 Bad Request</br>414 Request-URI Too Long|Wrong request formatting. See response body for more information.| +|401 Unauthorized|Invalid authentication credentials.| |403 Forbidden|Access denied.| -|404 Not Found|The requested element was not found. It can be customer ID (a.k.a. mandator ID), model ID, scenario ID in the request.| -|409 Conflict|The combination of used models and given recommendation request parameters doesn't allow to provide recommendations. This could be e.g. if personalized recommendations are requested for a user who has no history at all.| -|500 Internal Server Error|Unspecified error. Please inform support if you get this error repeatedly.| +|404 Not Found|The requested element was not found. It could be customer ID (or "mandator ID"), model ID, or scenario ID.| +|409 Conflict|The requested combination of models and recommendation parameters cannot return recommendations. This could happen, for example, if you request personalized recommendations for a user who has no history.| +|500 Internal Server Error|Unspecified error. Contact Ibexa support if this error is recurring.| -The body of the response in case of errors contains human-readable error message. The format of error messages can be changed and should not be used for automated processing. +In case of errors, the response body contains human-readable error messages. +Error messages can change, do not use them for automated processing. -## Recommendation Caching +!!! note "Rendering data" -In most cases the response of the recommendation service can be cached for some time. Depending on the used recommendation model and context it can dramatically reduce the number of recommendation requests and therefore the price of using the recommendation service. Recommendation service support following HTTP headers to allow cache control on the client side: + If data import with the [Content API](content_api.md) was successful, you can also fetch data used for rendering (for example "title", "description" or "deeplink") from the recommendation response. -|Scope|||Example|Format| -|---|---|---|---|---| -|Request|`If-Modified-Since`|Allows a *304 Not Modified* to be returned if content is unchanged.|`If-Modified-Since: Sat, 29 Oct 2013 19:43:31 GMT`|"HTTP-date" format as defined by [RFC 2616](https://tools.ietf.org/html/rfc2616)| -|Response|`Last-Modified`|The last modification date of the recommendations.|`Last-Modified: Tue, 15 Nov 2013 12:45:26 GMT`|-| -||`Expires`|Gives the date/time after which the response is considered to be outdated|`Expires: Thu, 01 Dec 2013 16:00:00 GMT`|-| +## Recommendation caching -The last modification timestamp indicates a change, that could influence the recommendation response. It depends on an updated recommendation calculation, an update of an item or some scenario configuration changes. The expiration timestamp is a best-effort prediction based on the model building configuration and provided context. The shortest expiration period is 5 minutes from the request time. The longest is 24 hours. In the table below several examples are illustrated: +In most cases the recommendation engine's response can be cached. +Depending on the recommendation model and context, it can drastically reduce the number of recommendation requests. +The recommendation service supports the following HTTP headers to enable cache control on the client side (all date values must follow the "HTTP-date" format as defined by [RFC 2616](https://tools.ietf.org/html/rfc2616)): -| Model | Context | Expiration time | Description | -|------|------|-----|-----| -| Bestselling products last 7 days | no context | 24 hours | The model with the 7 days scope is usually built once a day. It can be easily cached for 24 hours. It has no context and can therefore be cached globally for all the users of a customer. | -| Also bought products in the last month | current product | 24 hours | The model with the 30 days scope is usually built once a day. The context is always the same. It can be cached for every product. The same result can be used for all users of a customer. | -| Also consumed read articles in the last hour | current article | 30 minutes | Models with a short scope are usually built several times a day or even per hour. In this case the expiration time is set to the half of the model build frequency/period. | -| Personalized recommendation based on the user's statistic | customers click history | now | Although the statistic model is not updated within minutes, it is very likely that the context will be changed shortly (customer clicks another product and therefore the click is added to his history). The expiration time should not be much longer than the user activity on the web page. | +|Scope|Header|Description|Example| +|---|---|---|---| +|Request|`If-Modified-Since`|Enables returning the *304 Not Modified* code if content is unchanged.|`If-Modified-Since: Sat, 29 Oct 2013 19:43:31 GMT`| +|Response|`Last-Modified`|The last modification date of the recommendations.|`Last-Modified: Tue, 15 Nov 2013 12:45:26 GMT`| +|Response|`Expires`|Gives the date/time after which the response is outdated|`Expires: Thu, 01 Dec 2013 16:00:00 GMT`| + +The last modification timestamp indicates a change that could influence the recommendation response. +It depends on an updated recommendation calculation, an update of an item or certain scenario configuration changes.  + +The expiration timestamp is a best-effort prediction based on the model configuration and provided context. +The shortest expiration period is 5 minutes from the request time, the longest is 24 hours. -In most cases you do not need to calculate the expiration time manually. The table above is provided for the orientation, how the Expires header of the HTTP response is calculated by the recommendation engine and already provided to your caching system. You just need to make sure that the Expires header is used in the configuration of your caching system instead of a static value out of your configuration. +In most cases you do not have to calculate the expiration time manually. +Instead, make sure that the `Expires` header is used in the configuration +of your caching system and not a static value out of your configuration. -## Further Reading +To learn how recommendation engine calculates the `Expires` header that is provided +to your caching system, see the following table with caching strategy examples: -- Best Practices: [Recommendation Integration](../best_practices/recommendation_integration.md) -- [Representational state transfer - Wikipedia](https://en.wikipedia.org/wiki/Representational_state_transfer) -- [Uniform Resource Identifier (URI): Generic Syntax](https://tools.ietf.org/html/rfc3986) -- [Apache Module mod\_proxy](https://httpd.apache.org/docs/2.2/mod/mod_proxy.html) -- [Hypertext Transfer Protocol - HTTP/1.1](https://tools.ietf.org/html/rfc2616) -- [Personalization quickstart](../personalization_quickstart.md) +| Model | Context | Expiration time | Description | +|------|------|-----|-----| +| Bestselling products (last 7 days) | No context | 24 hours | The model with the 7 days scope is usually built once a day. You can cache its results for 24 hours. It has no context, therefore it can be cached globally for all the users of the site. | +| Also bought products (last month) | Current product | 24 hours | The model with the 30 days scope is usually built once a day. The context is always the same, therefore it can be cached for every product, and the same result can be used for all users of the site. | +| Also consumed articles (last hour) | Current article | 30 minutes | Models with a short scope are usually built several times a day or even per hour. The expiration time is set to half of the model build frequency. | +| Personalized recommendation based on the user's statistic | User's click history | Now | Statistical models are not updated within minutes, and it is very likely that the context will change shortly (a user clicks another product, the click is added to their history). The expiration time should not be much longer than the user's activity on the web page. | diff --git a/docs/guide/personalization/developer_guide/tracking_api.md b/docs/guide/personalization/developer_guide/tracking_api.md index 6895762b57..2b15b9ddae 100644 --- a/docs/guide/personalization/developer_guide/tracking_api.md +++ b/docs/guide/personalization/developer_guide/tracking_api.md @@ -1,46 +1,58 @@ -# Tracking API - -If you are interested in importing historical user data, see [Importing historical user tracking data](importing_historical_user_tracking_data.md) as well. It speeds up enabling high quality recommendations. +--- +description: Allows to track items using it ID. It covers many Content Types with the same ID configured for tracking. +--- -If you want to use a javascript library to add tracking into your website, see [Tracking with yct.js](tracking_with_yct.md). - -In order to provide recommendations a tracking process needs to collect user behavior on the customer's site. Most popular examples of such events are: +# Tracking API -- "Click", if a user opens a detail page -- "Buy" or "Consume", if a user buys an item or consumes in some other way (watches videos, reads articles, etc.) -- "Rate" if a user likes, comments or rates an item -- "Login" if a user logs in on a website and -- "Clickrecommended" if a user decides to click on a recommendation +To provide recommendations, a tracking process needs to collect user behavior on the customer's site. +The most popular user events are: -See user guide chapter [4. Event Types](https://doc.ezplatform.com/projects/userguide/en/master/personalization/event_types.md) for a brief introduction to different types of events. Depending on the event type, some additional parameters like item price or user rating must be provided. +- Click - When a user opens a detail page. +- Buy/Consume - When a user buys an item or consumes content +- Rate - When a user likes, comments or rates an item +- Login - When a user logs in on a website +- Clickrecommended - When a user clicks a recommendation -## Wording of products, articles and the generic term items +For a complete list of events, see [Event types]([[= user_doc =]]/personalization/event_types) in the user documentation.  +Depending on the event type, some additional parameters, such as item price +or user rating, must be provided. -This documentation is primarily intended to be used for E-commerce and Publishers. It is hard to use generic wording in each domain as E-commerce mostly uses the term "product", whereas the Publisher domain widely uses the terms "content", "article", "images" or "videos". Because of this the generic term "item" will be used instead to cover all types, meaning "products", "content", "article", "images" or "videos". For further segmentation we use the term "itemtype" which in combination with the item ID itself defines a domain specific object. +Importing historical user data can help you reduce the delay in delivery of high quality recommendations. +For more information, see [Importing historical user tracking data](importing_historical_user_tracking_data.md). -The following tables show some examples of items that are defined by an item id and an item type: +Apart from the tracking API, you can add tracking to the website by integrating a JavaScript library. +For more information, see [Tracking with yct.js](tracking_with_yct.md). -|Domain||Item ID|Item Type| -|---|---|---|---| -|E-commerce|Product|id of a product|type of a product| +## Definitions -An electronic product could be defined by item type "1" and item id "3298" +You can use the tracking API both in eCommerce and content publishing scenarios. +eCommerce mostly uses the term "product", whereas the Publisher domain widely uses the terms "content", +"article", "images" or "videos". +Therefore, a generic term "item" is used instead to cover all the above types. +For further segmentation the term "item type" is used, which, in combination with the item ID itself, +defines a domain specific object. -A textile product could be defined by item type "2" and item id "3298" +For example, an electronic product could be defined by item type "1" and item id "3298", +while a textile product could be defined by item type "2" and item id "3298". !!! note - Usually only one item type is sufficient in the E-commerce business as each product available in an E-commerce shop has a unique identifier (see also chapter Item Identifiers) + Usually only one item type is sufficient in the eCommerce business as each product available in + an eCommerce shop has a unique identifier. -|Domain||Item ID|Item Type| -|---|---|---|---| -|Publisher|Article|id of an article|type of content| -|Publisher|Video|id of a video|type of content| -|Publisher|Photo Gallery|id of a photo gallery|type of content| +|Domain|Item ID|Item Type| +|---|---|---| +|Content publishing|Article|ID of an article| +|Content publishing|Video|ID of a video| +|Content publishing|Photo Gallery|ID of a photo gallery| -Assuming the numbering of content is independent and the same item ID is used twice but for different item types. It is therefore impossible to define the difference if there's no segmentation by item types. +Assuming the numbering of content is independent and the same item ID +is used for two items of different item types, it is impossible to tell +the difference if there is no segmentation by item types. -If an article, a video and a photo gallery have the same item ID, e.g. 29712, we would use three different item types to separate the items that are tracked: +If an article, a video and a photo gallery have the same item ID, use different +item types to separate the items that are tracked. +For example: - item type "1" and itemid "29712" -> article - item type "2" and itemid "29712" -> video @@ -48,64 +60,120 @@ If an article, a video and a photo gallery have the same item ID, e.g. 29712, we !!! note - Even if item ids cannot overlap in a customer's system, we strongly recommend using different item types in order to provide independent tracking and cross-item type recommendations like "Users who read this article also watched these videos" or "Users who liked this gallery also read these articles". + Even if item IDs cannot overlap in a customer's system, Ibexa recommends using + different item types to provide independent tracking and cross-item type recommendations, + such as, for example, "Users who read this article also watched these videos" + or "Users who liked this gallery also read these articles". ## Identifiers -### User Identifier +### User identifier -High quality recommendations can only be delivered if the underlying data is correct and consistent. For consistent tracking it is crucial to choose and use a consistent identifier for a user. Usually a user visits a website as an anonymous user. Therefore their identifier is either own first-party cookie or a session ID provided by the website. If there's no existing user ID handling that we can re-use, we encourage using own cookie and setting the expiry date to at least 90 days from the last usage. If there's a login mechanism, the user is usually tracked with a temporary identifier before the login. Immediately after a successful login process a "login" event (see below) must be sent and from then on the pseudonymous user identifier (e.g. a system's internal registration id) must be continuously used. After (automatic) logout, the anonymous user id can be used again. +High quality recommendations can only be delivered if the underlying data is correct and consistent.  +For consistent tracking it is crucial to choose and use a consistent identifier for a user. +A user usually visits a website anonymously. +Therefore, their identifier is either a first-party cookie or a session ID provided by the website.  +If there is no existing user ID handling that we can re-use, it is recommended that +you use your own cookie and set the expiry date to at least 90 days from the last usage. +If there is a login mechanism, the user is usually tracked with a temporary identifier before the login. +Immediately after a successful login process a Login event must be sent. +At this point a [pseudonymous](https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32016R0679&from=EN#d1e1489-1-1) user ID, +for example, a system's internal registration id, must be used.  +After logout, the anonymous user ID can be used again. !!! note The user identifier is required in tracking requests, otherwise it is discarded from the tracking servers. - If a browser has JavaScript or cookies disabled, please verify that you put some "dummy" value as identifier in the tracking request to avoid losing tracking information. Even if the event is not user-specific, it is still useful for popularity models (e.g. Topsellers, see [6. Recommendation Models](https://doc.ezplatform.com/projects/userguide/en/master/personalization/recommendation_models.md)). + If a browser has JavaScript or cookies disabled, make sure that you put some "dummy" value as identifier in the tracking request to avoid losing tracking information. + Even if the event is not user-specific, it is still useful for [popularity models]([[= user_doc =]]/personalization/recommendation_models/). -Internally the recommendation engine creates a hash of every user ID. The original ID is not saved. It is still possible that the original ID appears in the log files for debug purposes but log files are are purged regularly. The user ID is case sensitive. +The recommendation engine internally creates a hash of every user ID. +The original ID is not saved. +It is still possible that the original ID appears in the log files for the debugging +purposes but log files are purged regularly. +The user ID is case sensitive. -### Item Identifiers +### Item identifiers -As a customer or, more precisely, the specialty department you need to evaluate what should be presented as recommendations on your site. In the E-commerce business you mostly have the possibility to track items based on: +Persons responsible for the sales policy in place in your organization must decide +what should be presented as recommendations on the website. +In the eCommerce business you mostly have the possibility to track items based on: -- **Stock Keeping Unit** (SKU) or -- **Master Item Identifier** (MII) +- Stock Keeping Unit (SKU) or +- Universal Product Code (UPC) -The exact identifiers that are tracked will also be recommended ("*what-you-track-is-what-you-get"*). By default it is not possible to track SKUs but recommend MIIs. Following is an example of a typical decision case in the E-commerce business: +The exact identifiers that are tracked are also recommended ("what-you-track-is-what-you-get"). +By default, it is not possible to track SKUs but recommend UPCs. +The following use case is typical for eCommerce business: -Customer A implements the YOOCHOOSE Recommender Engine and decides to use the **SKU** as item identifiers and recommendable items. What could happen is that users browsing through the shop **will probably get recommendations of the same item that is currently displayed but in a different size and/or color, a so-called "variant"**. As the Recommender Engine doesn't know anything about the relations between items, every single SKU is used to calculate similarities between them. In the case of bestsellers, this could lead to the appearance of a shirt in size L on position 2 and the same shirt in size M on position 4. +Customer A implements the recommendation engine and decides to use the SKU +as item identifiers and recommendable items. +End users who browse through the shop probably get recommendations of +the same item that is currently displayed but in a different size and/or color, +a so-called "variant". +The recommendation engine does not recognize relations between items, therefore, +every single SKU is used to calculate similarities between them. +In the case of bestsellers, this could lead to the appearance of a shirt +in size L on position 2 and the same shirt in size M on position 4. -Customer B decides to use the **MII** as item identifiers. This will lead to recommendations **that will not contain variations of the currently shown item**. In this case it could not happen that on the detail page of shirt X the same shirt in a different size is recommended. In the case of bestsellers it is therefore not possible to have the same shirt on two positions in the list of recommendations. +Customer B decides to use the UPC as item identifiers. +This results in recommendations that do not contain variations of the currently shown item. +Therefore, the detail page of shirt X does not contain a recommendation +for the same shirt in a different size. +And the same shirt does not show up twice on a list of bestseller recommendations. -If the *size of an item or the color is selectable* on a detail page of an item, you may prefer to use the MII. If recommendations of the *same item in different sizes or colors* are desired, you should use the SKU as item identifiers. +If the size of an item or the color is selectable on a detail page of an item, +you may prefer to use the UPC. +If recommendations of the same item in different sizes or colors are desired, +you should use the SKU as item identifiers. -Remember to use the same identifiers in all interactions between your site and the recommendation engine, e.g. if a user buys an item, a recommendation is clicked or displayed etc. +Remember to use the same identifier in all interactions between your site +and the recommendation engine, for example, when a user buys an item, +clicks a recommendation or displays a product page. -## Request Parameter categorypath +## Request parameter categorypath -The category path is the logical tree structure which leads to an item and is used for recommendation filtering. For example "recommend only items from the same category". +Category paths are logical tree structures that lead to items and are used for recommendation filtering. +For example, "recommend only items from the same category". -During recommendation requests the category path must always be provided! The category path is a forward slash-separated list of categories from the root, such as: "%2FFoto%2FCameras%2FCompact%2FCanon". The first slash (if set) is ignored. Like all other parameters, the category path must be URL-encoded and due to a limitation of the HTTP server must not contain backslashes. +During recommendation requests, the category path must always be provided. +The category path is a forward slash-separated list of categories from the root, for example, `%2FFoto%2FCameras%2FCompact%2FCanon`. +The initial slash (if present) is ignored. +Like all other parameters, the category path must be URL-encoded and cannot contain backslashes. -The "categorypath" parameter plays an important role as it offers the possibility to provide category-based recommendations without an explicit export of the structure of a customer's website. If enabled by YOOCHOOSE it is used for on-the-fly updating of item categories. If an item is moved to another category, it will be handled as being in both categories until the old category ages out or is forcibly deleted. Multiple category locations of an item (multi-homing) are therefore possible. +The "categorypath" parameter offers the possibility to provide category-based +recommendations without an explicit export of the structure of a customer's website. +If enabled by Ibexa, it is used for on-the-fly updating of item categories. +If an item is moved to another category, it is handled as present in both categories until +the old category ages out or is forcibly deleted. +Multiple category locations of an item (multi-homing) are therefore possible. -!!! note "|Category Paths" +!!! note "Category paths" - If you import your own item metadata over the recommendation engine import interface, you do not necessarily need to provide the category path in the "click" event. As this is activated by default, please contact <support@yoochoose.com> to set the desired configuration. + When you import your own item metadata by using the recommendation engine import interface, + you might choose to not provide the category path in the Click event. + Category path is required by default, contact Ibexa to change the default configuration. -## Tracking Events +## Tracking events !!! note - Events are forwarded to the Recommendation System engine using "http" or "https" requests, so-called [RESTful-Requests](https://en.wikipedia.org/wiki/Representational_state_transfer). Both GET and POST methods are allowed for the event tracking. Make sure that all embedded and query string parameters are URL encoded (see [RFC 3986](https://tools.ietf.org/html/rfc3986)). Due to a restriction of the used HTTP server, it is not allowed to use a backslash \[encoded as %5C\] in the embedded parameters (the context path of the URL). + Events are forwarded to the recommendation engine with HTTP or HTTPS requests + (or [RESTful-Requests](https://en.wikipedia.org/wiki/Representational_state_transfer). + Both GET and POST methods are allowed for the event tracking. + Make sure that all embedded and query string parameters are URL encoded and do not use a backslash + [encoded as %5C\]. -### Click Event +### Click event -If a user opens an item/article detail, a "Click" events needs to be sent. Often "Click" events also provide information about the category structure of websites. +When the end user opens an item/article detail, a Click events is sent. +The Click event often provides additional information about the category structure of the website. !!! note - We do not store the user ID itself in the database. All user IDs are non-reversibly anonymized before the user profile is stored to the disk or the recommendation models are built. + User IDs are not stored in the database. + They are irreversibly anonymized before saving to disk or building the recommendation model. The URL to track user clicks has the following format: @@ -114,77 +182,108 @@ The URL to track user clicks has the following format: |Name|Description|Values| |---|---|---| -|customerid|Your customer ID, a.k.a. "mandator ID" (for example "00000").|alphanumeric| -|userid|A user's ID on the website of the customer. It could be an internal customer code, a session code or a cookie for anonymous users. See chapter "Request Format and Parameters" below in this document for more information on user ID.|URL-encoded alphanumeric| -|itemtypeid|Item type ID. See the chapter Item Type ID for more information. See chapter "Request Format and Parameters" bellow in this document for more information to the item type ID.|1 – 5| -|itemid|Unique ID of the item the user has clicked.</br>Product IDs based on string identifiers: We also support string identifiers for item IDs to track content on a website but it is discouraged due to fraud and security issues. If you are not able to provide numeric identifiers for the tracking process, please get in touch with us under support@yoochoose.com for further information and implementation notes.|1 to 2147483647| +|`customerid`|Your customer ID (for example "00000").|alphanumeric| +|userid|A user's ID on the website of the customer. It could be an internal customer code, a session code or a cookie for anonymous users.|URL-encoded alphanumeric| +|`itemtypeid`|Item type ID.|1 to 2147483647| +|`itemid`|A unique ID of the item the user has clicked.</br>String-based identifiers are also supported as item IDs to track content on a website, but it is discouraged due to fraud and security issues. If you are unable to provide numeric identifiers for the tracking process, contact Ibexa for further information and implementation notes.|1 to 2147483647| -All embedded parameters are required for the request. Some optional request parameters can be set over query string parameters (GET parameters). +All embedded parameters are required for the request. +Some optional request parameters can be set over query string parameters (GET parameters). `GET https://event.yoochoose.net/api/[customerid]/click/[userid]/[itemtypeid]/[itemid]?parameter1=value1¶meter2=value2` |Name|Description|Values| |---|---|---| -|categorypath|The forward slash-separated path of categories of the item. Like all other parameters it must be URL-encoded, for example `%2FCameras%26Foto%2FCompact%20Cameras%2FCanon`</br>See chapter [Category Filter](https://doc.ezplatform.com/projects/userguide/en/master/personalization/filters.md#category-filter) of the user guide for additional use cases of the category path.|URL-encoded string.</br>Forward and trailing slash is ignored; "/Cameras/" is the same as "Cameras"| +|`categorypath`|The forward slash-separated path of categories of the item. Like all other parameters it must be URL-encoded, for example `%2FCameras%26Foto%2FCompact%20Cameras%2FCanon`.</br>For use cases, see [Category path filters]([[= user_doc =]]/personalization/filters/#category-path-filters) in the user documentation.|URL-encoded string.</br>Initial and trailing slashes are ignored: "/Cameras/" is the same as "Cameras".| -### Consume Event +### Consume event -!!! note "Publisher vs. Shop solution" +!!! note "eCommerce vs. content publishing" - The "consume" event is important for publisher/media web pages (like a newspaper, news agency or online movie provider). For E-commerce stores this event is not necessarily needed but can of course be used in own implementations. + The Consume event is important for content publishing websites. + For eCommerce stores, this event is not required but can be used in custom implementations. -The event should be sent if the user stays on the page for some predefined period of time. In this case it is assumed that the user consumed the item (read an article or finished watching a video). +The event is sent when the end user stays on the page for a predefined period of time. +It is then assumed that the user consumed the item (read an article or watched a video). The URL has the following format: `GET https://event.yoochoose.net/api/[customerid]/consume/[userid]/[itemtypeid]/[itemid]` -All embedded parameters are the same as for a click event. The request parameters are: +All embedded parameters are the same as for a Click event. +The following table lists the request parameters: -| Name | Description | Values | -|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| -| percentage | It defines how much of an item was consumed, e.g. an article was read only in 20%, a movie was watched in 90% or someone finished 3/4 of all levels of a game | 0-100 | +|Name|Description|Values| +|---|---|---| +|`percentage`|Informs how much of an item was consumed, for example, that an article was read only in 20%, a movie was watched in 90% or someone finished 3/4 of all levels of a game.|0-100| -The logic for calculating the percentage is to be defined by the implementation. For articles this could be by scrolling down, for a movie/video based on the consumption part. Keep in mind that you should put some though into what 100% consumption means. For example a movie contains end titles which are probably almost never consumed and therefore should not be part of the percentage calculation. +The logic for calculating the percentage is defined by the implementation. +For articles, this could be by scrolling down, for a movie/video based on the consumption part.  +You must decide what 100% consumption means. +For example, a movie contains end titles that are almost never consumed. +Therefore, they should not be part of the percentage calculation. -The simplest implementation for articles is a JavaScript timer, which sends this event after some predefined time has elapsed (and the user did not leave the page). The timespan after that the event is triggered should be dependent on the content, e.g. it could be 30 seconds for a small newspaper article or a timespan calculated by the amount of words. +The simplest implementation for articles is a JavaScript timer, which sends this event after +a predefined time has elapsed (and the user did not leave the page). +The timespan after that the event is triggered should be dependent on the content, for example, +it could be 30 seconds for a small newspaper article or a timespan calculated by the amount of words. !!! note "Incremental tracking of consume events" - Consume events for a user can be sent incrementally as the recommender uses only the highest percentage rate. For example if a user watches a movie, the site could sent consume events in 10% steps in order not to lose tracking information when the browser window is forcibly closed. + Consume events for a user can be sent incrementally as the recommender uses only the highest percentage rate. For example, if a user watches a movie, the site could send Consume events in 10% steps to avoid losing tracking information when the browser window is forcibly closed. -### Buy Event +### Buy event -As the name suggests this event must be used if a user buys an item. It must be sent to the event tracker at the end of a successful check-out process to ensure that no further action of the user can result in an abort. +As the name suggests, this event is used when an end user buys an item. +It must be sent to the event tracker at the end of a successful check-out process to ensure that +no further action of the user can result in an abort. The URL has the following format:  `GET https://event.yoochoose.net/api/[customerid]/buy/[userid]/[itemtypeid]/[itemid]?fullprice=2.50EUR&quantity=4` -In addition to the fact that an item is bought, this event should provide information about the product price and quantity. +In addition to the fact that an item is bought, this event should provide information about +the product price and quantity. |Name|Description|Values| |---|---|---| -|quantity (optional)|The number of products a user has bought. Default value is "1". It is possible to send n events instead of setting this parameter to n.|Integer (default is "1")| -|fullprice (required)|A price for a **single** product. It contains the price in decimal format plus the currency ISO 4217 code. If the price has a decimal part, the point must be used. There can be no space between price and currency.|For example "12.45EUR" or "456JPY"| -|price (deprecated)|**This parameter is deprecated, use the "fullprice" instead!** The price for a single item in Euro cents, e.g. 2.25EUR is price=225, default currency "EUR" is assumed.|Integer| -|currency (deprecated)|**This parameter is deprecated, use the "fullprice" instead!** The currency of a price. If left out the default currency "EUR" is assumed.|ISO 4217| +|`quantity`|The number of products a user has bought. Default value is "1". You can send n events instead of setting this parameter to n. This parameter is optional.|integer (default is "1")| +|`fullprice`|A price for a single product. It contains the price in decimal format plus the currency ISO 4217 code. If the price has a decimal part, the point must be used. There can be no space between price and currency. This parameter is optional.|currency, for example "12.45EUR" or "456JPY"| -For example, if a user bought 4 pens for 10 Euros, the parameter "fullprice" must be set to "2.50EUR" and parameter "quantity" to 4. +For example, if a user bought 4 pens for 10 Euros, `fullprice` can be set to "2.50EUR" +and `quantity` can be set to 4. -The "buy" event is only relevant if the user is charged per product like it happens in a classic shop. If products are sold on a subscription base or the web presence is ad-sponsored, this event type is not needed. +The Buy event is only relevant if the user is charged per product, like in a classic shop. +If products are sold on a subscription basis, or the web presence is ad-sponsored, +this event type is not applicable. -#### Prices in a Buy Event +#### Prices in a Buy event -As mentioned above, every buy event can contain a price. If the price is set, it is stored with the event and used for calculating revenue for statistics. The price should be a price the user paid for the item including all taxes and discounts.  +Every Buy event can contain a price. +If the price is set, it is stored with the event and used for calculating the revenue for statistics. +The price must be a price the user paid for the item, including all taxes and discounts.  -If product price filtering is activated, the information provided over the product import is used. See chapter [Content API](content_api.md) for more information. +If product price filtering is activated, the information provided over the product import is used. +For more information, see [Content API](content_api.md). -The currency is stored with the price and normalized only when statistic information is requested. It is often a good choice to select a base currency and convert prices before sending the buy event. The price attached to a buy event never overwrites the price which was defined in an item import. +The currency is stored with the price and normalized only when statistic information is requested. +It is often a good choice to select a base currency and convert prices before sending the buy event. +The price attached to a buy event never overwrites the price which was defined in an item import. -### Login Event (a.k.a. Transfer event) +### Login event -Recommendations rely on the fact that user actions can be correlated over a longer period of time. Moreover, recommendations like "users who viewed this product ultimately bought it" require correlating click events with subsequent buy events. In general, users tend to browse a site anonymously and add products to their shopping cart. Up to this point, a user is identified by a visit-scoped variable (e.g. a session ID or a first party cookie). During the check-out of the shopping cart, a user probably logs in to an existing account. As a consequence, the user identifier changes from an anonymous visit-scoped ID (sourceuserid) to a pseudonymous, persistent account ID (targetuserid). It is highly desirable to correlate both IDs in order to correlate the buy events (account ID) with the preceding click events (visit-scoped ID). The administrative event 'login' serves exactly this purpose. +Recommendations rely on the fact that user actions can be correlated over a longer period of time. +Moreover, recommendations similar to "users who viewed this product ultimately bought it" +require correlating Click events with subsequent Buy events. +In general, users tend to browse a site anonymously and add products to their shopping cart. +Up to this point, a user is identified by a visit-scoped variable (for example, a session ID or +a first party cookie). +During the check-out of the shopping cart, a user probably logs in to an existing account. +As a result, the user identifier changes from an anonymous visit-scoped ID (sourceuserid) to a +pseudonymous, persistent account ID (targetuserid). +You should correlate both IDs to correlate the Buy events (account ID) with +the preceding Click events (visit-scoped ID). +The Login event serves exactly this purpose. The format of the URL is:  @@ -195,39 +294,45 @@ The format of the URL is:  |sourceuserid|User identifier valid up to now(usually some anonymous session ID)|URL-encoded alphanumeric| |targetuserid|User identifier valid from now on (usually an account ID or login name)|URL-encoded alphanumeric| -!!! note "Deprecated 'transfer' event" - - Based on feedback of our customers and to make the purpose a bit clearer, we renamed the formerly named 'transfer' event to 'login'. As the name implies, it should be used when a user logs in. It covers exactly the same functionality as the transfer event. There is no need to change anything on the customer's side for existing implementations as the transfer event https://event.yoochoose.net/\[solutionid\]/\[customerid\]/transfer/\[sourceuserid\]/\[targetuserid\] will still be supported but should be considered as deprecated! +### Basket event -### Basket Event - -The basket event can be used to add products to a user's shopping cart. This event is especially useful if anonymized checkout is allowed or no recurring user identification is possible. By using the shopping cart products as input for getting recommendations, the empty profile or cold-start problem (meaning the user has no buy history) can be solved. The more valuable basket events instead of recent user clicks can be used to provide personalized recommendations. It also happens quite often that users "store" products on their shopping wishlist and plan to buy them later. With the help of this information, personalized shopping cart-based recommendations can be provided in the whole shop. +The Basket event can be used to add products to a user's shopping cart. +This event is especially useful if anonymized checkout is allowed or no recurring user +identification is possible. +By using the shopping cart products as input for getting recommendations, problems with an empty profile or +no buy history for the user can be solved. +The more valuable Basket events instead of recent user clicks can be used to provide +personalized recommendations. +It also happens quite often that users "store" products on their shopping wishlist and plan +to buy them later. +With the help of this information, personalized shopping cart-based recommendations can be provided +in the whole shop. `GET https://event.yoochoose.net/api/[customerid]/basket/[userid]/[itemtypeid]/[itemid]` -|Name|Description|Values| -|---|---|---| -|categorypath|The categorypath from where the item is placed into the shopping cart.|URL-encoded string| - -The categorypath parameter should be added for statistical reasons. Often articles or products are multihomed in a shop or a media/publisher site. In order to analyze from which location products were sold most often, the categorypath should be provided. +There are no query string parameters for this event.  -### Rate Event +### Rate event -Publishers, media or shops often allow commenting/rating products, articles or movies. If a user comments on an item, it indicates a special interest an this topic that has to be treated separately. +Publishers, media or shops often allow commenting/rating products, articles or movies. +If a user comments on an item, it indicates a special interest in this topic that has +to be treated separately. The format of the URL is: `GET https://event.yoochoose.net/api/[customerid]/rate/[userid]/[itemtypeid]/[itemid]?rating=50` -This can also be used for explicit ratings like a five star rating or any other liquor scale-based rating. The predefined rating can be submitted when the user comments on an item. +This can also be used for explicit ratings like a five-star rating for hotels. +A predefined rating can be submitted when the user comments on an item. |Name|Description|Values| |---|---|---| -|rating|The rating a user gives an item|Integer from 0 to 100 (The rating value is normalized during the calculation of rate based recommendations. So there's no need to use the full scale of 0-100 but it needs to be consistent)| +|`rating`|The rating a user gives an item. The rating value is normalized during the calculation of rate-based recommendations. Therefore, there is no need to use the full scale of 0-100 but it needs to be consistent.|integer from 0 to 100| -### Blacklist Event +### Blacklist event -If a website offers a link or button that allows feedback like "do not recommend this product to me anymore" a user could express that they have bought it already on behalf of somebody else or in another shop. +If a website offers a link or button that allows feedback similar to "do not recommend this product +to me anymore", a user could express that they have bought it already in another shop. The format of the URL is: @@ -235,11 +340,17 @@ The format of the URL is: There are no query string parameters for this event.  -## Tracking Events based on Recommendations +## Tracking events based on recommendations -Tracking events based on integrated recommendations is the only way to measure success of recommendations. It is crucial to tell the recommendation engine which recommendations were shown and what recommendations were clicked. Otherwise reliable statistics cannot be calculated and used to check against a customer's KPIs. +Tracking events based on integrated recommendations are the only way to measure success of recommendations. +It is crucial to inform the recommendation engine about which recommendations were shown and +what recommendations were clicked. +Otherwise, reliable statistics cannot be calculated and used to check against a customer's KPIs. -In a recommendation response, the requests to generate a Clickrecommended or Rendered Event are already included. They should be used and executed when a recommendation was clicked/accepted or a recommendation was shown. Sending Rendered Events causes as much requests as recommendations to be displayed, a Clickrecommended Event is usually sent only once (if a user clicks on a specific recommendation item). +A recommendation response already includes the requests to generate a Clickrecommended or Rendered event. +They are used and executed when a recommendation is clicked/accepted or a recommendation is shown. +Sending Rendered events causes as many requests as recommendations to be displayed, +a Clickrecommended event is usually sent only once (when a user clicks on a specific recommendation item). Example of a recommendation response: @@ -261,80 +372,101 @@ Example of a recommendation response: }, ``` -In the field "links" you can see the delivered request string to be executed when displaying or clicking on recommendations. See [Recommendation API](recommendation_api.md) for more details. +In the `links` field, the delivered request string is visible, which is executed when the end user +displays or clicks a recommendations. +See [Recommendation API](recommendation_api.md) for more details. + +You can still implement the traditional way as mentioned below but it is strongly recommended against this. +If you do so, remember to examine it together with the Ibexa team because it is crucial +for statistical analysis. -You can still implement the traditional way as mentioned below but we **strongly advise against this**. If you do so, keep in mind to check it thoroughly with the YOOCHOOSE team as it is crucial for statistical analysis. +### Clickrecommended event -### Clickrecommended Event (a.k.a. Follow Event) +When the end users clicks a recommendation, the following event is sent +to gather statistics related to the acceptance of recommendations. -If users click on a recommendation, the following event should be sent to gather statistics over the acceptance of recommendations. Click-recommended events are also known as follow-events. The URL has the following format: +The URL has the following format: `GET https://event.yoochoose.net/api/[customerid]/clickrecommended/[userid]/[itemtypeid]/[itemid]?scenario=<scenarioid>` -All embedded parameters are the same as for a click event. The request parameters are: +The embedded parameters are the same as for a Click event. + +The request parameters are: |Name|Description|Values| |---|---|---| -|scenario (required)|Name of the scenario, where recommendations originated from.|URL-encoded alphanumeric| +|`scenario`|Name of the scenario, where recommendations originated from. This parameter is required.|URL-encoded alphanumeric| -The scenario parameter identifies the originating scenario to gain detailed statistics about which scenario motivated the user to click on a recommendation. This information comes with the recommendation from the recommendation controller.  +The scenario parameter identifies the originating scenario to gain detailed statistics about +the scenario that motivated the user to click on a recommendation. +This information comes with the recommendation from the recommendation controller.  -This event is used for providing statistics about how often recommendations of the configured recommendation scenario were accepted or considered as useful by users. Besides that this information is used for providing and evaluating A/B Tests. +The event is used for providing statistics about how often recommendations of +the configured recommendation scenario were accepted or considered as useful by users. -### Rendered Event +### Rendered event -Render events must be sent if the website uses the recommendation provided by the recommendation engine and renders it on the webpage. In a combination with a predefined threshold it allows the recommender engine to exclude this item from a future result and not recommend the same item to the same user multiple times during a session. +This event sent when the website uses the recommendation provided by the recommendation +engine and renders it on the webpage. +In combination with a predefined threshold, it allows the recommender engine to exclude this item +from future results and avoid recommending the same item to the same user multiple times during a session. -The URL for a render event has the following format: +The URL for a Rendered event has the following format: `GET https://event.yoochoose.net/api/[customerid]/rendered/[userid]/[itemtypeid]/[itemid[,itemid]]` -The render event has the same embedded parameters as a click event except for the item ID. It is common that recommendations are rendered as a block with multiple items. To save traffic and reduce latency, it is possible to bundle multiple recommendations in one request. Several item IDs must be comma-separated. +The Rendered event has the same embedded parameters as the Click event,except for the item ID. +It is common that recommendations are rendered as a block with multiple items. +To save traffic and reduce latency, you can bundle multiple recommendations in one request. +Several item IDs must be comma-separated. -## Examples of translating user actions into tracking events +## Tracking event examples -Below are examples for the translation of user actions on a website into tracking requests: +Below are examples for the translation of user actions on a website into tracking requests. -User "Js79009234YU7" navigates to an item with the id 123 and type "1" which is located under Shoes -> Children: +User "Js79009234YU7" navigates to an item 123 of type 1, located under `Shoes/Children`: `GET https://event.yoochoose.net/api/00000/click/Js79009234YU7/1/123?categorypath=%2FShoes%2FChildren` -Products 128, 129 and 155 of type 1 are rendered as recommendations for user "Js79009234YU7". Recommendations were delivered by the scenario "also\_bought": +Products 128, 129 and 155 of type 1 are rendered as recommendations for user "Js79009234YU7". +Recommendations were delivered by the scenario "also\_bought": `GET https://event.yoochoose.net/api/00000/rendered/Js79009234YU7/1/128,129,155` -They click on recommended product 155 that was delivered by the call of scenario "also\_bought": +User clicks a recommended product 155 that was delivered by the scenario "also\_bought": `GET https://event.yoochoose.net/api/00000/clickrecommended/Js79009234YU7/1/155?scenario=also_bought` -User "Js79009234YU7" has watched a video with the id 452 (videos have item type "3"): +User "Js79009234YU7" has watched a video 452 (all videos are item type "3"): `GET https://event.yoochoose.net/api/00000/consume/Js79009234YU7/3/452` -User "Js79009234YU7" has watched 60 percent of a video with the id 466 (videos have item type "3"): +User "Js79009234YU7" has watched 60 percent of a video 466: `GET https://event.yoochoose.net/api/00000/consume/Js79009234YU7/3/452?percentage=60` -User "Js79009234YU7" puts the product 128 and later 129 into the shopping basket. +User "Js79009234YU7" puts products 128 and 129 into the shopping basket. `GET https://event.yoochoose.net/api/00000/basket/Js79009234YU7/1/128` `GET https://event.yoochoose.net/api/00000/basket/Js79009234YU7/1/129` -To buy selected products, user "Js79009234YU7" logs in and gets an internal identifier "johndoe" (e.g. registration ID) from the site. Do not forget that it must be URL-encoded. +To buy selected products, user "Js79009234YU7" logs in and obtains an internal identifier +(for example, the registration ID) "johndoe" from the site. `GET https://event.yoochoose.net/api/00000/login/Js79009234YU7/johndoe` -They buy both products from the shopping basket: product 128 (one piece and the price of EUR 19.99) and product 129 (2 pieces with the price of EUR 4.44 each) +The user buys two products from the shopping basket: product 128 (one piece for the price of EUR 19.99) +and product 129 (2 pieces for the price of EUR 4.44 each). `GET https://event.yoochoose.net/api/00000/buy/johndoe/1/128?quantity=1&fullprice=19.99EUR` `GET https://event.yoochoose.net/api/00000/buy/johndoe/1/129?quantity=2&fullprice=4.44EUR` -User "johndoe" likes the product 133 and wants to rate it, e.g. with 5 stars +User "johndoe" likes the product 133 and wants to rate it with 5 stars. `GET https://event.yoochoose.net/api/00000/rate/Js79009234YU7/1/133?rating=5` -### Response Handling +### Response handling The following HTTP response codes are used by the event tracker.   @@ -342,9 +474,10 @@ The following HTTP response codes are used by the event tracker.   |---|---| |200 OK</br>204 No Content|Request was successfully processed.| |400 Bad Request</br>414 Request-URI Too Long|The request is wrongly formatted. See response body for more information.| -|401 Unauthorized|Illegal authentication credentials.| +|401 Unauthorized|Invalid authentication credentials.| |403 Forbidden|Access denied (not implemented yet).| -|404 Not Found|The customer ID (a.k.a. mandator ID) was not found. The event code was not found.| -|500 Internal Server Error| Some unspecified error. Please inform support@yoochoose.com if it you get this error repeatedly.| +|404 Not Found|The customer ID was not found. The event code was not found.| +|500 Internal Server Error|Unspecified error. Contact Ibexa if this error reoccurs frequently.| -In case of errors the body of the response contains human-readable error messages. Human-readable error messages can be changed and should not be used for automated processing. +In case of errors, the body of the response contains human-readable error messages. +Human-readable error messages can be changed and should not be used for automated processing. diff --git a/docs/guide/personalization/developer_guide/tracking_with_yct.md b/docs/guide/personalization/developer_guide/tracking_with_yct.md index 9ffd183702..273de5031e 100644 --- a/docs/guide/personalization/developer_guide/tracking_with_yct.md +++ b/docs/guide/personalization/developer_guide/tracking_with_yct.md @@ -1,3 +1,7 @@ +--- +description: Integrate tracking with a Google-style JavaScript. +--- + # Tracking with yct.js This is yet another example of how to integrate the tracking with a Google-style JavaScript in your site. It is a very generic approach and should be evaluated if this meets your **requirements and security policy!** @@ -29,17 +33,17 @@ In order to use this on your pages, copy the code snippet below, replacing `<YO </script> ``` -## How the Asynchronous Syntax Works +## Asynchronous syntax The `_ycq` object is what makes the asynchronous syntax possible. It acts as a queue, which is a *first-in, first-out* (FIFO) data structure that collects API calls until `yct.js` is ready to execute them. To add to the queue, use the `_ycq.push` method. To push an API call into the queue, you must convert it from the traditional JavaScript syntax to a *command array*. Command arrays are simply JavaScript arrays that conform to a certain format. The first element in a command array is the name of the tracker object method you want to call. It must be a string. The remaining elements are the arguments you want to pass to the tracker object method. These can be any JavaScript value. -## Tracking Code: The \_ycq Global Object +## Tracking code The `_ycq` global object can be used directly for asynchronous page tracking via the `push(...)` method.  -### \_ycq Object Methods +### `_ycq` object methods #### push @@ -55,13 +59,13 @@ _ycq.push(['_setMandator', '<YOUR_MANDATOR_ID>']); _ycq.push(['_trackEvent', '1', 'click', 'https://mydoc.pdf', 'user1234']); ``` -## Tracker Object Names +## Tracker object names | Object | Description | Example | | --------------- | ------------- | ----- | | `_setMandator ` | - Executed with one additional parameter: `MandatorId` | `_ycq.push (['_setMandator' , '<YOUR_MANDATOR_ID>']);` | -| `_trackEvent` | - Executed with 4 additional parameters: `ItemType`, `EventType`, `ItemId`, `UserId`.<br /> - `EventType` can be any of the [described types](https://doc.ezplatform.com/projects/userguide/en/master/personalization/event_types.md) | capturing an event: `_ycq.push(['_trackEvent', '1', 'buy', 'https://mydoc.pdf', 'user1234x']);` | -| `_trackTimedEvent` | - Executed with 5 additional parameters: `ItemType`, `EventType`, `ItemId`, `Timeout`, `UserId`.<br /> - `EventType` can be any of the [described types](https://doc.ezplatform.com/projects/userguide/en/master/personalization/event_types.md).<br /> - `Timeout` can be any integer greater than than 0 representing time in ms | consume event sent after 20s: `_ycq.push(['_trackTimedEvent', '1', 'consume', 'https://mydoc.pdf', '20000', 'user1234x']);` | +| `_trackEvent` | - Executed with 4 additional parameters: `ItemType`, `EventType`, `ItemId`, `UserId`.<br /> - `EventType` can be any of the [described types]([[= user_doc =]]/personalization/event_types/) | capturing an event: `_ycq.push(['_trackEvent', '1', 'buy', 'https://mydoc.pdf', 'user1234x']);` | +| `_trackTimedEvent` | - Executed with 5 additional parameters: `ItemType`, `EventType`, `ItemId`, `Timeout`, `UserId`.<br /> - `EventType` can be any of the [described types]([[= user_doc =]]/personalization/event_types/).<br /> - `Timeout` can be any integer greater than than 0 representing time in ms | consume event sent after 20s: `_ycq.push(['_trackTimedEvent', '1', 'consume', 'https://mydoc.pdf', '20000', 'user1234x']);` | | `_login` | - Executed with 2 additional parameters: anonymous userId, pseudonymous userId.<br /> - It is to be triggered when a user logs in and the tracking identity is changed.<br /> |-| | `ycreco=true` | - If you want to send a click recommended event you can append the following parameter to the recommended item URLs: | [https://mydomain.com/mypage.html?ycreco=true](https://mydomain.com/mypage.html?ycreco=true) or <br />[https://mydomain.com/mypage.html?myparameter=x&ycreco=true](https://mydomain.com/mypage.html?myparameter=x&ycreco=true) | diff --git a/docs/guide/personalization/developer_guide/user_api.md b/docs/guide/personalization/developer_guide/user_api.md index d5d33783e4..bf7bc3c72f 100644 --- a/docs/guide/personalization/developer_guide/user_api.md +++ b/docs/guide/personalization/developer_guide/user_api.md @@ -1,84 +1,110 @@ -# User API +--- +description: +--- -!!! note +# User API [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - All the features described in this chapter are available only for the advanced edition of the Recommender Engine. +When generating recommendations, it is useful to have the ability to correlate metadata +with user data and combine users into clusters of certain type. +Such metadata can be gender, ZIP code, discount rate, etc.  +You can use the following user metadata import format to enrich the tracked data with information that cannot be calculated and must be provided by the end-user. - **BASIC Authentication** is enabled by default. Use the customerid/mandator as username and the license key as password. The license key is displayed in the upper right in the Admin GUI ([https://admin.yoochoose.net](https://admin.yoochoose.net/)) after logging in with your registration credentials. +If you plan to import user metadata, contact support@ibexa.co to ensure that you are compliant with privacy regulations. -It is useful to add metadata to users in order to classify them and attach them to user-type clusters. We provide the following "user metadata" import format in order to enrich tracked user data with information that cannot be calculated from their behavior but only provided by a customer itself. An example for this metadata could be "gender", "postal code", "discount rate", etc.  +!!! note "Authentication" -!!! note + For importing metadata, basic authentication is enabled by default. + Use your customer ID and license key as username and password. + If authentication is enabled for recommendation requests and you want change this, contact support@ibexa.co. + +## GET requests - Please contact <support@yoochoose.com> if you are planning to import user metadata in order to be compliant with privacy regulations. +Use the following request to fetch user attributes for the specified users: -Following is an example user metadata in xml format. All attribute keys and values are chosen arbitrarily. - -``` xml -<users> - <user id="CUSTOMER_1234"> - <attributes> - <attribute key="country" value="DE" type="NOMINAL"/> <!-- NOMINAL is a default value --> - <attribute key="discountrate" value="2" type="NUMERIC"/> - <attribute key="wants" value="I am looking for good products here" type="TEXT"/> - <attribute key="type" value="reseller"/> - <attribute key="favorite_film_genre" value="comedy"/> - <attribute key="favorite_film_genre" value="horror"/> - <attribute key="favorite_film_genre" value="animation"/> - </attributes> - </user> -</users> -``` +`GET: https://import.yoochoose.net/api/[customerid]/[source]/user/[userid[,userid[...]]]` -The following request is used to fetch user attributes for the specified users: +User data is returned as an XML object. +Make sure that you use the **HTTP Content-Type=text/xml** header. -`GET: https://import.yoochoose.net/api/[customerid]/[source]/user/[userid[,userid[...]]]` +## POST requests -The following URL is used to update the set of the user attributes: +Use the following request to update the specified user's attribute set: `POST: https://import.yoochoose.net/api/[customerid]/[source]/user` -The users are provided in the specified XML format (do not forget the **HTTP Content-Type=text/xml** header). +## Request parameters -## User ID +For the requests to function, you must provide the following parameters: -User ID is any character combination. User IDs are case sensitive. +|Parameter|Description|Value| +|---|---|---| +|`customerid`|Your customer ID, as defined when [enabling Personalization](../enabling_personalization.md#set-up-customer-credentials) (for example, "00000").|alphanumeric| +|`source`|An ID of the source of the specified user's metadata.|alphanumeric| +|`userid`|An ID of the tracked user in the website (for example, an internal customer code, a session code or a cookie for anonymous users.|alphanumeric| + +!!! caution "Parameter encoding limitations" -!!! caution + All parameters must be URL-encoded (see RFC 3986) and cannot contain slash, backslash or space + characters. + +##### Source - Because of transferring as part of an URL in the event tracking it is recommended to **avoid space character and slashes** (both forward and back slashes). Apache web-servers (and products based on this Apache project) have very sophisticated and non-transparent rules for handling slashes in URLs. +The `source` parameter defines the system that stores the specified user's metadata. +If you have multiple source systems for updating user attributes, +for example, a registration service, where users define their gender and age, +or an application that integrates with Facebook to source the brands the user "liked" in your shop, +every new upload of attributes will replace the attribute set that already exists +for the same user/source pair. -If transferred in the URL, the user ID must be URL-encoded. If transferred in the XML attribute it must be XML-encoded, like this: +If you need to get all the available attributes for all sources, apply the `allSources` query string parameter, for example: -| User ID | URL encoded | XML encoded | -|---------------------|-------------------------|---------------------------| -| `Customer<12.2014>` | `Customer%3C12.2014%3E` | `Customer<12.2014>` | +`GET: https://import.yoochoose.net/api/00000/facebook/user/CUSTOMER_1234?allSources=true` + +When you do that, and the source returned is different from the source passed in the request (in this case, "facebook"), an additional attribute `source` is added to the XML object. -#### Key +##### User ID -The attribute key is a POSIX alphanumeric code \[A-Z\], \[0-9\] plus "\_" and "-". Attribute keys are case sensitive. +User ID is a case-sensitive combination of characters. +If transferred as part of the URL, the attribute must be URL-encoded.  +If transferred in the XML object, the attribute must be XML-encoded. -## Attribute Type +For example: -Following types are supported: +| User ID | URL encoded | XML encoded | +|---------------------|-------------------------|---------------------------| +| `Customer<12.2014>` | `Customer%3C12.2014%3E` | `Customer<12.2014>` | -- NUMERIC - Decimal value like "1.23" or "-2345".  -- NOMINAL - A value from a fixed length list like "gender" or "favorite film genre". *It is a default value.* -- TEXT - Longer, usually free entered text -- DATE - XSD-formatted date like "2014-08-07" -- DATETIME - XSD-formatted time without a time zone like "2014-08-07T14:43:12" -## Source +## Responses -The "source" is used to define the view on the specified user. If you have multiple sources/systems for updating user attributes like e.g.: +### Response object format -- registration service to set the gender and age of the user -- Facebook-linked application to set the favorite brands the user "liked" in your shop, +For an example of user metadata, see the following XML code. +The attribute keys and values are chosen at random. -every new upload of attributes will **replace** the previous attribute set of **the same user and the same source**. +``` xml +<users> + <user id="YOUR_CUSTOMER_ID"> + <attributes> + <attribute key="country" value="DE" type="NOMINAL"/> + <attribute key="discountrate" value="2" type="NUMERIC"/> + <attribute key="wants" value="I am looking for good products here" type="TEXT"/> + <attribute key="type" value="reseller"/> + <attribute key="favorite_film_genre" value="comedy"/> + <attribute key="favorite_film_genre" value="horror"/> + <attribute key="favorite_film_genre" value="animation"/> + </attributes> + </user> +</users> +``` -If you need to get all the available attributes for all sources, use query string parameter `allSources`: +Attribute keys are POSIX alphanumeric codes that can consist of the following characters: `\[A-Z\]`, `\[0-9\]`, `"\_"` and `"-"`.  +Attribute keys are case sensitive. -`GET: https://import.yoochoose.net/api/0000/facebook/user/CUSTOMER_1234?allSources` +The following attribute types are supported: -In this case an additional XML-attribute "source" will be added if the source is different from the source provided in the request ("facebook" in the example above). +- DATE - An XSD-formatted date, for example, "2014-08-07" +- DATETIME - An XSD-formatted time without a time zone, for example, "2014-08-07T14:43:12" +- NOMINAL - A value from a fixed length list, for example "gender" or "favorite film genre". If you do not set the attribute type, this is the default value +- NUMERIC - A decimal value, for example, "1.23" or "-2345" +- TEXT - A longer text, usually free form diff --git a/docs/guide/personalization/enabling_personalization.md b/docs/guide/personalization/enabling_personalization.md new file mode 100644 index 0000000000..9206bdc24f --- /dev/null +++ b/docs/guide/personalization/enabling_personalization.md @@ -0,0 +1,36 @@ +--- +description: Configure your project files to enable Personalization and set up items you want to track. +--- + +# Enabling personalization + +The personalization service is based on a client-server architecture. +To enable it, you must set up authentication parameters that you receive from Ibexa. + +## Getting authentication parameters + +First, either you or another Ibexa user responsible for managing the website +must [request access to the service]([[= user_doc =]]/personalization/enabling_personalization/#requesting-access-to-the-server). + +## Setting up customer credentials + +When you receive the confirmation email, add the credentials to your configuration. +In the root folder of your project, edit either the `.env` or `.env.local` file +by adding the following lines with your customer ID and license key: + +``` +RECOMMENDATION_CUSTOMER_ID=12345 +RECOMMENDATION_LICENSE_KEY=67890-1234-5678-90123-4567 +RECOMMENDATION_HOST_URI=https://server_uri +``` + +## Configuring event tracking + +Next, you must [configure the recommendation client](recommendation_client.md#configuration) +that tracks visitor events in relation to Content Types. + +## Configuring recommendation logic + +Once you enable the recommendation client, you can go back to the Back Office, +refresh the Personalization dashboard and proceed with [configuring the logic]([[= user_doc =]]/personalization/perso_configuration) used to calculate +the recommendation results. diff --git a/docs/guide/personalization/img/Diagram source/full_content_export.drawio b/docs/guide/personalization/img/Diagram source/full_content_export.drawio new file mode 100644 index 0000000000..3101659c4a --- /dev/null +++ b/docs/guide/personalization/img/Diagram source/full_content_export.drawio @@ -0,0 +1 @@ +<mxfile host="Electron" modified="2021-05-07T10:45:46.989Z" agent="5.0 (Macintosh; Intel Mac OS X 11_3_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36" etag="Ud7Wsb4NOKJWgDKWY6Py" version="14.5.1" type="device"><diagram id="Jn93XvOcXpL5n0wz-saM" name="Page-1">7Vtdb+o4EP01SN2Hotj5AB6BQvdqdbXV9kp779PKJC5EDTHrmAL99WsndkichKQ0YWlVHiAZO44954xnPJ32zOl6f0/RZvWdeDjoQcPb98y7HoQAGA7/EZJDIhmKOyFYUt+TnY6CR/8VS6EhpVvfw1GuIyMkYP4mL3RJGGKX5WSIUrLLd3siQf6tG7TEBcGji4Ki9G/fYyu1isFR/jv2lyv1ZuCMkpY1Up3lSqIV8sguIzJnPXNKCWHJ1Xo/xYFQntJL8ty8ojWdGMUha/LAZGL9mrPRhA69p5l99/oHWMxv5SgvKNjKBX9b4D3iorufD3Le7KCUQck29LAYD/TMyW7lM/y4Qa5o3XH4uWzF1oFsfiIhk3gCS93LseJ2PwimJCA0Htt8AjbioJuTiFHyjMtaxAgZ+Tz+cHlRE2pZmDK8z4ikZu4xWWNGD7yLbDUlSJKllrzdHSEHSrbKwK1kSLJsmQ58BIJfSCzegAss4NKDTsCkEnKoOP9uiWq4jWKFj3kHYG32x0Z+tWQxPkwYzljMDs6Rt/bD/oEQd0VIhPshZuo1vDV5U/LcRangjaBnwTIqpC1dUgHWcgEal+SCWeDClK8exzTw1xvCF0Yvio9hDKHtluGTtnSIj2Ndma1aJbaqoYFDbyycEb8LSciFEw9FqxSeDBSVCorIlrr41FZuJx2xl/NoRT1mFGWX6EnJKA4Q81/yfrBMefIND8SPKalANPIw2Y6m/2RB8qms36oZaKiNwxBdYlYYJ4YyXfX56NpXgq5xVehasCV09YEujK7TJrocVHr4yW+Mvq1uf70feTmlRBP1fuJKGDIALTFEH6ghQzhm6JDpthEdoubzVT7+SLhkxFbpN6in39sced5R49FwAXGZo05bWnDIo7ziACwLmIo8A6OuHPKwXqvulr6kSs1YuBugKPLdCrsGfZi1a6M/tFLBA6Y+nz4WCj4eY9SZEZ5Sda1lD67KsgGw8oDrkVVjz+5cxLLB0Cidb+W0tP5wcIGdYPQmR1RK0yLjUuLeGjpzTceqYu75PL2yIAVoW7ppnBukGDUDdRylqHV05Sdm84FhT8v8RNrSgp+wdA/rNHQUoCtHAYrpr1NWd25w/9Hs5rME9ypy7spsbDBxxlaZ2aQtLZjNQN98GsZXUN+k2jObYkbqHWZz1qmp3qTgVZnU/30aas2kGiS7zoyt8/HJyL5EYH1tLNFTz+a5LKnbwds6NGuhslUTWhf6jy4QWoMGKbwPd8o2h828gA58e06g1dTZO0z4K+3dyUbfIDX19gNpJcxXgh7U9yf96NEYPVAzUNfwNciBvSOfYOSdtVXprD+Na26NGbbmms2Grrk1ZnSeadIiOfDpuWFp1SPmuQlRS8886inyjrmhqFlZdqKKQRLZQgluwG8CjVeKXbJec/ZwbZKwZ47xXpQn8Au6DTOVJYvCSLRybP6L1iIACxeR+FniEFPEMI9KRdGXrIOQL4IGj81EU/Wr6gpbeDjGytiuwjoZyGRjQClCgb8MhcnwKQmST0Rw57soGMuGte954jWl8eUxAlWlTtmKjAAtcDBB7vMy7petjoo/7cSYutcCg5Igc1BiSIOuokxYzNDdQEG2hz8ff1wpdg2wOl1w0wKQ2o4Ey4B0SoDUD5ztAQmLQJoCyB/UXy5FAZPhC9WHSAy1I/T5KeAO6AvhqmS67nPUOagOYdgZwsWs4I0tEP4WRjjem4/bNXcQKEY8D3vECMXRF+iVqWC9TLUkFQzKQO/sT+2wmA+8sQTo97MfJx20odWzuqk6jyWrpuFYNvKKVayidHXj82/8uuHfLzC+/EcLP+C83+9Xuv2qwOPUfGT5aWE+39EzFpTdUvFzIFuh6iDev4y/ZsJRGch1cRTFsIhOHykWuQLiA1vb7UqyX3ZZUe4Z6S9+eyzKTyLr4782mLP/AA==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/personalization/img/Diagram source/incremental_content_export.drawio b/docs/guide/personalization/img/Diagram source/incremental_content_export.drawio new file mode 100644 index 0000000000..2f41710ca6 --- /dev/null +++ b/docs/guide/personalization/img/Diagram source/incremental_content_export.drawio @@ -0,0 +1 @@ +<mxfile host="Electron" modified="2021-05-07T10:45:14.737Z" agent="5.0 (Macintosh; Intel Mac OS X 11_3_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.5.1 Chrome/89.0.4389.82 Electron/12.0.1 Safari/537.36" etag="SNzHNQZ_0Q3awOO6OOci" version="14.5.1" type="device"><diagram id="8kTb6UhRsKarVwCsZ0IR" name="Page-1">7VttT+M4EP41lbgPVInz0vZj24Xd/cBqtdxp9z6d3MS0Fmmcc1xo99efndh5cVISwGk5BEKlmXEcZ55nPOPRMHKW2/1nCpPNDQlRNAJWuB85n0YAzAAYiV8rPOQCf+rlgjXFYS6yS8Et/o2k0JLSHQ5RWhvICIkYTurCgMQxClhNBiklj/VhdySqPzWBa9QQ3AYwakp/4pBtcukUTEr5F4TXG/Vk25/lmi1Ug+WbpBsYkseKyLkaOUtKCMu/bfdLFAnbKbvk910f0RYLoyhmfW64efTX33xmh3chI1+8vwL8c35py2keYLSTb/x1hfaQiz79+i4Xzg7KGpTs4hCJCe2Rs3jcYIZuExgI7SOHn8s2bBtJ9R2JmQTUdtW1nCvT4yhakojQbG7nzvYgR91ZpIySe9SmETNU5NfZD5fLd0CUof1R69iFzTlXEdkiRg98iLzBkShJmrry8rHE3FayTQVvJYOSZuti4hIJ/kWC8RxgQAOYEfAjJq1Qg8X/d0eU4jLNLD7nA2w32ZdK/m3NMoCYcJ25WB64huEWx+MDIcGGkBSNY8TUY7g2f1J+30m5EM5A6II2LhSaIbkAOskArJOSwWmQYclfH2U8wNuE8JelJwXIsqbAC9oAKjQDAuS7b81b3RZv1eBAcTgXAYlfxSTmwkUI002BTwWL/FYUNiKTZh8+PdnRAD2xMOC1G7JiKa/FUEpGUQQZfqivo8168gnfCc44qYC16jh5vgZAvn55VzV2dUw01eZhkK4Ra8yTYVm89Svg9d4qvNY54XWBIXj1iU4Nr28SXg4iPfziF9bYU5d/DwC9XGNum87gcS6OTGxDHNEn6skRDho8VIYlYkDaf70q0JeUy2c0S8BJNwGfF83r0RrNpiuA2qJ1oTEQlWd1y9mgLW1qEs2eDRaVp91mDXb0obBqxcmDCKYpDo64tj0GVde2xlO3EHxHFPP1I2Hh8jijDo/gWbtAt3NPzhrfVd6jINcTrN7x3T+Jc9tTq3W9R5eljQeTU2wGs2dFo1aiNjlXUPfS0rnr+O4x7hpk6nlTFVvb1h3rpamK1THRwLmK2kGHChVX1xPLW7aFikJjIFS4epT1e8YKe6hYAZqFsKe8zliK/8b95r2k+ArNodzGsxf+3G1zm0JjwG0m+ubTM8UC+iZlzm2alalXuI2Zo1O3S4FzutS5T0TGXKpHzeuF2XU9P5l5Z0mtz8wSvQbtvJQlXTu4qYOzliu7Hbl1Y/zsBLk16FHI+98dtJ1pvyigA28uCBitn5l04Y/qtwmn6VGdev6BtC/M50IP6PuTfvTojZ7dMdHQ8PWogr2inmDVg7V7NFi/m9BsjBmeFpqdnqHZGDMGrzRpmZz97rnhan0kzksroq5eetSr5ANzQ71HS//JqmwJMdGQcmH/wXXzOCMAZjxhAlZAEWSIZ5t88dYuCfMLK9OFKEL5ZaB6II40rBTi1dEmFp6EsTZCq8xN5irVNE+KYITXsfAKvgLB44VI6XAAo7lUbHEYise0ppBlkqn6mqrNFxFcoWgBg/t1Nq7aCpX9mEkjbY2r9qRnn8tkqETSaRbhLoBgxzfC8B03LsNE8AQL9FMkmf/28OyB39P9NubBBW3g+i3g6udMc+CCJrgOB9eHW2HBeJUmFa9NExgrr/2BArLd8kik8EfxGnPQRPiUWxJVYzNqxBldOBzlFlCd74M1R+vyeviyvH6sAYOxpllgvPDElvA1TvmbFuEhrMYDC2e7hIAkhlGGNaEo/cD9aGFZb6hr66h7Km0yj3uzunjhCtw/X/3ZDnr7PiJzk6Awapl+OJbvejBsZiSiLzbB/BP9TvjnA8i+/kO1beh6PB535B7lvtRnPbK3tbGeG3gvNrt0R8WfA9kJg0cRT8qB9ePqVtgDBgFK0wwcMahfB+8H/fcatZ+oqHltmdALSmr8suz5z7P18h8nnKv/AA==</diagram></mxfile> \ No newline at end of file diff --git a/docs/guide/personalization/img/full_content_export.png b/docs/guide/personalization/img/full_content_export.png index e6516fce84..9d29ea34b7 100644 Binary files a/docs/guide/personalization/img/full_content_export.png and b/docs/guide/personalization/img/full_content_export.png differ diff --git a/docs/guide/personalization/img/incremental_content_export.png b/docs/guide/personalization/img/incremental_content_export.png index da438f0e3e..3a955a10cc 100644 Binary files a/docs/guide/personalization/img/incremental_content_export.png and b/docs/guide/personalization/img/incremental_content_export.png differ diff --git a/docs/guide/personalization/img/reco_full_import.png b/docs/guide/personalization/img/reco_full_import.png index 385567e3b8..ed4f50d723 100644 Binary files a/docs/guide/personalization/img/reco_full_import.png and b/docs/guide/personalization/img/reco_full_import.png differ diff --git a/docs/guide/personalization/img/recommendation_overview.png b/docs/guide/personalization/img/recommendation_overview.png index 38fc6ccc27..08268bf8bd 100644 Binary files a/docs/guide/personalization/img/recommendation_overview.png and b/docs/guide/personalization/img/recommendation_overview.png differ diff --git a/docs/guide/personalization/personalization.md b/docs/guide/personalization/personalization.md index b2d5f0ad28..49e34ecbfe 100644 --- a/docs/guide/personalization/personalization.md +++ b/docs/guide/personalization/personalization.md @@ -1,34 +1,34 @@ -# Personalization +--- +description: Personalization tracks consumed content and suggests targeted content to your website visitors. +--- -Ibexa Personalization is a service for offering personalized recommendations to visitors to your website. -It can be used to serve targeted content to users based on their preferences and behavior. +# Personalization -Ibexa Personalization can be used both by publishers (to serve personalized content and track its consumption), and in e-commerce (to recommend other products to purchase). +Ibexa Personalization is a service that offers personalized recommendations +or serves targeted content to the visitors of your website, based on their +preferences and behavior. -## Benefits +Personalization can be used both by publishers, for example, to track content consumption +and promote content, and in e-commerce, to drive purchases. -Recommending items helps users navigate and make choices when faced with a mass of content. -Recommendations show relevant information to users who are browsing a website, improve their online experience and increase the chance to keep them on your site. +To provide online recommendations, the service must at least: -## Online recommendations +- Track user actions through a web browser or a mobile app +- Store the user actions +- Use the data to generate recommendations +- Provide recommendations on a customer's website -**Online recommendations** are well-known in form of the "What other users bought" and the "Also interesting for you" boxes on e-commerce websites such as Amazon. -These are pure online recommendations that are displayed within a given context. -This could either be a detail page with a product or the personalized landing page, where recommendations are based on a user's personal history. -Online recommendations are used to a similar effect in the publishing sector. -While reading you often find "Related content" boxes on the site which are mostly based on the reading behavior of the visitors or the popularity of current content. +## How it works -In order to provide online recommendations a recommendation engine must at least: +The idea behind the Personalization service is built upon four main steps. -- track user actions through a web browser or a mobile app -- store the user actions -- use the data to generate recommendations -- provide recommendations on a customer's website +1. A User visits a news service or an online store, navigates through it and leaves some footprints. The most popular footprints are selecting (CLICK) and purchasing  products (BUY). +2. This information is sent to the Recommendation client every time these events happen. +3. The Recommendation engine accumulates the tracked events, categorizes them and calculates recommendations for every product and every user in the shop. +4. Recommendations can be fetched from the Personalization service and presented to users during their sessions. ![Overview of how recommendation works](img/recommendation_overview.png) -When visiting a website, users usually consume content and - if on an e-commerce site - they may buy products. -Clicking, buying and consuming content are examples of actions that are collected in a recommender engine and later processed to generate recommendations. -After collecting sufficient user activity, the recommender engine can be "asked" for recommendations in the current context. -Usually the context is the current content or product where recommendations should be presented for. -But it could also be the personal history of a user, meaning the content they were reading or the products they have bought. +For more information about available functionalities, see [User Documentation]([[= user_doc =]]/personalization/personalization). + +For more information about enabling the Personalization service, see [Enabling Personalization](enabling_personalization.md). diff --git a/docs/guide/personalization/personalization_quickstart.md b/docs/guide/personalization/personalization_quickstart.md deleted file mode 100644 index c1a83ea8ac..0000000000 --- a/docs/guide/personalization/personalization_quickstart.md +++ /dev/null @@ -1,176 +0,0 @@ -# Quickstart guide - -This quickstart guide gives a basic overview of how the personalization engine works and how it is typically enabled. -Following is an example for the integration in an e-commerce shop. - -## How it works - -The idea of the recommendation engine is quite simple and built upon four main steps. - -A User visits an online shop, navigates through it and leaves some footprints. -The most popular footprints are selecting and purchasing  products **(1)**. -This information is sent to the event tracker service of the recommendation engine every time these events happen **(2)**. -The recommendation engine accumulates the tracked events, categorizes them and calculates recommendations for every product and every user in the shop **(3)**. -All recommendations are available over the recommender service. -They can be fetched and presented to users during their sessions or sent out e.g. as a newsletter **(4)**. - -![Overview of how recommendation works](img/recommendation_overview.png) - -#### Integration - -Integration is based on the following: - -- activation of user tracking (we respect privacy rules and never store any data related to a person, every event is anonymized) -- embedding recommendations into a website. - -!!! note - - Below we use the mandator ID '00000' for creating requests. - You must substitute it with your own ID which is generated and sent to you by mail at the end of a successful registration process under [https://admin.yoochoose.net](https://admin.yoochoose.net/). - -### Tracking Events - -Step one is called tracking. Every page must call special tracking URL. -The simplest way is to place a tiny image on every product page, just as it is usually done for analytic tools or visitor counter. -It looks like this: - -**`<img href="https://event.yoochoose.net/ebl/00000/click/john.smith/1/123" width="1" height="1">`** - -Replace "john.smith" with the user ID or session ID of the user currently signed in on your website (any URL encoded string is allowed). -Replace "123" with the ID of the content you want to track and later on recommend. - -The following code snippets provide example code for the integration of a click event: - -PHP: - -``` php -$mandator_id = '00000'; -$product_id = '123'; -$server = '//event.yoochoose.net'; -$tracking = $server.'/ebl/'.$mandator_id.'/click/'.urlencode(session_id()).'/1/'.$product_id; -echo "<img href='$tracking' width='1' height='1'>"; -``` - -Javascript: - -``` js -var mandator_id = '00000'; -var product_id = '123'; -var server = '//event.yoochoose.net'; -var url = server + '/api/' + mandator_id + '/click/' + getSessionId() + '/1/' + product_id; -var ycimg=new Image(1,1); -ycimg.src=url; -``` - -A similar tracking image must be placed on the confirmation page after the payment process. - -``` php -$server = '//event.yoochoose.net'; -foreach ($just_bought_products as $product_id) { - $tracking = $server.'/ebl/'.$mandator_id.'/buy/'.urlencode(session_id()).'/1/'.$product_id; - echo "<img href='$tracking' width='1' height='1'>\n"; -} -``` - -### Embedding Recommendations - -Depending on the page impressions/visits the recommendation engine has soon collected enough events and is able to generate recommendations. -The more tracking data is available, the better and sharper the recommendations will be. -Recommendations can be fetched using following calls, the response is returned as JSON. - -**`https://reco.yoochoose.net/api/v2/00000/john.smith/landing_page.json`** -Returns the most popular products. - -**`https://reco.yoochoose.net/api/v2/00000/john.smith/cross_sell.json?contextitems=OWNS,CLICKED`** Returns products the current user (here "**`john.smith`**") is most probably interested in, so-called personalized recommendations. - -**`https://reco.yoochoose.net/api/v2/00000/john.smith/cross_sell.json?contextitems=123`** -Returns products most probably interesting for any user who is interested in product 123. - -Example result with two recommendations: - -``` json -{ - "contextItems":[ - { - "itemId":123, - "itemType":1, - "sources":[ - "REQUEST" - ], - "viewers":0 - } - ], - "recommendationItems":[ - { - "itemId":555, - "itemType":1, - "relevance":127, - "links":{ - "clickRecommended":"//event.yoochoose.net/api/00000/clickrecommended/user/1/555?scenario=landing_page&modelid=5768", - "rendered":"//event.yoochoose.net/api/00000/rendered/user/1/555?scenario=landing_page&modelid=5768" - } - }, - { - "itemId":444, - "itemType":1, - "relevance":126, - "links":{ - "clickRecommended":"//event.yoochoose.net/api/00000/clickrecommended/user/1/444?scenario=landing_page&modelid=5768", - "rendered":"//event.yoochoose.net/api/00000/rendered/user/1/444?scenario=landing_page&modelid=5768" - } - } - ] -} -``` - -Here is a small PHP snippet for making request and parsing results: - -``` php -$mandator_id = '00000'; -$license_key = '86109-2778-8757-7597-4319'; -$server = "https://reco.yoochoose.net"; -$scenario = "category_page"; -$url = $server.'/ebl/00000/'.urlencode(session_id()).'/'.urlencode($scenario).'.json'; - -$curl = curl_init(); -$request = array( - CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_USERPWD => "$mandator_id:$license_key"); -curl_setopt_array($curl, $request); -$body = curl_exec($curl); -$recommendations = json_decode($body); -if ($recommendations && isset($recommendations->recommendationResponseList)) { - foreach ($recommendations->recommendationResponseList as $product) { - $product_id = $product->itemId; - # load the product and create the recommendation HTML here - echo($product->itemId); - echo(" \n"); - }; -} else { - echo("Error: ".$body); -} -curl_close($curl); -``` - -## Next steps - -For the most simple but still very effective recommendations the steps described above are sufficient. -Tracking more events, using additional parameters and custom scenario configurations enable features like: - -- Filtering recommendations based on category -- Getting usage and revenue statistics -- Recommendations based on the shopping basket -- Filtering repeated recommendations - -Additional features are available for the Advanced edition of the recommendation engine: - -- A/B testing -- Personalized Search -- Using "rate" and "like" events -- Additional models such as content-based, random, history etc. -- Multiple product types (for example food/non-food or article/image/video) and cross-type recommendations -- Price-based filtering (do not recommend cheap products) -- Grouping products by custom attributes (size, color, source, theme etc.) - -More detailed information can be found in the [User Guide](https://doc.ezplatform.com/projects/userguide/en/master/personalization/introduction.md). diff --git a/docs/guide/personalization/recommendation_client.md b/docs/guide/personalization/recommendation_client.md index b73b1adc94..4a77692013 100644 --- a/docs/guide/personalization/recommendation_client.md +++ b/docs/guide/personalization/recommendation_client.md @@ -1,132 +1,30 @@ -# Recommendation +# Recommendation client -[`ezrecommendation-client`](https://github.com/ezsystems/ezrecommendation-client) adds a personalization solution to [[= product_name =]]. +The [`ezrecommentation-client`](https://github.com/ezsystems/ezrecommendation-client) package +adds a personalization solution to [[= product_name =]] and communicates with +the server that runs the recommendation engine. -It enables you to track the way visitors use your website and recommends content based on their behavior. -You can also use the personalized search (content suggestions) -and personalized newsletter features (embedding personalized content in your newsletters). +Its job is to track the way visitors use your website and recommends content +based on their behavior. -!!! tip "More information" - - This page covers the `ezrecommentation-client`, which communicates with the Recommendation engine. - -## Installation - -[`ezrecommendation-client`](https://github.com/ezsystems/ezrecommendation-client) -is provided in a separate package and is not included in [[= product_name =]] by default. - -To use it, you need to install the package: - -### 1. Install and enable the bundle - -Run `composer require` from your [[= product_name =]] installation root: - -``` bash -composer require --no-update ezsystems/ezrecommendation-client -composer update --prefer-dist -``` - -Next, enable the bundle in `config/bundles.php`: - -``` php -<?php - -return [ - // existing bundles - EzSystems\EzRecommendationClientBundle\EzRecommendationClientBundle::class => ['all' => true], -]; -``` - -### 2. Import additional routing - -Import additional routing by adding the following lines to your `config/routes.yaml` file: - -``` yaml -ezplatform_recommendation_client_rest: - resource: '@EzRecommendationClientBundle/Resources/config/routing_rest.yaml' - prefix: '%ezpublish_rest.path_prefix%' -``` - -### 3. Register an Ibexa Personalization account - -Register an account (customerID) with your Ibexa Sales manager. - -!!! tip - - If you want to use the Recommendation engine with the open source version of [[= product_name =]], - send an email to support@yoochoose.com. - -### 4. Allow public HTTP(S) access - -Allow public HTTP(S) access to the recommendation bundle API (`<yourdomain>/api/ezp/v2/ez_recommendation/**`) - -!!! note "IP whitelisting" - - The Recommendation engine servers need to access the API of an [[= product_name =]] installation - in order to continuously sync content. - If it's not possible to allow public access, - the following IP addresses can be used for whitelisting on, for example, a firewall. - - `54.229.102.177, 54.171.192.161, 54.77.201.13, 52.215.22.234, 52.18.150.96, 52.17.60.35, 52.17.36.104` - -!!! note "If BASIC AUTH is required by policy" - - If your organization's policy is to use BASIC AUTH on the API interfaces, - you need to add specific configuration. - -You can define access restrictions on your site in `config/packages/security.yaml`. - -``` yaml -security: - providers: - ezpublish: - id: ezpublish.security.user_provider - firewalls: - ezpublish_rest: - pattern: ^/api/ezp/v2 - stateless: true - ezpublish_http_basic: - realm: eZ Publish REST API -``` - -Create a User with the name of the customerID and a password which is the license key in your local security provider. -This User must have access granted on the URLs provided by the bundle API (see above). -In order to tell the recommender to use this User and password to request resources on the [[= product_name =]] instance, -you can configure this as follows (an example file is available in the bundle under `Resources/config/default_settings.yaml`): - -``` yaml -ezrecommendation: - system: - <siteaccess>: - export: - authentication: - # Export folder authentication method: - # basic - password generated by script - # user - login and passwords set by user - # none - no authentication !Warning - allow users to download content without permission - method: basic - login: 12345 - password: 1234-5678-9012-3456-7890 - document_root: '%kernel.project_dir%/public/var/export/' -``` - -With the `user` authentication method, login must be the same as `customer_id` and `password` must be the same as `license_key`. - -Place this in a settings file which won't be affected by an update to the bundle. +Once you become familiar with this article, for more information about integrating +the Personalization service, see [Developer guide](developer_guide/tracking_api.md) and [Best practices](best_practices/tracking_integration.md). ## Configuration +Before you can use the Personalization service, you must configure the client. + ### Set up Content Type tracking -Visitor events (clicks, buys, etc.) on the site need to be sent to the Recommendation engine for the recommendations to be calculated. -The Content Types that are tracked are also exported to the engine. -Tracking Content Types is required for displaying recommendations. +For the recommendations to be calculated, apart from visitor events (CLICK, BUY, etc.), +the Recommendation engine must be fed with the list of Content Types that are tracked. -You define Content Types to track in the `config/packages/ezplatform.yaml` file. -The content will then be initially exported by a script. -After this, it will be kept in sync with the Personalization Solution every time a change occurs in the [[= product_name =]] Back Office. +You define Content Types to be tracked in the `config/packages/ezplatform.yaml` file. +The content is then initially exported by a script. +After this, it is synchronized with the Personalization service every time a change +occurs in the Back Office. -The bundle's configuration is SiteAccess-aware, and can resemble the following example: +The client's configuration is SiteAccess-aware, and can resemble the following example: ``` yaml ezrecommendation: @@ -137,20 +35,21 @@ ezrecommendation: license_key: 1234-5678-9012-3456-7890 included_content_types: [blog, article] random_content_types: [blog] - host_uri: http://example.com + host_uri: https://server_uri ``` !!! tip - It is recommended to provide your `host_uri`, `customer_id` and `license_key` through environment variables: `'%env()%'`. + It is recommended that you provide your `host_uri`, `customer_id` and + `license_key` through environment variables: `'%env()%'`. | Parameter | Description | |--------------------------------------|-----------------------------------------------------------| | `authentication.customer_id` | Your customer ID. | | `authentication.license_key` | Your license key. | | `host_uri` | The URI your site's REST API can be accessed from. | -| `included_content_types` | Content Types on which the tracking script will be shown. | -| `random_content_types` | Content Types which will be returned when the response from the Recommendation engine contains co content. | +| `included_content_types` | A list of alphanumerical identifiers of Content Types on which the tracking script is shown. | +| `random_content_types` | A list of alphanumerical identifiers of Content Types that are returned when the response from the server contains no content. | #### Advanced configuration @@ -201,9 +100,20 @@ ezrecommendation: endpoint: 'https://admin.yoochoose.net' ``` +You can use an alphanumeric content identificator `remoteId` instead of a numeric `id`. To enable it, add the following code to the configuration file: + +``` yaml +ezrecommendation: + system: + <scope>: + repository: + content: + use_remote_id: true +``` + !!! caution - Changing any of these parameters without a valid reason will break all calls to the Recommendation engine. + Changing any of these parameters without a valid reason breaks all calls to the Recommendation engine. #### Enable tracking @@ -213,22 +123,20 @@ Place the following code snippet in the `<head>` section of your header template ``` html+twig {% if content is defined %} - {{ ez_recommendation_track_user(content.id) }} + {{ ez_recommendation_track_user(content) }} {% endif %} ``` !!! tip "How tracking works" - In the eZ Services documentation you can find more information - about [tracking in general](https://doc.ezplatform.com/projects/ezservices/en/latest/personalization/developer_guide/tracking_api/) - and about the [generic asynchronous JavaScript tracker](https://doc.ezplatform.com/projects/ezpersonalization/en/latest/personalization/developer_guide/tracking_with_yct/). + For more information about tracking in general, see [Tracking API](developer_guide/tracking_api.md) and [Tracking with yct.js](developer_guide/tracking_with_yct.md). -### Checking if the bundle provides REST data +### Checking whether the bundle provides REST data You can verify the import controller of the bundle by calling the local API. -You should use the `Accept` header and may need to add an `Authorization` header if authentication is required. +Use the `Accept` header; you may need to add an `Authorization` header if authentication is required. -To check if the `content` endpoint is working as expected, perform the following request: +To check whether the `content` endpoint is working as expected, perform the following request: ``` GET http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/content/{contentId} @@ -236,7 +144,7 @@ Accept application/vnd.ez.api.Content+json Authorization Basic xxxxxxxx ``` -Additionally you should check if the `contenttypes` endpoint is working with the following request: +Additionally, check whether the `contenttypes` endpoint is working with the following request: ``` GET http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/contenttypes/38?page=1&page_size=10 @@ -291,46 +199,47 @@ The `content` endpoint returns one Content item and the `contenttypes` endpoint To get recommendations you must first export the content information to the Recommendation engine. -After [defining which Content Types should be tracked and recommended](#set-up-content-type-tracking), +After you [define Content Types to be tracked and recommended](#set-up-content-type-tracking), start the full export. -You can do it with the `ezrecommendation:export:run` command or by accessing the `http://<yourdomain>/api/ezp/v2/ez_recommendation/` URL/endpoint. +You do it either with the `ibexa:recommendation:run-export` command... ``` bash -php bin/console ezrecommendation:export:run +php bin/console ibexa:recommendation:run-export --contentTypeIdList=<contentTypeId>,<contentTypeId> --webHook=https://admin.yoochoose.net/api/<your_customer_id>/items - --hidden=1 --mandatorId=<your_customer_id> --host=<your_ezplatform_host_with_scheme> ``` +... or by accessing the `http://<yourdomain>/api/ezp/v2/ez_recommendation/` endpoint: + ``` http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/runExport/<contentTypeIdList>?webHook=<webhook>&customerId=<customerId>&licenseKey=<licenseKey>&host=<host> ``` -The bundle exporter collects all content related to the SiteAccesses of this customerID and stores it in files (1). +The bundle exporter collects all content related to the SiteAccesses of this `customerID` and stores it in files (1). After finishing, the systems sends a POST request to the `webHook` endpoint and informs the Recommendation engine to fetch new content (2). An internal workflow is then triggered (3) so that the generated files are downloaded (4) and imported in the Recommendation engine's content store (5). -The export process can take a few minutes. +The export process can take several minutes. ![Recommendation Full Content Export](img/full_content_export.png) !!! caution "Re-exporting modified Content Types" - If the Content Types to be recommended are changed, you need to perform a new full export - by running the `php bin/console ezrecommendation:export:run` command again. + If the Content Types to be recommended change, you must perform a new full export + by running the `php bin/console ibexa:recommendation:run-export` command again. ### Checking export results -There are three ways to check if content was transferred and stored successfully in the Recommendation engine: +There are three ways to check whether content was transferred and stored successfully in the Recommendation engine: -#### REST request to the recommender's content store +#### REST request to the client's content store To get the content of an imported item you can request the following REST resource: `GET https://admin.yoochoose.net/api/<your_customer_id>/item/<your_content_type_id>/<your_content_id>` -This way requires BASIC Auth. BASIC Auth username is the customerID and the password is the license key. +This way requires BASIC Auth. BASIC Auth username is the `customerID` and the password is the license key. ??? note "Example response" @@ -373,61 +282,19 @@ This way requires BASIC Auth. BASIC Auth username is the customerID and the pass </items> ``` -#### Recommender Backend +#### Recommendation client backend -You can log in to [admin.yoochoose.net](https://admin.yoochoose.net/), -switch to the Item Import tab and check if a FULL import was successful. +In the Back Office, navigate to **Personalization** > **Import** and review a list of historical import operations to see whether a full import was successful. ![Item Import tab with full import results](img/reco_full_import.png) -#### Personalized Search Requests - -Since the search functionality is included by default, -you can also create and perform a search request to look for content that matches certain criteria. -The easiest way to do this is to assign the content ID to the request parameter `q`. -Make sure that the content ID is at least 2 characters long (for example, `&q=73`). - -`GET https://reco.yoochoose.net/api/v4/search/<your_customer_id>/get_suggestions.json?item=5&itemtype=<your_content_type>&q=<your_content_id>&attribute=name&attribute=author&attribute=uri&attribute=<your_custom_attribute>` - -??? note "Example response" - - ``` json - { - "ITEM": [ - { - "yc_score": 2.209763288497925, - "yc_id": "72", - "yc_itemtype": "38", - "name": "Kochin, India", - "uri": "/Places-Tastes/Places/Kochin-India", - "author": "Sandip Patel" - }, - { - "yc_score": 1.152622938156128, - "yc_id": "73", - "yc_itemtype": "38", - "name": "Santo Domingo, Dominican Republic", - "uri": "/Places-Tastes/Places/Santo-Domingo-Dominican-Republic", - "author": "Michael Wang" - } - ], - "details": [ - { - "scope": "ITEM", - "itemType": 38, - "count": 2 - } - ] - } - ``` - ### Subsequent content exports The Recommendation engine is automatically kept in sync with the content in [[= product_name =]]. Every time an editor creates, updates or deletes content in the Back Office (1), a notification is sent to https://admin.yoochoose.net (2). -The recommendation service also notifies other components of the Recommendation engine (3) +The personalization service also notifies other components of the Recommendation engine (3) and it eventually fetches the affected content (4) and updates it internally (5). ![Subsequent Content exports](img/incremental_content_export.png) @@ -437,28 +304,25 @@ and it eventually fetches the affected content (4) and updates it internally (5) !!! note "Client-based recommendations" Recommendations are fetched and rendered asynchronously in the client, so there is no additional load on the server. - Therefore it is crucial to check if the content export has been successful, as e.g. deeplinks and image references are included. - If the export is NOT successful, the Recommendation engine will not have the full content information. - This will break the recommendations. Even if the recommendations are displayed, - there is a big chance they won't have images, titles or deeplinks. + Therefore, it is crucial that you check whether the content export was successful, because certain references, for example, deeplinks and image references, are included. + If the export fails, the Recommendation engine does not have full content information. + As a result, even if the recommendations are displayed, they might miss images, titles or deeplinks. -In order to display recommendations on your site, you need to add the following JavaScript assets to your header template: +To display recommendations on your site, you must include the asset in the template using the following code: ``` html+twig -{% javascripts - '@EzRecommendationClientBundle/Resources/public/js/EzRecommendationClient.js' -%} +{{ encore_entry_script_tags('ezrecommendation-client-js', null, 'ezplatform') }} ``` -This file is responsible for sending notifications to recommendation API after the user clicks on a tracking element. +This file is responsible for sending notifications to the [Recommendation API](developer_guide/recommendation_api.md) after the user clicks on a tracking element. To render recommended content, use a dedicated `showRecommendationsAction` from the `RecommendationController.php`: ``` html+twig -render_esi(controller('ez_recommendation::showRecommendationsAction', { - 'contextItems': content.id, +render(controller('ez_recommendation::showRecommendationsAction', { + 'contextItems': content, 'scenario': 'front', - 'outputTypeId': 'blog_post', + 'outputTypeId': 57, 'limit': 3, 'template': 'EzRecommendationClientBundle::recommendations.html.twig', 'attributes': ['title', 'intro', 'image', 'uri'] @@ -467,7 +331,7 @@ render_esi(controller('ez_recommendation::showRecommendationsAction', { !!! tip - To check if tracking is enabled on the front end, use the `ez_recommendation_enabled()` Twig function. + To check whether tracking is enabled on the front end, use the `ez_recommendation_enabled()` Twig function. You can wrap the call to the `RecommendationController` with: ``` html+twig @@ -482,23 +346,23 @@ render_esi(controller('ez_recommendation::showRecommendationsAction', { | Parameter | Type | Description | |------------------|--------|---------------| -| `contextItems` | int | ID of the content you want to get recommendations for. | -| `scenario` | string | Scenario used to display recommendations. You can create custom scenarios in the [Recommendation engine's dashboard](https://admin.yoochoose.net). | -| `outputTypeId` | string | Content Type you are expecting in response, e.g. blog_post. | +| `contextItems` | int | instance of eZ\Publish\API\Repository\Values\Content\Content | Content you want to get recommendations for. | +| `scenario` | string | Scenario used to display recommendations. You can create custom scenarios in the Back Office. | +| `outputTypeId` | int | Content Type you are expecting in response, for example, 10. | | `limit` | int | Number of recommendations to fetch. | | `template` | string | Template name. | -| `attributes` | array | Fields which are required and will be requested from the Recommendation engine. These Field names are also used inside Handlebars templates. | +| `attributes` | array | Fields that are required and are requested from the Recommendation engine. These Field names are also used inside Handlebars templates. | -You can also bypass named arguments using standard value passing as arguments. +You can also bypass named arguments with standard value passing as arguments. !!! note "Custom templates" - If you want to use a custom template for displaying recommendations, - it must include `event_tracking.html.twig`: + To use a custom template for displaying recommendations, + ensure that it includes `event_tracking.html.twig`: `{% include 'EzRecommendationClientBundle::event_tracking.html.twig' %}`. -Recommendation responses contain all content data which is requested as attribute in the recommendation call. +Recommendation responses contain all content data that is requested as attribute in the recommendation call. This response data can be used in templates to render and style recommendations. For example, the following GET request should deliver the response below @@ -597,9 +461,9 @@ if the content Fields were previously exported by the export script. ### Modifying recommendation data -You can retrieve data returned from the recommendation engine and modify it before displaying. +You can retrieve data returned from the recommendation engine and modify it before it is shown to the user. -To do it, subscribe to `RecommendationResponseEvent`. +To modify recommendation data, subscribe to `RecommendationResponseEvent`. See [`Event/Subscriber/RecommendationEventSubscriber.php`](https://github.com/ezsystems/ezrecommendation-client/blob/master/src/lib/Event/Subscriber/RecommendationEventSubscriber.php) for example: ``` php @@ -615,12 +479,12 @@ The `-10` refers to priority, which must be negative so this action is performed ### Image variations -Displaying image variations is not currently supported out of the box. +Displaying image variations is not readily supported yet. -You can work around this limitation by creating your own template +You can work around this limitation by creating a template (based on [recommendations.html.twig](https://github.com/ezsystems/ezrecommendation-client/blob/master/src/bundle/Resources/views/recommendations.html.twig)). -If you want to access a specific image variation through API, you need to add the `image` parameter to the request URL with the name of the variation as its value. +To access a specific image variation through API, add the `image` parameter to the request URL with the name of the variation as its value. For example, to retrieve the `rss` variation of the image, use: `/api/ezp/v2/ez_recommendation/v1/contenttypes/16?lang=eng-GB&fields=title,description,image,intro,name&page=1&page_size=20&image=rss` @@ -629,7 +493,7 @@ For example, to retrieve the `rss` variation of the image, use: ### Logging -Most operations are logged via the `ez_recommendation` [Monolog channel](http://symfony.com/doc/5.0/cookbook/logging/channels_handlers.html). +Most operations are logged via the `ez_recommendation` [Monolog channel]([[= symfony_doc =]]/cookbook/logging/channels_handlers.html). To log everything about Recommendation to `dev.recommendation.log`, add the following to the `ezplatform.yaml`: ``` yaml @@ -642,4 +506,4 @@ monolog: level: info ``` -You can replace `info` with `debug` for more verbosity. +You can replace `info` with `debug` to increase verbosity. diff --git a/docs/guide/pricing/price_api/price_data_model.md b/docs/guide/pricing/price_api/price_data_model.md index a63ea6f7ea..8b0e4671cd 100644 --- a/docs/guide/pricing/price_api/price_data_model.md +++ b/docs/guide/pricing/price_api/price_data_model.md @@ -2,7 +2,7 @@ The `Siso\Bundle\PriceBundle\Model` class defines a generic price object. When attached to a [`ProductNode`](../../catalog/catalog_api/productnode.md) object, -[`PriceField`](../../../api/commerce_api/fields_for_ecommerce_data/pricefield.md) is used. +`PriceField` is used. ## Properties diff --git a/docs/guide/pricing/price_api/price_providers.md b/docs/guide/pricing/price_api/price_providers.md index 78c247ca67..aa98ad704b 100644 --- a/docs/guide/pricing/price_api/price_providers.md +++ b/docs/guide/pricing/price_api/price_providers.md @@ -67,6 +67,24 @@ Customer currency is used (set in the price request). Prices (and currency) are usually set up per country and provided during the import from the ERP or PIM system. +#### VAT settings + +You can configure VAT settings for each delivery country separately: + +``` yaml +siso_core.default.vat: + DE: + VATREDUCED: 7 + VATNORMAL: 19 + US: + VATREDUCED: 4 + VATNORMAL: 4 + default: + VATREDUCED: 7 + VATNORMAL: 19 + +``` + ## Checking which price provider is used You might want to know the price provider that is used to calculate a price (e.g. in the basket). diff --git a/docs/guide/pricing/price_engine.md b/docs/guide/pricing/price_engine.md index 6d5cdf8995..95e8864f18 100644 --- a/docs/guide/pricing/price_engine.md +++ b/docs/guide/pricing/price_engine.md @@ -1,4 +1,8 @@ -# Price engine +--- +description: The price engine calculates product prices taking into account customer groups, currencies and taxes. +--- + +# Prices The price engine is responsible for calculating prices in the shop. diff --git a/docs/guide/pricing/price_engine_faq.md b/docs/guide/pricing/price_engine_faq.md deleted file mode 100644 index 8de954b8d5..0000000000 --- a/docs/guide/pricing/price_engine_faq.md +++ /dev/null @@ -1,93 +0,0 @@ -# Price engine FAQ - -## Does the price engine support volume-based prices? - -It depends on the price provider. - -The remote price engine provides volume-based prices as defined in the ERP. - -## How do I get delivery costs or additional costs? - -The price provider returns such information as additional lines with a special type. - -## How can I access stock information? - -You can get stock information from `PriceLine` in `PriceResponse`: - -``` php -$priceResponse = $priceService->getPrices($priceRequest, $contextId); - -foreach ($priceResponse->getLines() as $priceLine) { - //get stock - $stockNumeric = $priceLine->getStockNumeric(); - if (isset($stockNumeric)) { - $stock = new StockField(array('stockNumeric' => $stockNumeric)); - $productNode->setStock($stock); - } -} -``` - -## How is currency handled? - -It depends on the price provider. - -- Local price provider uses the customer currency that is set in the price request. -- Remote price provider uses the currency returned from the ERP and if not set, also uses the customer currency. - -## How can I find out which provider calculates my prices? - -If the price provider calculates the prices, it sets a source type in `PriceResponse`. - -Possible source types: - -``` -PriceConstants::PRICE_RESPONSE_SOURCE_TYPE_REMOTE = 'remote' -PriceConstants::PRICE_RESPONSE_SOURCE_TYPE_LOCAL = 'local' -``` - -``` php -//if the prices are not calculated by a remote source, set an error message in the basket -if ($priceResponse->getSourceType() !== PriceConstants::PRICE_RESPONSE_SOURCE_TYPE_REMOTE) { - $basket->setErrorMessage( - $this->transService->translate('Remote prices can not be calculated!') - ); -} -``` - -## What happens if ERP is not available? - -For the basket, the local price provider calculates prices as a fallback. -The customer sees an error message that real-time prices are not available at the moment, -if the remote price provider is configured in the chain on the first place. - -## When is an error message shown? - -When the chain for the context ID is configured so that the first provider is remote -and the remote price calculation fails, en error message can be displayed. -The shop checks the chain for the context ID. -An error message can be displayed in the basket if the remote price calculation fails, but not in the wishlist. - -You have to configure the service ID of the remote price provider: - -``` yaml -siso_price.default.price_service_chain_remote: siso_price.price_provider.remote - -siso_price.default.price_service_chain.basket: - - siso_price.price_provider.remote - - siso_price.price_provider.local - -siso_price.default.price_service_chain.stored_basket: - - siso_price.price_provider.local - -siso_price.default.price_service_chain.wish_list: - - siso_price.price_provider.local -``` - -## Why can's the user see prices on product detail page with the necessary Role? - -Caching has to be configured to use `user-hash` in order to display the correct product detail page to anonymous or registered users. -If this is not done, the first call is cached for all users. - -## Why can't a user see prices in sliders, catalog list, product detail or comparison? - -The user doesn't have the necessary `siso_policy/see_product_price` Policy to see prices. diff --git a/docs/guide/product_comparison/product_comparison.md b/docs/guide/product_comparison/product_comparison.md index 36cf7e0ad6..ab5b399f52 100644 --- a/docs/guide/product_comparison/product_comparison.md +++ b/docs/guide/product_comparison/product_comparison.md @@ -1,4 +1,8 @@ -# Product comparison [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Product comparison [[= product_name_com =]] offers the option to compare products on a single page. Customers can save products on their own comparison lists. diff --git a/docs/guide/product_comparison/product_comparison_api.md b/docs/guide/product_comparison/product_comparison_api.md index 062cf90d35..b905f0a980 100644 --- a/docs/guide/product_comparison/product_comparison_api.md +++ b/docs/guide/product_comparison/product_comparison_api.md @@ -1,4 +1,8 @@ -# Product comparison API [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Product comparison API ## Basket type diff --git a/docs/guide/product_comparison/product_comparison_templates.md b/docs/guide/product_comparison/product_comparison_templates.md index bc86d4c9aa..970d950860 100644 --- a/docs/guide/product_comparison/product_comparison_templates.md +++ b/docs/guide/product_comparison/product_comparison_templates.md @@ -1,4 +1,8 @@ -# Product comparison templates [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Product comparison templates ## Template list diff --git a/docs/guide/project_organization.md b/docs/guide/project_organization.md index 1d329c9637..f10b56eb03 100644 --- a/docs/guide/project_organization.md +++ b/docs/guide/project_organization.md @@ -1,3 +1,7 @@ +--- +description: An Ibexa DXP project follows Symfony's directory structure to organize files in the project. +--- + # Project organization [[= product_name =]] is a Symfony application and follows the project structure used by Symfony. @@ -33,155 +37,17 @@ All project assets are accessible through the `assets` path. php bin/console bazinga:js-translation:dump assets --merge-domains ``` -### Importing assets from a bundle - -[[= product_name =]] uses [Webpack Encore](https://symfony.com/doc/5.0/frontend.html#webpack-encore) for asset management. - -#### Configuration from a bundle - -To import assets from a bundle, you need to configure them in the bundle's `Resources/encore/ez.config.js`: - -``` js -const path = require('path'); - -module.exports = (Encore) => { - Encore.addEntry('<entry-name>', [ - path.resolve(__dirname, '<path_to_file>'), - ]); -}; -``` - -Use `<entry-name>` to refer to this configuration entry from Twig templates: - -`{{ encore_entry_script_tags('<entry-name>', null, 'ezplatform') }}` - -To import CSS files only, use: - -`{{ encore_entry_link_tags('<entry-name>', null, 'ezplatform') }}` - -!!! tip - - After adding new files, run `php bin/console cache:clear`. - - For a full example of importing asset configuration, - see [`ez.config.js`](https://github.com/ezsystems/ezplatform-admin-ui/blob/v2.0.2/src/bundle/Resources/encore/ez.config.js) - -To edit existing configuration entries, create a `Resources/encore/ez.config.manager.js` file: - -``` js -const path = require('path'); - -module.exports = (eZConfig, eZConfigManager) => { - eZConfigManager.replace({ - eZConfig, - entryName: '<entry-name>', - itemToReplace: path.resolve(__dirname, '<path_to_old_file>'), - newItem: path.resolve(__dirname, '<path_to_new_file>'), - }); - eZConfigManager.remove({ - eZConfig, - entryName: '<entry-name>', - itemsToRemove: [ - path.resolve(__dirname, '<path_to_old_file>'), - path.resolve(__dirname, '<path_to_old_file>'), - ], - }); - eZConfigManager.add({ - eZConfig, - entryName: '<entry-name>', - newItems: [ - path.resolve(__dirname, '<path_to_new_file>'), - path.resolve(__dirname, '<path_to_new_file>'), - ], - }); -}; -``` - -!!! tip - - If you do not know what `entryName` to use, you can check the dev tools for files that are loaded on the given page. - Use the file name as `entryName`. - -!!! tip - - After adding new files, run `php bin/console cache:clear`. - - For a full example of overriding configuration, - see [`ez.config.manager.js`](https://github.com/ezsystems/ezplatform-matrix-fieldtype/blob/v2.0.0/src/bundle/Resources/encore/ez.config.manager.js). - -To add a new configuration under your own namespace and with its own dependencies, -add a `Resources/encore/ez.webpack.custom.config.js` file, for example: - -``` js - const Encore = require('@symfony/webpack-encore'); - - Encore.setOutputPath('<custom-path>') - .setPublicPath('<custom-path>') - .addExternals('<custom-externals>') - // ... - .addEntry('<entry-name>', ['<JS-path>']); - - const customConfig = Encore.getWebpackConfig(); - - customConfig.name = 'customConfigName'; - - // Config or array of configs: [customConfig1, customConfig2]; - module.exports = customConfig; -``` - -!!! tip - - If you don't plan to add multiple entry files on the same page in your custom config, - use the `disableSingleRuntimeChunk()` funtion to avoid adding a separate `runtime.js` file. - Otherwise, your JS code may be run multiple times. - By default, the `enableSingleRuntimeChunk()` function is used. - -#### Configuration from main project files - -If you prefer to include the asset configuration in the main project files, -add it in [`webpack.config.js`](https://github.com/ezsystems/ezplatform/blob/v3.0.3/webpack.config.js#L15). - -To overwrite built-in assets, use the following configuration to replace, remove or add asset files -in `webpack.config.js`: - -``` js -eZConfigManager.replace({ - eZConfig, - entryName: '<entry-name>', - itemToReplace: path.resolve(__dirname, '<path_to_old_file>'), - newItem: path.resolve(__dirname, '<path_to_new_file>'), -}); - -eZConfigManager.remove({ - eZConfig, - entryName: '<entry-name>', - itemsToRemove: [ - path.resolve(__dirname, '<path_to_old_file>'), - path.resolve(__dirname, '<path_to_old_file>'), - ], -}); - -eZConfigManager.add({ - eZConfig, - entryName: '<entry-name>', - newItems: [ - path.resolve(__dirname, '<path_to_new_file>'), - path.resolve(__dirname, '<path_to_new_file>'), - ], -}); -``` - ## Configuration You project's configuration is placed in the respective files in `config/packages`. -See [Configuration](configuration.md) for more information. +See [Configuration](configuration/configuration.md) for more information. ### Importing configuration from a bundle If you are keeping some of your code in a bundle, dealing with core bundle semantic configuration can be tedious if you maintain it in the main `config/packages/ezplatform.yaml` configuration file. -You can import configuration from a bundle by following the Symfony tutorial [How to Import Configuration Files/Resources](https://symfony.com/doc/5.0/service_container/import.html). +You can import configuration from a bundle by following the Symfony tutorial [How to Import Configuration Files/Resources]([[= symfony_doc =]]/service_container/import.html). ## Versioning a project diff --git a/docs/guide/quick_order/quick_order.md b/docs/guide/quick_order/quick_order.md index 2dffb6a7dc..d0b4590c4d 100644 --- a/docs/guide/quick_order/quick_order.md +++ b/docs/guide/quick_order/quick_order.md @@ -1,4 +1,9 @@ -# Quick order [[% include 'snippets/commerce_badge.md' %]] +--- +description: Use quick order to quickly add products to the basket in bulk. +edition: commerce +--- + +# Quick order Quick order is an order form that speeds up the checkout and order process. The customer can enter a list of SKUs and add them all together to the basket. diff --git a/docs/guide/quick_order/quick_order_configuration.md b/docs/guide/quick_order/quick_order_configuration.md index 9a2a95e448..6d8ad81329 100644 --- a/docs/guide/quick_order/quick_order_configuration.md +++ b/docs/guide/quick_order/quick_order_configuration.md @@ -1,6 +1,11 @@ -# Quick order configuration [[% include 'snippets/commerce_badge.md' %]] +--- +description: Configure the setup of the quick order functionality for quickly ordering products in bulk. +edition: commerce +--- -## Disable autosuggestion +# Quick order configuration + +## Disabling autosuggestion To disable autosuggestions in quick orders, use the `auto_suggest_limit` setting: diff --git a/docs/guide/quick_order/quick_order_templates.md b/docs/guide/quick_order/quick_order_templates.md index fce1524b43..cb320a551e 100644 --- a/docs/guide/quick_order/quick_order_templates.md +++ b/docs/guide/quick_order/quick_order_templates.md @@ -1,4 +1,8 @@ -# Quick order templates [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Quick order templates ### Template list diff --git a/docs/guide/reporting_issues.md b/docs/guide/reporting_issues.md index ccfcd8cfa7..e2b713b2f9 100644 --- a/docs/guide/reporting_issues.md +++ b/docs/guide/reporting_issues.md @@ -1,3 +1,7 @@ +--- +description: How to report security issues in Ibexa DXP? +--- + # Reporting security issues in Ibexa products The security of our software is a primary concern and we take it seriously. @@ -7,8 +11,8 @@ No engineering team is perfect though, and if you do discover a security issue i ## Channels -- If you're a customer or partner, please log in to your Service Portal at <https://support.ez.no/> and, in the "eZ Support Services" section, follow the link to the "Open new ticket", and report the issue as you would report a normal support request. Ibexa Product Support will respond, take care of the report, and keep you informed of the developments. -- If you're not a customer or partner, please log in to the Ibexa JIRA issue tracker: <https://jira.ez.no/> Create an account if you don't have one, it's free. Click the "Create" button in the top menu to create your report. For "Project", select either "eZ Publish / Platform" or "eZ Platform Enterprise Edition", depending on which product is affected by the bug. **Important: Use "Security Level": "Security"!** The engineering team will take care of your report. +- If you're a customer or partner, please log in to your Service Portal at <https://support.ibexa.co/>, click "New Ticket", and report the issue as you would report a normal support request. Ibexa Product Support will respond, take care of the report, and keep you informed of the developments. +- If you're not a customer or partner, please log in to the Ibexa JIRA issue tracker: <https://issues.ibexa.co/>. Create an account if you don't have one, it's free. Click the "Create" button in the top menu to create your report. For "Project", select "Ibexa IBX", or "eZ Publish / Platform", or "eZ Platform Enterprise Edition", depending on which product is affected by the bug. **Important: Select "Security Level": "Security"!** The engineering team will take care of your report. - It is also possible to report security issues by email to <security@ibexa.co> - this requires no account. ## Verbosity diff --git a/docs/guide/repository.md b/docs/guide/repository.md index 958d37961e..bf3a325edb 100644 --- a/docs/guide/repository.md +++ b/docs/guide/repository.md @@ -1,3 +1,7 @@ +--- +description: Ibexa DXP's content Repository stores all content and its related information and exposes a set of services to interact with via API. +--- + # Repository The content Repository is where all your content is stored. @@ -45,7 +49,7 @@ You should always check full list of changes for each release in corresponding r - custom Limitations - limited portions of IO - image variation handling - - Search Engine layer for custom Search Engine implementations (e.g. Legacy search engine, Solr search engine and in the future Elasticsearch) + - Search Engine layer for custom Search Engine implementations (e.g. Legacy search engine, Solr search engine and Elasticsearch) !!! caution diff --git a/docs/guide/repository/event_reference/content_events.md b/docs/guide/repository/event_reference/content_events.md new file mode 100644 index 0000000000..9e2fcce99a --- /dev/null +++ b/docs/guide/repository/event_reference/content_events.md @@ -0,0 +1,49 @@ +--- +description: Events that are triggered when working with content. +--- + +# Content events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateContentDraftEvent`|`ContentService::createContentDraft`|`ContentInfo $contentInfo`</br>`VersionInfo $versionInfo`</br>`User $creator`</br>`Language|null $language`</br>`Content|null $contentDraft`| +|`CreateContentDraftEvent`|`ContentService::createContentDraft`|`Content $contentDraft`</br>`ContentInfo $contentInfo`</br>`VersionInfo $versionInfo`</br>`User $creator`</br>`Language|null $language`| +|`BeforeCreateContentEvent`|`ContentService::createContent`|`ContentCreateStruct $contentCreateStruct`</br>`array $locationCreateStructs`</br>`Content|null $content`</br>`string[]|null $fieldIdentifiersToValidate`| +|`CreateContentEvent`|`ContentService::createContent`|`ContentCreateStruct $contentCreateStruct`</br>`array $locationCreateStructs`</br>`Content $content`</br>`string[]|null $fieldIdentifiersToValidate`| +|`BeforeUpdateContentEvent`|`ContentService::updateContent`|`VersionInfo $versionInfo`</br>`ContentUpdateStruct $contentUpdateStruct`</br>`Content|null $content`</br>`string[]|null $fieldIdentifiersToValidate`| +|`UpdateContentEvent`|`ContentService::updateContent`|`Content $content`</br>`VersionInfo $versionInfo`</br>`ContentUpdateStruct $contentUpdateStruct`</br>`string[]|null $fieldIdentifiersToValidate`| +|`BeforeUpdateContentMetadataEvent`|`ContentService::updateContentMetadata`|`ContentInfo $contentInfo`</br>`ContentMetadataUpdateStruct $contentMetadataUpdateStruct`</br>`Content|null $content`| +|`UpdateContentMetadataEvent`|`ContentService::updateContentMetadata`|`Content $content`</br>`ContentInfo $contentInfo`</br>`ContentMetadataUpdateStruct $contentMetadataUpdateStruct`| +|`BeforeCopyContentEvent`|`ContentService::copyContent`|`ContentInfo $contentInfo`</br>`LocationCreateStruct $destinationLocationCreateStruct`</br>`VersionInfo $versionInfo`</br>`Content|null $content`| +|`CopyContentEvent`|`ContentService::copyContent`|`Content $content`</br>`ContentInfo $contentInfo`</br>`LocationCreateStruct $destinationLocationCreateStruct`</br>`VersionInfo $versionInfo`| +|`BeforePublishVersionEvent`|`ContentService::publishVersion`|`VersionInfo $versionInfo`</br>`Content|null $content`</br>`string[] $translations`| +|`PublishVersionEvent`|`ContentService::publishVersion`|`Content $content`</br>`VersionInfo $versionInfo`</br>`string[] $translations`| +|`BeforeDeleteContentEvent`|`ContentService::deleteContent`|`ContentInfo $contentInfo`</br>`array|null $locations`| +|`DeleteContentEvent`|`ContentService::deleteContent`|`array $locations`</br>`ContentInfo $contentInfo`| +|`BeforeDeleteVersionEvent`|`ContentService::deleteVersion`|`VersionInfo $versionInfo`| +|`DeleteVersionEvent`|`ContentService::deleteVersion`|`VersionInfo $versionInfo`| + +## Relations + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAddRelationEvent`|`ContentService::addRelation`|`VersionInfo $sourceVersion`</br>`ContentInfo $destinationContent`</br>`Relation|null $relation`| +|`AddRelationEvent`|`ContentService::addRelation`|`Relation $relation`</br>`VersionInfo $sourceVersion`</br>`ContentInfo $destinationContent`| +|`BeforeDeleteRelationEvent`|`ContentService::deleteRelation`|`VersionInfo $sourceVersion`</br>`ContentInfo $destinationContent`| +|`DeleteRelationEvent`|`ContentService::deleteRelation`|`VersionInfo $sourceVersion`</br>`ContentInfo $destinationContent`| + +## Content translations + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeDeleteTranslationEvent`|`ContentService::deleteTranslation`|`ContentInfo $contentInfo`</br>`$languageCode`| +|`DeleteTranslationEvent`|`ContentService::deleteTranslation`|`ContentInfo $contentInfo`</br>`$languageCode`| + +## Hiding and revealing + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeHideContentEvent`|`ContentService::hideContent`|`ContentInfo $contentInfo`| +|`HideContentEvent`|`ContentService::hideContent`|`ContentInfo $contentInfo| +|`BeforeRevealContentEvent`|`ContentService::revealContent`|`ContentInfo $contentInfo`| +|`RevealContentEvent`|`ContentService::revealContent`|`ContentInfo $contentInfo`| diff --git a/docs/guide/repository/event_reference/content_type_events.md b/docs/guide/repository/event_reference/content_type_events.md new file mode 100644 index 0000000000..4e59808ec0 --- /dev/null +++ b/docs/guide/repository/event_reference/content_type_events.md @@ -0,0 +1,58 @@ +--- +description: Events that are triggered when working with Content Types. +--- + +# Content Type events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateContentTypeDraftEvent`|`ContentTypeService::createContentTypeDraft`|`ContentType $contentType`</br>`ContentTypeDraft|null $contentTypeDraft`| +|`CreateContentTypeDraftEvent`|`ContentTypeService::createContentTypeDraft`|`ContentTypeDraft $contentTypeDraft`</br>`ContentType $contentType`| +|`BeforeCreateContentTypeEvent`|`ContentTypeService::createContentType`|`ContentTypeCreateStruct $contentTypeCreateStruct`</br>`array $contentTypeGroups`</br>`ContentTypeDraft|null $contentTypeDraft`| +|`CreateContentTypeEvent`|`ContentTypeService::createContentType`|`ContentTypeDraft $contentTypeDraft`</br>`ContentTypeCreateStruct $contentTypeCreateStruct`</br>`array $contentTypeGroups`| +|`BeforeUpdateContentTypeDraftEvent`|`ContentTypeService::updateContentTypeDraft`|`ContentTypeDraft $contentTypeDraft`</br>`ContentTypeUpdateStruct $contentTypeUpdateStruct`| +|`UpdateContentTypeDraftEvent`|`ContentTypeService::updateContentTypeDraft`|`ContentTypeDraft $contentTypeDraft`</br>`ContentTypeUpdateStruct $contentTypeUpdateStruct`| +|`BeforeCopyContentTypeEvent`|`ContentTypeService::copyContentType`|`ContentType $contentType`</br>`User $creator`</br>`ContentType|null $contentTypeCopy`| +|`CopyContentTypeEvent`|`ContentTypeService::copyContentType`|`ContentType $contentTypeCopy`</br>`ContentType $contentType`</br>`User $creator`| +|`BeforePublishContentTypeDraftEvent`|`ContentTypeService::publishContentTypeDraft`|`ContentTypeDraft $contentTypeDraft`| +|`PublishContentTypeDraftEvent`|`ContentTypeService::publishContentTypeDraft`|`ContentTypeDraft $contentTypeDraft`| +|`BeforeDeleteContentTypeEvent`|`ContentTypeService::deleteContentType`|`ContentType $contentType`| +|`DeleteContentTypeEvent`|`ContentTypeService::deleteContentType`|`ContentType $contentType`| + +## Content Type groups + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateContentTypeGroupEvent`|`ContentTypeService::createContentTypeGroup`|`ContentTypeCreateStruct $contentTypeCreateStruct`</br>`array $contentTypeGroups`</br>`ContentTypeDraft|null $contentTypeDraft`| +|`CreateContentTypeGroupEvent`|`ContentTypeService::createContentTypeGroup`|`ContentTypeGroup $contentTypeGroup`</br>`ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct`| +|`BeforeUpdateContentTypeGroupEvent`|`ContentTypeService::updateContentTypeGroup`|`ContentTypeGroup $contentTypeGroup`</br>`ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct`| +|`UpdateContentTypeGroupEvent`|`ContentTypeService::updateContentTypeGroup`|`ContentTypeGroup $contentTypeGroup`</br>`ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct`| +|`BeforeDeleteContentTypeGroupEvent`|`ContentTypeService::deleteContentTypeGroup`|`ContentTypeGroup $contentTypeGroup`| +|`DeleteContentTypeGroupEvent`|`ContentTypeService::deleteContentTypeGroup`|`ContentTypeGroup $contentTypeGroup`| + +## Content Type translations + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeRemoveContentTypeTranslationEvent`|`ContentTypeService::removeContentTypeTranslation`|`ContentTypeDraft $contentTypeDraft`</br>`string $languageCode`</br>`ContentTypeDraft|null $newContentTypeDraft`| +|`RemoveContentTypeTranslationEvent`|`ContentTypeService::removeContentTypeTranslation`|`ContentTypeDraft $newContentTypeDraft`</br>`ContentTypeDraft $contentTypeDraft`</br>`string $languageCode`| + +## Field definitions + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAddFieldDefinitionEvent`|`ContentTypeService::addFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinitionCreateStruct $fieldDefinitionCreateStruct`| +|`AddFieldDefinitionEvent`|`ContentTypeService::addFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinitionCreateStruct $fieldDefinitionCreateStruct`| +|`BeforeUpdateFieldDefinitionEvent`|`ContentTypeService::updateFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinition $fieldDefinition`</br>`FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct`| +|`UpdateFieldDefinitionEvent`|`ContentTypeService::updateFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinition $fieldDefinition`</br>`FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct`| +|`BeforeRemoveFieldDefinitionEvent`|`ContentTypeService::removeFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinition $fieldDefinition`| +|`RemoveFieldDefinitionEvent`|`ContentTypeService::removeFieldDefinition`|`ContentTypeDraft $contentTypeDraft`</br>`FieldDefinition $fieldDefinition`| + +## Assigning to groups + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAssignContentTypeGroupEvent`|`ContentTypeService::assignContentTypeGroup`|`ContentType $contentType`</br>`ContentTypeGroup $contentTypeGroup`| +|`AssignContentTypeGroupEvent`|`ContentTypeService::assignContentTypeGroup`|`ContentType $contentType`</br>`ContentTypeGroup $contentTypeGroup`| +|`BeforeUnassignContentTypeGroupEvent`|`ContentTypeService::unassignContentTypeGroup`|`ContentType $contentType`</br>`ContentTypeGroup $contentTypeGroup`| +|`UnassignContentTypeGroupEvent`|`ContentTypeService::unassignContentTypeGroup`|`ContentType $contentType`</br>`ContentTypeGroup $contentTypeGroup`| diff --git a/docs/guide/repository/event_reference/event_reference.md b/docs/guide/repository/event_reference/event_reference.md new file mode 100644 index 0000000000..0930cfe89d --- /dev/null +++ b/docs/guide/repository/event_reference/event_reference.md @@ -0,0 +1,38 @@ +--- +description: Ibexa DXP dispatches events before and after you perform different operations in the Back Office and on the Repository. +--- + +# Event reference + +[[= product_name =]] dispatches events during different actions. +You can subscribe to these events to extend the functionality of the system. + +In most cases, two events are dispatched for every action, +one before the action is completed, and one after. + +For example, copying a Content item is connected with two events: +`BeforeCopyContentEvent` and `CopyContentEvent`. + +``` php +<?php + +namespace App\EventSubscriber; + +use eZ\Publish\API\Repository\Events\Content\CopyContentEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class MyEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return [ + CopyContentEvent::class => ['onCopyContent', 0], + ]; + } + + public function onCopyContent(CopyContentEvent $event): void + { + // your implementation + } +} +``` diff --git a/docs/guide/repository/event_reference/language_events.md b/docs/guide/repository/event_reference/language_events.md new file mode 100644 index 0000000000..0454e9d133 --- /dev/null +++ b/docs/guide/repository/event_reference/language_events.md @@ -0,0 +1,23 @@ +--- +description: Events that are triggered when working with languages. +--- + +# Language events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateLanguageEvent`|`LanguageService::createLanguage`|`LanguageCreateStruct $languageCreateStruct`</br>`Language|null $language`| +|`CreateLanguageEvent`|`LanguageService::createLanguage`|`Language $language`</br>`LanguageCreateStruct $languageCreateStruct`| +|`BeforeUpdateLanguageNameEvent`|`LanguageService::updateLanguageName`|`Language $language`</br>`string $newName`</br>`Language|null $updatedLanguage`| +|`UpdateLanguageNameEvent`|`LanguageService::updateLanguageName`|`Language $updatedLanguage`</br>`Language $language`</br>`string $newName`| +|`BeforeDeleteLanguageEvent`|`LanguageService::deleteLanguage`|`Language $language`| +|`DeleteLanguageEvent`|`LanguageService::deleteLanguage`|`Language $language`| + +## Enabling languages + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeEnableLanguageEvent`|`LanguageService::enableLanguage`|`Language $language`</br>`Language|null $enabledLanguage`| +|`EnableLanguageEvent`|`LanguageService::enableLanguage`|`Language $enabledLanguage`</br>`Language $language`| +|`BeforeDisableLanguageEvent`|`LanguageService::disableLanguage`|`Language $language`</br>`Language|null $disabledLanguage`| +|`DisableLanguageEvent`|`LanguageService::disableLanguage`|`Language $disabledLanguage`</br>`Language $language`| diff --git a/docs/guide/repository/event_reference/location_events.md b/docs/guide/repository/event_reference/location_events.md new file mode 100644 index 0000000000..9c125f35ff --- /dev/null +++ b/docs/guide/repository/event_reference/location_events.md @@ -0,0 +1,34 @@ +--- +description: Events that are triggered when working with content Locations. +--- + +# Location events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateLocationEvent`|`LocationService::createLocation`|`ContentInfo $contentInfo`</br>`LocationCreateStruct $locationCreateStruct`</br>`Location|null $location`| +|`CreateLocationEvent`|`LocationService::createLocation`|`Location $location`</br>`ContentInfo $contentInfo`</br>`LocationCreateStruct $locationCreateStruct`| +|`BeforeUpdateLocationEvent`|`LocationService::updateLocation`|`Location $location`</br>`LocationUpdateStruct $locationUpdateStruct`</br>`Location|null $updatedLocation`| +|`UpdateLocationEvent`|`LocationService::updateLocation`|`Location $updatedLocation`</br>`Location $location`</br>`LocationUpdateStruct $locationUpdateStruct`| +|`BeforeDeleteLocationEvent`|`LocationService::deleteLocation`|`Location $location`| +|`DeleteLocationEvent`|`LocationService::deleteLocation`|`Location $location`| + +## Hiding and revealing + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeHideLocationEvent`|`LocationService::hideLocation`|`Location $location`</br>`Location|null $hiddenLocation`| +|`HideLocationEvent`|`LocationService::hideLocation`|`Location $hiddenLocation`</br>`Location $location`| +|`BeforeUnhideLocationEvent`|`LocationService::unhideLocation`|`Location $location`</br>`Location|null $revealedLocation`| +|`UnhideLocationEvent`|`LocationService::unhideLocation`|`Location $revealedLocation`</br>`Location $location`| + +## Subtree and Location management + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCopySubtreeEvent`|`LocationService::copySubtree`|`Location $subtree`</br>`Location $targetParentLocation`</br>`Location|null $location`| +|`CopySubtreeEvent`|`LocationService::copySubtree`|`Location $location`</br>`Location $subtree`</br>`Location $targetParentLocation`| +|`BeforeMoveSubtreeEvent`|`LocationService::moveSubtree`|`Location $location`</br>`Location $newParentLocation`| +|`MoveSubtreeEvent`|`LocationService::moveSubtree`|`Location $location`</br>`Location $newParentLocation`| +|`BeforeSwapLocationEvent`|`LocationService::swapLocation`|`Location $location1`</br>`Location $location2`| +|`SwapLocationEvent`|`LocationService::swapLocation`|`Location $location1`</br>`Location $location2`| diff --git a/docs/guide/repository/event_reference/object_state_events.md b/docs/guide/repository/event_reference/object_state_events.md new file mode 100644 index 0000000000..b2ec574d19 --- /dev/null +++ b/docs/guide/repository/event_reference/object_state_events.md @@ -0,0 +1,34 @@ +--- +description: Events that are triggered when working with object states and object state groups. +--- + +# Object state events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateObjectStateEvent`|`ObjectStateService::createObjectState`|`ObjectStateGroup $objectStateGroup`</br>`ObjectStateCreateStruct $objectStateCreateStruct`</br>`ObjectState|null $objectState`| +|`CreateObjectStateEvent`|`ObjectStateService::createObjectState`|`ObjectState $objectState`</br>`ObjectStateGroup $objectStateGroup`</br>`ObjectStateCreateStruct $objectStateCreateStruct`| +|`BeforeUpdateObjectStateEvent`|`ObjectStateService::updateObjectState`|`ObjectState $objectState`</br>`ObjectStateUpdateStruct $objectStateUpdateStruct`</br>`ObjectState|null $updatedObjectState`| +|`UpdateObjectStateEvent`|`ObjectStateService::updateObjectState`|`ObjectState $updatedObjectState`</br>`ObjectState $objectState`</br>`ObjectStateUpdateStruct $objectStateUpdateStruct`| +|`BeforeDeleteObjectStateEvent`|`ObjectStateService::deleteObjectState`|`ObjectState $objectState`| +|`DeleteObjectStateEvent`|`ObjectStateService::deleteObjectState`|`ObjectState $objectState`| + +## Object state groups + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateObjectStateGroupEvent`|`ObjectStateService::createObjectStateGroup`|`ObjectStateGroupCreateStruct $objectStateGroupCreateStruct`</br>`ObjectStateGroup|null $objectStateGroup`| +|`CreateObjectStateGroupEvent`|`ObjectStateService::createObjectStateGroup`|`ObjectStateGroup $objectStateGroup`</br>`ObjectStateGroupCreateStruct $objectStateGroupCreateStruct`| +|`BeforeUpdateObjectStateGroupEvent`|`ObjectStateService::updateObjectStateGroup`|`ObjectStateGroup $objectStateGroup`</br>`ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct`</br>`ObjectStateGroup|null $updatedObjectStateGroup`| +|`UpdateObjectStateGroupEvent`|`ObjectStateService::updateObjectStateGroup`|`ObjectStateGroup $updatedObjectStateGroup`</br>`ObjectStateGroup $objectStateGroup`</br>`ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct`| +|`BeforeDeleteObjectStateGroupEvent`|`ObjectStateService::deleteObjectStateGroup`|`ObjectStateGroup $objectStateGroup`| +|`DeleteObjectStateGroupEvent`|`ObjectStateService::deleteObjectStateGroup`|`ObjectStateGroup $objectStateGroup`| + +## Setting states + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeSetContentStateEvent`|`ObjectStateService::deleteObjectState`|`ContentInfo $contentInfo`</br>`ObjectStateGroup $objectStateGroup`</br>`ObjectState $objectState`| +|`SetContentStateEvent`|`ObjectStateService::deleteObjectState`|`ContentInfo $contentInfo`</br>`ObjectStateGroup $objectStateGroup`</br>`ObjectState $objectState`| +|`BeforeSetPriorityOfObjectStateEvent`|`ObjectStateService::setPriorityOfObjectState`|`ObjectState $objectState`</br>`private $priority`| +|`SetPriorityOfObjectStateEvent`|`ObjectStateService::setPriorityOfObjectState`|`ObjectState $objectState`</br>`private $priority`| diff --git a/docs/guide/repository/event_reference/other_events.md b/docs/guide/repository/event_reference/other_events.md new file mode 100644 index 0000000000..12c9fb2ee2 --- /dev/null +++ b/docs/guide/repository/event_reference/other_events.md @@ -0,0 +1,67 @@ +--- +description: Events that are triggered when working with bookmarks, notifications, settings, forms and others. +--- + +# Other events + +## Bookmarks + +The following events are dispatched when adding Content items to bookmarks. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateBookmarkEvent`|`BookmarkService::createBookmark`|`Location $location`| +|`CreateBookmarkEvent`|`BookmarkService::createBookmark`|`Location $location`| +|`BeforeDeleteBookmarkEvent`|`BookmarkService::deleteBookmark`|`Location $location`| +|`DeleteBookmarkEvent`|`BookmarkService::deleteBookmark`|`Location $location`| + +## Notifications + +The following events refer to [notifications displayed in the user menu](../../sending_notifications.md#create-custom-notifications). + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateNotificationEvent`|`NotificationService::createNotification`|`CreateStruct $createStruct`</br>`Notification|null $notification`| +|`CreateNotificationEvent`|`NotificationService::createNotification`|`Notification $notification`</br>`CreateStruct $createStruct`| +|`BeforeDeleteNotificationEvent`|`NotificationService::deleteNotification`|`Notification $notification`| +|`DeleteNotificationEvent`|`NotificationService::deleteNotification`|`Notification $notification`| +|`BeforeMarkNotificationAsReadEvent`|`NotificationService::markNotificationAsRead`|`Notification $notification`| +|`MarkNotificationAsReadEvent`|`NotificationService::markNotificationAsRead`|`Notification $notification`| + +## Settings + +The following events refer to key/value application-wide settings in database. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateSettingEvent`|`SettingService::createSetting`|`SettingCreateStruct $settingCreateStruct`</br>`Setting|null $setting`| +|`CreateSettingEvent`|`SettingService::createSetting`|`Setting $setting`</br>`SettingCreateStruct $settingCreateStruct`| +|`BeforeUpdateSettingEvent`|`SettingService::updateSetting`|`Setting $setting`</br>`SettingUpdateStruct $settingUpdateStruct`</br>`Setting|null $updatedSetting`| +|`UpdateSettingEvent`|`SettingService::updateSetting`|`Setting $updatedSetting`</br>`Setting $setting`</br>`SettingUpdateStruct $settingUpdateStruct`| +|`BeforeDeleteSettingEvent`|`SettingService::deleteSetting`|`Setting $setting`| +|`DeleteSettingEvent`|`SettingService::deleteSetting`|`Setting $setting`| + +## User preferences + +The following events are dispatched when changing the user settings available in the user menu. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeSetUserPreferenceEvent`|`UserPreferenceService::setUserPreference`|`UserPreferenceSetStruct[] $userPreferenceSetStructs`| +|`SetUserPreferenceEvent`|`UserPreferenceService::setUserPreference`|`UserPreferenceSetStruct[] $userPreferenceSetStructs`| + +## DAM assets + +| Event | Dispatched by | Properties | +|---|---|---| +|`PublishVersionEvent`|`PublishAssetEventDispatcher::emitPublishAssetEvent`|`Content $content`</br>`Connector\Dam\AssetIdentifier $assetIdentifier`</br>`Connector\Dam\AssetSource $assetSource`| + +## Form Builder [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +| Event | Dispatched by | Properties | +|---|---|---| +|`FieldAttributeDefinitionEvent`|`FieldDefinitionFactory::getAttributesDefinitions`|`FieldAttributeDefinitionBuilder $definitionBuilder`</br>`array $configuration`| +|`FieldDefinitionEvent`|`FieldDefinitionFactory::getFieldDefinition`|`FieldDefinitionBuilder $definitionBuilder`</br>`array $configuration`| +|`FieldValidatorDefinitionEvent`|`FieldDefinitionFactory::getValidatorsDefinitions`|`FieldDefinitionBuilder $definitionBuilder`</br>`array $configuration`| +|`FormActionEvent`|`HandleFormSubmission::handleFormSubmission`|`ContentView $contentView`</br>`EzPlatformFormBuilder\FieldType\Model\Form $form`</br>`string $action`</br>`mixed $data`| +|`FormSubmitEvent`|`HandleFormSubmission::handleFormSubmission`|`ContentView $contentView`</br>`EzPlatformFormBuilder\FieldType\Model\Form $form`</br>`array $data`| diff --git a/docs/guide/repository/event_reference/page_events.md b/docs/guide/repository/event_reference/page_events.md new file mode 100644 index 0000000000..ecc3cc07ad --- /dev/null +++ b/docs/guide/repository/event_reference/page_events.md @@ -0,0 +1,36 @@ +--- +description: Events that are triggered when working with Pages and Page blocks. +edition: experience +--- + +# Page events + +| Event | Dispatched by | Properties | +|---|---|---| +|`AttributeSerializationEvent`|`AttributeSerializationDispatcher::serialize`|`LandingPage\Model\BlockValue $blockValue`</br>`string $attributeIdentifier`</br>`mixed|null $serializedValue`</br>`mixed $deserializedValue`| +|`BlockContextEvent`|`BlockService::createBlockContextFromRequest`|`Request $request`</br>`BlockContextInterface|null $blockContext`| +|`BlockFragmentRenderEvent`|`BlockRenderOptionsFragmentRenderer::dispatchFragmentRenderEvent`|`Content $content`</br>`Location|null $location`</br>`LandingPage\Model\Page $page`</br>`LandingPage\Model\BlockValue $blockValue`</br>`ControllerReference $uri`</br>`Request $request`</br>`array $options`| +|`BlockResponseEvent`|`BlockResponseSubscriber::getSubscribedEvents`|`BlockContextInterface $blockContext`</br>`LandingPage\Model\BlockValue $blockValue`</br>`Request $request`</br>`Response $response`| +|`CollectBlockRelationsEvent`|`CollectRelationsSubscriber::onCollectBlockRelations`|`LandingPage\Value $fieldValue`</br>`LandingPage\Model\BlockValue $blockValue`</br>`int[] $relations`| +|`PageRenderEvent`|`PageService::dispatchRenderPageEvent`|`Content $content`</br>`Location|null $location`</br>`LandingPage\Model\Page $page`</br>`Request $request`| + +## Page Builder + +The following events are dispatched when editing a Page in the Page Builder. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BlockConfigurationViewEvent`|`BlockController::dispatchBlockConfigurationViewEvent`|`BlockConfigurationView $blockConfigurationView`</br>`BlockDefinition $blockDefinition`</br>`BlockConfiguration $blockConfiguration`</br>`FormInterface $blockConfigurationForm`| +|`BlockPreviewPageContextEvent`|`PreviewController::dispatchPageContextEvent`|`BlockContextInterface $blockContext`</br>`LandingPage\Model\Page $page`</br>`array $pagePreviewParameters`| +|`BlockPreviewResponseEvent`|`PreviewController::dispatchBlockPreviewResponseEvent`|`BlockContextInterface $blockContext`</br>`array $pagePreviewParameters`</br>`LandingPage\Model\Page $page`</br>`BlockValue $blockValue`</br>`array $responseData`| + +## Page blocks + +The following events are dispatched when editing a Page block. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BlockDefinitionEvent`|`BlockDefinitionFactory::getBlockDefinition`|`BlockDefinition $definition`</br>`array $configuration`| +|`BlockAttributeDefinitionEvent`|`BlockDefinitionFactory::getBlockDefinition`|`BlockAttributeDefinition $definition`</br>`array $configuration`| +|`PreRenderEvent`|`BlockService::render`|`BlockContextInterface $blockContext`</br>`BlockValue $blockValue`</br>`RenderRequestInterface $renderRequest`| +|`PostRenderEvent`|`BlockService::render`|`BlockContextInterface $blockContext`</br>`BlockValue $blockValue`</br>`string $renderedBlock`| diff --git a/docs/guide/repository/event_reference/role_events.md b/docs/guide/repository/event_reference/role_events.md new file mode 100644 index 0000000000..1a82fe61c6 --- /dev/null +++ b/docs/guide/repository/event_reference/role_events.md @@ -0,0 +1,44 @@ +--- +description: Events that are triggered when working with Roles. +--- + +# Role events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateRoleDraftEvent`|`RoleService::createRoleDraft`|`Role $role`</br>`RoleDraft|null $roleDraft`| +|`CreateRoleDraftEvent`|`RoleService::createRoleDraft`|`Role $role`</br>`RoleDraft $roleDraft`| +|`BeforeCreateRoleEvent`|`RoleService::createRole`|`RoleCreateStruct $roleCreateStruct`</br>`RoleDraft|null $roleDraft`| +|`CreateRoleEvent`|`RoleService::createRole`|`RoleCreateStruct $roleCreateStruct`</br>`RoleDraft $roleDraft`| +|`BeforeUpdateRoleDraftEvent`|`RoleService::updateRoleDraft`|`RoleDraft $roleDraft`</br>`RoleUpdateStruct $roleUpdateStruct`</br>`RoleDraft|null $updatedRoleDraft`| +|`UpdateRoleDraftEvent`|`RoleService::updateRoleDraft`|`RoleDraft $roleDraft`</br>`RoleUpdateStruct $roleUpdateStruct`</br>`RoleDraft $updatedRoleDraft`| +|`BeforeCopyRoleEvent`|`RoleService::copyRole`|`Role $role`</br>`RoleCopyStruct $roleCopyStruct`</br>`Role|null $copiedRole`| +|`CopyRoleEvent`|`RoleService::copyRole`|`Role $copiedRole`</br>`Role $role`</br>`RoleCopyStruct $roleCopyStruct`| +|`BeforePublishRoleDraftEvent`|`RoleService::publishRoleDraft`|`RoleDraft $roleDraft`| +|`PublishRoleDraftEvent`|`RoleService::publishRoleDraft`|`RoleDraft $roleDraft`| +|`BeforeDeleteRoleDraftEvent`|`RoleService::deleteRoleDraft`|`RoleDraft $roleDraft`| +|`DeleteRoleDraftEvent`|`RoleService::deleteRoleDraft`|`RoleDraft $roleDraft`| +|`BeforeDeleteRoleEvent`|`RoleService::deleteRole`|`Role $role`| +|`DeleteRoleEvent`|`RoleService::deleteRole`|`Role $role`| + +## Adding Policies + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAddPolicyByRoleDraftEvent`|`RoleService::addPolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyCreateStruct $policyCreateStruct`</br>`RoleDraft|null $updatedRoleDraft`| +|`AddPolicyByRoleDraftEvent`|`RoleService::addPolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyCreateStruct $policyCreateStruct`</br>`private $updatedRoleDraft`| +|`BeforeUpdatePolicyByRoleDraftEvent`|`RoleService::updatePolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyDraft $policy`</br>`PolicyUpdateStruct $policyUpdateStruct`</br>`PolicyDraft|null $updatedPolicyDraft`| +|`UpdatePolicyByRoleDraftEvent`|`RoleService::updatePolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyDraft $policy`</br>`PolicyUpdateStruct $policyUpdateStruct`</br>`PolicyDraft $updatedPolicyDraft`| +|`BeforeRemovePolicyByRoleDraftEvent`|`RoleService::removePolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyDraft $policyDraft`</br>`RoleDraft|null $updatedRoleDraft`| +|`RemovePolicyByRoleDraftEvent`|`RoleService::removePolicyByRoleDraft`|`RoleDraft $roleDraft`</br>`PolicyDraft $policyDraft`</br>`RoleDraft $updatedRoleDraft`| + +## Assigning Roles + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAssignRoleToUserEvent`|`RoleService::assignRoleToUser`|`Role $role`</br>`User $user`</br>`Limitation\RoleLimitation $roleLimitation`| +|`AssignRoleToUserEvent`|`RoleService::assignRoleToUser`|`Role $role`</br>`User $user`</br>`Limitation\RoleLimitation $roleLimitation`| +|`BeforeAssignRoleToUserGroupEvent`|`RoleService::assignRoleToUserGroup`|`Role $role`</br>`UserGroup $userGroup`</br>`Limitation\RoleLimitation $roleLimitation`| +|`AssignRoleToUserGroupEvent`|`RoleService::assignRoleToUserGroup`|`Role $role`</br>`UserGroup $userGroup`</br>`Limitation\RoleLimitation $roleLimitation`| +|`BeforeRemoveRoleAssignmentEvent`|`RoleService::removeRoleAssignment`|`RoleAssignment $roleAssignment`| +|`RemoveRoleAssignmentEvent`|`RoleService::removeRoleAssignment`|`RoleAssignment $roleAssignment`| diff --git a/docs/guide/repository/event_reference/section_events.md b/docs/guide/repository/event_reference/section_events.md new file mode 100644 index 0000000000..0cb08612de --- /dev/null +++ b/docs/guide/repository/event_reference/section_events.md @@ -0,0 +1,23 @@ +--- +description: Events that are triggered when working with Sections. +--- + +# Section events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateSectionEvent`|`SectionService::createSection`|`SectionCreateStruct $sectionCreateStruct`</br>`Section|null $section`| +|`CreateSectionEvent`|`SectionService::createSection`|`SectionCreateStruct $sectionCreateStruct`</br>`Section $section`| +|`BeforeDeleteSectionEvent`|`SectionService::deleteSection`|`Section $section`| +|`DeleteSectionEvent`|`SectionService::deleteSection`|`Section $section`| +|`BeforeUpdateSectionEvent`|`SectionService::updateSection`|`Section $section`</br>`SectionUpdateStruct $sectionUpdateStruct`</br>`Section|null $updatedSection`| +|`UpdateSectionEvent`|`SectionService::updateSection`|`Section $section`</br>`SectionUpdateStruct $sectionUpdateStruct`</br>`Section $updatedSection`| + +## Assigning Sections + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAssignSectionEvent`|`SectionService::assignSection`|`ContentInfo $contentInfo`</br>`Section $section`| +|`AssignSectionEvent`|`SectionService::assignSection`|`ContentInfo $contentInfo`</br>`Section $section`| +|`BeforeAssignSectionToSubtreeEvent`|`SectionService::assignSectionToSubtree`|`Location $location`</br>`Section $section`| +|`AssignSectionToSubtreeEvent`|`SectionService::assignSectionToSubtree`|`Location $location`</br>`Section $section`| diff --git a/docs/guide/repository/event_reference/site_events.md b/docs/guide/repository/event_reference/site_events.md new file mode 100644 index 0000000000..eb462b0001 --- /dev/null +++ b/docs/guide/repository/event_reference/site_events.md @@ -0,0 +1,17 @@ +--- +description: Events that are triggered when working with sites. +edition: experience +--- + +# Site events + +The following events are dispatched when managing [Sites](../../multisite/site_factory.md). + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateSiteEvent`|`SiteService::createSite`|`SiteCreateStruct $siteCreateStruct`</br>`Site $site`| +|`CreateSiteEvent`|`SiteService::createSite`|`Site $site`</br>`SiteCreateStruct $siteCreateStruct`| +|`BeforeUpdateSiteEvent`|`SiteService::updateSite`|`Site $site`</br>`SiteUpdateStruct $siteUpdateStruct`</br>`Site|null $updatedSite`| +|`UpdateSiteEvent`|`SiteService::updateSite`|`Site $updatedSite`</br>`Site $site`</br>`SiteUpdateStruct $siteUpdateStruct`| +|`BeforeDeleteSiteEvent`|`SiteService::deleteSite`|`Site $site`| +|`DeleteSiteEvent`|`SiteService::deleteSite`|`Site $site`| diff --git a/docs/guide/repository/event_reference/trash_events.md b/docs/guide/repository/event_reference/trash_events.md new file mode 100644 index 0000000000..5ad1c699a7 --- /dev/null +++ b/docs/guide/repository/event_reference/trash_events.md @@ -0,0 +1,18 @@ +--- +description: Events that are triggered when working with Trash. +--- + +# Trash events + +The following events are dispatched when managing Trash. + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeDeleteTrashItemEvent`|`TrashService::deleteTrashItem`|`TrashItem $trashItem`</br>`TrashItemDeleteResult|null $result`| +|`DeleteTrashItemEvent`|`TrashService::deleteTrashItem`|`TrashItem $trashItem`</br>`TrashItemDeleteResult $result`| +|`BeforeEmptyTrashEvent`|`TrashService::emptyTrash`|`TrashItemDeleteResultList|null $resultList`| +|`EmptyTrashEvent`|`TrashService::emptyTrash`|`TrashItemDeleteResultList $resultList`| +|`BeforeRecoverEvent`|`TrashService::recover`|`TrashItem $trashItem`</br>`Location $newParentLocation`</br>`Location|null $location`| +|`RecoverEvent`|`TrashService::recover`|`TrashItem $trashItem`</br>`Location $newParentLocation`</br>`Location $location`| +|`BeforeTrashEvent`|`TrashService::trash`|`Location $location`</br>`TrashItem|null $result`</br>`bool $resultSet = false`| +|`TrashEvent`|`TrashService::trash`|`Location $location`</br>`TrashItem|null $trashItem`| diff --git a/docs/guide/repository/event_reference/url_events.md b/docs/guide/repository/event_reference/url_events.md new file mode 100644 index 0000000000..f8066415b8 --- /dev/null +++ b/docs/guide/repository/event_reference/url_events.md @@ -0,0 +1,42 @@ +--- +description: Events that are triggered when working with URLs, URL aliases and URL wildcards. +--- + +# URL events + +## URLs + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeUpdateUrlEvent`|`URLService::updateUrl`|`URL $url`</br>`URLUpdateStruct $struct`</br>`URL|null $updatedUrl`| +|`UpdateUrlEvent`|`URLService::updateUrl`|`URL $url`</br>`URLUpdateStruct $struct`</br>`URL $updatedUrl`| + +## URL aliases + +The following events are dispatched when creating and managing [URL aliases](../../url_management.md#url-aliases). + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateGlobalUrlAliasEvent`|`URLAliasService::createGlobalUrlAlias`|`private $resource`</br>`private $path`</br>`private $languageCode`</br>`private $forwarding`</br>`private $alwaysAvailable`</br>`URLAlias|null $urlAlias`| +|`CreateGlobalUrlAliasEvent`|`URLAliasService::createGlobalUrlAlias`|`private $resource`</br>`private $path`</br>`private $languageCode`</br>`private $forwarding`</br>`private $alwaysAvailable`</br>`URLAlias $urlAlias`| +|`BeforeCreateUrlAliasEvent`|`URLAliasService::createUrlAlias`|`Location $location`</br>`private $path`</br>`private $languageCode`</br>`private $forwarding`</br>`private $alwaysAvailable`</br>`URLAlias|null $urlAlias`| +|`CreateUrlAliasEvent`|`URLAliasService::createUrlAlias`|`Location $location`</br>`private $path`</br>`private $languageCode`</br>`private $forwarding`</br>`private $alwaysAvailable`</br>`URLAlias $urlAlias`| +|`BeforeRefreshSystemUrlAliasesForLocationEvent`|`URLAliasService::refreshSystemUrlAliasesForLocation`|`Location $location`| +|`RefreshSystemUrlAliasesForLocationEvent`|`URLAliasService::refreshSystemUrlAliasesForLocation`|`Location $location`| +|`BeforeRemoveAliasesEvent`|`URLAliasService::removeAliases`|`array $aliasList`| +|`RemoveAliasesEvent`|`URLAliasService::removeAliases`|`array $aliasList`| + +## URL wildcards + +The following events are dispatched when creating and managing [URL wildcards](../../url_management.md#url-wildcards). + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateEvent`|`URLWildcardService::create`|`private $sourceUrl`</br>`private $destinationUrl`</br>`private $forward`</br>`URLWildcard|null $urlWildcard`| +|`CreateEvent`|`URLWildcardService::create`|`private $sourceUrl`</br>`private $destinationUrl`</br>`private $forward`</br>`URLWildcard $urlWildcard`| +|`BeforeUpdateEvent`|`URLWildcardService::update`|`URLWildcard $urlWildcard`</br>`URLWildcardUpdateStruct $updateStruct`| +|`UpdateEvent`|`URLWildcardService::update`|`URLWildcard $urlWildcard`</br>`URLWildcardUpdateStruct $updateStruct`| +|`BeforeTranslateEvent`|`URLWildcardService::translate`|`private $url`</br>`URLWildcardTranslationResult|null $result`| +|`TranslateEvent`|`URLWildcardService::translate`|`private $url`</br>`URLWildcardTranslationResult $result`| +|`BeforeRemoveEvent`|`URLWildcardService::remove`|`URLWildcard $urlWildcard`| +|`RemoveEvent`|`URLWildcardService::remove`|`URLWildcard $urlWildcard`| diff --git a/docs/guide/repository/event_reference/user_events.md b/docs/guide/repository/event_reference/user_events.md new file mode 100644 index 0000000000..5d74c5769d --- /dev/null +++ b/docs/guide/repository/event_reference/user_events.md @@ -0,0 +1,45 @@ +--- +description: Events that are triggered when working with users and User Groups. +--- + +# User events + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateUserEvent`|`UserService::createUser`|`UserCreateStruct $userCreateStruct`</br>`array $parentGroups`</br>`User|null $user`| +|`CreateUserEvent`|`UserService::createUser`|`UserCreateStruct $userCreateStruct`</br>`array $parentGroups`</br>`User $user`| +|`BeforeUpdateUserEvent`|`UserService::updateUser`|`User $user`</br>`UserUpdateStruct $userUpdateStruct`</br>`User|null $updatedUser`| +|`UpdateUserEvent`|`UserService::updateUser`|`User $user`</br>`UserUpdateStruct $userUpdateStruct`</br>`User $updatedUser`| +|`BeforeDeleteUserEvent`|`UserService::deleteUser`|`User $user`</br>`array|null $locations`| +|`DeleteUserEvent`|`UserService::deleteUser`|`User $user`</br>`array $locations`| + +## User Groups + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeCreateUserGroupEvent`|`UserService::createUserGroup`|`UserGroupCreateStruct $userGroupCreateStruct`</br>`UserGroup $parentGroup`</br>`UserGroup|null $userGroup`| +|`CreateUserGroupEvent`|`UserService::createUserGroup`|`UserGroupCreateStruct $userGroupCreateStruct`</br>`UserGroup $parentGroup`</br>`UserGroup $userGroup`| +|`BeforeUpdateUserGroupEvent`|`UserService::updateUserGroup`|`UserGroup $userGroup`</br>`UserGroupUpdateStruct $userGroupUpdateStruct`</br>`UserGroup|null $updatedUserGroup`| +|`UpdateUserGroupEvent`|`UserService::updateUserGroup`|`UserGroup $userGroup`</br>`UserGroupUpdateStruct $userGroupUpdateStruct`| +|`BeforeDeleteUserGroupEvent`|`UserService::deleteUserGroup`|`UserGroup $userGroup`</br>`array|null $locations`| +|`DeleteUserGroupEvent`|`UserService::deleteUserGroup`|`UserGroup $userGroup`</br>`array $locations`| +|`BeforeMoveUserGroupEvent`|`UserService::moveUserGroup`|`UserGroup $userGroup`</br>`UserGroup $newParent`| +|`MoveUserGroupEvent`|`UserService::moveUserGroup`|`UserGroup $userGroup`</br>`UserGroup $newParent`| + +## Assigning to User Groups + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeAssignUserToUserGroupEvent`|`UserService::assignUserToUserGroup`|`User $user`</br>`UserGroup $userGroup`| +|`AssignUserToUserGroupEvent`|`UserService::assignUserToUserGroup`|`User $user`</br>`UserGroup $userGroup`| +|`BeforeUnAssignUserFromUserGroupEvent`|`UserService::unAssignUserFromUserGroup`|`User $user`</br>`UserGroup $userGroup`| +|`UnAssignUserFromUserGroupEvent`|`UserService::unAssignUserFromUserGroup`|`User $user`</br>`UserGroup $userGroup`| + +## Updating User data + +| Event | Dispatched by | Properties | +|---|---|---| +|`BeforeUpdateUserPasswordEvent`|`UserService::updateUserPassword`|`User $user`</br>`string $newPassword`</br>`User|null $updatedUser`| +|`UpdateUserPasswordEvent`|`UserService::updateUserPassword`|`User $user`</br>`string $newPassword`</br>`User $updatedUser`| +|`BeforeUpdateUserTokenEvent`|`UserService::updateUserToken`|`User $user`</br>`UserTokenUpdateStruct $userTokenUpdateStruct`</br>`User|null $updatedUser`| +|`UpdateUserTokenEvent`|`UserService::updateUserToken`|`User $user`</br>`UserTokenUpdateStruct $userTokenUpdateStruct`</br>`User $updatedUser`| diff --git a/docs/guide/request_lifecycle.md b/docs/guide/request_lifecycle.md new file mode 100644 index 0000000000..20b0bbad14 --- /dev/null +++ b/docs/guide/request_lifecycle.md @@ -0,0 +1,255 @@ +--- +description: See the lifecycle of an HTTP request in Ibexa DXP, from request to response. +--- + +# Request lifecycle: from request to response + + +## Beginning of HTTP request + +When entering the server infrastructure, the HTTP request can be handled by several component such as a firewall, a load balancer, or a reverse proxy before arriving on the web server itself. + +For an overview of what happens on a reverse proxy like Varnish or Fastly, see [Context-aware HTTP cache / Request lifecycle](cache/context_aware_cache.md#request-lifecycle). + +When arriving at a web server, the request is filtered by Apache Virtual Host, Nginx Server Blocks or equivalent. There, requests of static resources are separated from requests to PHP interpreter. + +As [[= product_name =]] is a Symfony application, the handling of requests starts like in Symfony (see [Symfony and HTTP Fundamentals](https://symfony.com/doc/current/introduction/http_fundamentals.html)). + +If the HTTP request is to be treated by [[= product_name =]], it goes to the `public/index.php` of the [Symfony Front Controller](https://symfony.com/doc/current/configuration/front_controllers_and_kernel.html#the-front-controller). + +The front controller transforms the HTTP request into a PHP [`Request` object](https://symfony.com/doc/current/introduction/http_fundamentals.html#symfony-request-object) and passes it to Symfony's Kernel to get a [`Response` object](https://symfony.com/doc/current/introduction/http_fundamentals.html#symfony-response-object) that is transformed and sent back as an HTTP response. + +The schemas start with a regular `Request` object from a browser that enters Symfony and [[= product_name =]]. There is no ESI, no REST, and no GraphQL request performed. + + +## Lifecycle flowcharts + +### Concept flowchart + +The chart below introduces the logic of the request treatment. + +![Simplified request lifecycle flowchart](img/request_lifecycle_concept.png) + +### Kernel events flowchart + +The following chart shows events, listeners and attributes added to the request or its wrapping event object. + +![Detailed request lifecycle flowchart organised around kernel events](img/request_lifecycle_events.png){: width="741" height="2061" style="max-height: none;"} + +This schema is described below event by event. + +!!! tip + + To list all listeners that listen to an event, run `php bin/console debug:event-dispatcher <event.name>`, for example: + + ```bash + php bin/console debug:event-dispatcher kernel.request + ``` + + To view details of a service (including class, arguments and tags), run `php bin/console debug:container --show-arguments <service.name>`, for example: + + ```bash + php bin/console debug:container --show-arguments ezpublish.siteaccess_match_listener` + ``` + + To list all services with a specific tag, run `php bin/console debug:container --tag=<tag>`, for example: + + ```bash + php bin/console debug:container --tag=router + ``` + + +## Kernel's request event + +When the request enters the Symfony's kernel (and goes underneath the [`HttpKernel`](https://symfony.com/doc/current/components/http_kernel.html), `http_kernel`), a `kernel.request` event (`KernelEvents::REQUEST`) is dispatched. +Several listeners are called in decreasing priority. + +### SiteAccess matching + +The [`FragmentListener`](https://github.com/symfony/http-kernel/blob/5.3/EventListener/FragmentListener.php) (priority 48) handles the request first, and then it passes to the `ezpublish.siteaccess_match_listener` service (priority 45). +This service can be either: + +- purely the `SiteAccessMatchListener` or +- its `UserContextSiteAccessMatchSubscriber` decoration when HTTP cache is used. + +The `ezpublish.siteaccess_match_listener` service: + +- finds the current SiteAccess using the `SiteAccess\Router` (`ezpublish.siteaccess_router`) regarding the [SiteAccess Matching configurations](multisite/siteaccess_matching.md), +- adds the current SiteAccess to the `Request` object's **`siteaccess`** attribute, +- then dispatches the `ezpublish.siteaccess` event (`MVCEvents::SITEACCESS`). + +The `SiteAccessListener` (`ezpublish.siteaccess_listener`) subscribes to this `ezpublish.siteaccess` event with top priority (priority 255). +The `SiteAccessListener` adds the **`semanticPathinfo`** attribute, the path without SiteAccess indications ([`URIElement`](multisite/siteaccess_matching.md#urielement), [`URIText`](multisite/siteaccess_matching.md#uritext), +or [`Map\URI`](multisite/siteaccess_matching.md#mapuri) implementing the `URILexer` interface) to the request. + +### Routing + +Finally, the `Symfony\Component\HttpKernel\EventListener\RouterListener` (`router_listener`) (priority 32), which also listens to the `kernel.request` event, +calls `eZ\Publish\Core\MVC\Symfony\Routing\ChainRouter::matchRequest` and adds its returned parameters to the request. + +#### `ChainRouter` + +The [`ChainRouter`](https://symfony.com/doc/current/cmf/components/routing/chain.html) is a Symfony Content Management Framework (CMF) component. [[= product_name =]] makes it a service named `ezpublish.chain_router`. +It has a collection of prioritized routers where to find one matching the request. +The `ChainRouter` router collection is built by the `ChainRoutingPass`, collecting the services tagged `router`. +The `DefaultRouter` is always added to the collection with top priority (priority 255). + +#### `DefaultRouter` + +`DefaultRouter` (`router.default`): +The `DefaultRouter` tries to match the `semanticPathinfo` against routes, close to [the way pure Symfony does](https://symfony.com/doc/current/routing.html), by extending and using `Symfony\Component\Routing\Router`. +If a route matches, the controller associated with it is responsible for building a `View` or `Response` object. + +### `UrlWildcardRouter` + +`UrlWildcardRouter` (`ezpublish.urlwildcard_router`): +If [URL Wildcards](url_management.md#url-wildcards) have been enabled, then the `URLWildcardRouter` is the next router tried. +If a wildcard matches, the request's `semanticPathinfo` is updated and the router throws a `ResourceNotFoundException` to continue with the `ChainRouter` collection's next entry. + +### `UrlAliasRouter` + +`UrlAliasRouter` (`ezpublish.urlalias_router`): +This router uses the `UrlAliasService` to associate the `semanticPathinfo` to a Location. +If it finds a Location, the request receives the attributes **`locationId`** and **`contentId`**, **`viewType`** is set to `full`, and the **`_controller`** is set to `ez_content:viewAction` for now. + +The `locale_listener` (priority 16) sets the request's **`_locale`** attribute. + +!!! note "Permission control" + + Another `kernel.request` event listener is the `EzSystems\EzPlatformAdminUi\EventListener\RequestListener` (priority 13). + When a route gets a `siteaccess_group_whitelist` parameter, this listener checks that the current SiteAccess is in one of the listed groups. + For example, the Back Office sets an early protection of its routes by passing them a `siteaccess_group_whitelist` containing only the `admin_group`. + +Now, when the `Request` knows its controller, the `HttpKernel` dispatches the `kernel.controller` event. + + +## Kernel's controller event + +### View building and matching + +When HttpKernel dispatches the `kernel.controller` event, the following things happen. + +Listening to `kernel.controller`, the `ViewControllerListener` (`ezpublish.view_controller_listener`) (priority 10) checks if the `_controller` request attribute is associated with a `ViewBuilder` (a service tagged `ibexa.view_builder`) in the `ViewBuilderRegistry` (`ezpublish.view_builder.registry`). +The `ContentViewBuilder` (`ezpublish.view_builder.content`) matches on controller starting with `ez_content:` (see `eZ\Publish\Core\MVC\Symfony\View\Builder\ContentViewBuilder::matches`). +The `ContentViewBuilder` builds a `ContentView`. + +First, the `ContentViewBuilder` loads the `Location` and the `Content`, and adds them to the `ContentView` object. + +!!! caution "Permission control" + + `content/read` and/or `content/view_embed` permissions are controlled during this `ContentView` building. + +Then, the `ContentViewBuilder` passes the `ContentView` to its `View\Configurator` (`ezpublish.view.configurator`). +It's implemented by the `View\Configurator\ViewProvider` and its `View\Provider\Registry`. This registry receives the services tagged `ezpublish.view_provider` thanks to the `ViewProviderPass`. +Among the view providers, the services using the `eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured` have an implementation of the `MatcherFactoryInterface` (`ezpublish.content_view.matcher_factory`). +Through service decoration and class inheritance, the `ClassNameMatcherFactory` is responsible for the [view matching](content_rendering/templates/template_configuration.md#view-rules-and-matching). +The `View\Configurator\ViewProvider` uses the matched view rule to add possible **`templateIdentifier`** and **`controllerReference`** to the `ContentView` object. + +The `ViewControllerListener` adds the ContentView to the `Request` as the **`view`** attribute. +The `ViewControllerListener` eventually updates the request's `_controller` attribute with the `ContentView`'s `controllerReference`. + +The `HttpKernel` then dispatches a `kernel.controller_arguments` (`KernelEvents::CONTROLLER_ARGUMENTS`) but nothing from [[= product_name =]] is listening to it. + + +## Controller execution + +The `HttpKernel` extracts from the request the controller and the arguments to pass to the controller. [Argument resolvers](https://symfony.com/doc/current/controller/argument_value_resolver.html) work in a way similar to autowiring. +The `HttpKernel` executes the controller with those arguments. + +As a reminder, the controller and its argument can be: + +- A controller set by the matched route and the request as its argument. +- The default `ez_content:viewAction` controller and a `ContentView` as its argument. +- A [custom controller](content_rendering/queries_and_controllers/controllers.md) set by the matched view rule and a `View` or the request as its argument (most likely a `ContentView` but there is no restriction). + +!!! caution "Permission control" + + See [Permissions for custom controller](permissions.md#permissions-for-custom-controllers). + + +## Kernel's view event and `ContentView` rendering + +If the controller returns something other than `Response`, the `HttpKernel` dispatches a `kernel.view` event (`KernelEvents::VIEW`). +In the case of a URL Alias, the controller most likely returns a ContentView. +The `ViewRendererListener` (`ezpublish.view.renderer_listener`) uses the `ContentView` and the `TemplateRenderer` (`ezpublish.view.template_renderer`) to get the content of the `Response` and attach this new `Response` to the event. +The `HttpKernel` retrieves the response attached to the event and continues. + + +## Kernel's response event and `Response` sending + +The `HttpKernel` sends a `kernel.response` event (`KernelEvents::RESPONSE`). For example, if HTTP cache is used, response's headers may be enhanced. + +The `HttpKernel` sends a `kernel.finish_request` event (`KernelEvents::FINISH_REQUEST`). The `VerifyUserPoliciesRequestListener` (`siso_core.verify_user_policies_request_listener`) (priority 100) filters routes on its policy configuration. + +!!! caution "Permission control" + + See [Permissions for routes](permissions.md#permissions-for-routes). + +Finally, the `HttpKernel` send the response. + +If an exception occurs during this chain of events, the `HttpKernel` sends a `kernel.exception` and tries to get a `Response` from its listeners. + +The `HttpKernel` sends the last `kernel.terminate` event (`KernelEvents::TERMINATE`). For example, the `BackgroundIndexingTerminateListener` (`ezpublish.search.background_indexer`) (priority 0) removes from the `SearchService` index possible content existing in the index but not in the database. + + +## Summary + +### Summary of events and services + +* event=`kernel.request` + - 45:`ezpublish.siteaccess_match_listener` + - `ezpublish.siteaccess_router` + - event=`ezpublish.siteaccess` + - 255:`ezpublish.siteaccess_listener` + - 32:`router_listener` + - `ezpublish.chain_router` + - tag=`router` + - `router.default` + - `ezpublish.urlwildcard_router` + - `ezpublish.urlalias_router` + - 16:`locale_listener` + - 13:`EzSystems\EzPlatformAdminUi\EventListener\RequestListener` +* event=`kernel.controller` + - 10:`ezpublish.view_controller_listener` + - `ezpublish.view_builder.registry` + - tag=`ibexa.view_builder` + - `ezpublish.view_builder.content` + - `ezpublish.view.configurator` +* event=`kernel.controller_arguments` +* event=`kernel.view` + - 0:`ezpublish.view.renderer_listener` + - `ezpublish.view.template_renderer` +* event=`kernel.response` +* event=`kernel.finish_request` + - 100:`siso_core.verify_user_policies_request_listener` +* event=`kernel.terminate` + - 0:`ezpublish.search.background_indexer` + +### Examples request attributes timeline + +| Event | Service | Request attribute | Example | +| ----------------------- | ------------------------------------- | ------------------- | ------------- | +| | http_kernel | pathInfo | /en/about | +| kernel.request | ezpublish.siteaccess_match_listener | siteaccess | en | +| ezpublish.siteaccess | ezpublish.siteaccess_listener | semanticPathinfo | /about | +| kernel.request | router.default | _route | N/A | +| kernel.request | router.default | _controller | N/A | +| kernel.request | ezpublish.urlalias_router | _route | ez_urlalias | +| kernel.request | ezpublish.urlalias_router | _controller | <strong>ez_content:</strong>viewAction +| kernel.request | ezpublish.urlalias_router | viewType | full | +| kernel.request | ezpublish.urlalias_router | contentId | 1 | +| kernel.request | ezpublish.urlalias_router | locationId | 42 | +| kernel.request | locale_listener | _locale | en_GB | +| kernel.controller | ezpublish.view_builder.content | view.content | Content | +| kernel.controller | ezpublish.view_builder.content | view.location | Location | +| kernel.controller | ezpublish.view.configurator | view.templateIdentifier | @EzPublishCore/default/content/full.html.twig | +| kernel.controller | ezpublish.view.configurator | view.controllerReference | null | +| kernel.controller | ezpublish.view_controller_listener | view | ContentView | +| kernel.controller | ezpublish.view_controller_listener | _controller | ez_content:viewAction | +| (controller execution) | http_kernel | | ContentView | +| kernel.view | ezpublish.view.renderer_listener | response | Response | + + +## End of HTTP response + +The web server outputs the HTTP response. Depending on the architecture, few things may still occur. For example, Varnish or Fastly can take specific headers into account when setting the cache or serving it. diff --git a/docs/guide/retrieving_root_location.md b/docs/guide/retrieving_root_location.md deleted file mode 100644 index 777fee6d11..0000000000 --- a/docs/guide/retrieving_root_location.md +++ /dev/null @@ -1,64 +0,0 @@ -# Retrieving root Location - -The root Location can be a starting point for API queries, or even links to home page. - -By default, the root Location ID is `2`, but the best practice is to retrieve it dynamically. -This is because [[= product_name =]] can be used for [multisite development](multisite.md) and the root Location can vary. -The Location can also be changed by configuration. - -### Retrieving root Location ID - -Root location ID can be retrieved from [ConfigResolver](config_dynamic.md#configresolver). -The parameter name is `content.tree_root.location_id`. - -``` php -<?php - -namespace App\Controller; - -use eZ\Bundle\EzPublishCoreBundle\Controller; - -class DefaultController extends Controller -{ - public function fooAction() - { - // ... -  - $rootLocationId = $this->getConfigResolver()->getParameter( 'content.tree_root.location_id' ); -  - // ... - } -} -``` - -### Retrieving the root Location - -#### From template - -Root Location is exposed in the [global Twig helper](content_rendering.md#twig-helper). - -``` html+twig -<a href="{{ ez_path( ezplatform.rootLocation ) }}" title="Link to homepage">Home page</a> -``` - -#### From controller - -``` php -<?php - -namespace App\Controller; - -use eZ\Bundle\EzPublishCoreBundle\Controller; - -class DefaultController extends Controller -{ - public function fooAction() - { - // ... - - $rootLocation = $this->getRootLocation(); - - // ... - } -} -``` diff --git a/docs/guide/routereference.md b/docs/guide/routereference.md deleted file mode 100644 index 87c69b0b21..0000000000 --- a/docs/guide/routereference.md +++ /dev/null @@ -1,126 +0,0 @@ -# Using RouteReference - -Sometimes, when generating links to a resource, you need to modify the default router's behavior. - -Example use cases are: - -- Language switch links -- Download links -- Passing a Content item instead of a Location (and using its `mainLocationId`) - -`RouteReference` represents a route (to a Location object, a declared route, etc.) with its parameters and can be passed to the `Router` for generating a link. -`RouteReference` works like [Symfony's `ControllerReference`](http://api.symfony.com/4.3/Symfony/Component/HttpKernel/Controller/ControllerReference.html) for sub-requests. - -The advantage of a `RouteReference` is that its parameters can be modified later, -and then passed to the router (e.g. to generate a link to the same location in several different languages). - -Furthermore, the `RouteReference` generation process can be extended to fit specific needs. - -## Twig - -Prototype: - -``` html+twig -ez_route( [routing_resource[, parameters_hash]] ) -``` - -- `routing_resource` can be any valid resource (route name, Location object, etc.). -If omitted (`null`), the current route will be taken into account. -- `parameters_hash` is a hash with arbitrary key/values. It will be passed to the router in the end - -Minimal usage is pretty straightforward: - -``` html+twig -{# With a declared route. #} -{% set routeRef = ez_route( "my_route" ) %} - -{# With a Location, given that the "location" variable is a valid Location object. #} -{% set routeRef = ez_route( location ) %} - -{# Then pass the routeRef variable to ez_path() to generate the link #} -<a href="{{ ez_path( routeRef ) }}">My link</a> -``` - -Passing parameters and playing with the RouteReference: - -``` html+twig -{% set routeRef = ez_route( "my_route", {"some": "thing"} ) %} - -{# You can then add parameters further on #} -{% do routeRef.set( "foo", ["bar", "baz"] ) %} - -{# Or even modify the route resource itself #} -{% do routeRef.setRoute( "another_route" ) %} - -<a href="{{ ez_path( routeRef ) }}">My link</a> -``` - -## PHP - -You can generate links based on a `RouteReference` from PHP too, with the `RouteReferenceGenerator` service. - -Assuming you're in a controller: - -``` php -/** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface $routeRefGenerator */ -$routeRefGenerator = $this->get( 'ezpublish.route_reference.generator' ); -$routeRef = $routeRefGenerator->generate( 'my_route', [ 'some' => 'thing' ]); -$routeRef->set( 'foo', [ 'bar', 'baz' ] ); -$routeRef->setRoute( 'another_route' ); - -$link = $this->generateUrl($routeRef); -``` - -## Extending the RouteReference generation process - -When generating the route reference, the `RouteReferenceGenerator` service sends the `MVCEvents::ROUTE_REFERENCE_GENERATION` (`ezpublish.routing.reference_generation`) event. -This event can be listened to in order to modify the final route reference -(adding/changing parameters, changing the route name, etc.). - -All listeners receive the `eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent` object, -which contains the current request object and the route reference. - -``` php -<?php - -namespace App\EventListener; - -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class MyRouteReferenceListener implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return [ - MVCEvents::ROUTE_REFERENCE_GENERATION => 'onRouteReferenceGeneration' - ]; - } - - public function onRouteReferenceGeneration(RouteReferenceGenerationEvent $event) - { - $routeReference = $event->getRouteReference(); - $request = $event->getRequest(); - - // Let's say you want to change the route name if "some_parameter" param is present - if ( $routeReference->has( 'some_parameter' ) ) - { - $routeReference->setRoute( 'a_specific_route' ); - // Remove "some_parameter", as you don't need it any more - $routeReference->remove( 'some_parameter' ); - // Add another parameter - $routeReference->set( 'another_parameter', [ 'parameters', 'are', 'fun' ] ); - } - } -} -``` - -Service declaration (in `config/services.yaml`): - -``` yaml -services: - App\EventListener\MyRouteReferenceListener: - tags: - - { name: kernel.event_subscriber } -``` diff --git a/docs/guide/search/aggregation_reference.md b/docs/guide/search/aggregation_reference.md deleted file mode 100644 index 9eb524989a..0000000000 --- a/docs/guide/search/aggregation_reference.md +++ /dev/null @@ -1,43 +0,0 @@ -# Aggregation reference - -[Aggregation](../../api/public_php_api_search.md#aggregation) is used to group search results into categories. - -There are three types of aggregations: - -- Term aggregations group by value and count object in each group -- Range aggregations count values in specified ranges -- Stats aggregations compute stats over numeric fields: minimum, average and maximum value, count and sum of values - -## Content aggregations - -|Name | Type | Based on| -|---|---|---| -|[ContentTypeTermAggregation](aggregation_reference/contenttypeterm_aggregation.md) | Term | Content Type | -|[ContentTypeGroupTermAggregation](aggregation_reference/contenttypegroupterm_aggregation.md) | Term | Content Type group | -|[DateMetadataRangeAggregation](aggregation_reference/datemetadatarange_aggregation.md) | Range | Date metadata | -|[LanguageTermAggregation](aggregation_reference/languageterm_aggregation.md) | Term | Content language | -|[ObjectStateTermAggregation](aggregation_reference/objectstateterm_aggregation.md) | Term | Object state | -|[RawRangeAggregation](aggregation_reference/rawrange_aggregation.md) | Range | Search index field | -|[RawStatsAggregation](aggregation_reference/rawstats_aggregation.md) | Stats | Search index field | -|[RawTermAggregation](aggregation_reference/rawterm_aggregation.md) | Term | Search index field | -|[SectionTermAggregation](aggregation_reference/sectionterm_aggregation.md) | Term | Section | -|[SubtreeTermAggregation](aggregation_reference/subtreeterm_aggregation.md) | Term | Location subtree path | -|[UserMetadataTermAggregation](aggregation_reference/usermetadataterm_aggregation.md) | Term | Content owner/owner group or modifier | -|[VisibilityTermAggregation](aggregation_reference/visibilityterm_aggregation.md) | Term | Content/Location visibility | - -## Field aggregations - -|Name | Type | Based on Field| -|---|---|---| -|[AuthorTermAggregation](aggregation_reference/authorterm_aggregation.md) | Term | [Author](../../api/field_type_reference.md#author-field-type) | -|[CheckboxTermAggregation](aggregation_reference/checkboxterm_aggregation.md) | Term |[Checkbox](../../api/field_type_reference.md#checkbox-field-type)| -|[CountryTermAggregation](aggregation_reference/countryterm_aggregation.md) | Term |[Country](../../api/field_type_reference.md#country-field-type)| -|[DateRangeAggregation](aggregation_reference/daterange_aggregation.md) | Range |[Date](../../api/field_type_reference.md#date-field-type)| -|[DateTimeRangeAggregation](aggregation_reference/datetimerange_aggregation.md) | Range |[DateTime](../../api/field_type_reference.md#dateandtime-field-type)| -|[FloatRangeAggregation](aggregation_reference/floatrange_aggregation.md) | Range |[Float](../../api/field_type_reference.md#float-field-type)| -|[FloatStatsAggregation](aggregation_reference/floatstats_aggregation.md) | Stats |[Float](../../api/field_type_reference.md#float-field-type)| -|[IntegerRangeAggregation](aggregation_reference/integerrange_aggregation.md) | Range |[Integer](../../api/field_type_reference.md#integer-field-type)| -|[IntegerStatsAggregation](aggregation_reference/integerstats_aggregation.md) | Stats |[Integer](../../api/field_type_reference.md#integer-field-type)| -|[KeywordTermAggregation](aggregation_reference/keywordterm_aggregation.md) | Term |[Keyword](../../api/field_type_reference.md#keyword-field-type)| -|[SelectionTermAggregation](aggregation_reference/selectionterm_aggregation.md) | Term |[Selection](../../api/field_type_reference.md#selection-field-type)| -|[TimeRangeAggregation](aggregation_reference/timerange_aggregation.md) | Range |[Time](../../api/field_type_reference.md#time-field-type)| diff --git a/docs/guide/search/aggregation_reference/aggregation_reference.md b/docs/guide/search/aggregation_reference/aggregation_reference.md new file mode 100644 index 0000000000..07aadedc45 --- /dev/null +++ b/docs/guide/search/aggregation_reference/aggregation_reference.md @@ -0,0 +1,48 @@ +--- +description: Aggregations help fine-tune search for content and Locations by grouping results into categories. +--- + +# Aggregation reference + +[Aggregation](../../../api/public_php_api_search.md#aggregation) is used to group search results into categories. + +There are three types of aggregations: + +- Term aggregations group by value and count object in each group +- Range aggregations count values in specified ranges +- Stats aggregations compute stats over numeric fields: minimum, average and maximum value, count and sum of values + +## Content aggregations + +|Name | Type | Based on| +|---|---|---| +|[ContentTypeTermAggregation](contenttypeterm_aggregation.md) | Term | Content Type | +|[ContentTypeGroupTermAggregation](contenttypegroupterm_aggregation.md) | Term | Content Type group | +|[DateMetadataRangeAggregation](datemetadatarange_aggregation.md) | Range | Date metadata | +|[LocationChildrenTermAggregation](locationchildrenterm_aggregation.md) | Term | Children on a Location | +|[LanguageTermAggregation](languageterm_aggregation.md) | Term | Content language | +|[ObjectStateTermAggregation](objectstateterm_aggregation.md) | Term | Object state | +|[RawRangeAggregation](rawrange_aggregation.md) | Range | Search index field | +|[RawStatsAggregation](rawstats_aggregation.md) | Stats | Search index field | +|[RawTermAggregation](rawterm_aggregation.md) | Term | Search index field | +|[SectionTermAggregation](sectionterm_aggregation.md) | Term | Section | +|[SubtreeTermAggregation](subtreeterm_aggregation.md) | Term | Location subtree path | +|[UserMetadataTermAggregation](usermetadataterm_aggregation.md) | Term | Content owner/owner group or modifier | +|[VisibilityTermAggregation](visibilityterm_aggregation.md) | Term | Content/Location visibility | + +## Field aggregations + +|Name | Type | Based on Field| +|---|---|---| +|[AuthorTermAggregation](authorterm_aggregation.md) | Term | [Author](../../../api/field_types_reference/authorfield.md) | +|[CheckboxTermAggregation](checkboxterm_aggregation.md) | Term |[Checkbox](../../../api/field_types_reference/checkboxfield.md)| +|[CountryTermAggregation](countryterm_aggregation.md) | Term |[Country](../../../api/field_types_reference/countryfield.md)| +|[DateRangeAggregation](daterange_aggregation.md) | Range |[Date](../../../api/field_types_reference/datefield.md)| +|[DateTimeRangeAggregation](datetimerange_aggregation.md) | Range |[DateTime](../../../api/field_types_reference/dateandtimefield.md)| +|[FloatRangeAggregation](floatrange_aggregation.md) | Range |[Float](../../../api/field_types_reference/floatfield.md)| +|[FloatStatsAggregation](floatstats_aggregation.md) | Stats |[Float](../../../api/field_types_reference/floatfield.md)| +|[IntegerRangeAggregation](integerrange_aggregation.md) | Range |[Integer](../../../api/field_types_reference/integerfield.md)| +|[IntegerStatsAggregation](integerstats_aggregation.md) | Stats |[Integer](../../../api/field_types_reference/integerfield.md)| +|[KeywordTermAggregation](keywordterm_aggregation.md) | Term |[Keyword](../../../api/field_types_reference/keywordfield.md)| +|[SelectionTermAggregation](selectionterm_aggregation.md) | Term |[Selection](../../../api/field_types_reference/selectionfield.md)| +|[TimeRangeAggregation](timerange_aggregation.md) | Range |[Time](../../../api/field_types_reference/timefield.md)| diff --git a/docs/guide/search/aggregation_reference/authorterm_aggregation.md b/docs/guide/search/aggregation_reference/authorterm_aggregation.md index adb4260c2f..217dcd0058 100644 --- a/docs/guide/search/aggregation_reference/authorterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/authorterm_aggregation.md @@ -14,3 +14,5 @@ The Field-based [AuthorTermAggregation](https://github.com/ezsystems/ezplatform- $query = new Query(); $query->aggregations[] = new Aggregation\Field\AuthorTermAggregation('author', 'article', 'authors'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/checkboxterm_aggregation.md b/docs/guide/search/aggregation_reference/checkboxterm_aggregation.md index 4be588c59a..b886165716 100644 --- a/docs/guide/search/aggregation_reference/checkboxterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/checkboxterm_aggregation.md @@ -14,3 +14,5 @@ The Field-based [CheckboxTermAggregation](https://github.com/ezsystems/ezplatfor $query = new Query(); $query->aggregations[] = new Aggregation\Field\CheckboxTermAggregation('checkbox', 'article', 'enable_comments'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/contenttypegroupterm_aggregation.md b/docs/guide/search/aggregation_reference/contenttypegroupterm_aggregation.md index 98e03da557..4b2bf89d43 100644 --- a/docs/guide/search/aggregation_reference/contenttypegroupterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/contenttypegroupterm_aggregation.md @@ -12,3 +12,5 @@ The [ContentTypeGroupTermAggregation](https://github.com/ezsystems/ezplatform-ke $query = new Query(); $query->aggregations[] = new Aggregation\ContentTypeGroupTermAggregation('content_type_group'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/contenttypeterm_aggregation.md b/docs/guide/search/aggregation_reference/contenttypeterm_aggregation.md index cc97af300e..83555e6d92 100644 --- a/docs/guide/search/aggregation_reference/contenttypeterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/contenttypeterm_aggregation.md @@ -12,3 +12,5 @@ The [ContentTypeTermAggregation](https://github.com/ezsystems/ezplatform-kernel/ $query = new Query(); $query->aggregations[] = new Aggregation\ContentTypeTermAggregation('content_type'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/countryterm_aggregation.md b/docs/guide/search/aggregation_reference/countryterm_aggregation.md index c13e86975b..85fd5e63de 100644 --- a/docs/guide/search/aggregation_reference/countryterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/countryterm_aggregation.md @@ -14,3 +14,5 @@ The Field-based [CountryTermAggregation](https://github.com/ezsystems/ezplatform $query = new Query(); $query->aggregations[] = new Aggregation\Field\CountryTermAggregation('country', 'article', 'country'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/floatstats_aggregation.md b/docs/guide/search/aggregation_reference/floatstats_aggregation.md index abd2fbaad8..f697c47d0b 100644 --- a/docs/guide/search/aggregation_reference/floatstats_aggregation.md +++ b/docs/guide/search/aggregation_reference/floatstats_aggregation.md @@ -1,13 +1,13 @@ # FloatStatsAggregation The Field-based [FloatStatsAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php) aggregates search results by the value of the Float Field -and provides statistical information for the values including: +and provides statistical information for the values. You can use the provided getters to access the values: -- sum -- count of values -- minimum value -- maximum value -- average +- sum (`getSum()`) +- count of values (`getCount()`) +- minimum value (`getMin()`) +- maximum value (`getMax()`) +- average (`getAvg()`) ## Arguments diff --git a/docs/guide/search/aggregation_reference/integerstats_aggregation.md b/docs/guide/search/aggregation_reference/integerstats_aggregation.md index bf209c32c3..fe85e75df5 100644 --- a/docs/guide/search/aggregation_reference/integerstats_aggregation.md +++ b/docs/guide/search/aggregation_reference/integerstats_aggregation.md @@ -1,13 +1,13 @@ # IntegerStatsAggregation The Field-based [IntegerStatsAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php) aggregates search results by the value of the Integer Field -and provides statistical information for the values including: +and provides statistical information for the values. You can use the provided getters to access the values: -- sum -- count of values -- minimum value -- maximum value -- average +- sum (`getSum()`) +- count of values (`getCount()`) +- minimum value (`getMin()`) +- maximum value (`getMax()`) +- average (`getAvg()`) ## Arguments @@ -19,5 +19,6 @@ and provides statistical information for the values including: ``` php $query = new Query(); -$query->aggregations[] = new Aggregation\Field\IntegerRangeAggregation('integer', 'product', 'amount'); +$query->aggregations[] = new Aggregation\Field\IntegerStatsAggregation('integer', 'product', 'amount'); ``` + diff --git a/docs/guide/search/aggregation_reference/keywordterm_aggregation.md b/docs/guide/search/aggregation_reference/keywordterm_aggregation.md index e713bf209d..2da1f53916 100644 --- a/docs/guide/search/aggregation_reference/keywordterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/keywordterm_aggregation.md @@ -14,3 +14,5 @@ The Field-based [KeywordTermAggregation](https://github.com/ezsystems/ezplatform $query = new Query(); $query->aggregations[] = new Aggregation\Field\KeywordTermAggregation('keyword', 'article', 'tags'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/languageterm_aggregation.md b/docs/guide/search/aggregation_reference/languageterm_aggregation.md index fda81ded60..b4990d9f04 100644 --- a/docs/guide/search/aggregation_reference/languageterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/languageterm_aggregation.md @@ -12,3 +12,5 @@ The [LanguageTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blo $query = new Query(); $query->aggregations[] = new Aggregation\LanguageTermAggregation('language'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/locationchildrenterm_aggregation.md b/docs/guide/search/aggregation_reference/locationchildrenterm_aggregation.md new file mode 100644 index 0000000000..1f2a6540dc --- /dev/null +++ b/docs/guide/search/aggregation_reference/locationchildrenterm_aggregation.md @@ -0,0 +1,16 @@ +# LocationChildrenTermAggregation + +The [LocationChildrenTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/LocationChildrenTermAggregation.php) aggregates search results by the number of children of a Location. + +## Arguments + +- `name` - name of the Aggregation object + +## Example + +``` php +$query = new LocationQuery(); +$query->aggregations[] = new Aggregation\Location\LocationChildrenTermAggregation('location_children'); +``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/objectstateterm_aggregation.md b/docs/guide/search/aggregation_reference/objectstateterm_aggregation.md index 76289247be..6179a2ff86 100644 --- a/docs/guide/search/aggregation_reference/objectstateterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/objectstateterm_aggregation.md @@ -13,3 +13,5 @@ The [ObjectStateTermAggregation](https://github.com/ezsystems/ezplatform-kernel/ $query = new Query(); $query->aggregations[] = new Aggregation\Location\ObjectStateTermAggregation('object_state', 'ez_lock'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/rawstats_aggregation.md b/docs/guide/search/aggregation_reference/rawstats_aggregation.md index 90a2add0be..d4573d55f6 100644 --- a/docs/guide/search/aggregation_reference/rawstats_aggregation.md +++ b/docs/guide/search/aggregation_reference/rawstats_aggregation.md @@ -1,13 +1,13 @@ # RawStatsAggregation The [RawStatsAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php) aggregates search results by the value of the selected search index field -and provides statistical information for the values including: +and provides statistical information for the values. You can use the provided getters to access the values: -- sum -- count of values -- minimum value -- maximum value -- average +- sum (`getSum()`) +- count of values (`getCount()`) +- minimum value (`getMin()`) +- maximum value (`getMax()`) +- average (`getAvg()`) ## Arguments diff --git a/docs/guide/search/aggregation_reference/rawterm_aggregation.md b/docs/guide/search/aggregation_reference/rawterm_aggregation.md index 9ec4108104..406ad74bf6 100644 --- a/docs/guide/search/aggregation_reference/rawterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/rawterm_aggregation.md @@ -11,7 +11,7 @@ The [RawTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/mas !!! caution - To keep your project search engine independent, do not use the `RawRangeAggregation` Aggregation in production code. + To keep your project search engine independent, do not use the `RawTermAggregation` Aggregation in production code. Valid use cases are: testing, or temporary (one-off) tools. ## Example @@ -20,3 +20,5 @@ The [RawTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blob/mas $query = new Query(); $query->aggregations[] = new Aggregation\RawTermAggregation('content_per_content_type', 'content_type_id_id'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/sectionterm_aggregation.md b/docs/guide/search/aggregation_reference/sectionterm_aggregation.md index b7345c8a84..d6663b1a3c 100644 --- a/docs/guide/search/aggregation_reference/sectionterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/sectionterm_aggregation.md @@ -12,3 +12,5 @@ The [SectionTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blob $query = new Query(); $query->aggregations[] = new Aggregation\SectionTermAggregation('section'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/selectionterm_aggregation.md b/docs/guide/search/aggregation_reference/selectionterm_aggregation.md index c47a964ee1..78547292c9 100644 --- a/docs/guide/search/aggregation_reference/selectionterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/selectionterm_aggregation.md @@ -14,3 +14,5 @@ The Field-based [SelectionTermAggregation](https://github.com/ezsystems/ezplatfo $query = new Query(); $query->aggregations[] = new Aggregation\Field\SelectionTermAggregation('selection', 'article', 'select'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/subtreeterm_aggregation.md b/docs/guide/search/aggregation_reference/subtreeterm_aggregation.md index 4c7f1e72d0..62f7521e68 100644 --- a/docs/guide/search/aggregation_reference/subtreeterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/subtreeterm_aggregation.md @@ -13,3 +13,5 @@ The [SubtreeTermAggregation](https://github.com/ezsystems/ezplatform-kernel/blob $query = new Query(); $query->aggregations[] = new Aggregation\Location\SubtreeTermAggregation('pathstring', '/1/2/'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/usermetadataterm_aggregation.md b/docs/guide/search/aggregation_reference/usermetadataterm_aggregation.md index f08dc32c58..c748d38c6a 100644 --- a/docs/guide/search/aggregation_reference/usermetadataterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/usermetadataterm_aggregation.md @@ -12,3 +12,5 @@ The [UserMetadataTermAggregation](https://github.com/ezsystems/ezplatform-kernel $query = new Query(); $query->aggregations[] = new Aggregation\UserMetadataTermAggregation('user_metadata'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/aggregation_reference/visibilityterm_aggregation.md b/docs/guide/search/aggregation_reference/visibilityterm_aggregation.md index f9362e5a1a..2ab6c8a9ab 100644 --- a/docs/guide/search/aggregation_reference/visibilityterm_aggregation.md +++ b/docs/guide/search/aggregation_reference/visibilityterm_aggregation.md @@ -12,3 +12,5 @@ The [VisibilityTermAggregation](https://github.com/ezsystems/ezplatform-kernel/b $query = new Query(); $query->aggregations[] = new Aggregation\VisibilityTermAggregation('visibility'); ``` + +[[= include_file('docs/snippets/search_term_aggregation_settings.md') =]] diff --git a/docs/guide/search/criteria_reference/contenttypeid_criterion.md b/docs/guide/search/criteria_reference/contenttypeid_criterion.md index bae1e2995a..d4d2549483 100644 --- a/docs/guide/search/criteria_reference/contenttypeid_criterion.md +++ b/docs/guide/search/criteria_reference/contenttypeid_criterion.md @@ -1,6 +1,6 @@ # ContentTypeId Criterion -The [`ContentTypeIdentifier` Search Criterion](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeId.php) +The [`ContentTypeId` Search Criterion](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeId.php) searches for content based on the ID of its Content Type. ## Arguments diff --git a/docs/guide/search/criteria_reference/isuserbased_criterion.md b/docs/guide/search/criteria_reference/isuserbased_criterion.md index 99d9807800..c147e4668d 100644 --- a/docs/guide/search/criteria_reference/isuserbased_criterion.md +++ b/docs/guide/search/criteria_reference/isuserbased_criterion.md @@ -6,7 +6,7 @@ searches for content that plays the role of a User account. !!! note In the default setup only the User Content Type is treated as User accounts. - However, you can also [set other Content Types to be treated as such](../../config_repository.md#user-identifiers). + However, you can also [set other Content Types to be treated as such](../../configuration/config_repository.md#user-identifiers). ## Arguments diff --git a/docs/guide/search/criteria_reference/search_criteria_reference.md b/docs/guide/search/criteria_reference/search_criteria_reference.md new file mode 100644 index 0000000000..7fd2b87e3f --- /dev/null +++ b/docs/guide/search/criteria_reference/search_criteria_reference.md @@ -0,0 +1,67 @@ +--- +description: Search Criteria help define and fine-tune search queries for content and Locations. +--- + +# Search Criteria reference + +Search Criteria are filters for Content and Location Search and +[Repository filtering](../../../api/public_php_api_search.md#repository-filtering). + +Criteria can take some of the following arguments: + +- `target` - when the Criterion supports targeting a specific Field, example: `FieldDefinition` or Metadata identifier +- `value` - the value(s) to filter on, typically a scalar or array of scalars +- `operator` - constants on `Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator`: `IN`, `EQ`, `GT`, `GTE`, `LT`, `LTE`, `LIKE`, `BETWEEN`, `CONTAINS`. Most Criteria do not expose this and select `EQ` or `IN` depending on whether the value is scalar or an array. `IN` and `BETWEEN` always act on an array of values, while the other operators act on single scalar value +- `valueData` - additional value data, required by some Criteria, for instance `MapLocationDistance` + +Support and capabilities of individual Criteria can depend on the search engine. + +In the Legacy search engine, the field index/sort key column is limited to 255 characters by design. +Due to this storage limitation, searching content using the Country Field Type or Keyword when there are multiple values selected may not return all the expected results. + +## Search Criteria + +|Search Criterion|Search based on|Supported by| +|-----|-----|-----| +|[Ancestor](ancestor_criterion.md)|Whether the Content item is an ancestor of the provided Location|Content and Location Search; Filtering| +|[ContentId](contentid_criterion.md)|Content item's ID|Content and Location Search; Filtering| +|[ContentTypeGroupId](contenttypegroupid_criterion.md)|ID of the Content item's Content Type group|Content and Location Search; Filtering| +|[ContentTypeId](contenttypeid_criterion.md)|ID of the Content item's Content Type|Content and Location Search; Filtering| +|[ContentTypeIdentifier](contenttypeidentifier_criterion.md)|Identifier of the Content item's Content Type|Content and Location Search; Filtering| +|[DateMetadata](datemetadata_criterion.md)|The date when content was created or last modified|Content and Location Search; Filtering| +|[Depth](depth_criterion.md)|Location depth in the Content tree|Location Search, Filtering| +|[Field](field_criterion.md)|Content of one of Content item's Fields|Content and Location Search| +|[FieldRelation](fieldrelation_criterion.md)|Content items the content in question has Relations to|Content and Location Search| +|[FullText](fulltext_criterion.md)|Full text content of a Content item's Fields|Content and Location Search| +|[IsFieldEmpty](isfieldempty_criterion.md)|Whether a specified Field of a Content item is empty or not|Content and Location Search +|[IsMainLocation](ismainlocation_criterion.md)|Whether a Location is the main Location of a Content item|Location Search, Filtering| +|[IsUserBased](isuserbased_criterion.md)|Whether content represents a User account|Content and Location Search; Filtering| +|[IsUserEnabled](isuserenabled_criterion.md)|Whether a User account is enabled|Content and Location Search; Filtering| +|[LanguageCode](languagecode_criterion.md)|Whether a Content item is translated into the selected language|Content and Location Search; Filtering| +|[LocationId](locationid_criterion.md)|Location ID|Content and Location Search; Filtering| +|[LocationRemoteId](locationremoteid_criterion.md)|Location remote ID|Content and Location Search; Filtering| +|[MapLocationDistance](maplocationdistance_criterion.md)|Distance between the location contained in a MapLocation Field and the provided coordinates|Content and Location Search| +|[MatchAll](matchall_criterion.md)|Returns all search results|Content and Location Search; Filtering| +|[MatchNone](matchnone_criterion.md)|Returns no search results|Content and Location Search; Filtering| +|[ObjectStateId](objectstateid_criterion.md)|Object State ID|Content and Location Search; Filtering| +|[ObjectStateIdentifier](objectstateidentifier_criterion.md)|Object State Identifier|Content and Location Search; Filtering| +|[ParentLocationId](parentlocationid_criterion.md)|Location ID of a Content item's parent|Content and Location Search; Filtering| +|[Priority](priority_criterion.md)|Location priority|Location Search, Filtering| +|[RemoteId](remoteid_criterion.md)|Remote content ID|Content and Location Search; Filtering| +|[SectionId](sectionid_criterion.md)|ID of the Section content is assigned to|Content and Location Search; Filtering| +|[SectionIdentifier](sectionidentifier_criterion.md)|Identifier of the Section content is assigned to|Content and Location Search; Filtering| +|[Sibling](sibling_criterion.md)|Locations that are children of the same parent|Content and Location Search; Filtering| +|[Subtree](subtree_criterion.md)|Location subtree|Content and Location Search; Filtering| +|[UserEmail](useremail_criterion.md)|Email address of a User account|Content and Location Search; Filtering| +|[UserId](userid_criterion.md)|User ID|Content and Location Search; Filtering| +|[UserLogin](userlogin_criterion.md)|User login|Content and Location Search; Filtering| +|[UserMetadata](usermetadata_criterion.md)|The creator or modifier of a Content item|Content and Location Search; Filtering| +|[Visibility](visibility_criterion.md)|Whether the Content item is visible or not|Content and Location Search; Filtering| + +### Logical operators + +|Search Criterion|Search based on|Supported by| +|-----|-----|-----| +|[LogicalAnd](logicaland_criterion.md)|Implements a logical AND Criterion. It matches if ALL of the provided Criteria match.|Content and Location Search; Filtering| +|[LogicalNot](logicalnot_criterion.md)|Implements a logical NOT Criterion. It matches if the provided Criterion doesn't match.|Content and Location Search; Filtering| +|[LogicalOr](logicalor_criterion.md)|Implements a logical OR Criterion. It matches if at least one of the provided Criteria matches.|Content and Location Search; Filtering| diff --git a/docs/guide/search/criteria_reference/useremail_criterion.md b/docs/guide/search/criteria_reference/useremail_criterion.md index d2932229bb..26925d80fb 100644 --- a/docs/guide/search/criteria_reference/useremail_criterion.md +++ b/docs/guide/search/criteria_reference/useremail_criterion.md @@ -10,7 +10,7 @@ searches for content based on the email assigned to the User account. ## Limitations -The `UserEmail` Criterion is not available in Elastic search engine. +Solr search engine and Elasticsearch support IN and EQ operators only. ## Example diff --git a/docs/guide/search/criteria_reference/userid_criterion.md b/docs/guide/search/criteria_reference/userid_criterion.md index 8c957ec389..1974411189 100644 --- a/docs/guide/search/criteria_reference/userid_criterion.md +++ b/docs/guide/search/criteria_reference/userid_criterion.md @@ -1,16 +1,12 @@ # UserId Criterion -The [`UserIdentifier` Search Criterion](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserId.php) +The [`UserId` Search Criterion](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserId.php) searches for content based on the User ID. ## Arguments - `value` - int(s) representing the User ID(s). -## Limitations - -The `UserIdentifier` Criterion is not available in Elastic search engine. - ## Example ``` php diff --git a/docs/guide/search/criteria_reference/userlogin_criterion.md b/docs/guide/search/criteria_reference/userlogin_criterion.md index 55945c853d..f75e4b0c68 100644 --- a/docs/guide/search/criteria_reference/userlogin_criterion.md +++ b/docs/guide/search/criteria_reference/userlogin_criterion.md @@ -10,7 +10,7 @@ searches for content based on the User ID. ## Limitations -The `UserLogin` Criterion is not available in Elastic search engine. +Solr search engine and Elasticsearch support IN and EQ operators only. ## Example diff --git a/docs/guide/search/elastic.md b/docs/guide/search/elastic.md index 6390ce1846..a1dc85fa76 100644 --- a/docs/guide/search/elastic.md +++ b/docs/guide/search/elastic.md @@ -1,3 +1,7 @@ +--- +description: Configure Elasticsearch to use with Ibexa DXP. +--- + # Elasticsearch search engine Elasticsearch is an open-source, distributed, Java-based search engine that responds to queries @@ -16,12 +20,12 @@ To proceed you need to be familiar with how indexing, filtering and queries work For example, use the following [Docker](https://docs.docker.com/get-started/overview/) command: ``` -docker run -d --name ibexa-dxp-elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.7.1 +docker run -d --name ibexa-dxp-elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.16.2 ``` !!! note - [[= product_name =]] supports Elasticsearch in version 7.7. + [[= product_name =]] supports Elasticsearch in version 7.16.2 or higher. ## Step 2: Verify that the Elasticsearch instance is up @@ -70,16 +74,16 @@ All configuration is made in the `/config/packages/ezplatform_elastic_search_eng the Elasticsearch documentation. First, decide how [[= product_name =]] connects to Elasticsearch and configure other connection settings. -For more information, see [Configuring connections](#configuring-connections). +For more information, see [Configuring connections](#connections). -Then, define a field type mappings template that instructs Elasticsearch to interpret [[= product_name =]] fields as specific types. For more information, see [Configuring field type mappings](#configuring-field-type-mapping-templates). +Then, define a field type mappings template that instructs Elasticsearch to interpret [[= product_name =]] fields as specific types. For more information, see [Configuring field type mappings](#field-type-mapping-templates). ## Step 5: Push the templates For each of your defined connections, push the templates to the Elasticsearch engine by running the following command: ``` bash -php bin/console ezplatform:elasticsearch:put-index-template +php bin/console ibexa:elasticsearch:put-index-template ``` You can modify the behavior of the command with a number of switches. Use the `-h` switch to display a complete list of available options. @@ -89,7 +93,7 @@ You can modify the behavior of the command with a number of switches. Use the `- After creating index templates, run the following command to reindex your data: ``` bash -php bin/console ezplatform:reindex +php bin/console ibexa:reindex ``` !!! caution "Risks of premature indexing" @@ -97,12 +101,12 @@ php bin/console ezplatform:reindex Do not reindex your data before you create index templates. Otherwise Elasticsearch attempts to use its [dynamic field mapping](https://www.elastic.co/guide/en/elasticsearch/reference/7.7/dynamic-field-mapping.html) feature to create type mappings automatically. -## Elasticsearch configuration reference +## Elasticsearch configuration -### Configuring connections +### Connections When you configure the Elasticsearch integration, you must first configure the connections. -You either connect to a [cluster of Elasticsearch nodes](#configure-a-cluster) or the [Elasticsearch Cloud](#configure-elasticsearch-cloud). +You either connect to a [cluster of Elasticsearch nodes](#cluster) or the [Elasticsearch Cloud](#elasticsearch-cloud). Define the connection settings under the `connections` key. First, set a name of the connection: @@ -114,8 +118,8 @@ ez_platform_elastic_search_engine: Then decide whether to add a cluster that you administer and manage yourself, or use a cloud solution from Elastic, as well as configure additional parameters. -You can then decide how the cluster [handles communication with individual nodes](#configure-the-multi-node-cluster-behavior), -and configure the [security settings](#configure-security). +You can then decide how the cluster [handles communication with individual nodes](#multi-node-cluster-behavior), +and configure the [security settings](#security). !!! tip "A default connection" @@ -128,7 +132,7 @@ and configure the [security settings](#configure-security). default_connection: <connection_name> ``` -### Configuring a cluster +### Cluster A cluster consists of a number of nodes. You might start with just one node and add more nodes if you need more processing power. @@ -200,7 +204,7 @@ ezplatform_elastic_search_engine: default_connection: simple ``` -#### Configuring the multi-node cluster behavior +#### Multi-node cluster behavior When you configure a cluster-based connection, and the cluster consists of many nodes, you can choose strategies that govern how the cluster reacts to changing operating conditions, or how @@ -262,7 +266,7 @@ number of retries might differ. For more information, see [Set retries](https://www.elastic.co/guide/en/elasticsearch/client/php-api/7.x/configuration.html#_set_retries). -### Configuring Elasticsearch Cloud +### Elasticsearch Cloud As an alternative to using your own cluster, you can use Elasticsearch Cloud, a commercial SaaS solution. With Elasticsearch Cloud you do not have to build or manage your own Elasticsearch cluster. @@ -278,7 +282,7 @@ providing an alphanumerical ID string that you obtain from the cloud's user inte With the ID set, you must configure authentication to be able to access the remote environment. -### Configuring security +### Security Elasticsearch instances support `basic` and `api_key` authentication methods. You select authentication type and configure the settings under the `authentication` key. By default, authentication is disabled: @@ -292,7 +296,7 @@ You select authentication type and configure the settings under the `authenticat If you connect to Elasticsearch hosts outside of your local network, you might also need to configure SSL encryption. -#### Configuring basic authentication +#### Basic authentication If your Elasticsearch server is protected by HTTP authentication, you must provide [[= product_name =]] with the credentials. When using basic authentication, you must pass the following parameters: @@ -318,7 +322,7 @@ ezplatform_elastic_search_engine: credentials: ['elastic', '1htFY83VvX2JRDw88MOkOejk'] ``` -#### Configuring API key authentication +#### API key authentication If your Elasticsearch cluster is protected by API keys, you must provide the key and secret in authentication configuration to connect [[= product_name =]] with the cluster. With API key authentication you can define different @@ -351,7 +355,7 @@ ezplatform_elastic_search_engine: credentials: ['8Ek5f3IBGQlWj6v4M7zG', 'rmI6IechSnSJymWJ4LZqUw'] ``` -#### Configuring SSL +#### SSL When you need to protect your communication with the Elasticsearch server, you can use SSL encryption. When configuring SSL for your internal infrastructure, you can use your own client certificates signed by a public CA. @@ -425,7 +429,7 @@ By default, debugging is disabled. To enable debugging, you can toggle either of Make sure that you disable debugging in a production environment. -### Configuring field type mapping templates +### Field Type mapping templates Before you can re-index the [[= product_name =]] data, so that Elasticsearch can search through its contents, you must define an index template. Templates instruct Elasticsearch to recognize [[= product_name =]] Fields as specific data types, based on, for example, a field name. @@ -507,7 +511,8 @@ For more information about mappings, see [Elasticsearch documentation](https://w When you create a custom index template, with settings for your own field and document types, make sure that it contains mappings for all searchable fields that are available in [[= product_name =]]. - For an example of default configuration with a list of searchable fields, see [Default configuration](https://github.com/ezsystems/ezplatform-elastic-search-engine/blob/v1.0.0/src/bundle/Resources/config/default-config.yaml). + For an example of default configuration with a list of searchable fields. + To see the default configuration, go to `vendor/ezsystems/ezplatform-elastic-search-engine/src/bundle/Resources/config/` and open the `default-config.yaml` file. #### Fine-tuning the search results @@ -587,5 +592,5 @@ For more information about how Elasticsearch handles settings and mappings from ## Extending Elasticsearch -To learn how to create custom Search Criteria, Sort Clauses and Facets for use with Elasticsearch, -and how to index custom data and manipulate the query, see [Elasticsearch extensibility](extend_elasticsearch.md). +To learn how you can create document field mappers, custom Search Criteria, +custom Sort Clauses and Aggregations, see [Search extensibility](extensibility/create_custom_search_criterion.md). diff --git a/docs/guide/search/extend_elasticsearch.md b/docs/guide/search/extend_elasticsearch.md deleted file mode 100644 index 6796066379..0000000000 --- a/docs/guide/search/extend_elasticsearch.md +++ /dev/null @@ -1,655 +0,0 @@ -# Elasticsearch extensibility - -## Indexing custom data - -[Elasticsearch](elastic.md) indexes content and Location data out of the box. -Besides what is indexed automatically, you can add additional data to the Elasticsearch index. - -To do so, subscribe to one of the following events: - -- `Ibexa\Platform\Contracts\ElasticSearchEngine\Mapping\Event\ContentIndexCreateEvent` -- `Ibexa\Platform\Contracts\ElasticSearchEngine\Mapping\Event\LocationIndexCreateEvent` - -These events are called when the index is created for the content and Location documents, respectively. - -You can pass the event to a subscriber which gives you access to the document that you can modify. - -In the following example, when an index in created for a content or a Location document, -the event subscriber adds a `custom_field` of the type `StringField` to the index: - -``` php hl_lines="19 20 21" -<?php - -declare(strict_types=1); - -namespace App\EventSubscriber; - -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\StringField; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Mapping\Event\ContentIndexCreateEvent; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Mapping\Event\LocationIndexCreateEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -final class CustomIndexDataSubscriber implements EventSubscriberInterface -{ - public function onContentDocumentCreate(ContentIndexCreateEvent $event): void - { - $document = $event->getDocument(); - $document->fields[] = new Field( - 'custom_field', - 'Custom field value', - new StringField() - ); - } - - public function onLocationDocumentCreate(LocationIndexCreateEvent $event): void - { - $document = $event->getDocument(); - $document->fields[] = new Field( - 'custom_field', - 'Custom field value', - new StringField() - ); - } - - public static function getSubscribedEvents(): array - { - return [ - ContentIndexCreateEvent::class => 'onContentDocumentCreate', - LocationIndexCreateEvent::class => 'onLocationDocumentCreate' - ]; - } -} -``` - -Remember to register the subscriber as a service: - -``` yaml -services: - App\EventSubscriber\CustomIndexDataSubscriber: - tags: - - { name: kernel.event_subscriber } -``` - -## Manipulating the query - -You can customize the search query before it is executed. -To do it, subscribe to `Ibexa\Platform\Contracts\ElasticSearchEngine\Query\Event\QueryFilterEvent`. - -The following example shows how to add an additional Search Criterion to all queries. - -Depending on your configuration, this might impact all search queries, -including those used for search and content tree in the Back Office. - -``` php hl_lines="35" -<?php - -declare(strict_types=1); - -namespace App\EventSubscriber; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateIdentifier; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\Event\QueryFilterEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -final class CustomQueryFilterSubscriber implements EventSubscriberInterface -{ - public function onQueryFilter(QueryFilterEvent $event): void - { - $query = $event->getQuery(); - - $additionalCriteria = new ObjectStateIdentifier('locked'); - - if ($query->filter !== null) { - $query->filter = $additionalCriteria; - } else { - // Append Criterion to existing filter - $query->filter = new LogicalAnd([ - $query->filter, - $additionalCriteria - ]); - } - } - - public static function getSubscribedEvents(): array - { - return [ - QueryFilterEvent::class => 'onQueryFilter' - ]; - } -} -``` - -Remember to register the subscriber as a service: - -``` yaml -services: - App\EventSubscriber\CustomQueryFilterSubscriber: - tags: - - { name: kernel.event_subscriber } -``` - -## Custom Search Criterion - -To provide support for a custom Search Criterion, you need to implement `CriterionVisitor`: - -``` php hl_lines="8" -<?php - -declare(strict_types=1); - -namespace App\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\CriterionVisitor; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\LanguageFilter; - -final class CameraManufacturerVisitor implements CriterionVisitor -{ - public function supports(Criterion $criterion, LanguageFilter $languageFilter): bool - { - return $criterion instanceof CameraManufacturer; - } - - public function visit(CriterionVisitor $dispatcher, Criterion $criterion, LanguageFilter $languageFilter): array - { - return [ - 'terms' => [ - 'exif_camera_manufacturer_id' => (array)$criterion->value - ] - ]; - } -} -``` - -Next, add the Search Criterion class itself in `src/Query/Criterion/CameraManufacturerCriterion.php`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; - -final class CameraManufacturer extends Criterion -{ - /** - * @param string|string[] $value One or more manufacturer names that must be matched. - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_STRING - ), - ]; - } -} -``` - -Search Criteria can be valid for both content and Location search. -To choose the search type, use either `content` or `location` in the tag when registering the visitor as a service: - -``` yaml -services: - App\Query\Criterion\CameraManufacturerVisitor: - tags: - - { name: ezplatform.search.elasticsearch.query.content.criterion_visitor } - - { name: ezplatform.search.elasticsearch.query.location.criterion_visitor } -``` - -## Custom Sort Clause - -To create a custom Sort Clause for use with Elasticsearch, -implement `SortClauseVisitor` -in `src/Query/SortClause/ScoreVisitor.php`: - -``` php hl_lines="10" -<?php - -declare(strict_types=1); - -namespace App\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\LanguageFilter; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\SortClauseVisitor; - -final class ScoreVisitor implements SortClauseVisitor -{ - public function supports(SortClause $sortClause, LanguageFilter $languageFilter): bool - { - return $sortClause instanceof Score; - } - - public function visit(SortClauseVisitor $visitor, SortClause $sortClause, LanguageFilter $languageFilter): array - { - $order = $sortClause->direction === Query::SORT_ASC ? 'asc' : 'desc'; - - return [ - '_score' => [ - 'order' => $order, - ], - ]; - } -} -``` - -The `supports()` method checks if the implementation can handle the given Sort Clause. -The `visit()` method contains the logic that translates Sort Clause information into data understandable by Elasticsearch. -The `visit()` method takes the Sort Clause visitor, the Sort Clause itself and the language filter as arguments. - -Next, add the Sort Clause class itself in `src/Query/SortClause/ScoreSortClause.php`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -final class Score extends SortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('_score', $sortDirection); - } -} -``` - -Sort Clauses can be valid for both content and Location search. -To choose the search type, use either `content` or `location` in the tag when registering the visitor as a service: - -``` yaml -services: - App\Query\SortClause\ScoreVisitor: - tags: - - { name: ezplatform.search.elasticsearch.query.content.sort_clause_visitor } - - { name: ezplatform.search.elasticsearch.query.location.sort_clause_visitor } -``` - -## Custom Aggregation - -To create a custom aggregation for use with Elasticsearch, create an aggregation class. -In the following example, an aggregation groups Location query results according to the Location priority: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\LocationAggregation; - -final class PriorityRangeAggregation extends AbstractRangeAggregation implements LocationAggregation -{ - -} -``` - -PriorityRangeAggregation` in the example above extends `AbstractRangeAggregation`. -The name indicates that it is going to aggregate the results according to the Location priority, using Range aggregation. - -An aggregation must implement the `eZ\Publish\API\Repository\Values\Content\Query\Aggregation` interface or inherit one of following abstract classes: - -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation` -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation` -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation` - -An aggregation can also implement one of the following interfaces: - -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\FieldAggregation`, based on a content Field -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\LocationAggregation`, based on content Location -- `eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawAggregation`, based on details of the index structure - -!!! note "Aggregation definition" - - Aggregation definition must contain at least aggregation name and optional aggregation parameters. - e.g. path string used to limit aggregation results to specific subtree or Content Type identifier / Field definition identifier - which will be mapped to search index field name. - - Aggregation definition should be independent of the search engine used. - -A custom aggregation requires: - -- an aggregation visitor which returns an array of results -- a result extractor which transforms raw aggregation results from Elasticsearch into `AggregationResult` objects. - -For simpler cases, you can use one of the built-in visitors corresponding to the aggregation type. -In the example below it is `RangeAggregationVisitor`: - -``` yaml -services: - App\Query\Aggregation\Elasticsearch\PriorityAggregationVisitor: - class: Ibexa\Platform\ElasticSearchEngine\Query\AggregationVisitor\RangeAggregationVisitor - factory: ['@Ibexa\Platform\ElasticSearchEngine\Query\AggregationVisitor\Factory\SearchFieldAggregationVisitorFactory', 'createRangeAggregationVisitor'] - arguments: - $aggregationClass: 'App\Query\Aggregation\PriorityRangeAggregation' - $searchIndexFieldName: 'priority_i' - tags: - - { name: ezplatform.search.elasticsearch.query.location.aggregation_visitor } -``` - -The visitor is created by `SearchFieldAggregationVisitorFactory`. -You provide it with two arguments: - -- the aggregation class in `aggregationClass` -- the field name in search index in `searchIndexFieldName` - -Tag the service with `ezplatform.search.elasticsearch.query.location.aggregation_visitor`. - -For the result extractor, you can use the built-in `RangeAggregationResultExtractor` -and provide it with the aggregation class in the `aggregationClass` parameter. - -Tag the service with `ezplatform.search.elasticsearch.query.location.aggregation_result_extractor`. - -``` yaml -services: - App\Query\Aggregation\Elasticsearch\PriorityAggregationResultExtractor: - class: Ibexa\Platform\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\RangeAggregationResultExtractor - arguments: - $aggregationClass: 'App\Query\Aggregation\PriorityRangeAggregation' - tags: - - { name: ezplatform.search.elasticsearch.query.location.aggregation_result_extractor } -``` - -If you are using a different type of aggregation than range, you can also use respective visitor and extractor classes: - -- `Ibexa\Platform\ElasticSearchEngine\Query\AggregationVisitor\RangeAggregationVisitor` -- `Ibexa\Platform\ElasticSearchEngine\Query\AggregationVisitor\StatsAggregationVisitor` -- `Ibexa\Platform\ElasticSearchEngine\Query\AggregationVisitor\TermAggregationVisitor` - -- `Ibexa\Platform\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\RangeAggregationResultExtractor` -- `Ibexa\Platform\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\StatsAggregationResultExtractor` -- `Ibexa\Platform\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\TermAggregationResultExtractor` - -If you have a more complex use case, you need to create your own visitor and extractor. - -### Custom aggregation visitor - -The aggregation visitor must implement `Ibexa\Platform\Contracts\ElasticSearchEngine\Query\AggregationVisitor`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\Aggregation\Elasticsearch; - -use App\Query\Aggregation\PriorityRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\AggregationVisitor; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\LanguageFilter; - -final class PriorityAggregationVisitor implements AggregationVisitor -{ - - public function supports(Aggregation $aggregation, LanguageFilter $languageFilter): bool - { - return $aggregation instanceof PriorityRangeAggregation; - } - - /** - * - * @param PriorityRangeAggregation $aggregation - * - */ - public function visit(AggregationVisitor $dispatcher, Aggregation $aggregation, LanguageFilter $languageFilter): array - { - $ranges = []; - - foreach ($aggregation->getRanges() as $range) { - if ($range->getFrom() !== null && $range->getTo() !== null) { - $ranges[] = [ - 'from' => $range->getFrom(), - 'to' => $range->getTo() - ]; - } elseif ($range->getFrom() === null && $range->getTo() !== null) { - $ranges[] = [ - 'to' => $range->getTo() - ]; - } elseif ($range->getFrom() !== null && $range->getTo() === null) { - $ranges[] = [ - 'from' => $range->getFrom() - ]; - } else { - // invalid range - } - } - - return [ - 'range' => [ - 'field' => 'priority_i', - 'ranges' => $ranges - ] - ]; - } -} -``` - -The `supports()` method checks whether the provided aggregation is of the supported type -(in this case, your custom `PriorityRangeAggregation`). - -The `visit()` method returns an array of results. - -### Custom result extractor - -You also need to create a result extractor, implementing `Ibexa\Platform\Contracts\ElasticSearchEngine\Query\AggregationResultExtractor`, -that transforms raw aggregation results from Elasticsearch into `AggregationResult` objects: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\Aggregation\Elasticsearch; - -use App\Query\Aggregation\PriorityRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\AggregationResultExtractor; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\LanguageFilter; -use stdClass; - -final class PriorityAggregationResultExtractor implements AggregationResultExtractor -{ - - public function supports(Aggregation $aggregation, LanguageFilter $languageFilter): bool - { - return $aggregation instanceof PriorityRangeAggregation; - } - - public function extract(Aggregation $aggregation, LanguageFilter $languageFilter, array $data): AggregationResult - { - $entries = []; - - foreach ($data['buckets'] as $bucket) { - $entries[] = new AggregationResult\RangeAggregationResultEntry( - new Aggregation\Range($bucket['from'] ?? null, $bucket['to'] ?? null), - $bucket['doc_count'] - ); - } - - return new AggregationResult\RangeAggregationResult($aggregation->getName(), $entries); - } -} -``` - -The `supports()` method checks whether the provided aggregation is of the supported type -(in this case, your custom `PriorityRangeAggregation`). - -The `extract()` method converts the [raw data provided by the search engine](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html) to a `RangeAggregationResult` object. - -Finally, register both the aggregation visitor and the result extractor as services. - -Tag the aggregation visitor with `ezplatform.search.elasticsearch.query.location.aggregation_visitor` -and the result extractor with `ezplatform.search.elasticsearch.query.location.aggregation_result_extractor`: - -``` yaml -services: - App\Query\Aggregation\Elasticsearch\PriorityAggregationVisitor: - tags: - - { name: 'ezplatform.search.elasticsearch.query.location.aggregation_visitor' } - App\Query\Aggregation\Elasticsearch\PriorityAggregationResultExtractor: - tags: - - { name: 'ezplatform.search.elasticsearch.query.location.aggregation_result_extractor' } -``` - -For content-based aggregations, use the `ezplatform.search.elasticsearch.query.content.aggregation_visitor` and `ezplatform.search.elasticsearch.query.content.aggregation_result_extractor` tags respectively. - -## Custom Facet - -!!! caution "Deprecated" - - Search Facets are deprecated since version v3.2. - -To create a custom search Facet for use with Elasticsearch, create a Facet class and a Facet builder. -You also need to add a visitor and a result extractor. - -The following example shows how to create a Facet that filters results according to their Content Type group. - -`src/Query/ContentTypeGroupFacet`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class holds counts of content with content type. - */ -final class ContentTypeGroupFacet extends Facet -{ - /** - * An array with ContentTypeGroup::$id as key and count of matching content objects as value. - * - * @var int[] - */ - public $entries = []; -} -``` - -`src/Query/ContentTypeGroupFacetBuilder`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -final class ContentTypeGroupFacetBuilder extends FacetBuilder -{ -} -``` - -`src/Query/ContentTypeGroupFacetBuilderVisitor`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\FacetBuilderVisitor; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\LanguageFilter; - -/** - * Example (simplified) visitor implementation for ContentTypeGroupFacetBuilder - */ -final class ContentTypeGroupFacetBuilderVisitor implements FacetBuilderVisitor -{ - public function supports(FacetBuilder $builder, LanguageFilter $languageFilter): bool - { - return $builder instanceof ContentTypeGroupFacetBuilder; - } - - public function visit(FacetBuilderVisitor $dispatcher, FacetBuilder $builder, LanguageFilter $languageFilter): array - { - return [ - 'terms' => [ - 'field' => 'content_type_group_id_mid', - ], - ]; - } -} -``` - -`src/Query/ContentTypeGroupFacetResultExtractor`: - -``` php -<?php - -declare(strict_types=1); - -namespace App\Query\FacetBuilder; - -use App\Query\Facet\ContentTypeGroupFacet; -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; -use eZ\Publish\API\Repository\Values\Content\Search\Facet; -use Ibexa\Platform\Contracts\ElasticSearchEngine\Query\FacetResultExtractor; - -final class ContentTypeGroupFacetResultExtractor implements FacetResultExtractor -{ - public function supports(FacetBuilder $builder): bool - { - return $builder instanceof ContentTypeGroupFacetBuilder; - } - - public function extract(FacetBuilder $builder, array $data): Facet - { - $facet = new ContentTypeGroupFacet(); - $facet->name = $builder->name; - foreach ($data['buckets'] as $bucket) { - $facet->entries[$bucket['key']] = $bucket['doc_count']; - } - - return $facet; - } -} -``` - -Remember to register the facet classes as services: - -``` yaml -services: - App\Query\FacetBuilder\ContentTypeGroupFacetBuilderVisitor: - tags: - - { name: ezplatform.search.elasticsearch.query.content.facet_builder_visitor } - - { name: ezplatform.search.elasticsearch.query.location.facet_builder_visitor } - - App\Query\FacetBuilder\ContentTypeGroupFacetResultExtractor: - tags: - - { name: ezplatform.search.elasticsearch.query.facet_result_extractor } -``` diff --git a/docs/guide/search/extensibility/create_custom_aggregation.md b/docs/guide/search/extensibility/create_custom_aggregation.md new file mode 100644 index 0000000000..696cfb4112 --- /dev/null +++ b/docs/guide/search/extensibility/create_custom_aggregation.md @@ -0,0 +1,245 @@ +--- +description: Create custom Aggregation to use with Solr and Elasticsearch search engines. +--- + +# Create custom Aggregation + +To create a custom Aggregation, create an aggregation class. +In the following example, an aggregation groups the Location query results by the Location priority: + +=== "Solr" + + ``` php + --8<-- + code_samples/search/solr/src/Query/Aggregation/Solr/PriorityRangeAggregation.php + --8<-- + ``` + +=== "Elasticsearch" + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityRangeAggregation.php + --8<-- + ``` + +The `PriorityRangeAggregation` class extends `AbstractRangeAggregation`. +The name of the class indicates that it aggregates the results by using the Range aggregation. + +An aggregation must implement the `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation` +interface or inherit one of following abstract classes: + +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation` +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation` +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation` + +An aggregation can also implement one of the following interfaces: + +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\FieldAggregation`, based on content Field +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\LocationAggregation`, based on content Location +- `Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\RawAggregation`, based on details of the index structure + +!!! note "Aggregation definition" + + An aggregation definition must contain at least the name of an aggregation + and optional aggregation parameters, such as, for example, the path (string) + that is used to limit aggregation results to a specific subtree, Content + Type identifier, or Field definition identifier, which will be mapped + to the search index field name. + + Aggregation definition must be independent of the search engine used. + +A custom aggregation requires that the following elements are provided: + +- An aggregation visitor that returns an array of results +- A result extractor that transforms raw aggregation results from the search engine +into `AggregationResult` objects + +In simpler cases, you can apply one of the built-in visitors that correspond +to the aggregation type. +The example below uses `RangeAggregationVisitor`: + +=== "Solr" + + ``` yaml + App\Query\Aggregation\Solr\PriorityAggregationVisitor: + class: EzSystems\EzPlatformSolrSearchEngine\Query\Common\AggregationVisitor\RangeAggregationVisitor + factory: ['@EzSystems\EzPlatformSolrSearchEngine\Query\Common\AggregationVisitor\Factory\ContentFieldAggregationVisitorFactory', 'createRangeAggregationVisitor'] + arguments: + $aggregationClass: 'App\Query\Aggregation\PriorityRangeAggregation' + $searchIndexFieldName: 'priority_i' + tags: + - { name: ezplatform.search.solr.query.content.aggregation_visitor } + - { name: ezplatform.search.solr.query.location.aggregation_visitor } + ``` + +=== "Elasticsearch" + + ``` yaml + app.search.elasticsearch.query.aggregation_visitor.priority_range_aggregation: + class: Ibexa\Elasticsearch\Query\AggregationVisitor\RangeAggregationVisitor + factory: [ '@Ibexa\Elasticsearch\Query\AggregationVisitor\Factory\SearchFieldAggregationVisitorFactory', 'createRangeAggregationVisitor' ] + arguments: + $aggregationClass: 'App\Query\Aggregation\Elasticsearch\PriorityRangeAggregation' + $searchIndexFieldName: 'priority_i' + tags: + - { name: ibexa.search.elasticsearch.query.location.aggregation.visitor } + - { name: ibexa.search.elasticsearch.query.content.aggregation.visitor } + ``` + +The visitor is created by `SearchFieldAggregationVisitorFactory`. +You provide it with two arguments: + +- The aggregation class in `aggregationClass` +- The field name in search index in `searchIndexFieldName` + +=== "Solr" + + Tag the service with `ibexa.solr.query.location.aggregation_visitor`. + +=== "Elasticsearch" + + Tag the service with `ibexa.elasticsearch.query.location.aggregation_visitor`. + +For the result extractor, you can use the built-in `RangeAggregationResultExtractor` +and provide it with the aggregation class in the `aggregationClass` parameter. + +=== "Solr" + + Tag the service with `ibexa.solr.query.location.aggregation_result_extractor`. + + ``` yaml + App\Query\Aggregation\Solr\PriorityAggregationResultExtractor: + class: EzSystems\EzPlatformSolrSearchEngine\ResultExtractor\AggregationResultExtractor\RangeAggregationResultExtractor + arguments: + $aggregationClass: 'App\Query\Aggregation\PriorityRangeAggregation' + tags: + - { name: ezplatform.search.solr.query.location.aggregation_result_extractor } + ``` + +=== "Elasticsearch" + + Tag the service with `ibexa.search.elasticsearch.query.location.aggregation.result.extractor`. + + ``` yaml + app.search.elasticsearch.query.aggregation_result_extractor.priority_range_aggregation: + class: Ibexa\Elasticsearch\Query\ResultExtractor\AggregationResultExtractor\RangeAggregationResultExtractor + arguments: + $aggregationClass: 'App\Query\Aggregation\Elasticsearch\PriorityRangeAggregation' + tags: + - { name: ibexa.search.elasticsearch.query.location.aggregation.result.extractor } + - { name: ibexa.search.elasticsearch.query.content.aggregation.result.extractor } + ``` + +You can use a different type of aggregation, followed by respective visitor and extractor classes: + +=== "Solr" + + - `Ibexa\Solr\Query\Common\AggregationVisitor\StatsAggregationVisitor` + - `Ibexa\Solr\Query\Common\AggregationVisitor\TermAggregationVisitor` + - `Ibexa\Solr\ResultExtractor\AggregationResultExtractor\StatsAggregationResultExtractor` + - `Ibexa\Solr\ResultExtractor\AggregationResultExtractor\TermAggregationResultExtractor` + +=== "Elasticsearch" + + - `Ibexa\ElasticSearchEngine\Query\AggregationVisitor\RangeAggregationVisitor` + - `Ibexa\ElasticSearchEngine\Query\AggregationVisitor\StatsAggregationVisitor` + - `Ibexa\ElasticSearchEngine\Query\AggregationVisitor\TermAggregationVisitor` + + - `Ibexa\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\RangeAggregationResultExtractor` + - `Ibexa\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\StatsAggregationResultExtractor` + - `Ibexa\ElasticSearchEngine\Query\ResultExtractor\AggregationResultExtractor\TermAggregationResultExtractor` + +In a more complex use case, you must create your own visitor and extractor. + +### Create aggregation visitor + +=== "Solr" + + The aggregation visitor must implement `Ibexa\Contracts\Solr\Query\AggregationVisitor`: + + ``` php + --8<-- + code_samples/search/solr/src/Query/Aggregation/Solr/PriorityRangeAggregationVisitor.php + --8<-- + ``` + +=== "Elasticsearch" + + The aggregation visitor must implement `Ibexa\Contracts\ElasticSearchEngine\Query\AggregationVisitor`: + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationVisitor.php + --8<-- + ``` + +The `canVisit()` method checks whether the provided aggregation is of the supported type +(in this case, your custom `PriorityRangeAggregation`). + +The `visit()` method returns an array of results. + +### Create result extractor + +=== "Solr" + + You must also create a result extractor, which implements `Ibexa\Solr\ResultExtractor\AggregationResultExtractor` + that transforms raw aggregation results from Solr into `AggregationResult` objects: + + ``` php + --8<-- + code_samples/search/solr/src/Query/Aggregation/Solr/PriorityAggregationResultExtractor.php + --8<-- + ``` + + The `canVisit()` method checks whether the provided aggregation is of the supported type + (in this case, your custom `PriorityRangeAggregation`). + + The `extract()` method converts the [raw data provided by the search engine](https://solr.apache.org/guide/8_8/search-sample.html#aggregation) to a `RangeAggregationResult` object. + +=== "Elasticsearch" + + You must also create a result extractor, which implements `Ibexa\Contracts\ElasticSearchEngine\Query\AggregationResultExtractor` + that transforms raw aggregation results from Elasticsearch into `AggregationResult` objects: + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/Aggregation/Elasticsearch/PriorityAggregationResultExtractor.php + --8<-- + ``` + + The `supports()` method checks whether the provided aggregation is of the supported type + (in this case, your custom `PriorityRangeAggregation`). + + The `extract()` method converts the [raw data provided by the search engine](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html) to a `RangeAggregationResult` object. + + +Finally, register both the aggregation visitor and the result extractor as services. + +=== "Solr" + + Tag the aggregation visitor with `ibexa.solr.query.location.aggregation_visitor` + and the result extractor with `ibexa.solr.query.location.aggregation_result_extractor`: + + ``` yaml + --8<-- + code_samples/search/solr/config/aggregation_services.yaml + --8<-- + ``` + + For content-based aggregations, use the `ibexa.solr.query.content.aggregation_visitor` + and `ibexa.solr.query.content.aggregation_result_extractor` tags respectively. + +=== "Elasticsearch" + + Tag the aggregation visitor with `ibexa.elasticsearch.query.location.aggregation_visitor` + and the result extractor with `ibexa.elasticsearch.query.location.aggregation_result_extractor`: + + ``` yaml + --8<-- + code_samples/search/elasticsearch/config/aggregation_services.yaml + --8<-- + ``` + + For content-based aggregations, use the `ibexa.elasticsearch.query.content.aggregation_visitor` + and `ibexa.elasticsearch.query.content.aggregation_result_extractor` tags respectively. diff --git a/docs/guide/search/extensibility/create_custom_search_criterion.md b/docs/guide/search/extensibility/create_custom_search_criterion.md new file mode 100644 index 0000000000..83bbb9330a --- /dev/null +++ b/docs/guide/search/extensibility/create_custom_search_criterion.md @@ -0,0 +1,69 @@ +--- +description: Create custom Search Criterion to use with Solr and Elasticsearch search engines. +--- + +# Create custom Search Criterion + +To provide support for a custom Search Criterion, do the following. + +## Create Criterion class + +First, create a `CameraManufacturerCriterion.php` file +that contains the Criterion class: + +=== "Solr" + + ``` php + --8<-- + code_samples/search/solr/src/Query/Criterion/Solr/CameraManufacturerCriterion.php + --8<-- + ``` + +=== "Elasticsearch" + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/Criterion/Elasticsearch/CameraManufacturerCriterion.php + --8<-- + ``` + +## Create Criterion visitor + +Then, add a `CameraManufacturerVisitor` class, implementing `CriterionVisitor`: + +=== "Solr" + + ``` php + --8<-- + code_samples/search/solr/src/Query/Criterion/Solr/CameraManufacturerVisitor.php + --8<-- + ``` + +=== "Elasticsearch" + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/Criterion/Elasticsearch/CameraManufacturerVisitor.php + --8<-- + ``` + +Finally, register the visitor as a service. + +Search Criteria can be valid for both Content and Location search. +To choose the search type, use either `content` or `location` in the tag when registering the visitor as a service:: + +=== "Solr" + + ``` yaml + --8<-- + code_samples/search/solr/config/criterion_services.yaml + --8<-- + ``` + +=== "Elasticsearch" + + ``` yaml + --8<-- + code_samples/search/elasticsearch/config/criterion_services.yaml + --8<-- + ``` diff --git a/docs/guide/search/extensibility/create_custom_sort_clause.md b/docs/guide/search/extensibility/create_custom_sort_clause.md new file mode 100644 index 0000000000..62e844054b --- /dev/null +++ b/docs/guide/search/extensibility/create_custom_sort_clause.md @@ -0,0 +1,72 @@ +--- +description: Create custom Sort Clause to use with Solr and Elasticsearch search engines. +--- + +# Create custom Search Criterion + +To create a custom Sort Clause, do the following. + +## Create Sort Clause class + +First, add a `ScoreSortClause.php` file with the Sort Clause class: + +=== "Solr" + + ``` php + --8<-- + code_samples/search/solr/src/Query/SortClause/Solr/ScoreSortClause.php + --8<-- + ``` + +=== "Elasticsearch" + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/SortClause/Elasticsearch/ScoreSortClause.php + --8<-- + ``` + +## Create Sort Clause visitor + +Then, add a `ScoreVisitor` class that implements `SortClauseVisitor`: + +=== "Solr" + + ``` php + --8<-- + code_samples/search/solr/src/Query/SortClause/Solr/ScoreVisitor.php + --8<-- + ``` + +=== "Elasticsearch" + + ``` php + --8<-- + code_samples/search/elasticsearch/src/Query/SortClause/Elasticsearch/ScoreVisitor.php + --8<-- + ``` + + The `supports()` method checks if the implementation can handle the given Sort Clause. + The `visit()` method contains the logic that translates Sort Clause information into data understandable by the search engine. + The `visit()` method takes the Sort Clause visitor, the Sort Clause itself and the language filter as arguments. + +Finally, register the visitor as a service. + +Sort Clauses can be valid for both content and Location search. +To choose the search type, use either `content` or `location` in the tag when registering the visitor as a service: + +=== "Solr" + + ``` yaml + --8<-- + code_samples/search/solr/config/sort_clause_services.yaml + --8<-- + ``` + +=== "Elasticsearch" + + ``` yaml + --8<-- + code_samples/search/elasticsearch/config/sort_clause_services.yaml + --8<-- + ``` diff --git a/docs/guide/search/extensibility/customize_elasticsearch_index_structure.md b/docs/guide/search/extensibility/customize_elasticsearch_index_structure.md new file mode 100644 index 0000000000..f048c0c39e --- /dev/null +++ b/docs/guide/search/extensibility/customize_elasticsearch_index_structure.md @@ -0,0 +1,67 @@ +--- +description: You can adapt the structure of Elasticsearch index to the data in your Repository to improve performance and avoid instability. +--- + +# Customize Elasticsearch index structure + +You can customize the structure of your Elasticsearch search index to manage how documents in the index are grouped. + +This lets you control the size of [Elasticsearch shards](https://www.elastic.co/guide/en/elasticsearch/reference/current/scalability.html) that the index is divided into. + +By customizing the structure to your needs, you can avoid "oversharding" (having too many shards), +which negatively affects performance and can lead to instability. + +!!! tip "Sizing Elasticsearch shards" + + See [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/8.4/size-your-shards.html) for more information about adapting the size of your search index shards. + +## Selecting indexing strategy + +In your Elasticsearch configuration you can select one of three built-in strategies +that control grouping documents in the index. + +The strategies are: + +- `LanguageGroupResolver` - groups documents by language code. +- `ContentTypeGroupResolver`- groups documents by Content Type ID. +- `CompositeGroupResolver` - allows combining multiple group resolves together to have a more granular index. + +The default strategy is the composite of language and Content Type ID, +resulting in indexes in the form of `<repository>_<document_type>_<language>_<content_type_id>`. + +To change the strategy, use the `ezplatform_elastic_search_engine.document_group_resolver` configuration key: + +``` yaml +ezplatform_elastic_search_engine: + document_group_resolver: 'Ibexa\Elasticsearch\ElasticSearch\Index\Group\ContentTypeGroupResolver' +``` + +Select the strategy based on the structure of your Repository, taking into accounts data such as the number of Content items, +Content Types or languages. + +## Custom indexing strategy + +You can also create a group resolver that provides a custom indexing strategy. +This resolver must implement `Ibexa\Contracts\Elasticsearch\ElasticSearch\Index\Group\GroupResolverInterface`. + +### Create group resolver + +In this example, create a `ContentTypeGroupGroupResolver` based on the Content Type Group ID of the document: + +``` php +[[= include_file('code_samples/search/elasticsearch/src/GroupResolver/ContentTypeGroupGroupResolver.php') =]] +``` + +Register the resolver as a service: + +``` yaml +[[= include_file('code_samples/search/elasticsearch/config/group_resolver_services.yaml') =]] +``` + +### Configure indexing strategy + +Finally, in configuration indicate that Elasticsearch should use your custom indexing strategy: + +``` yaml +[[= include_file('code_samples/search/elasticsearch/config/packages/elasticsearch.yaml') =]] +``` diff --git a/docs/guide/search/extensibility/index_custom_elasticsearch_data.md b/docs/guide/search/extensibility/index_custom_elasticsearch_data.md new file mode 100644 index 0000000000..55ab3fafbd --- /dev/null +++ b/docs/guide/search/extensibility/index_custom_elasticsearch_data.md @@ -0,0 +1,74 @@ +--- +description: Index custom data when using the Elasticsearch search engine. +--- + +# Index custom Elasticsearch data + +[Elasticsearch](elastic.md) indexes content and Location data out of the box. +Besides what is indexed automatically, you can add additional data to the Elasticsearch index. + +To do so, subscribe to one of the following events: + +- `Ibexa\Contracts\ElasticSearchEngine\Mapping\Event\ContentIndexCreateEvent` +- `Ibexa\Contracts\ElasticSearchEngine\Mapping\Event\LocationIndexCreateEvent` + +These events are called when the index is created for the content and Location documents, respectively. + +You can pass the event to a subscriber which gives you access to the document that you can modify. + +In the following example, when an index in created for a content or a Location document, +the event subscriber adds a `custom_field` of the type `StringField` to the index: + +``` php hl_lines="19 20 21" +<?php + +declare(strict_types=1); + +namespace App\EventSubscriber; + +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType\StringField; +use Ibexa\Contracts\Elasticsearch\Mapping\Event\ContentIndexCreateEvent; +use Ibexa\Contracts\Elasticsearch\Mapping\Event\LocationIndexCreateEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +final class CustomIndexDataSubscriber implements EventSubscriberInterface +{ + public function onContentDocumentCreate(ContentIndexCreateEvent $event): void + { + $document = $event->getDocument(); + $document->fields[] = new Field( + 'custom_field', + 'Custom field value', + new StringField() + ); + } + + public function onLocationDocumentCreate(LocationIndexCreateEvent $event): void + { + $document = $event->getDocument(); + $document->fields[] = new Field( + 'custom_field', + 'Custom field value', + new StringField() + ); + } + + public static function getSubscribedEvents(): array + { + return [ + ContentIndexCreateEvent::class => 'onContentDocumentCreate', + LocationIndexCreateEvent::class => 'onLocationDocumentCreate' + ]; + } +} +``` + +Remember to register the subscriber as a service: + +``` yaml +services: + App\EventSubscriber\CustomIndexDataSubscriber: + tags: + - { name: kernel.event_subscriber } +``` diff --git a/docs/guide/search/extensibility/manipulate_elasticsearch_query.md b/docs/guide/search/extensibility/manipulate_elasticsearch_query.md new file mode 100644 index 0000000000..f5793426f5 --- /dev/null +++ b/docs/guide/search/extensibility/manipulate_elasticsearch_query.md @@ -0,0 +1,62 @@ +--- +description: Manipulate the search query when using the Elasticsearch search engine. +--- + +# Manipulate Elasticsearch query + +You can customize the search query before it is executed. +To do it, subscribe to `Ibexa\Contracts\ElasticSearchEngine\Query\Event\QueryFilterEvent`. + +The following example shows how to add an additional Search Criterion to all queries. + +Depending on your configuration, this might impact all search queries, +including those used for search and content tree in the Back Office. + +``` php hl_lines="35" +<?php + +declare(strict_types=1); + +namespace App\EventSubscriber; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ObjectStateIdentifier; +use Ibexa\Contracts\ElasticSearch\Query\Event\QueryFilterEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +final class CustomQueryFilterSubscriber implements EventSubscriberInterface +{ + public function onQueryFilter(QueryFilterEvent $event): void + { + $query = $event->getQuery(); + + $additionalCriteria = new ObjectStateIdentifier('locked'); + + if ($query->filter !== null) { + $query->filter = $additionalCriteria; + } else { + // Append Criterion to existing filter + $query->filter = new LogicalAnd([ + $query->filter, + $additionalCriteria + ]); + } + } + + public static function getSubscribedEvents(): array + { + return [ + QueryFilterEvent::class => 'onQueryFilter' + ]; + } +} +``` + +Remember to register the subscriber as a service: + +``` yaml +services: + App\EventSubscriber\CustomQueryFilterSubscriber: + tags: + - { name: kernel.event_subscriber } +``` diff --git a/docs/guide/search/extensibility/solr_document_field_mappers.md b/docs/guide/search/extensibility/solr_document_field_mappers.md new file mode 100644 index 0000000000..965257da43 --- /dev/null +++ b/docs/guide/search/extensibility/solr_document_field_mappers.md @@ -0,0 +1,87 @@ +--- +description: Use document field mappers to add additional data in Solr search engine. +--- + +# Solr document field mappers + +You can use document field mappers to index additional data in the search engine. + +The additional data can come from external sources (for example, the [Personalization +service](../../personalization/personalization.md)), or from internal ones. +An example of indexing internal data is indexing data through the Location hierarchy: +from the parent Location to the child Location, or indexing child data on the parent Location. +You can use this to find the content with full-text search, or to simplify a search +in a complicated data model. + +To do this effectively, you must understand how the data is indexed with the Solr search engine. +Solr uses [documents](https://lucene.apache.org/solr/guide/7_7/overview-of-documents-fields-and-schema-design.html#how-solr-sees-the-world) as a unit of data that is indexed. +Documents are indexed per translation, as content blocks. +A block is a nested document structure. +When used in [[= product_name =]], a parent document represents content, +and Locations are indexed as child documents of the Content item. +To avoid duplication, full-text data is indexed on the Content document only. +Knowing this, you can index additional data by the following: + +- All block documents (meaning content and its Locations, all translations) +- All block documents per translation +- Content documents +- Content documents per translation +- Location documents + +Additional data is indexed by implementing a document field mapper and registering it +at one of the five extension points described above. +You can create the field mapper class anywhere inside your bundle, +as long as you register it as a Symfony service. +There are three different field mappers. +Each mapper implements two methods, by the same name, but accepting different arguments: + +- `ContentFieldMapper` + - `::accept(Content $content)` + - `::mapFields(Content $content)` +- `ContentTranslationFieldMapper` + - `::accept(Content $content, $languageCode)` + - `::mapFields(Content $content, $languageCode)` +- `LocationFieldMapper` + - `::accept(Location $content)` + - `::mapFields(Location $content)` + +Mappers can be used on the extension points by registering them with the [service container](../../../api/public_php_api.md#service-container) by using service tags, as follows: + +- All block documents + - `ContentFieldMapper` + - `ibexa.solr.field_mapper.block` +- All block documents per translation + - `ContentTranslationFieldMapper` + - `ibexa.solr.field_mapper.block_translation` +- Content documents + - `ContentFieldMapper` + - `ibexa.solr.field_mapper.content` +- Content documents per translation + - `ContentTranslationFieldMapper` + - `ibexa.solr.field_mapper.content_translation` +- Location documents + - `LocationFieldMapper` + - `Ibexa\Solr\FieldMapper\LocationFieldMapper\Aggregate` + +The following example shows how you can index data from the parent Location content, +to make it available for full-text search on the child content. +The example relies on a use case of indexing webinar data on the webinar events, +which are children of the webinar. +The field mapper could then look like this: + +```php +[[= include_file('code_samples/search/solr/src/Search/FieldMapper/WebinarEventTitleFulltextFieldMapper.php') =]] +``` + +You index full text data only on the content document, therefore, you would register the service like this: + +``` yaml +[[= include_file('code_samples/search/solr/config/field_mapper_services.yaml') =]] +``` + +!!! caution "Permission issues when using Repository API in document field mappers" + + Document field mappers are low-level and expect to be able to index all content + regardless of current user permissions. + If you use PHP API in your custom document field mappers, apply [`sudo()`](../../../api/public_php_api.md#using-sudo), + or use the Persistence SPI layer as in the example above. diff --git a/docs/guide/search/search.md b/docs/guide/search/search.md index 9fea95061e..042478d95b 100644 --- a/docs/guide/search/search.md +++ b/docs/guide/search/search.md @@ -1,14 +1,49 @@ +--- +description: Ibexa DXP search functionalities allow working with three search engines and using search API to run complex and precise queries about content and products. +--- + # Search [[= product_name =]] exposes a very powerful [Search API](../../api/public_php_api_search.md), allowing both full-text search and querying the content Repository using several built-in Search Criteria and Sort Clauses. These are supported across different search engines, allowing you to plug in another search engine without changing your code. +## Search engines + Currently, the following search engines exist in their own [[= product_name =]] Bundles: -1. [Legacy search engine](search_engines.md#legacy-search-engine-bundle), a database-powered search engine for basic needs. +1. [Legacy search engine](#legacy-search-engine), a database-powered search engine for basic needs. 1. [Solr](solr.md), an integration providing better overall performance, much better scalability and support for more advanced search capabilities. -1. [Elasticsearch](elastic.md), available for [[= product_name_exp =]] customers, a document-oriented engine providing even better performance and scalability. +1. [Elasticsearch](elastic.md), a document-oriented engine providing even better performance and scalability. + +### Legacy search engine + +Legacy search engine is the default search engine, it is SQL-based and uses Doctrine's database connection. +Its connections are defined in the same way as for storage engine, and no further specific configuration is needed. + +!!! tip + + The features and performance of Legacy search engine are limited. + If you have specific search or performance needs you should look towards using [Solr](solr.md) + or [Elasticsearch](elastic.md). + + Using the Legacy search engine disables most shop features, such as product search. -### Feature comparison +#### Configuring Repository with Legacy search engine + +Search can be configured independently from storage, and the following configuration example shows both the default values, and how you configure legacy as the search engine: + +``` yaml +ezplatform: + repositories: + main: + storage: + engine: legacy + connection: default + search: + engine: legacy + connection: default +``` + +### Search engine comparison | Feature | Elasticsearch | Apache Solr | Legacy Search Engine (SQL) | | --- | --- | --- | --- | @@ -42,42 +77,6 @@ which will be used to translate the value object into a storage-specific search As an example take a look at the [`ContentId` Criterion handler](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php) in Legacy search engine or [`ContentId` Criterion handler](https://github.com/ezsystems/ezplatform-solr-search-engine/blob/v1.7.0/lib/Query/Common/CriterionVisitor/ContentIdIn.php) in Solr search engine. -## Search Facet reference - -!!! caution "Deprecated" - - Search Facets are deprecated since version v3.2. - -Search Facets enable you to apply [faceted search](../../api/public_php_api_search.md#faceted-search) -to get a count of search results for each Facet value. - -### Available FacetBuilders - -#### ContentTypeFacetBuilder - -Arguments: - -- `name`: `string` -- `minCount` (optional): `integer` -- `limit` (optional): `integer` - -#### SectionFacetBuilder - -Arguments: - -- `name`: `string` -- `minCount` (optional): `integer` -- `limit` (optional): `integer` - -#### UserFacetBuilder - -Arguments: - -- `name`: `string` -- `type`: `string` [`OWNER = 'owner'`, `GROUP = 'group'`, `MODIFIER = 'modifier'`] -- `minCount` (optional): `integer` -- `limit` (optional): `integer` - ## Custom Criteria and Sort Clauses Sometimes you will find that standard Search Criteria and Sort Clauses provided with [[= product_name =]] are not sufficient for your needs. Most often this will be the case if you have a custom Field Type using external storage which cannot be searched using the standard Field Criterion. @@ -116,9 +115,9 @@ Content Search explicitly refuses to accept Criteria and Sort Clauses implementi - `eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location` - `eZ\Publish\API\Repository\Values\Content\SortClause\Criterion\Location` -#### How to configure your own Criterion and Sort Clause Handlers +#### Configuring custom Criterion and Sort Clause handlers -After you have implemented your Criterion / Sort Clause and its handler, you will need to configure the handler for the service container using dedicated service tags for each type of search. Doing so will automatically register it and handle your Criterion / Search Clause when it is given as a parameter to one of the Search Service methods. +After you have implemented your Criterion / Sort Clause and its handler, you will need to configure the handler for the [service container](../../api/public_php_api.md#service-container) by using dedicated service tags for each type of search. Doing so will automatically register it and handle your Criterion / Search Clause when it is given as a parameter to one of the Search Service methods. Available tags for Criterion handlers in Legacy Storage Engine are: @@ -145,7 +144,7 @@ Available tags for Sort Clause handlers in Legacy Storage Engine are: For more information about the Criteria and Sort Clauses that are supported when searching for trashed Content items, see [Searching in trash reference](search_in_trash_reference.md). -##### Example of registering a ContentId Criterion handler, common for both Content and Location Search +The following example shows how to register a ContentId Criterion handler, common for both Content and Location Search: ``` yaml services: @@ -156,7 +155,7 @@ services: - {name: ezpublish.search.legacy.gateway.criterion_handler.location} ``` -##### Example of registering a Depth Sort Clause handler for Location Search +The following example shows how to register a Depth Sort Clause handler for Location Search: ``` yaml eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Depth: @@ -167,7 +166,7 @@ eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Locatio !!! note "See also" - See also [Symfony documentation about Service Container](http://symfony.com/doc/5.0/book/service_container.html#service-parameters) for passing parameters. + For more information about passing parameters, see [Symfony Service Container documentation]([[= symfony_doc =]]/book/service_container.html#service-parameters). ### Search using custom Field Criterion [REST] @@ -224,25 +223,25 @@ Custom Field Criterion search mirrors the one already existing in PHP API `eZ\Pu ## Reindexing -To (re)create or refresh the search engine index for configured search engines (per SiteAccess repository), use the `php bin/console ezplatform:reindex` command. +To (re)create or refresh the search engine index for configured search engines (per SiteAccess repository), use the `php bin/console ibexa:reindex` command. Some examples of common usage: ```bash # Reindex the whole index using parallel process (by default starts by purging the whole index) # (with the 'auto' option which detects the number of CPU cores -1, default behavior) -php bin/console ezplatform:reindex --processes=auto +php bin/console ibexa:reindex --processes=auto # Refresh a part of the subtree (implies --no-purge) -php bin/console ezplatform:reindex --subtree=2 +php bin/console ibexa:reindex --subtree=2 # Refresh content updated since a date (implies --no-purge) -php bin/console ezplatform:reindex --since=yesterday +php bin/console ibexa:reindex --since=yesterday # Refresh (or delete when not found) content by IDs (implies --no-purge) -php bin/console ezplatform:reindex --content-ids=3,45,33 +php bin/console ibexa:reindex --content-ids=3,45,33 ``` -For further info on possible options, see `php bin/console ezplatform:reindex --help`. +For further info on possible options, see `php bin/console ibexa:reindex --help`. ## Search view diff --git a/docs/guide/search/search_criteria_reference.md b/docs/guide/search/search_criteria_reference.md deleted file mode 100644 index 26c3f39a83..0000000000 --- a/docs/guide/search/search_criteria_reference.md +++ /dev/null @@ -1,63 +0,0 @@ -# Search reference - -Search Criteria are filters for Content and Location Search and -[Repository filtering](../../api/public_php_api_search.md#repository-filtering). - -Criteria can take some of the following arguments: - -- `target` - when the Criterion supports targeting a specific Field, example: `FieldDefinition` or Metadata identifier -- `value` - the value(s) to filter on, typically a scalar or array of scalars -- `operator` - constants on `eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator`: `IN`, `EQ`, `GT`, `GTE`, `LT`, `LTE`, `LIKE`, `BETWEEN`, `CONTAINS`. Most Criteria do not expose this and select `EQ` or `IN` depending on whether the value is scalar or an array. `IN` and `BETWEEN` always act on an array of values, while the other operators act on single scalar value -- `valueData` - additional value data, required by some Criteria, for instance `MapLocationDistance` - -Support and capabilities of individual Criteria can depend on the search engine. - -In the Legacy search engine, the field index/sort key column is limited to 255 characters by design. -Due to this storage limitation, searching content using the eZ Country Field Type or Keyword when there are multiple values selected may not return all the expected results. - -## Search Criteria - -|Search Criterion|Search based on|Supported by| -|-----|-----|-----| -|[Ancestor](criteria_reference/ancestor_criterion.md)|Whether the Content item is an ancestor of the provided Location|Content and Location Search; Filtering| -|[ContentId](criteria_reference/contentid_criterion.md)|Content item's ID|Content and Location Search; Filtering| -|[ContentTypeGroupId](criteria_reference/contenttypegroupid_criterion.md)|ID of the Content item's Content Type group|Content and Location Search; Filtering| -|[ContentTypeId](criteria_reference/contenttypeid_criterion.md)|ID of the Content item's Content Type|Content and Location Search; Filtering| -|[ContentTypeIdentifier](criteria_reference/contenttypeidentifier_criterion.md)|Identifier of the Content item's Content Type|Content and Location Search; Filtering| -|[DateMetadata](criteria_reference/datemetadata_criterion.md)|The date when content was created or last modified|Content and Location Search; Filtering| -|[Depth](criteria_reference/depth_criterion.md)|Location depth in the Content tree|Location Search, Filtering| -|[Field](criteria_reference/field_criterion.md)|Content of one of Content item's Fields|Content and Location Search| -|[FieldRelation](criteria_reference/fieldrelation_criterion.md)|Content items the content in question has Relations to|Content and Location Search| -|[FullText](criteria_reference/fulltext_criterion.md)|Full text content of a Content item's Fields|Content and Location Search| -|[IsFieldEmpty](criteria_reference/isfieldempty_criterion.md)|Whether a specified Field of a Content item is empty or not|Content and Location Search -|[IsMainLocation](criteria_reference/ismainlocation_criterion.md)|Whether a Location is the main Location of a Content item|Location Search, Filtering| -|[IsUserBased](criteria_reference/isuserbased_criterion.md)|Whether content represents a User account|Content and Location Search; Filtering| -|[IsUserEnabled](criteria_reference/isuserenabled_criterion.md)|Whether a User account is enabled|Content and Location Search; Filtering| -|[LanguageCode](criteria_reference/languagecode_criterion.md)|Whether a Content item is translated into the selected language|Content and Location Search; Filtering| -|[LocationId](criteria_reference/locationid_criterion.md)|Location ID|Content and Location Search; Filtering| -|[LocationRemoteId](criteria_reference/locationremoteid_criterion.md)|Location remote ID|Content and Location Search; Filtering| -|[MapLocationDistance](criteria_reference/maplocationdistance_criterion.md)|Distance between the location contained in a MapLocation Field and the provided coordinates|Content and Location Search| -|[MatchAll](criteria_reference/matchall_criterion.md)|Returns all search results|Content and Location Search; Filtering| -|[MatchNone](criteria_reference/matchnone_criterion.md)|Returns no search results|Content and Location Search; Filtering| -|[ObjectStateId](criteria_reference/objectstateid_criterion.md)|Object State ID|Content and Location Search; Filtering| -|[ObjectStateIdentifier](criteria_reference/objectstateidentifier_criterion.md)|Object State Identifier|Content and Location Search; Filtering| -|[ParentLocationId](criteria_reference/parentlocationid_criterion.md)|Location ID of a Content item's parent|Content and Location Search; Filtering| -|[Priority](criteria_reference/priority_criterion.md)|Location priority|Location Search, Filtering| -|[RemoteId](criteria_reference/remoteid_criterion.md)|Remote content ID|Content and Location Search; Filtering| -|[SectionId](criteria_reference/sectionid_criterion.md)|ID of the Section content is assigned to|Content and Location Search; Filtering| -|[SectionIdentifier](criteria_reference/sectionidentifier_criterion.md)|Identifier of the Section content is assigned to|Content and Location Search; Filtering| -|[Sibling](criteria_reference/sibling_criterion.md)|Locations that are children of the same parent|Content and Location Search; Filtering| -|[Subtree](criteria_reference/subtree_criterion.md)|Location subtree|Content and Location Search; Filtering| -|[UserEmail](criteria_reference/useremail_criterion.md)|Email address of a User account|Content and Location Search; Filtering| -|[UserId](criteria_reference/userid_criterion.md)|User ID|Content and Location Search; Filtering| -|[UserLogin](criteria_reference/userlogin_criterion.md)|User login|Content and Location Search; Filtering| -|[UserMetadata](criteria_reference/usermetadata_criterion.md)|The creator or modifier of a Content item|Content and Location Search; Filtering| -|[Visibility](criteria_reference/visibility_criterion.md)|Whether the Content item is visible or not|Content and Location Search; Filtering| - -### Logical operators - -|Search Criterion|Search based on|Supported by| -|-----|-----|-----| -|[LogicalAnd](criteria_reference/logicaland_criterion.md)|Implements a logical AND Criterion. It matches if ALL of the provided Criteria match.|Content and Location Search; Filtering| -|[LogicalNot](criteria_reference/logicalnot_criterion.md)|Implements a logical NOT Criterion. It matches if the provided Criterion doesn't match.|Content and Location Search; Filtering| -|[LogicalOr](criteria_reference/logicalor_criterion.md)|Implements a logical OR Criterion. It matches if at least one of the provided Criteria matches.|Content and Location Search; Filtering| diff --git a/docs/guide/search/search_engines.md b/docs/guide/search/search_engines.md deleted file mode 100644 index 0099222295..0000000000 --- a/docs/guide/search/search_engines.md +++ /dev/null @@ -1,27 +0,0 @@ -# Other search engines - -## Legacy Search Engine Bundle - -Legacy search engine is the default search engine, it is SQL-based and uses Doctrine's database connection. -Its connections are defined in the same way as for storage engine, and no further specific configuration is needed. - -!!! tip - - The features and performance of Legacy search engine are limited. - If you have specific search or performance needs you should look towards using [Solr](solr.md). - -### Configuring the Repository with the Legacy search engine - -Search can be configured independently from storage, and the following configuration example shows both the default values, and how you configure legacy as the search engine: - -``` yaml -ezplatform: - repositories: - main: - storage: - engine: legacy - connection: default - search: - engine: legacy - connection: default -``` diff --git a/docs/guide/search/search_in_trash_reference.md b/docs/guide/search/search_in_trash_reference.md index 9b6743ec6c..747fc4c836 100644 --- a/docs/guide/search/search_in_trash_reference.md +++ b/docs/guide/search/search_in_trash_reference.md @@ -1,9 +1,13 @@ -# Searching in trash reference +--- +description: Trash Search Criteria and Sort Clauses help define and fine-tune search queries for content in trash. +--- + +# Search in trash reference When you [search for Content items that are held in trash](../../api/public_php_api_search.md#searching-in-trash), you can apply only a limited set of Search Criteria and Sort Clauses. They can be used by `eZ\Publish\API\Repository\TrashService::findTrashItems` only. -## Supported Search Criteria +## Search Criteria | Search Criterion | Search based on | Search type | |-----|-----|-----| @@ -14,7 +18,7 @@ They can be used by `eZ\Publish\API\Repository\TrashService::findTrashItems` onl |[SectionId](criteria_reference/sectionid_criterion.md)|ID of the Section the Content item was assigned to|Content and Location| |[UserMetadata](criteria_reference/usermetadata_criterion.md)|The creator or modifier of a Content item|Content and Location| -## Supported logical operators +## logical operators |Search Criterion|Search based on|Search type| |-----|-----|-----| @@ -22,7 +26,7 @@ They can be used by `eZ\Publish\API\Repository\TrashService::findTrashItems` onl |[LogicalNot](criteria_reference/logicalor_criterion.md)|Implements a logical NOT Criterion. It matches if the provided Criterion doesn't match.|Content and Location| |[LogicalOr](criteria_reference/logicalor_criterion.md)|Implements a logical OR Criterion. It matches if at least one of the provided Criteria matches.|Content and Location| -## Supported Sort Clauses +## Sort Clauses | Sort Clause | Sorting based on | Search type| |-----|-----|-----| diff --git a/docs/guide/search/shop_search/extending_search/adding_elements_to_autosuggestion.md b/docs/guide/search/shop_search/extending_search/adding_elements_to_autosuggestion.md deleted file mode 100644 index c46fe4c3cd..0000000000 --- a/docs/guide/search/shop_search/extending_search/adding_elements_to_autosuggestion.md +++ /dev/null @@ -1,131 +0,0 @@ -# Adding elements to autosuggestion module [[% include 'snippets/commerce_badge.md' %]] - -Any Solr field can be displayed in autosuggest results. - -1\. Identify the Solr field you want to show. - -You can find the required field using Solr HTTP interface. - -The following example displays a preview of PDF content in the download module. - -Content of indexed files is stored in the `file_file_file_content_t` Solr field. - -2\. Add Solr field to configuration: - -``` yaml hl_lines="11" -siso_search.default.autosuggest_module_definitions: -download_autosuggest: - search_limit: 5 - images: true - search_fields: - - file_name_value_s - - file_description_value_s - result_fields: - - file_name_value_s - - file_description_value_s - - file_file_file_content_t - mime_types: - - application/pdf - result_fields_separator: ' - ' - text_limit: 60 -``` - -3\. PDF content is displayed in each result line. - -You can also customize the result line. -Each template has the full result line to allow template customization without service modification. -For example, the template for download module is `Resources/views/Search/autosuggest/search_autosuggest_download_line.html.twig`. - -The `resultLine` array has all information received from Solr, so you can use it to change field rendering. - -## Adding a label to products - -You can also show in autosuggestion information which is not available directly in Solr and requires processing. -To do this, create an indexer plugin and have your information processed. - -In this example you display a flag for new products. - -First, define what is a "new" product. -The example assumes a new product is a product that was published within the last month. - -You can use the Solr field `"published_dt": "2016-01-19T09:59:24Z"` to calculate whether the product is new or not. - -[Create an indexer plugin](indexer_plugin_for_custom_field_types.md) to add a new boolean field which is set to true if the current date - product published date <= 30 days. - -If the new field is `ext_product_new_s`, add it to the result fields of Solr in product configuration: - -``` yaml hl_lines="15" -product_autosuggest: - search_limit: 10 - images_field: main_image_url_s - display_cart_info: true #add to cart - types: - - 2 - path: '/2/' - section: 1 - search_fields: - - ses_product_ses_sku_value_s - - ses_product_ses_name_value_s - result_fields: - - ses_product_ses_sku_value_s - - ses_product_ses_name_value_s - - ext_product_new_s - result_fields_separator: ' - ' - text_limit: 35 -``` - -Now modify the `/Resources/views/Search/autosuggest/search_autosuggest_product_line.html.twig` template to read that value. - -Even it is not used, in every template you have the `resultLine` array with all Solr information. - -``` php -array (size=5) - 'id' => string 'content9982gerde' (length=16) - 'ses_product_ses_name_value_s' => string 'DMX Followspot HMI-1200' (length=23) - 'ses_product_ses_sku_value_s' => string '40180' (length=5) - 'main_image_url_s' => string '/var/ezdemo_site/storage/images/4/4/1/1/531144-2-ger-DE/40180.jpg' (length=65) - 'meta_indexed_language_code_s' => string 'ger-DE' (length=6) -``` - -Additionally, if you change the configuration to support your new indexed field, that new field should be part of this array: - -``` php hl_lines="7" -array (size=5) - 'id' => string 'content9982gerde' (length=16) - 'ses_product_ses_name_value_s' => string 'DMX Followspot HMI-1200' (length=23) - 'ses_product_ses_sku_value_s' => string '40180' (length=5) - 'main_image_url_s' => string '/var/ezdemo_site/storage/images/4/4/1/1/531144-2-ger-DE/40180.jpg' (length=65) - 'meta_indexed_language_code_s' => string 'ger-DE' (length=6) - 'ext_product_new_s' => bool true -``` - -The template modification might look like this: - -``` html+twig hl_lines="8 9 10 11" -<div class="row"> - <div class="small-8 large-8 columns"> - <a href="{{ ('/redirect_switcher?sku=' ~ sku ~ '&type=' ~ type)|st_siteaccess_path }}"> - {% if imageSrc is defined %} - <img src="{{ imageSrc }}" width="50px"/> - {% endif %} - {{ set_bold_text(searchQueryString, productText) }} - {% if resultLine.ext_product_new_s is defined and resultLine.ext_product_new_s %} - <!-- DISPLAY FANCY ANIMATED GIF WITH NEW LABEL --> - <img border="0" alt="animated gif" src="new3__e0.gif"> - {% endif %} - </a> - </div> - {% if addToBasket %} -<div class="small-4 large-4 columns"> -<form action="/basket/add" method="post"> - <section class="js-add-to-basket-parent"> - <input type="hidden" name="ses_basket[{{ number }}][quantity]" value="1"> - <input type="hidden" name="ses_basket[{{ number }}][sku]" value="{{ sku }}"> - <input type="hidden" name="autosuggest" value="autosuggest"> - <button class="button tiny js-add-to-basket" style="float:right;" data-sku="{{ sku }}" type="submit" name="add_to_basket"> - <i class="fa fa-cart-plus fa-lg fa-fw"></i> - </button> - </section> -</form> -</div> -``` diff --git a/docs/guide/search/shop_search/extending_search/custom_search.md b/docs/guide/search/shop_search/extending_search/custom_search.md deleted file mode 100644 index aaa0b8e9a1..0000000000 --- a/docs/guide/search/shop_search/extending_search/custom_search.md +++ /dev/null @@ -1,79 +0,0 @@ -# Implement custom search [[% include 'snippets/commerce_badge.md' %]] - -This example shows how to create a new service which searches for products that: - -- are discontinued -- are visible - -It sets the sorting option to `relevance`, adds a Manufacturer facet, -and boosts some of the fields (they will be more relevant than others). - -The built-in implementation of search interface has all methods required for searching using the Solr gateway. - -To get discontinued products the controller uses `SearchTermCondition`, which has the search phrase and the specific field. -In this case, in the current data model, all discontinued fields have the word `DECLINE` in Solr field `ses_product_ses_discontinued_value_s`. -A condition for visibility ensures that only visible products are returned. - -Boosting is set up so that SKU and name are more important than long description. - -The field boosting condition is implementation dependent. This means that the field name values which are passed to the constructor are different for eContent and content model search. -For content model data, you need to pass the content Field identifier strings. -For eContent you need to pass the raw Solr field names, including the default search field (which is `text` by default). - -``` php -class CustomSearchController extends BaseController -{ - public function searchDiscontinuedProductsAction() { - - // Inject the search and facet service - $searchService = $this->get('siso_search.ezsolr_search_service'); - - // Create the query with all the parameters we want. - $eshopQuery = new EshopQuery(); - $eshopQuery->addCondition(new SearchTermCondition(array( - 'searchTerm' => 'DECLINE', - 'fieldRestrictions' => 'ses_product_ses_discontinued_value_s' - ))); - - // Add visibility - $eshopQuery->addCondition(new VisibilityCondition(array('visibility' => 0))); - - // Add boosting (Content items) - $boosts = array( - 'ses_name' => 50, - 'ses_sku' => 100, - 'ses_intro' => 20, - 'ses_short_description' => 2, - 'ses_long_description' => 1.5, - 'ses_manufacturer' => 2, - ); - // Add boosting (Econtent) - $boosts = array( - 'text' => 1, - 'ses_product_ses_name_value_t' => 50, - 'ses_product_ses_sku_value_s' => 100, - 'ses_product_ses_intro_value_t' => 20, - 'ses_product_ses_short_description_value_t' => 2, - 'ses_product_long_description_value_t' => 1.5, - 'ses_product_ses_manufacturer_value_s' => 2, - ); - $eshopQuery->addCondition(new FieldBoosting(array('boost' => $boosts))); - - // Add offset and limit - $eshopQuery->setOffset(0); - $eshopQuery->setLimit(10); - - // Add sorting option by relevance - $eshopQuery->setSortCriteria(array(new RelevanceSorting())); - - // Perform the search - $productSearchResult = $searchService->searchProducts($eshopQuery, new SearchContext()); - - // The hits can be found here - $numberOfHits = $productSearchResult->numFound; - - // The search results can be found in this array: - $productSearchResult->resultLines; - } -} -``` diff --git a/docs/guide/search/shop_search/extending_search/custom_search_conditions.md b/docs/guide/search/shop_search/extending_search/custom_search_conditions.md deleted file mode 100644 index 0cd3febabd..0000000000 --- a/docs/guide/search/shop_search/extending_search/custom_search_conditions.md +++ /dev/null @@ -1,67 +0,0 @@ -# Implement custom search condition [[% include 'snippets/commerce_badge.md' %]] - -To create a custom search condition, implement the following classes: - -### Step 1: Create new class that implements `ConditionInterface` - -Create a value object which checks if all necessary information for this condition is set: - -``` php -class CustomCondition extends ValueObject implements ConditionInterface -{ - /** - * @var null|array $checkProperties - */ - protected $checkProperties = array( - array('name' => 'customString', 'mandatory' => true, 'type' => 'string'), - ); - - /** - * @var string - */ - protected $customString; -} -``` - -### Step 2: Create new handler class - -You can use any Search Criteria here. The example uses `Query\Criterion\Visibility`: - -```php -class CustomConditionHandler implements EzSearchClauseHandlerInterface -{ - /** - * @param SearchClauseInterface $searchClause - * @param Query $query - */ - public function handleSearchClause(SearchClauseInterface $searchClause, Query $query) - { - if (!($query->query instanceof Query\Criterion\LogicalOperator)) { - return; - } - $ezCriterion = new Query\Criterion\Visibility($searchClause->customString); - $query->query->criteria[] = $ezCriterion; - } - - /** - * @param SearchClauseInterface $searchClause - * @return bool - */ - public function canHandle(SearchClauseInterface $searchClause) - { - return $searchClause instanceof CustomCondition; - } -} -``` - -### Step 3: Register the service - -Add configuration in `services.xml` to register the handler. The handler is then used whenever a Search Clause is an instance of `CustomCondition`. - -``` xml -<parameter key="siso_search.search_clause_handler.custom.ezsolr.class">path\to\CustomConditionHandler</parameter> - -<service id="siso_search.search_clause_handler.custom.ezsolr" class="%siso_search.search_clause_handler.custom.ezsolr.class%"> - <tag name="siso_search.search_clause_handler" type="ezsolr" /> -</service> -``` diff --git a/docs/guide/search/shop_search/extending_search/custom_sorting_options.md b/docs/guide/search/shop_search/extending_search/custom_sorting_options.md deleted file mode 100644 index 9fa4983435..0000000000 --- a/docs/guide/search/shop_search/extending_search/custom_sorting_options.md +++ /dev/null @@ -1,71 +0,0 @@ -# Implement custom sorting option for search [[% include 'snippets/commerce_badge.md' %]] - -To create a custom sorting option, implement the following classes: - -### Step 1: Create new class that extends `AbstractSortCriterion` - -Create a value object which checks if all necessary information for this condition is set: - -``` php -class CustomFieldSorting extends AbstractSortCriterion -{ -} -``` - -### Step 2: Create new handler class - -``` php -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field; - - -class CustomFieldSortingHandler implements EzSearchClauseHandlerInterface -{ - /** - * @param SearchClauseInterface $searchClause - * @param Query $query - */ - public function handleSearchClause(SearchClauseInterface $searchClause, Query $query) - { - $field = new Field('ses_custom_field', 'ext_ses_custom_field_f'); - $query->sortClauses[] = $field; - } - - /** - * @param SearchClauseInterface $searchClause - * @return bool - */ - public function canHandle(SearchClauseInterface $searchClause) - { - return $searchClause instanceof CustomFieldSorting; - } -} -``` - -### Step 3: Register the service - -Add configuration in `services.xml` to register the handler. The handler is then used whenever a Search Clause is an instance of `CustomFieldSorting`. - -``` xml -<parameter key="siso_search.search_clause_handler.custom_sort.ezsolr.class">path\to\CustomFieldSorting</parameter> - -<service id="siso_search.search_sort_handler.custom_sort.ezsolr" class="%siso_search.search_sort_handler.custom_sort.ezsolr.class%"> - <tag name="siso_search.search_clause_handler" type="ezsolr" /> -</service> -``` - -#### Multiple sorting criteria - -A query can have several sorting criteria. The order of the sorting criteria is important. -In the following example the sorting is performed in the order they are placed in the array: - -```php -$eshopQuery->setSortCriteria( - array( - new MyCustomSorting1(array('direction' => 'asc')), - new MyCustomSorting2(array('direction' => 'desc')), - new MyCustomSorting3(array('direction' => 'desc')), - ... - ) -) -``` diff --git a/docs/guide/search/shop_search/extending_search/indexer_plugin_for_custom_field_types.md b/docs/guide/search/shop_search/extending_search/indexer_plugin_for_custom_field_types.md deleted file mode 100644 index 2c5a6cb645..0000000000 --- a/docs/guide/search/shop_search/extending_search/indexer_plugin_for_custom_field_types.md +++ /dev/null @@ -1,159 +0,0 @@ -# Implement an indexer plugin for custom Field Types [[% include 'snippets/commerce_badge.md' %]] - -The indexer is a class that implements the `DocumentMapperPluginInterface` interface from `ezplatform-solr-search-engine`. - -It enables indexing additional data with Solr. For example, it enables searching with additional complex data type like specification matrix, variant matrix, etc., as conditions. - -The interface has the following methods that must be implemented: - -- `canExtend(Type $type)` - defines which Content Type is handled by the indexer plugin. -- `createExtensionFields(Content $content, Type $type, $languageCode)` - is called for every indexed Content item and language. -The output is an array of SPI Field objects which are used by the main indexer to create Solr fields. - -The generated Field instances have the `ext_` prefix in their names to avoid naming conflicts with existing content Fields -and to distinguish them from standard fields. - -### Step 1: Create a class - -Create a class that extends `DocumentMapperPluginInterface` and define a service for that class: - -``` php -class MyCustomMapperPlugin implements DocumentMapperPluginInterface -``` - -Make sure your plugin uses this files: - -``` php -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\FloatField; -use EzSystems\EzPlatformSolrSearchEngine\DocumentMapperPluginInterface; -use Siso\Bundle\SearchBundle\Helper\EzSolrSpecificationsIndexHelper; -use Siso\Bundle\SearchBundle\Helper\StaticDocumentMapperPluginHelper as Helper; -``` - -### Step 2. Create a service for the class - -``` xml -<parameter key="siso_search.my_custom_mapper_plugin.class">Siso\Bundle\SearchBundle\Service\MyCustomMapperPlugin</parameter> - -<!-- Start Indexer Plugins --> -<service id="siso_search.my_custom_mapper_plugin" class="%siso_search.my_custom_mapper_plugin.class%"> - <tag name="ezpublish.search.solr.document_mapper_plugin" /> -</service> -``` - -This allows plugin execution every time the index command is executed. - -### Step 3. Define elements to handle - -Define which elements will be handled by your plugin using `canExtend()`. - -``` php -public function canExtend(Type $type) -{ - return $type->identifier === 'ses_product'; -} -``` - -In this example you want to create additional indexes for `ses_product`. - -### Step 4. Create indexer logic - -Create the logic you want to extend the indexer with. - -The input parameters of the main methods are not regular catalog elements but standard Content items and Content Types. - -In the following example you use additional logic to get the Field names you need for custom indexing by implementing: - -``` php -public function createExtensionFields(Content $content, Type $type, $languageCode); -``` - -If you know the Field identifier you can use the static method `getFieldDefinitionId()` from the helper class to get the Field IDs that will be processed. - -``` php -$myFieldId = Helper::getFieldDefinitionId($type, $myFieldIdentifier); -``` - -Then you need to iterate each Field over `$content` and if the Field ID matches the Fields you want to process, add your logic. - -``` php -// Iteration over each Field inside the content. -foreach ($content->fields as $field) { - // If the Field matches the ID, you want to process we can add our logic. - if ($field->fieldDefinitionId === $myFieldId - && $field->languageCode === $languageCode - ) { - // The index logic indexes price elements as floats: - $productPrice = Helper::getFieldValue($content, $myFieldId); // This helper function retrieves the field value. - if ($productPrice !== null) { - $outputFields[] = new Field( // Returns an array of Fields. - 'ext_ses_product_price', //this is the name of the Solr index. - Helper::normalizeFloatString((string) $productPrice), // this transforms the field to float. - new FloatField() // This specifies element data type. - // Valid types are: IntegerField(), FloatField(), - // StringField(), MultipleIntegerField and MultipleStringField(). - ); - } - } -} -``` - -!!! note - - Multiple float Fields for the external Solr field are not supported. - -At the end return the array of fields: - -``` php -return $outputFields; -``` - -### Step 5. Names ready in Solr index - -The Solr index should have the name you defined + `_` + the datatype. -The datatype is represented by: - -- `i` - integer, -- `s` - string, -- `f` - float, -- `mi` - multi integer, -- `ms` - multi string. - -For example: - -``` php -{ - "ext_ses_product_price_f": 12.5 -}, -``` - -Multiple string example: - -``` php -"language_code_ms": [ - "eng-US", - "ger-DE" -], -``` - -### Step 6. Test new fields - -If you want to test how your new fields look like in Solr you have to run the reindex command -and perform a Solr query directly in Solr interface. - -``` bash -php -d memory_limit=-1 bin/console ezplatform:solr_create_index -``` - -Make sure you execute this in your project home directory. - -To perform a Solr query go to Solr URL, usually: `http://<yourdomain>:8983/solr/` - -Then select your collection (usually `collection1`) and select a query. - -Additionally you can set Solr to only show the new field by adding the field name to the `fl` field. - -![](../../../img/search_9.png) diff --git a/docs/guide/search/shop_search/extending_search/indexing_email_data.md b/docs/guide/search/shop_search/extending_search/indexing_email_data.md deleted file mode 100644 index f3c8ec0880..0000000000 --- a/docs/guide/search/shop_search/extending_search/indexing_email_data.md +++ /dev/null @@ -1,110 +0,0 @@ -# Indexing email data [[% include 'snippets/commerce_badge.md' %]] - -Indexing email data requires creating the `SearchField` class for the User Content item. -The User Field Type doesn't have an implementation of `SearchField`, so this `SearchField` class must implement the `Indexable` interface. - -Service definition: - -``` xml -<service id="ezpublish.fieldType.indexable.user" class="%ezpublish.fieldType.indexable.user.class%"> - <tag name="ezpublish.fieldType.indexable" alias="ezuser" /> -</service> -``` - -Tag the service with `ezpublish.fieldType.indexable`. This enables the indexer to execute this service in the indexer process. - -The alias indicates which element is to be extended. In the example above it is `ezuser`. - -### Indexing email - -User email is taken from an SPI Field which has an `externalData` array with several fields. -The following example gets the email Field and indexes it in two Solr fields: - -``` php -/** - * Get index data for field for backend search - * - * @param Field $field - * @param FieldDefinition $fieldDefinition - * - * @return Search\Field[] - */ -public function getIndexData(Field $field, FieldDefinition $fieldDefinition) -{ - return array( - new Search\Field( - 'email_value', - $field->value->externalData['email'], - new Search\FieldType\StringField() - ), - new Search\Field( - 'email_value', - $field->value->externalData['email'], - new Search\FieldType\FullTextField() - ), - ); -} -``` - -### Indexing additional user data - -The `externalData` array has the following data: - -- `hasStoredLogin` -- `contentId` -- `login` -- `email` -- `passwordHash` -- `passwordHashType` -- `enabled` -- `maxLogin` - -To index additional data related to the user, you can add new `Search\Field` to the return array of method `getIndexData`. - -``` php -return array( - new Search\Field( - 'email_value', - $field->value->externalData['email'], - new Search\FieldType\StringField() - ), - new Search\Field( - 'email_value', - $field->value->externalData['email'], - new Search\FieldType\FullTextField() - ), - - // New data to be indexed: - new Search\Field( - 'login_value', - $field->value->externalData['login'], - new Search\FieldType\StringField() - ), -); -``` - -If you add a new search field, you also need to add the field definition to the `getIndexDefinition()` method: - -``` php -/** - * Get index field types for backend search - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ -public function getIndexDefinition() -{ - return array( - 'email_value' => new Search\FieldType\StringField(), - - // New field definition for login name: - 'login_value' => new Search\FieldType\StringField(), - ); -} -``` - -This example uses Solr string field and full text field. - -String fields and text fields should be visible as a search result in Solr web administration. -Full text fields are only visible in schema browser. - -In this example the Solr text field name for email is `user_user_account_email_value_s`. diff --git a/docs/guide/search/shop_search/extending_search/indexing_file_content.md b/docs/guide/search/shop_search/extending_search/indexing_file_content.md deleted file mode 100644 index 98e68b7054..0000000000 --- a/docs/guide/search/shop_search/extending_search/indexing_file_content.md +++ /dev/null @@ -1,93 +0,0 @@ -# Indexing file content [[% include 'snippets/commerce_badge.md' %]] - -Indexing the content of files stored in the content model uses an additional component called Apache Tika. - -Apache Tika has two available modes: Server Mode and App Mode. [[= product_name_com =]] uses Apache Tika in App mode. - -You can specify which MIME types are indexed in configuration. -If you need additional file types to be indexed, add them to this configuration, for example: - -``` yaml -siso_search.default.index_content: - - application/pdf - - application/vnd.ms-excel - - application/msword -``` - -See [Supported Document Formats](http://tika.apache.org/1.13/formats.html) for a list of formats supported by Apache Tika. - -!!! caution - - Make sure [Apache Tika Bundle](https://packagist.org/packages/jolicode/apache-tika-bundle) is installed - and is enabled in the kernel. - - Apache Tika log file (`/tmp/tika-error.log`) is hardcoded. Make sure the file is writable both by the command-line user who executes the indexer - and the Apache user, because the indexer is triggered after a file is modified in the backend. - - If this file is not writable, the indexer does not index file contents. - -## Extending file content indexer - -In order to extend the indexer for files you have to extend the `Ibexa\Platform\Commerce\FieldTypes\FieldType\BinaryFile\SearchField` service. - -The `getIndexData(Field $field, FieldDefinition $fieldDefinition)` returns an array of `\eZ\Publish\SPI\Search\Field` objects. - -This object is created with the following parameters: - -- `name` - a name that is used to build the Solr field name. -- `data` - the data to be indexed. -- Solr field type - you can use any Solr field type. In the PDF example you index the data both as FullTextField and as TextField. - -``` php -$parentData = parent::getIndexData($field, $fieldDefinition); - -// Return the data that is coming from Apache Tika -$fileContent = $this->getFileContent($field); - -// Create a Solr FullTextField -$parentData[] = new Search\Field( - 'file_content', - $fileContent, - new Search\FieldType\FullTextField() -); - -// Create a Solr TextField -$parentData[] = new Search\Field( - 'file_content', - $fileContent, - new Search\FieldType\TextField() -); - -return $parentData; -``` - -String fields and text fields should be visible as a search result in Solr web administration. -Full text fields are only visible in schema browser. - -In this example the Solr text field name for `file_content` is `file_file_file_content_t`. - -## Extracting information from a file - -The core of this indexer is Apache Tika, a component that can fetch a file and return its contents in plain text, or in HTML. - -Apache Tika is injected as a service into the constructor of this class: - -``` php -public function __construct($apacheTikaService) -{ - $this->apacheTikaService = $apacheTikaService; -} -``` - -The main method is `getText`: - -``` php -$fileContent = $this->apacheTikaService->getText($fileName); -``` - -You can also use additional methods to get HTML or metadata of the file: - -``` -getMetadata($fileName) -getHTML($fileName) -``` diff --git a/docs/guide/search/shop_search/extending_search/modifying_the_search_query.md b/docs/guide/search/shop_search/extending_search/modifying_the_search_query.md deleted file mode 100644 index a9b8b47048..0000000000 --- a/docs/guide/search/shop_search/extending_search/modifying_the_search_query.md +++ /dev/null @@ -1,68 +0,0 @@ -# Modifying the search query [[% include 'snippets/commerce_badge.md' %]] - -Sometimes you need to modify the [EshopQuery](../search_api.md) before it is sent to the search service. -The event listener handles this situation. -You can use it if you need to modify the search term, or add some sorting criteria. - -For example, when searching for a book ISBN number that needs to be modified. -When a user searches for `2-245-5-4878-0`, you want to modify the search term to remove the `-` and search only for `224558780`. - -`SearchController` already contains an event that is dispatched after the complete query is built. -If you want to modify it, you need to implement an event listener for your project. - -In the following example you modify the sort criteria of a query if the query is empty -and if you detect that current sort criteria is relevance sorting (default). - -You can use any [EshopQuery](../search_api.md) getter methods to check any condition or property -and then use any setter method to modify whatever you need. - -``` php -use Siso\Bundle\SearchBundle\Api\Common\RelevanceSorting; -use Siso\Bundle\SearchBundle\Api\Common\SearchTermCondition; -use Siso\Bundle\SearchBundle\Event\PostBuildEshopQueryEvent; -use MyProject\Bundle\ProjectBundle\Api\Search\PriorityFieldSorting; - -class EshopQueryListener -{ - /** - * modifies the eshop query after it was build - * - * @param PostBuildEshopQueryEvent $event - */ - public function onPostBuildEshopQuery(PostBuildEshopQueryEvent $event) - { - /** all form parameters are stored here incl. the search context: - * - product - * - catalog - * - content - */ - $params = $event->getParams(); - - $eshopQuery = $event->getEshopQuery(); - $conditions = $eshopQuery->getConditions(); - - foreach($conditions as $index => $condition) { - if ($condition instanceof SearchTermCondition) { - //if the search term is empty, sort additionally by the priority - if ((($condition->searchTerm === '') || ($condition->searchTerm === '*')) && - ($eshopQuery->getSortCriteria()[0] instanceof RelevanceSorting)) { - $eshopQuery->setSortCriteria(array(new PriorityFieldSorting(array('direction' => 'desc')))); - } - } - } - - //modify the query by setting new conditions - $eshopQuery->setConditions($conditions); - } -} -``` - -Service definition: - -``` php -<parameter key="myproject.eshop_query_listener.class">MyProject\Bundle\ProjectBundle\EventListener\EshopQueryListener</parameter> - -<service id="myproject.eshop_query_listener" class="%myproject.eshop_query_listener.class%"> - <tag name="kernel.event_listener" event="siso_search.post_build_eshop_query" method="onPostBuildEshopQuery" /> -</service> -``` diff --git a/docs/guide/search/shop_search/search_api.md b/docs/guide/search/shop_search/search_api.md index 49a6856253..03b8b2e392 100644 --- a/docs/guide/search/shop_search/search_api.md +++ b/docs/guide/search/shop_search/search_api.md @@ -1,4 +1,4 @@ -# Search API [[% include 'snippets/commerce_badge.md' %]] +# Search API ## Search interfaces diff --git a/docs/guide/search/shop_search/search_autosuggest.md b/docs/guide/search/shop_search/search_autosuggest.md index eb76ad6b3b..108baf3f6b 100644 --- a/docs/guide/search/shop_search/search_autosuggest.md +++ b/docs/guide/search/shop_search/search_autosuggest.md @@ -1,4 +1,4 @@ -# Autosuggestion [[% include 'snippets/commerce_badge.md' %]] +# Autosuggestion The built-in autosuggestion function provides a user-friendly way to find products, categories, content or downloads which match the given search criteria. @@ -226,6 +226,5 @@ Autosuggestion filters are applied automatically to avoid displaying elements th ### eContent Filters - Document Type -- Catalog segmentation - Languages - Visibility diff --git a/docs/guide/search/shop_search/search_configuration.md b/docs/guide/search/shop_search/search_configuration.md index 4c2167e1ed..d666d8aa7c 100644 --- a/docs/guide/search/shop_search/search_configuration.md +++ b/docs/guide/search/shop_search/search_configuration.md @@ -1,4 +1,4 @@ -# Search configuration [[% include 'snippets/commerce_badge.md' %]] +# Search configuration ## Product groups diff --git a/docs/guide/search/shop_search/search_indexing.md b/docs/guide/search/shop_search/search_indexing.md index e3c07bcce7..667f3c951d 100644 --- a/docs/guide/search/shop_search/search_indexing.md +++ b/docs/guide/search/shop_search/search_indexing.md @@ -1,16 +1,16 @@ -# Search indexing [[% include 'snippets/commerce_badge.md' %]] +# Search indexing ## Reindexing Use the following command to reindex the search content: ``` bash -php bin/console ezplatform:reindex +php bin/console ibexa:reindex ``` ## Removing index in production -When you use the content model as a data provider, `ezplatform:solr_create_index` removes the index in production. +When you use the content model as a data provider, `ibexa:reindex` removes the index in production. Details of this action depend on the configuration. If Solr is configured to auto-commit, the index is removed. If no auto-commit is configured, the index is removed as well, but the removal takes effect after the commit at the end. diff --git a/docs/guide/search/shop_search/search_synonyms.md b/docs/guide/search/shop_search/search_synonyms.md index a103c13637..74013f715a 100644 --- a/docs/guide/search/shop_search/search_synonyms.md +++ b/docs/guide/search/shop_search/search_synonyms.md @@ -1,4 +1,4 @@ -# Search synonyms [[% include 'snippets/commerce_badge.md' %]] +# Search synonyms Synonyms enable different phrase inputs that have the same meaning. diff --git a/docs/guide/search/shop_search/search_templates.md b/docs/guide/search/shop_search/search_templates.md index 5fa4889d63..05ba8bf02a 100644 --- a/docs/guide/search/shop_search/search_templates.md +++ b/docs/guide/search/shop_search/search_templates.md @@ -1,4 +1,4 @@ -# Search templates [[% include 'snippets/commerce_badge.md' %]] +# Search templates ## Products diff --git a/docs/guide/search/shop_search/searching_for_products.md b/docs/guide/search/shop_search/searching_for_products.md index 4e45d0f006..960e109df1 100644 --- a/docs/guide/search/shop_search/searching_for_products.md +++ b/docs/guide/search/shop_search/searching_for_products.md @@ -1,4 +1,4 @@ -# Product search [[% include 'snippets/commerce_badge.md' %]] +# Product search This example uses the product search API to search products using `searchterm`: diff --git a/docs/guide/search/shop_search/shop_search.md b/docs/guide/search/shop_search/shop_search.md index f3b57d688d..26b44251c3 100644 --- a/docs/guide/search/shop_search/shop_search.md +++ b/docs/guide/search/shop_search/shop_search.md @@ -1,6 +1,17 @@ -# Shop search [[% include 'snippets/commerce_badge.md' %]] +# Shop search -The built-in search engine based on Solr automatically indexes both content and products. +The built-in search engine automatically indexes both content and products. + +!!! caution + + You can use [all the supported search capabilities](../search.md#search-engine-comparison) + of either Solr or Elasticsearch with the shop. + + You can also run the shop using the Legacy search engine, + but it only covers the most basic functionalities such as displaying the product catalog. + + Running the Legacy search engine disables product search, + as well as other functionalities, such as unsupported sorting types in the catalog. The search displays the results in different groups. A group can consist of products, videos, downloads, etc. diff --git a/docs/guide/search/shop_search/using_solr_spellcheck.md b/docs/guide/search/shop_search/using_solr_spellcheck.md index 6140204514..129a67a79d 100644 --- a/docs/guide/search/shop_search/using_solr_spellcheck.md +++ b/docs/guide/search/shop_search/using_solr_spellcheck.md @@ -1,4 +1,4 @@ -# Solr spellcheck [[% include 'snippets/commerce_badge.md' %]] +# Solr spellcheck !!! caution diff --git a/docs/guide/search/solr.md b/docs/guide/search/solr.md index 90a5ba0af4..3d811a0cee 100644 --- a/docs/guide/search/solr.md +++ b/docs/guide/search/solr.md @@ -1,8 +1,12 @@ +--- +description: Configure Solr search engine to use with Ibexa DXP. +--- + # Solr search engine [ezplatform-solr-search-engine](https://github.com/ezsystems/ezplatform-solr-search-engine) aims to be a transparent drop-in replacement for the SQL-based Legacy search engine powering [[= product_name =]] Search API by default. When you enable Solr and re-index your content, all your existing Search queries using `SearchService` will be powered by Solr automatically. This allows you to scale up your [[= product_name =]] installation and be able to continue development locally against SQL engine, and have a test infrastructure, Staging and Prod powered by Solr. This removes considerable load from your database. See [further information on the architecture of [[= product_name =]]](../architecture.md). -## How to set up Solr search engine +## Set up Solr search engine !!! note "Installing the bundle" @@ -11,10 +15,10 @@ composer require --no-update ezsystems/ezplatform-solr-search-engine:~3.0 composer update ``` - + Symfony Flex will enable the bundle for you when installing the package. -### Step 1: Configuring and starting Solr +### Step 1: Configure and start Solr The example presents a configuration with a single core. For configuring Solr in other ways, including examples, see [Solr Cores and `solr.xml`](https://cwiki.apache.org/confluence/display/solr/Solr+Cores+and+solr.xml) and [core administration](https://wiki.apache.org/solr/CoreAdmin). @@ -36,13 +40,17 @@ Copy the necessary configuration files. In the example below from the root of yo # Make sure to replace the /opt/solr/ path with where you have placed Solr cd /opt/solr mkdir -p server/ez/template -cp -R <ezplatform-solr-search-engine>/lib/Resources/config/solr/* server/ez/template +cp -R <project_root>/vendor/ezsystems/ezplatform-solr-search-engine/lib/Resources/config/solr/* server/ez/template cp server/solr/configsets/_default/conf/{solrconfig.xml,stopwords.txt,synonyms.txt} server/ez/template cp server/solr/solr.xml server/ez +# If you are using Ibexa Commerce, additionally copy commerce-specific configuration files: +cat <project_root>/vendor/ezsystems/ezcommerce-shop/src/Siso/Bundle/SearchBundle/Resources/config/solr/custom-fields-types.xml >> server/ez/template/custom-fields-types.xml +cat <project_root>/vendor/ezsystems/ezcommerce-shop/src/Siso/Bundle/SearchBundle/Resources/config/solr/language-fieldtypes.xml >> server/ez/template/language-fieldtypes.xml + # Modify solrconfig.xml to remove the section that doesn't agree with your schema sed -i.bak '/<updateRequestProcessorChain name="add-unknown-fields-to-the-schema".*/,/<\/updateRequestProcessorChain>/d' server/ez/template/solrconfig.xml -  + # Start Solr (but apply autocommit settings below first if you need to) bin/solr -s ez bin/solr create_core -c collection1 -d server/ez/template @@ -89,7 +97,7 @@ Execute the script from the [[= product_name =]] root directory for further info ./vendor/ezsystems/ezplatform-solr-search-engine/bin/generate-solr-config.sh --help ``` -### Step 2: Configuring the bundle +### Step 2: Configure the bundle The Solr Search Engine Bundle can be configured in many ways. The config further below assumes you have parameters set up for Solr DSN and search engine *(however both are optional)*, for example: @@ -210,7 +218,7 @@ ez_search_engine_solr: endpoints: main: dsn: '%solr_dsn%' - core: '%solr_main_core%' + core: '%solr_main_core%' en: dsn: '%solr_dsn%' core: '%solr_en_core%' @@ -252,7 +260,7 @@ ez_search_engine_solr: Obviously, you should pass credentials for every configured and HTTP Basic secured Solr core. Configuration for multi core setup is exactly the same. -### Step 3: Configuring repository with the specific search engine +### Step 3: Configure repository with the specific search engine The following is an example of configuring Solr search engine, where `connection` name is same as in the example above, and engine is set to `solr`: @@ -281,7 +289,7 @@ php bin/console --env=prod cache:clear The last step is to execute the initial indexation of data: ``` bash -php bin/console --env=prod --siteaccess=<name> ezplatform:reindex +php bin/console --env=prod --siteaccess=<name> ibexa:reindex ``` #### Possible exceptions @@ -293,14 +301,14 @@ Here are the most common issues you may encounter: - Make sure `var_dir` is configured properly in `ezplatform.yaml` configuration. - If your database is inconsistent in regards to file paths, try to update entries to be correct *(make sure to make a backup first)*. - Exception on unsupported Field Types - - Make sure to implement all Field Types in your installation, or to configure missing ones as [NullType](../../api/field_type_reference.md#null-field-type) if implementation is not needed. + - Make sure to implement all Field Types in your installation, or to configure missing ones as [NullType](../../api/field_types_reference/nullfield.md) if implementation is not needed. - Content is not immediately available  - Solr Bundle on purpose does not commit changes directly on Repository updates *(on indexing)*, but lets you control this using Solr configuration. Adjust Solr's `autoSoftCommit` (visibility of changes to search index) and/or `autoCommit` (hard commit, for durability and replication) to balance performance and load on your Solr instance against needs you have for "[NRT](https://cwiki.apache.org/confluence/display/solr/Near+Real+Time+Searching)". - Running out of memory during indexing - In general make sure to run indexing using the prod environment to avoid debuggers and loggers from filling up memory. - Flysystem: You can find further info in https://jira.ez.no/browse/EZP-25325. -## Configuring the Solr Search Engine Bundle +## Solr configuration ### Boost configuration @@ -417,8 +425,9 @@ The configuration above will result in the following boosting (Content Type / Fi Remember to clear the cache and perform search engine reindex afterwords. The above configuration will result in the following boosting (Content Type / Field): + - `folder/name: 20.0` - - `folder/title: 10.0` + - `folder/description: 10.0` ### Indexing related objects @@ -440,153 +449,13 @@ ez_search_engine_solr: article: 2 ``` -## Extending the Solr Search Engine Bundle - -### Document field mappers - -!!! note - - Document Field Mappers are available since Solr bundle version 1.2. - -You can use document field mappers to index additional data in the search engine. - -The additional data can come from external sources (e.g. from a recommendation system), or from internal ones. -An example of the latter is indexing data through the Location hierarchy: from the parent Location to the child Location, or indexing child data on the parent Location. -This may be needed when you want to find the content with full-text search, or to simplify search for a complicated data model. - -To do this effectively, you first need to understand how the data is indexed with the Solr search engine. -Solr uses [documents](https://lucene.apache.org/solr/guide/7_7/overview-of-documents-fields-and-schema-design.html#how-solr-sees-the-world) as a unit of data that is indexed. -Documents are indexed per translation, as content blocks. A block is a nested document structure. -When used in [[= product_name =]], a parent document represents content, and Locations are indexed as child documents of the Content item. -To avoid duplication, full-text data is indexed on the Content document only. Knowing this, you have the option to index additional data on: - -- all block documents (meaning content and its Locations, all translations) -- all block documents per translation -- content documents -- content documents per translation -- Location documents - -Indexing additional data is done by implementing a document field mapper and registering it at one of the five extension points described above. -You can create the field mapper class anywhere inside your bundle, -as long as when you register it as a service, the `class` parameter in your `services.yaml` matches the correct path. -There are three different field mappers. Each mapper implements two methods, by the same name, but accepting different arguments: - -- `ContentFieldMapper` - - `::accept(Content $content)` - - `::mapFields(Content $content)` -- `ContentTranslationFieldMapper` - - `::accept(Content $content, $languageCode)` - - `::mapFields(Content $content, $languageCode)` -- `LocationFieldMapper` - - `::accept(Location $content)` - - `::mapFields(Location $content)` - -These can be used on the extension points by registering them with the container using service tags, as follows: - -- all block documents - - `ContentFieldMapper` - - `ezpublish.search.solr.field_mapper.block` -- all block documents per translation - - `ContentTranslationFieldMapper` - - `ezpublish.search.solr.field_mapper.block_translation` -- Content documents - - `ContentFieldMapper` - - `ezpublish.search.solr.field_mapper.content` -- Content documents per translation - - `ContentTranslationFieldMapper` - - `ezpublish.search.solr.field_mapper.content_translation` -- Location documents - - `LocationFieldMapper` - - `ezpublish.search.solr.field_mapper.location` - -The following example shows how to index data from the parent Location content, in order to make it available for full-text search on the children content. -It is based on the use case of indexing webinar data on the webinar events, which are children of the webinar. Field mapper could then look like this: - -```php - <?php - -namespace My\WebinarApp; - -use EzSystems\EzPlatformSolrSearchEngine\FieldMapper\ContentFieldMapper; -use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Search; - -class WebinarEventTitleFulltextFieldMapper extends ContentFieldMapper -{ - /** - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $contentHandler; - - /** - * @var \eZ\Publish\SPI\Persistence\Content\Location\Handler - */ - protected $locationHandler; - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Handler $contentHandler - * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $locationHandler - */ - public function __construct( - ContentHandler $contentHandler, - LocationHandler $locationHandler - ) { - $this->contentHandler = $contentHandler; - $this->locationHandler = $locationHandler; - } - - public function accept(Content $content) - { - // ContentType with ID 42 is webinar event - return $content->versionInfo->contentInfo->contentTypeId == 42; - } - - public function mapFields(Content $content) - { - $mainLocationId = $content->versionInfo->contentInfo->mainLocationId; - $location = $this->locationHandler->load($mainLocationId); - $parentLocation = $this->locationHandler->load($location->parentId); - $parentContentInfo = $this->contentHandler->loadContentInfo($parentLocation->contentId); - - return [ - new Search\Field( - 'fulltext', - $parentContentInfo->name, - new Search\FieldType\FullTextField() - ), - ]; - } -} -``` - -Since you index full text data only on the content document, you would register the service like this: - -``` yaml -my_webinar_app.webinar_event_title_fulltext_field_mapper: - class: My\WebinarApp\WebinarEventTitleFulltextFieldMapper - arguments: - - '@ezpublish.spi.persistence.content_handler' - - '@ezpublish.spi.persistence.location_handler' - tags: - - {name: ezpublish.search.solr.field_mapper.content} -``` - - -!!! caution "Permission issues when using Repository API in document field mappers" - - Document field mappers are low level. They expect to be able to index all content regardless of current user permissions. - If you use PHP API in your custom document field mappers, you need to apply [`sudo()`](../../api/public_php_api.md#using-sudo), - otherwise use the Persistence SPI layer as in the example above. - -## Configuring Solr Replication (master/slave) +### Configuring Solr Replication (master/slave) !!! note The configuration below has been tested on Solr 7.7. -### Configuring Master for replication +#### Configuring Master for replication First you need to change the core configuration in `solrconfig.xml` (for example `*/opt/solr/server/ez/collection1/conf/solrconfig.xml`). You can copy and paste the code below before any other `requestHandler` section. @@ -613,7 +482,7 @@ Then restart the master with: sudo su - solr -c "/opt/solr/bin/solr restart" ``` -### Configuring Slave for replication +#### Configuring Slave for replication You have to edit the same file on the slave server, and use the code below: @@ -660,3 +529,23 @@ Next, restart Solr slave. Connect to the Solr slave interface (http://localhost:8983/solr), go to your core and check the replication status: ![Solr Slave](../img/solr.PNG) + +## Configuring HTTP Client for Solr queries + +Ibexa Solr Bundle uses Symfony HTTP Client to fetch and update Solr index. +You can configure timeout and maximum number of retries for that client using Solr Bundle's Semantic configuration: + +```yaml +ez_search_engine_solr: + # ... + http_client: + # ... + timeout: 30 + max_retries: 5 +``` + + +## Extending Solr + +To learn how you can create document field mappers, custom Search Criteria, +custom Sort Clauses and Aggregations, see [Search extensibility](extensibility/create_custom_search_criterion.md). diff --git a/docs/guide/search/sort_clause_reference.md b/docs/guide/search/sort_clause_reference.md deleted file mode 100644 index 286d828b9b..0000000000 --- a/docs/guide/search/sort_clause_reference.md +++ /dev/null @@ -1,33 +0,0 @@ -# Sort Clause reference - -Sort Clauses are the sorting options for Content and Location Search and -[Repository filtering](../../api/public_php_api_search.md#repository-filtering). - -Capabilities of individual Sort Clauses can depend on the search engine. - -All Sort Clauses can take the following optional argument: - -- `sortDirection` - the direction of the sorting, either `Query::SORT_ASC` (default) or `Query::SORT_DESC` - -#### Sort Clauses - -| Sort Clause | Sorting based on | Supported by | -|-----|-----|-----| -|[ContentId](sort_clause_reference/contentid_sort_clause.md)|Content items' ID|Content and Location Search, and Filtering| -|[ContentName](sort_clause_reference/contentname_sort_clause.md)|Content names|Content and Location Search, and Filtering| -|[ContentTranslatedName](sort_clause_reference/contenttranslatedname_sort_clause.md)|Translated content names|Content and Location Search| -|[CustomField](sort_clause_reference/customfield_sort_clause.md)|Raw search index fields|Content and Location Search| -|[DateModified](sort_clause_reference/datemodified_sort_clause.md)|The date when content was last modified|Content and Location Search, and Filtering| -|[DatePublished](sort_clause_reference/datepublished_sort_clause.md)|The date when content was created|Content and Location Search, and Filtering| -|[Depth](sort_clause_reference/depth_sort_clause.md)|Location depth in the Content tree|Location Search, Filtering| -|[Field](sort_clause_reference/field_sort_clause.md)|Content of one of Content item's Fields|Content and Location Search| -|[Id](sort_clause_reference/id_sort_clause.md)|Location ID|Location Search, Filtering| -|[IsMainLocation](sort_clause_reference/ismainlocation_sort_clause.md)|Whether a Location is the main Location of a Content item|Location only| -|[MapLocationDistance](sort_clause_reference/maplocationdistance_sort_clause.md)|Distance between the location contained in a MapLocation Field and the provided coordinates|Content and Location Search| -|[Path](sort_clause_reference/path_sort_clause.md)|PathString of the Location|Location Search, Filtering| -|[Priority](sort_clause_reference/priority_sort_clause.md)|Location priority|Location Search, Filtering| -|[Random](sort_clause_reference/random_sort_clause.md)|Random seed|Content and Location Search| -|[Score](sort_clause_reference/score_sort_clause.md)|Score of the search result|Content and Location Search| -|[SectionIdentifier](sort_clause_reference/sectionidentifier_sort_clause.md)|ID of the Section content is assigned to|Content and Location Search, and Filtering| -|[SectionName](sort_clause_reference/sectionname_sort_clause.md)|Name of the Section content is assigned to|Content and Location Search, and Filtering| -|[Visibility](sort_clause_reference/visibility_sort_clause.md)|Whether the Location is visible or not|Location Search, Filtering| diff --git a/docs/guide/search/sort_clause_reference/sort_clause_reference.md b/docs/guide/search/sort_clause_reference/sort_clause_reference.md new file mode 100644 index 0000000000..6b7e76633b --- /dev/null +++ b/docs/guide/search/sort_clause_reference/sort_clause_reference.md @@ -0,0 +1,37 @@ +--- +description: Sort Clauses help fine-tune sorting order when searching for content and Locations. +--- + +# Sort Clause reference + +Sort Clauses are the sorting options for Content and Location Search and +[Repository filtering](../../../api/public_php_api_search.md#repository-filtering). + +Capabilities of individual Sort Clauses can depend on the search engine. + +All Sort Clauses can take the following optional argument: + +- `sortDirection` - the direction of the sorting, either `Query::SORT_ASC` (default) or `Query::SORT_DESC` + +## Sort Clauses + +| Sort Clause | Sorting based on | Supported by | +|-----|-----|-----| +|[ContentId](contentid_sort_clause.md)|Content items' ID|Content and Location Search, and Filtering| +|[ContentName](contentname_sort_clause.md)|Content names|Content and Location Search, and Filtering| +|[ContentTranslatedName](contenttranslatedname_sort_clause.md)|Translated content names|Content and Location Search| +|[CustomField](customfield_sort_clause.md)|Raw search index fields|Content and Location Search| +|[DateModified](datemodified_sort_clause.md)|The date when content was last modified|Content and Location Search, and Filtering| +|[DatePublished](datepublished_sort_clause.md)|The date when content was created|Content and Location Search, and Filtering| +|[Depth](depth_sort_clause.md)|Location depth in the Content tree|Location Search, Filtering| +|[Field](field_sort_clause.md)|Content of one of Content item's Fields|Content and Location Search| +|[Id](id_sort_clause.md)|Location ID|Location Search, Filtering| +|[IsMainLocation](ismainlocation_sort_clause.md)|Whether a Location is the main Location of a Content item|Location only| +|[MapLocationDistance](maplocationdistance_sort_clause.md)|Distance between the location contained in a MapLocation Field and the provided coordinates|Content and Location Search| +|[Path](path_sort_clause.md)|PathString of the Location|Location Search, Filtering| +|[Priority](priority_sort_clause.md)|Location priority|Location Search, Filtering| +|[Random](random_sort_clause.md)|Random seed|Content and Location Search| +|[Score](score_sort_clause.md)|Score of the search result|Content and Location Search| +|[SectionIdentifier](sectionidentifier_sort_clause.md)|ID of the Section content is assigned to|Content and Location Search, and Filtering| +|[SectionName](sectionname_sort_clause.md)|Name of the Section content is assigned to|Content and Location Search, and Filtering| +|[Visibility](visibility_sort_clause.md)|Whether the Location is visible or not|Location Search, Filtering| diff --git a/docs/api/url_reference/id_sort_clause.md b/docs/guide/search/url_search_reference/id_url_sort_clause.md similarity index 100% rename from docs/api/url_reference/id_sort_clause.md rename to docs/guide/search/url_search_reference/id_url_sort_clause.md diff --git a/docs/api/url_reference/logicaland_criterion.md b/docs/guide/search/url_search_reference/logicaland_url_criterion.md similarity index 100% rename from docs/api/url_reference/logicaland_criterion.md rename to docs/guide/search/url_search_reference/logicaland_url_criterion.md diff --git a/docs/api/url_reference/logicalnot_criterion.md b/docs/guide/search/url_search_reference/logicalnot_url_criterion.md similarity index 100% rename from docs/api/url_reference/logicalnot_criterion.md rename to docs/guide/search/url_search_reference/logicalnot_url_criterion.md diff --git a/docs/api/url_reference/logicalor_criterion.md b/docs/guide/search/url_search_reference/logicalor_url_criterion.md similarity index 100% rename from docs/api/url_reference/logicalor_criterion.md rename to docs/guide/search/url_search_reference/logicalor_url_criterion.md diff --git a/docs/api/url_reference/matchall_criterion.md b/docs/guide/search/url_search_reference/matchall_url_criterion.md similarity index 100% rename from docs/api/url_reference/matchall_criterion.md rename to docs/guide/search/url_search_reference/matchall_url_criterion.md diff --git a/docs/api/url_reference/matchnone_criterion.md b/docs/guide/search/url_search_reference/matchnone_url_criterion.md similarity index 100% rename from docs/api/url_reference/matchnone_criterion.md rename to docs/guide/search/url_search_reference/matchnone_url_criterion.md diff --git a/docs/api/url_reference/pattern_criterion.md b/docs/guide/search/url_search_reference/pattern_url_criterion.md similarity index 100% rename from docs/api/url_reference/pattern_criterion.md rename to docs/guide/search/url_search_reference/pattern_url_criterion.md diff --git a/docs/api/url_reference/sectionid_criterion.md b/docs/guide/search/url_search_reference/sectionid_url_criterion.md similarity index 100% rename from docs/api/url_reference/sectionid_criterion.md rename to docs/guide/search/url_search_reference/sectionid_url_criterion.md diff --git a/docs/api/url_reference/sectionidentifier_criterion.md b/docs/guide/search/url_search_reference/sectionidentifier_url_criterion.md similarity index 100% rename from docs/api/url_reference/sectionidentifier_criterion.md rename to docs/guide/search/url_search_reference/sectionidentifier_url_criterion.md diff --git a/docs/guide/search/url_search_reference/url_search_reference.md b/docs/guide/search/url_search_reference/url_search_reference.md new file mode 100644 index 0000000000..1950552aee --- /dev/null +++ b/docs/guide/search/url_search_reference/url_search_reference.md @@ -0,0 +1,33 @@ +--- +description: URL Search Criteria help define and fine-tune search queries for URLs. +--- + +# URL search reference + +## URL criteria reference + +|URL criteria|URL based on| +|------------|------------| +|[LogicalAnd](logicaland_url_criterion.md)|Implements a logical AND Criterion. It matches if ALL of the provided Criteria match.| +|[LogicalNot](logicalnot_url_criterion.md)|Implements a logical NOT Criterion. It matches if the provided Criterion doesn't match.| +|[LogicalOr](logicalor_url_criterion.md)|Implements a logical OR Criterion. It matches if at least one of the provided Criteria match.| +|[MatchAll](matchall_url_criterion.md)|Returns all URL results.| +|[MatchNone](matchnone_url_criterion.md)|Returns no URL results.| +|[Pattern](pattern_url_criterion.md)|Matches URLs that contain a pattern.| +|[SectionId](sectionid_url_criterion.md)|Matches URLs from content placed in the Section with the specified ID.| +|[SectionIdentifier](sectionidentifier_url_criterion.md)|Matches URLs from content placed in Sections with the specified identifiers.| +|[Validity](validity_url_criterion.md)|Matches URLs based on validity flag.| +|[VisibleOnly](visibleonly_url_criterion.md)|Matches URLs from published content.| + +## URL Sort Clauses reference + +Sort Clauses are the sorting options for URLs. + +All Sort Clauses can take the following optional argument: + +- `sortDirection` - the direction of the sorting, either `\Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause::SORT_ASC` (default) or `\Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause::SORT_DESC` + +| Sort Clause | Sorting based on | +|-----|-----| +|[Id](id_url_sort_clause.md)|URL ID| +|[URL](url_url_sort_clause.md)|URL address| diff --git a/docs/api/url_reference/url_sort_clause.md b/docs/guide/search/url_search_reference/url_url_sort_clause.md similarity index 100% rename from docs/api/url_reference/url_sort_clause.md rename to docs/guide/search/url_search_reference/url_url_sort_clause.md diff --git a/docs/api/url_reference/validity_criterion.md b/docs/guide/search/url_search_reference/validity_url_criterion.md similarity index 100% rename from docs/api/url_reference/validity_criterion.md rename to docs/guide/search/url_search_reference/validity_url_criterion.md diff --git a/docs/api/url_reference/visibleonly_criterion.md b/docs/guide/search/url_search_reference/visibleonly_url_criterion.md similarity index 100% rename from docs/api/url_reference/visibleonly_criterion.md rename to docs/guide/search/url_search_reference/visibleonly_url_criterion.md diff --git a/docs/guide/security.md b/docs/guide/security.md index ab72c20ca6..6506efa624 100644 --- a/docs/guide/security.md +++ b/docs/guide/security.md @@ -1,4 +1,8 @@ -# Development Security +--- +description: Ensure the security of your Ibexa DXP installation by using one of the available authentication methods. +--- + +# Development security !!! tip @@ -42,17 +46,17 @@ logout: You can fully customize the routes and/or the controller used for login. However, remember to match `login_path`, `check_path` and `logout.path` from `security.yaml`. - See [security configuration reference](http://symfony.com/doc/5.0/reference/configuration/security.html) and [standard login form documentation](http://symfony.com/doc/5.0/security/form_login_setup.html). + See [security configuration reference]([[= symfony_doc =]]/reference/configuration/security.html) and [standard login form documentation]([[= symfony_doc =]]/security/form_login_setup.html). ### Authentication using Symfony Security component Authentication is provided using the Symfony Security component. -[Native and universal `form_login`](http://symfony.com/doc/5.0/security/form_login_setup.html) is used, in conjunction with an extended `DaoAuthenticationProvider` (DAO stands for *Data Access Object*), the `RepositoryAuthenticationProvider`. Native behavior of `DaoAuthenticationProvider` has been preserved, making it possible to still use it for pure Symfony applications. +[Native and universal `form_login`]([[= symfony_doc =]]/security/form_login_setup.html) is used, in conjunction with an extended `DaoAuthenticationProvider` (DAO stands for *Data Access Object*), the `RepositoryAuthenticationProvider`. Native behavior of `DaoAuthenticationProvider` has been preserved, making it possible to still use it for pure Symfony applications. #### Security controller -A `SecurityController` is used to manage all security-related actions and is thus used to display the login form. It follows all standards explained in [Symfony security documentation](http://symfony.com/doc/5.0/security/form_login_setup.html). +A `SecurityController` is used to manage all security-related actions and is thus used to display the login form. It follows all standards explained in [Symfony security documentation]([[= symfony_doc =]]/security/form_login_setup.html). The base template used is [`EzPublishCore/Security/login.html.twig`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig). @@ -69,12 +73,12 @@ ezplatform: ##### Redirection after login -By default, Symfony redirects to the [URI configured in `security.yaml` as `default_target_path`](http://symfony.com/doc/5.0/reference/configuration/security.html). If not set, it defaults to `/`. +By default, Symfony redirects to the [URI configured in `security.yaml` as `default_target_path`]([[= symfony_doc =]]/reference/configuration/security.html). If not set, it defaults to `/`. #### Remember me It is possible to use the "Remember me" functionality. -Refer to the [Symfony cookbook on this topic](http://symfony.com/doc/5.0/security/remember_me.html). +Refer to the [Symfony cookbook on this topic]([[= symfony_doc =]]/security/remember_me.html). If you want to use this feature, you must at least extend the login template in order to add the required checkbox: @@ -90,13 +94,13 @@ If you want to use this feature, you must at least extend the login template in #### Login handlers / SSO -Symfony provides native support for [multiple user providers](https://symfony.com/doc/5.0/security/multiple_user_providers.html). This makes it easy to integrate any kind of login handlers, including SSO and existing third-party bundles (e.g. [FR3DLdapBundle](https://github.com/Maks3w/FR3DLdapBundle), [HWIOauthBundle](https://github.com/hwi/HWIOAuthBundle), [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), [BeSimpleSsoAuthBundle](http://github.com/BeSimple/BeSimpleSsoAuthBundle), etc.). +Symfony provides native support for [multiple user providers]([[= symfony_doc =]]/security/multiple_user_providers.html). This makes it easy to integrate any kind of login handlers, including SSO and existing third-party bundles (e.g. [FR3DLdapBundle](https://github.com/Maks3w/FR3DLdapBundle), [HWIOauthBundle](https://github.com/hwi/HWIOAuthBundle), [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), [BeSimpleSsoAuthBundle](http://github.com/BeSimple/BeSimpleSsoAuthBundle), etc.). -See [Authenticating a user with multiple user provider](user_management/user_management.md#authenticating-user-with-multiple-user-providers) for more information. +See [Authenticating a user with multiple user provider](user_management/user_management.md#authenticate-user-with-multiple-user-providers) for more information. ## JWT authentication -To use [JWT authentication](https://jwt.io/) with eZ Platform, in the provided ` config/packages/lexik_jwt_authentication.yaml` file, +To use [JWT authentication](https://jwt.io/) with Ibexa DXP, in the provided ` config/packages/lexik_jwt_authentication.yaml` file, modify the existing configuration by setting `authorization_header` to `enabled`: ``` yaml hl_lines="8" @@ -140,42 +144,3 @@ security: entry_point: lexik_jwt_authentication.jwt_token_authenticator stateless: true ``` - -## Security advisories - -!!! caution - - ### Executable packages with Legacy Bridge - - [This issue](http://share.ez.no/community-project/security-advisories/ezsa-2018-002-the-files-uploaded-via-packages-component-are-executable) affects installations using eZ Publish Legacy, either stand-alone, or as part of eZ Platform 5.x, or in eZ Platform 1.11 and newer using LegacyBridge. If you are not using Legacy in any way, you are not affected. - - The package system, by design, allows you to package an extension into a file, and export/import such packages. Extensions can of course contain PHP scripts, and they usually do. Such scripts can be used in an attack on the server. This problem is fundamental and cannot be fixed by any other means than by removing the feature. - - By default, only the Administrator has the permissions to use the package system. It follows that the Administrator role, and any others granted packaging permissions, can only be held by users who already have access to the server, and/or can be trusted not to exploit this access. - - As a consequence eZ Publish legacy should not be used in the type of shared hosting installation where Administrators are not supposed to have access to the underlying operating system, or to other eZ Publish installations on the same server. The package system is an old part of eZ Publish legacy, and it was not designed for that kind of installation. Currently this is not considered best practice anyway - setups using e.g. Docker and Platform.sh allow you to completely separate installations from each other. This is a better way to keep things secure than relying on PHP scripts being read-only even for administrators. (The package system does not exist in [[= product_name =]] and will not be added there, since extensions are not used there.) - - In summary: - - If you are responsible for legacy installations where administrators cannot be fully trusted not to exploit their privileges, make sure to properly lock down the package system and/or fully separate web sites from each other. - Make sure that the administrator password(s) are secure, and not using the default administrator password. - - **Proposed quick solution for those affected:** - - If you are administrating a shared hosting solution of this kind, it may take a while to change the setup. Meanwhile, one quick way to lock down the package system is to use rewrite rules to block all access to package URLs. Apache example: - - `RewriteRule ^/package/.* - [R=403,L]` - - or with URL-based SiteAccess: - - `RewriteRule ^/my_site_access/package/.* - [R=403,L]` - - or supporting both cases, and multiple SiteAccesses: - - `RewriteRule ^(/my_site_access|/my_site_access_admin)?/package/.* - [R=403,L]` - - This can be placed before other rules. - - To be absolutely certain you can also (or instead of this) delete the `/kernel/package` directory in the eZ Publish web root. Please note that this will break the legacy installation wizard, since it relies on the package system to install the demo design. - - Once the situation is resolved these measures should be reversed, to bring back the package features. You may want to do a review of whether the issue may have been exploited on your server(s). diff --git a/docs/guide/security_checklist.md b/docs/guide/security_checklist.md index 645af48a5a..adaaee8e1f 100644 --- a/docs/guide/security_checklist.md +++ b/docs/guide/security_checklist.md @@ -1,3 +1,7 @@ +--- +description: Ensure that your Ibexa DXP installation is secure by following our set of recommendations. +--- + # Security checklist When getting ready to go live with your project for the first time, or when re-launching it, @@ -9,8 +13,6 @@ make sure that your setup is secure. released via [your service portal,](https://support.ibexa.co/) or via [Security advisories](https://developers.ibexa.co/security-advisories) if you're not a subscriber. - Please also refer to [development guidelines](../community_resources/development_guidelines.md) during development. - ## Symfony ### `APP_SECRET` @@ -25,7 +27,7 @@ make sure that your setup is secure. The following command will generate a 64-character-long secure random value: - `print bin2hex(random_bytes(32));` + `php -r "print bin2hex(random_bytes(32));"` !!! note @@ -41,8 +43,19 @@ Exposing the dev mode exposes things like `phpinfo`, environment variables and s For more information about securing Symfony-based systems, see: - - [Authentication and authorisation](https://symfony.com/doc/current/security.html), and [more on this subject](https://symfony.com/doc/current/security.html#learn-more) - - Symfony's [secrets management system](https://symfony.com/doc/current/configuration/secrets.html) + - [Authentication and authorisation]([[= symfony_doc =]]/security.html), and [more on this subject]([[= symfony_doc =]]/security.html#learn-more) + - Symfony's [secrets management system]([[= symfony_doc =]]/configuration/secrets.html) + +## PHP + +### Enable `zend.exception_ignore_args` in PHP 7.4 and newer + +PHP 7.4 introduced the `zend.exception_ignore_args` setting in `php.ini`. +The default value is 0 (disabled) for backwards compatibility. +On production sites this should be set to 1 (enabled), to ensure stack traces do not include arguments passed to functions. +Such arguments could include passwords or other sensitive information. +You should also make sure no stack trace is ever visible to end users of production sites, +though visible arguments are unsafe even if the stack traces only show up in log files. ## [[= product_name =]] @@ -62,6 +75,7 @@ This is specially important for admin accounts and other privileged users. - Never go online with admin password set to `publish` or any other default value. - Introduce password quality checks. Make sure the checks are strict enough (length/complexity). - 16 characters is a quite secure minimum length. Do not go below 10. +- If using Ibexa DXP v4.5 or newer, enable the password rule that rejects any password which has been exposed in a public breach. !!! tip "Password rules" @@ -74,33 +88,51 @@ and any other application-specific secrets. ### Protect against brute force attacks -Introduce a measure against brute force login attacks (captcha, etc.). +Consider introducing a measure against brute force login attacks, like CAPTCHA. Adjust timeout limits to your needs: + +When using the "forgot password" feature, a token is created which expires if the user doesn't click the password reset +link that gets mailed to them in time. The time before it expires is set in the configuration parameter +`ezsettings.default.security.token_interval_spec`. By nature this feature must be available to users +before they have logged in, including would-be attackers. If an attacker uses this feature with someone else's email +address, the attacker does not receive the email. But they could still try to guess the password reset link. That's why +this interval should be as short as possible. 5 minutes is often enough. -### Disable Fastly when using Varnish +These timeouts are both entered as [PHP DateInterval duration strings](https://www.php.net/manual/en/dateinterval.construct.php). +The forgot password feature defaults to "PT1H" (one hour). +The account invitation feature defaults to "P7D" (seven days). + +### Disable Varnish when using Fastly If you are using Fastly, disable Varnish. See [Security advisory: EZSA-2020-002.](https://developers.ibexa.co/security-advisories/ezsa-2020-002-unauthorised-cache-purge-with-misconfigured-fastly) -### Block the execution of scripts in the `var` directory +### Block upload of unwanted file types -Make sure the web server blocks the execution of PHP files and other scripts in the `var` directory. -See [vhost.template.](https://github.com/ezsystems/ezplatform/blob/master/doc/apache2/vhost.template#L80) +The `ibexa.site_access.config.default.io.file_storage.file_type_blacklist` setting is defined in the config file `src/bundle/Core/Resources/config/default_settings.yml` in the Core bundle. +It prevents uploading files that might be executed on the server, a Remote Code Execution (RCE) vulnerability. The setting lists filename extensions for files that shouldn't be uploaded. +Attempting to upload files from the list results in an error message. +There are also other safety measures in place, like using the web server configuration to block execution of uploaded scripts, see the next point. -### Use secure password hashing +You should adapt this list to your needs. Note that `svg` images are blocked because they may contain JavaScript code. +If you opt to allow them, make sure you take steps to mitigate the risk. + +The default list of blocked file types contains: `hta htm html jar js jse pgif phar php php3 php4 php5 phps phpt pht phtml svg swf xhtm xhtml`. + +### Block execution of scripts in `var` directory -Use the most secure supported password hashing method. -This is currently `bcrypt`, see [Increased password hash length](../updating/4_update_1.13.md#increased-password-hash-length). +Make sure the web server blocks the execution of PHP files and other scripts in the `var` directory. +See the line below `# Disable .php(3) and other executable extensions in the var directory` in the example virtual host files for Apache and Nginx, provided in the [installation documentation](install_ez_platform.md#set-up-virtual-host). -### Restrict access to the back end +### Use secure password hashing -If possible, make the back end unavailable on the open internet. +Use the most secure supported password hashing method. This is currently `bcrypt`, and it is enabled by default. ### Use UTF8MB4 with MySQL/MariaDB If you are using MySQL/MariaDB, use the UTF8MB4 database character set and related collation. The older UTF8 can lead to truncation with 4-byte characters, like some emoji, which may have unpredictable side effects. -See [Change from UTF8 to UTF8MB4](../updating/4_update_2.2.md#change-from-utf8-to-utf8mb4). +See [Change from UTF8 to UTF8MB4](../updating/from_1.x_2.x/update_db_to_2.5.md#change-from-utf8-to-utf8mb4). ### Use secure Roles and Policies @@ -114,11 +146,76 @@ Use the following checklist to ensure the Roles and Policies are secure: - Is there a clear Role separation between the organisation's internal and external users? - Is access to user data properly restricted, in accordance with GDPR? +### Do not use "hide" for read access restriction + +The [visibility switcher](https://doc.ibexa.co/en/latest/content_management/locations/#location-visibility) is a convenient feature for withdrawing content from the frontend. +It acts as a filter in the frontend by default. You can choose to respect it or ignore it in your code. +It isn't permission-based, and doesn't restrict read access to content. Hidden content can be read through other means, like the REST API or GraphQL. + +If you need to restrict read access to a given Content item, you could create a role that grants read access for a given +[**Section**](https://doc.ibexa.co/en/latest/administration/content_organization/sections/) +or [**Object State**](https://doc.ibexa.co/en/latest/administration/content_organization/object_states/), +and set a different Section or Object State for the given Content. +Or use other permission-based [**Limitations**](https://doc.ibexa.co/en/latest/permissions/limitations/). + +### Minimize exposure + +Security should be a multi-layered exercise. It is wise to minimize what features you make available to the world, even if there are no known or suspected vulnerabilities in those features, and even if your content is properly protected by roles and policies. Reduce your attack surface by exposing only what you must. + +- If possible, make the Back Office unavailable on the open internet. +- [Symfony FOSJsRoutingBundle](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle) is required in those releases where it is included, to expose routes to JavaScript. It exposes only the required routes, nothing more. It is only required in the Back Office site access though, so you can consider blocking it in other site accesses. You should also go through your own custom routes, and decide for each if you need to expose them or not. See the documentation on [YAML route definitions for exposure](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/blob/master/Resources/doc/usage.rst#generating-uris). +- By default, a [Powered-By header](https://doc.ibexa.co/en/latest/update_and_migration/from_1.x_2.x/update_db_to_2.5/#powered-by-header) is set. It specifies what version of the DXP is running. For example, `x-powered-by: Ibexa Experience v4`. This doesn't expose anything that couldn't be detected through other means. But if you wish to obscure this, you can either omit the version number, or disable the header entirely. +- Consider whether certain interfaces must be left available on the open internet. For example: + - The `/search` and `/graphql` endpoints + - The REST API endpoints + +!!! tip "Access control" + One way to lock down an endpoint that should not be openly available is to restrict access to logged-in users, by using the [`access_control`](https://symfony.com/doc/5.4/security/access_control.html) feature. In your YAML configuration, under the `security` key, add an entry similar to the following one, which redirects requests to a login page: + + ```yaml + security: + access_control: + - { path: ^/search, roles: ROLE_USER} + ``` + ## Underlying stack +Once you have properly configured secure user roles and permissions, to avoid exposing your application to any DDOS vulnerabilities or other yet unknown security threats, make sure that you do the following: + - Avoid exposing servers on the open internet when not strictly required. -- Ensure any servers, services, ports and virtual hosts that were opened for testing purposes are locked down before going live. -- Secure the database with a good password, keys, firewall, etc. -- Run the server on a recent operating system and dependencies with security patches installed. -- Configure the server to alert you about security updates from vendors. -Pay special attention to dependencies used by your project directly, or by PHP. +- Ensure any servers, services, ports and virtual hosts that were opened for testing purposes are shut down before going live. +- Secure the database with a good password, keys, firewall, etc. Ensure that the database user used by the web app only has access to do the operations needed by Ibexa DXP. The Data Definition Language (DDL) commands (create, alter, drop, truncate, comment) are not needed for running Ibexa DXP, only for installing and upgrading it. If the web app user does not have these rights, then that reduces the damage that can be done if there is a security breach. + +### Security headers + +There are a number of security related HTTP response headers that you can use to improve your security. +Headers must be adapted to the site in question, and in most cases it is site owner's responsibility. +The headers can be set either by the web server, or by a proxy like Varnish. +You can also set headers in PHP code by making a Symfony `RequestListener` for the `kernel.response` event and adding the header to the response object headers list. + +You will likely need to vary the security headers based on the SiteAccess in question and site implementation details, such as frontend code and libraries used. + +- `Strict-Transport-Security` - ensures that all requests are sent over HTTPS, with no fallback to HTTP. +All production sites should use HTTPS and this header unless they have very particular needs. +This header is less important during development provided that the site is on an internal, protected network. +- `X-Frame-Options` - ensures that the site is not be embedded in a frame by a compliant browser. +Set the header to `SAMEORIGIN` to allow embedding by your own site, or `DENY` to block framing completely. +- `X-Content-Type-Options` - prevents the browser from second-guessing the mime-type of delivered content. +This header is less important if users cannot upload content and/or you trust your editors. However, it is safer to use it at all times. +Make sure that the `Content-Type` header is also correctly set, including for the top-level document, to avoid issues with HTML documents being downloaded while they should be rendered. +- `Content-Security-Policy` - blocks cross site scripting (XSS) attacks by setting an allowlist (whitelist) of resources to be loaded for a given page. +You can set separate lists for scripts, images, fonts, and so on. +For experimentation and testing, you can use `Content-Security-Policy-Report-Only` before activating the actual policy. +- `Referrer-Policy` - limits what information is sent from the previous page or site when navigating to a new page or site. +This header has several directives for fine-tuning the referrer information. +- `Permissions-Policy` - limits what features the browser can use, such as fullscreen, notifications, location, camera, or microphone. +For example, if someone succeeds in injecting their JavaScript into your site, this header prevents them from using those features to attack your users. + +### Track dependencies + +- Run servers on a recent operating system and install security patches for dependencies. +- Configure servers to alert you about security updates from vendors. Pay special attention to dependencies used by your project directly, or by PHP. The provider of the operating system usually has a service for this. +- Enable [GitHub Dependabot](https://docs.github.com/en/code-security/supply-chain-security/managing-vulnerabilities-in-your-projects-dependencies/about-dependabot-security-updates) +to receive notifications when a security fix is released in a Github-hosted dependency. +- If you're not using Github for your project, you can create a dummy project on Github with the same dependencies as your real project, and enable Dependabot notifications for that. +- Ensure you get notifications about security fixes in JavaScript dependencies. diff --git a/docs/guide/sending_notifications.md b/docs/guide/sending_notifications.md index 708413746e..4b3e56f9f1 100644 --- a/docs/guide/sending_notifications.md +++ b/docs/guide/sending_notifications.md @@ -1,8 +1,12 @@ -# Sending notifications +--- +description: You can send notifications to users who work with the Back Office by using notification bars or notifications in the user menu. +--- + +# Notifications You can send two types on notifications to the users. -[Notification bar](#display-notification-bars) is displayed in specific situations as a message bar appearing at the bottom of the page. +[Notification bar](#notification-bars) is displayed in specific situations as a message bar appearing at the bottom of the page. It appears to whoever is doing a specific operation in the Back Office. ![Example of an info notification](img/notification2.png "Example of the notification bar") @@ -12,12 +16,12 @@ They will appear in their profile in the Back Office. ![Notification in profile](img/notification3.png) -## Display notification bars +## Notification bars Notifications are displayed as a message bar in the Back Office. There are four types of notifications: `info`, `success`, `warning` and `error`. -### Display notifications from PHP +### Displaying notifications from PHP To send a notification from PHP, inject the `TranslatableNotificationHandlerInterface` into your class. @@ -32,7 +36,7 @@ $this->notificationHandler->info( To have the notification translated, provide the message strings in the translation files under the correct domain and key. -### Display notifications from front end +### Displaying notifications from front end To create a notification from the front end (in this example, of type `info`), use the following code: @@ -189,3 +193,25 @@ services: tags: - { name: ezpublish.notification.renderer, alias: MyNotification:TypeName } ``` + +## Notification timeout + +To define the timeout for hiding Back-Office notification bars, per notification type, +use the following configuration (times are provided in milliseconds): + +``` yaml +ibexa: + system: + admin: + notifications: + error: + timeout: 0 + warning: + timeout: 0 + success: + timeout: 5000 + info: + timeout: 0 +``` + +The values shown above are the defaults. `0` means the notification does not hide automatically. \ No newline at end of file diff --git a/docs/guide/service_container.md b/docs/guide/service_container.md deleted file mode 100644 index bd72c5b84d..0000000000 --- a/docs/guide/service_container.md +++ /dev/null @@ -1,52 +0,0 @@ -# Service container - -A service container (aka Dependency Injection Container, DIC) is a special object that facilitates dependency resolution. -It is based on the [dependency injection design pattern](http://en.wikipedia.org/wiki/Dependency_injection). - -Dependency injection proposes to inject all needed objects and configuration into your business logic objects (known as **services**). -It avoids the massive use of singletons, global variables or complicated factories and makes your code much more readable and testable. - -The main issue with this pattern is how to resolve the dependencies for your services. -This is where the service container comes into play. The role of a service container is to build and maintain your services and their dependencies. -Each time you need a service, you may ask the service container for it. -It will either build the service with the configuration you provided, or give you an existing instance if it is already available. - -[[= product_name =]] uses the [Symfony service container](http://symfony.com/doc/5.0/service_container.html). - -!!! tip - - To learn more about the service container, see: - - - [Full documentation of the Dependency Injection Component](http://symfony.com/doc/5.0/components/dependency_injection.html) - - [Base service tags](http://symfony.com/doc/5.0/reference/dic_tags.html) - -## Service tags - -Service tags in Symfony DIC are a useful way of dedicating services to a specific purpose. They are usually used for extension points. - -For instance, if you want to register a [Twig extension](http://twig.sensiolabs.org/doc/advanced.html#creating-extensions) to add custom filters, -you need to create the PHP class and declare it as a service in the DIC configuration with the `twig.extension` tag -(see the [Symfony cookbook entry](http://symfony.com/doc/5.0/templating/twig_extension.html) for a full example). - -[[= product_name =]] exposes several features this way (see the [list of core service tags](#core-and-api)). -This is for example the case with Field Types. - -You will find all service tags exposed by Symfony in [its reference documentation](http://symfony.com/doc/5.0/reference/dic_tags.html). - -### Core and API - -|Tag name|Usage| -|------|------| -|`router`|Adds a specific router to the chain router| -|`twig.loader`|Registers a template loader for Twig| -|`ezpublish.content_view_provider`|Registers a ContentViewProvider for template selection depending on content/Location being viewed| -|`ezpublish.storageEngine`|Registers a storage engine in the Repository factory| -|[`ezplatform.field_type`](../api/field_type_type_and_value.md#registration)|Registers a Field Type| - -### Legacy - -|Tag name|Usage| -|------|------| -|`ezplatform.field_type.legacy_storage.converter`|Registers a converter for a Field Type in Legacy storage engine| -|`ezplatform.field_type.external_storage_handler`|Registers an external storage handler for a Field Type| -|`ezplatform.field_type.external_storage_handler.gateway`|Registers an external storage gateway for a Field Type in Legacy storage engine| diff --git a/docs/guide/sessions.md b/docs/guide/sessions.md index 730d6cffea..5c7bece170 100644 --- a/docs/guide/sessions.md +++ b/docs/guide/sessions.md @@ -1,3 +1,7 @@ +--- +description: Ibexa DXP uses Symfony to handle user sessions, with support for SiteAccess-aware session cookie configuration. +--- + # Sessions Sessions are handled by the Symfony framework, specifically API and underlying session handlers provided by the HttpFoundation component. @@ -11,7 +15,7 @@ It is further enhanced in [[= product_name =]] with support for SiteAccess-awar ## Configuration Symfony offers the possibility to change many session options at application level -(i.e. in Symfony [`framework` configuration](https://symfony.com/doc/5.0/reference/configuration/framework.html#session)). +(for example, in Symfony [`framework` configuration]([[= symfony_doc =]]/reference/configuration/framework.html#session)). These options include: - `cookie_domain` @@ -59,7 +63,7 @@ Symfony can be configured to use custom handlers, or just fall back to what is framework: session: # handler_id can be set to null (~) like default in Symfony, if it so will use default session handler from php.ini - # But in order to use %ezplatform.session.save_path%, default eZ Platform instead sets %ezplatform.session.handler_id% to: + # But in order to use %ezplatform.session.save_path%, default Ibexa DXP instead sets %ezplatform.session.handler_id% to: # - session.handler.native_file (default) # - ezplatform.core.session.handler.native_redis (recommended value for Cluster usage, using php-redis session handler ) handler_id: '%ezplatform.session.handler_id%' @@ -118,8 +122,6 @@ Alternatively if you have needs to configure Redis servers dynamically: If you are on `php-redis` v4.2.0 and higher, you can optionally tweak [`php-redis` settings](https://github.com/phpredis/phpredis#session-locking) for session locking. -###### Additional notes on using Redis for Sessions - Ideally keep [persistence cache](persistence_cache.md) and session data separated: - Sessions can't risk getting [randomly evicted](https://redis.io/topics/lru-cache#eviction-policies) when you run out of memory for cache. @@ -133,7 +135,7 @@ If you want to make sure sessions survive Redis or server restarts, consider usi For setups where database is preferred for storing sessions, you may use Symfony's PdoSessionHandler, although it is not currently recommended from performance perspective. -Below is a configuration example for [[= product_name =]]. Refer to the [Symfony Cookbook](http://symfony.com/doc/5.0/doctrine/pdo_session_storage.html) for full documentation. +Below is a configuration example for [[= product_name =]]. Refer to the [Symfony Cookbook]([[= symfony_doc =]]/doctrine/pdo_session_storage.html) for full documentation. ``` yaml framework: diff --git a/docs/guide/shop_configuration/advanced_configuration.md b/docs/guide/shop_configuration/advanced_configuration.md deleted file mode 100644 index 0370921832..0000000000 --- a/docs/guide/shop_configuration/advanced_configuration.md +++ /dev/null @@ -1,161 +0,0 @@ -# Advanced configuration [[% include 'snippets/commerce_badge.md' %]] - -## Cache configuration - -``` yaml -stash: - caches: - default: - drivers: - - FileSystem - inMemory: true - registerDoctrineAdapter: false - FileSystem: - keyHashFunction: crc32 -``` - -## Settings for Users - -[[= product_name_com =]] uses specific User groups where new Users are placed. -You can configure the Location IDs of these User groups in the following way: - -``` yaml -siso_core.default.user_group_location: 106 -siso_core.default.user_group_location.business: 106 -siso_core.default.user_group_location.private: 106 -``` - -## Supported country list - -This configuration controls which countries will be offered in [[= product_name_com =]] (e.g. in registration forms or in the checkout). - -``` yaml -siso_tools.default.countries: ['DE','US','NO'] -``` - -## Navigation menu - -``` yaml -siso_core.default.breadcrumb_content_label_fields: ['name', 'title'] -siso_core.default.navigation.content: - types: ["st_module", "folder", "article", "landing_page", "ses_productcatalog", "folder_news", "folder_events", "blog", "folder_contacts"] - sections: [1, 2, 9] - enable_priority_zero: false - #additional field keys for translating navigation node label - label_fields: ['name', 'title'] - additional_fields: ['intro', 'media', 'alternative_title', 'alternative_intro', 'alternative_image'] -``` - -## Search - -``` yaml -siso_search.default.groups.search: - product: - types: - - ses_product - # activate if you want to find product types as well - #- ses_product_type - path: '/1/2/' - section: - - 1 - visibility: true - # activate if you want to enable usage of the flag: displayInSearch - # if false, the flag will be ignored in the search even if set in eZ - #use_display_in_search_flag: true - content: - types: - - st_module - - folder - - article - - landing_page - - blog_post - - event - # add ez classes like news or folder_news, folder_events, folder_contacts - - path: '/1/2/' - section: - - 1 - visibility: true - # this parameter only make sence if there are some products/product types in the tab, see above - #use_display_in_search_flag: false - files: - types: - - file - - path: '/1/43/' - section: - - 3 - visibility: true - # this parameter only make sence if there are some products/product types in the tab, see above - #use_display_in_search_flag: false - -siso_search.default.autosuggest_module_definitions: - product_autosuggest: - search_limit: 5 - images_field: main_image_url_s - add_to_basket: true #add to cart - price_field: ses_product_ses_unit_price_f - search_fields: - - ses_product_ses_sku_value_s - - ses_product_ses_name_value_s - result_fields: - - ses_product_ses_sku_value_s - - ses_product_ses_name_value_s - result_fields_separator: ' - ' - text_limit: 35 - search_service_id: siso_search.autosuggest_service.product - redirect_generator_id: siso_search.autosuggest_redirect_generator.product - category_autosuggest: - search_limit: 5 - images: true - search_fields: - - ses_category_ses_name_value_s - result_fields: - - ses_category_ses_name_value_s - result_fields_separator: ' - ' - text_limit: 60 - search_service_id: siso_search.autosuggest_service.category - redirect_generator_id: siso_search.autosuggest_redirect_generator.category - content_autosuggest: - search_limit: 5 - images: true - section: - - 1 - search_fields: - - article_title_value_s - - article_intro_value_s - - article_body_value_s - - blog_post_title_value_s - - blog_post_intro_value_s - - news_title_value_s - - news_intro_value_s - - event_title_value_s - - event_intro_value_s - result_fields: - - article_title_value_s - - article_intro_value_s - - article_body_value_s - - news_title_value_s - - news_intro_value_s - - event_title_value_s - - event_intro_value_s - result_fields_separator: ' - ' - text_limit: 60 - search_service_id: siso_search.autosuggest_service.content - redirect_generator_id: ~ - download_autosuggest: - search_limit: 5 - images: true - search_fields: - - file_name_value_s - - file_description_value_s - result_fields: - - file_name_value_s - - file_description_value_s - mime_types: - - application/pdf - result_fields_separator: ' - ' - text_limit: 60 - search_service_id: siso_search.autosuggest_service.download - redirect_generator_id: siso_search.autosuggest_redirect_generator.download -``` diff --git a/docs/guide/shop_configuration/configure_erp_connection.md b/docs/guide/shop_configuration/configure_erp_connection.md deleted file mode 100644 index 3e30ef75ce..0000000000 --- a/docs/guide/shop_configuration/configure_erp_connection.md +++ /dev/null @@ -1,21 +0,0 @@ -# Configure ERP connection [[% include 'snippets/commerce_badge.md' %]] - -By default [[= product_name_com =]] uses the `TsoWebconnectorLayer` which enables the shop to communicate with a Microsoft Dynamics NAV System. - -## Requirements - -- An ERP System Microsoft Dynamics NAV and the TSO Webconnector product installed. -- The main URL to the webservice - -## Configuration - -Configure the ERP connection in the backend: - -![](../img/configure_erp.png) - -The following configuration enables using the ERP: - -``` yaml -siso_local_order_management.default.send_order_to_erp: true - siso_order_history.default.use_local_documents: false -``` diff --git a/docs/guide/shop_configuration/email_server.md b/docs/guide/shop_configuration/email_server.md deleted file mode 100644 index 367f421290..0000000000 --- a/docs/guide/shop_configuration/email_server.md +++ /dev/null @@ -1,26 +0,0 @@ -# Email server [[% include 'snippets/commerce_badge.md' %]] - -GDPR requires handling emails in a secure way. - -We recommend configuring a secure SMTP transport (here in `parameters.yml`): - -``` yaml -mailer_host: <your mail server> -mailer_port: 465 -mailer_encryption: ssl -# the following stream_options configuration is required only if the mail server uses a self signed certificate -mailer_stream_options: - ssl: - verify_peer: false - verify_peer_name: false -``` - -Required configuration for Swift Mailer: - -``` yaml -swiftmailer: -... - port: '%mailer_port%' - encryption: '%mailer_encryption%' - stream_options: '%mailer_stream_option%' -``` diff --git a/docs/guide/shop_configuration/required_crontab_tasks.md b/docs/guide/shop_configuration/required_crontab_tasks.md deleted file mode 100644 index cc618008bc..0000000000 --- a/docs/guide/shop_configuration/required_crontab_tasks.md +++ /dev/null @@ -1,40 +0,0 @@ -# Required crontab tasks [[% include 'snippets/commerce_badge.md' %]] - -## Remove the translation and navigation caches - -The shop collects changes regarding translations (textmodules) and navigation. -If there are changes (e.g. performed in the backend) the cache will be refreshed. - -``` -# Checks for changes and refresh cache -*/5 * * * * cd '/var/www/my_project' && /usr/bin/php bin/console silversolutions:cache:refresh --env=prod -``` - -## Send lost orders to the ERP - -Lost orders can be re-sent using a command-line tool. We recommend running this tool e.g. every 5 minutes. - -``` -# resends lost orders every 5 minutes -*/5 * * * * cd '/var/www/my_project' && /usr/bin/php bin/console silversolutions:lostorder:process --env=prod -``` - -## Use job-queue-system - -See [JMSJobQueueBundle documentation](http://jmsyst.com/bundles/JMSJobQueueBundle/master/installation) -for more information - -If you can't use the suggested `supervisord` configuration, you can [set up a cron job.](https://github.com/schmittjoh/JMSJobQueueBundle/issues/205) - -## Calculate statistical data for active sessions - -The dashboard uses statistical data about sessions recorded in a database table. -This command line will refresh the data every 5 minutes. - -!!! note - - This feature is available only if sessions are handled in the database. - -``` -*/5 * * * * cd /var/www/my_project && /usr/bin/php bin/console silversolutions:sessions write_stat --env=prod -``` diff --git a/docs/guide/shop_configuration/rotation_for_logfiles.md b/docs/guide/shop_configuration/rotation_for_logfiles.md deleted file mode 100644 index ee9e5d7b4f..0000000000 --- a/docs/guide/shop_configuration/rotation_for_logfiles.md +++ /dev/null @@ -1,36 +0,0 @@ -# Rotation for logfiles [[% include 'snippets/commerce_badge.md' %]] - -For systems using prod as environment: - -- `var/logs/prod.log` -- `var/logs/silver.eshop.log` -- `var/logs/prod-siso.eshop.erp.log` - -## logrotate - -The `logrotate` utility is a standard tool in Linux systems. It is run by `cron.daily` once a day at 6:25 am (on Ubuntu systems). -When `logrotate` runs, it reads all the configuration scripts in `/etc/logrotate.d/`. - -This directory contains already several scripts for e.g. apache2, samba, apt, etc. - -## Configuration for rotating logs - -Create a new configuration file for `logrotate` in `/etc/logrotate.d/silver-eshop`: - -``` yaml -/var/www/projects/<your-project>/var/logs/prod.log /var/www/projects/<your-project>/var/logs/silver.eshop.log /var/www/projects/<your-project>/var/logs/prod-siso.eshop.erp.log { - su www-data www-data - daily - size 50M - rotate 30 - missingok - create 674 devel devel - compress -} -``` - -If the logfiles grow very quickly, you can run `logrotate` once per hour by putting it in `cron.hourly` (it will run at 17 minutes past every hour): - -``` bash -mv /etc/cron.daily/logrotate /etc/cron.hourly -``` diff --git a/docs/guide/shop_configuration/session_handling.md b/docs/guide/shop_configuration/session_handling.md deleted file mode 100644 index a6c414d2b2..0000000000 --- a/docs/guide/shop_configuration/session_handling.md +++ /dev/null @@ -1,72 +0,0 @@ -# Session handling [[% include 'snippets/commerce_badge.md' %]] - -The default session handling should be changed, because after clearing the cache all users will be logged out. -Sessions are handled with PDO, but you can switch to Memcache. - -## Session table - -You need to create a session table in the database if database storage is used: - -``` sql -CREATE TABLE session -( - session_id varchar (255) PRIMARY KEY NOT NULL, - session_value longtext NOT NULL, - session_time INT NOT NULL -); -``` - -## Set up name for SiteAccesses - -It is very important to set a session name. Otherwise [[= product_name =]] will generate a unique name per SiteAccess. -This can cause issues - for example if you switch the language/SiteAccess, users cannot share a basket and login across SiteAccesses. - -``` yaml -site_group: - session: - name: eZCommerce - cookie_lifetime: 86400 -``` - -### General settings - -`config.yml`: - -``` yaml -framework: - ... - session: - #you can switch between pdo or memcache - handler_id: session.handler.pdo - -services: - pdo: - class: PDO - arguments: - - "mysql:host=%database_host%;dbname=%database_name%" - - "%database_user%" - - "%database_password%" - calls: - - [setAttribute, [3, 2]] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION - - session.handler.pdo: - class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler - arguments: ["@pdo", "%pdo.db_options%"] - - session.memcache: - class: Memcache - calls: - - [addServer , [%session_memcache_host%, %session_memcache_port%]] - session.handler.mc: - class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler - arguments: ["@session.memcache"] - -parameters: - pdo.db_options: - db_table: session - db_id_col: session_id - db_data_col: session_value - db_time_col: session_time - session_memcache_host: 127.0.0.1 - session_memcache_port: 11211 -``` diff --git a/docs/guide/shop_configuration/set_up_a_new_language.md b/docs/guide/shop_configuration/set_up_a_new_language.md deleted file mode 100644 index 5345275b0f..0000000000 --- a/docs/guide/shop_configuration/set_up_a_new_language.md +++ /dev/null @@ -1,38 +0,0 @@ -# Set up a new language [[% include 'snippets/commerce_badge.md' %]] - -!!! note - - If you introduce a new SiteAccess, it is important that you add a new Policy for the anonymous User Role - - Allow login for the new SiteAccess: - - ![](../img/configuration.png) - -## Configuration - -- `ezpublish.siteaccess.list` - general configuration for the new SiteAccess -- `ezpublish.siteaccess.groups.ezdemo_site_clean_group` - assign the new SiteAccess to the general group `ezdemo_site_clean_group` -- `ezpublish.siteaccess.match.Compound\LogicalAnd` - define the domain and language for this new SiteAccess, e.g.: - -``` yaml -website_be_nl: - matchers: - Map\URI: - nl: true - Map\Host: - %domain_website_be%: true - match: website_be_nl -``` - -Next, configure a session name and used languages: - -``` yaml -website_be_nl: - session: - name: eZSESSID - languages: - - dut-NL -``` - -- `ezpublish.siteaccess.ses_admin.languages` - enables the new language for admin SiteAccess. -- `ezsettings.default.languages` - adds the new language. diff --git a/docs/guide/shop_configuration/shop_configuration.md b/docs/guide/shop_configuration/shop_configuration.md deleted file mode 100644 index fed9a9e45e..0000000000 --- a/docs/guide/shop_configuration/shop_configuration.md +++ /dev/null @@ -1,150 +0,0 @@ -# Configuration [[% include 'snippets/commerce_badge.md' %]] - -## HTTP server configuration - -For more information about the HTTP server configuration, -see [the installation guide](../../getting_started/install_ez_platform.md#prepare-installation-for-production). - -[[= product_name_com =]] requires one more rule in order to display images. The following examples show the settings for Apache: - -``` -RewriteRule ^/var/assets/.* - [L] -``` - -## PHP settings - -### Session settings - -Set the `gc_maxlifetime` parameter in `php.ini` (e.g. `/etc/php5/apache2/php.ini`) to reduce the lifetime of the session: - -``` -session.gc_maxlifetime = 86400 -``` - -It can be set in a `.htaccess` as well: - -``` -php_value session.gc_maxlifetime 86400 -``` - -## Bundles - -The `registerBundles()` method in Kernel loads different bundles based on the environment. - -In addition, you can use this method to load client-specific bundles: - -``` php -//custom bundles for clients -switch ($this->getClient()) { - case "demo": - $bundles[] = new Silversolutions\Bundle\DemoBundle\SilversolutionsDemoBundle(); - } -``` - -The value that is considered here is passed from the vhost configuration into the script. - -## System-specific settings - -The `ezcommerce_parameters.yml` file contains specific settings for your installation. It is generated/updated by the installer. - -### Settings for emails - -``` yaml -parameters: - siso_core.default.ses_swiftmailer: - mailSender: shop@silversolutions.de - mailReceiver: shop@silversolutions.de - lostOrderEmailReceiver: shop@silversolutions.de - contactMailReceiver: shop@silversolutions.de - cancellationMailReceiver: shop@silversolutions.de - shopOwnerMailReceiver: shop@silversolutions.de - ses_eshop.forms.business.default_contact_recipient: shop@silversolutions.de - ses_eshop.order.sales_contact: shop@silversolutions.de - ses_eshop.lostorder_email: shop@silversolutions.de - siso_paypal_payment.receiver_email: shop@silver.de - silver_tools.default.translationFolderId: 61 # root (1) / Components (59) / Textmodules (61) - siso_core.default.user_group_location: 106 - siso_core.default.user_group_location.business: 385 - siso_core.default.user_group_location.private: 388 - siso_core.default.product_catalog_content_type_id: 45 - - one_sky_api_key: my-one-sky-key - one_sky_secret: my-onesky-password -``` - -## General settings - -### General shop settings prices - -#### Currencies used per country - -``` yaml -siso_core.de.standard_price_factory.fallback_currency: EUR -siso_core.en.standard_price_factory.fallback_currency: GBP -``` - -#### Shipping costs - -You can set up special rules for shipping costs in the Back Office. -Navigate to eCommerce -> Price and stock management and click **Shipping cost management**. - -![](../img/base_configuration.png) - -#### Settings for VAT - -The settings can be done for each delivery country you want to support in your shop. - -``` yaml -siso_core.default.vat: - DE: - VATREDUCED: 7 - VATNORMAL: 19 - US: - VATREDUCED: 4 - VATNORMAL: 4 - default: - VATREDUCED: 7 - VATNORMAL: 19 - -``` - -### Payment - -If you want to enable PayPal as a payment provider you need to configure: - -``` yaml -jms_payment_core: - encryption: - provider: defuse_php_encryption - secret: 'de---xxxxxxxxxxx' - enabled: true - -siso_paypal_api.user: 'my-api-user' -``` - -Important: The secret has to be created by a command: - -``` -php vendor/defuse/php-encryption/bin/generate-defuse-key -``` - -Configure the PayPal parameters in the `parameters.yml` file: - -``` yaml -siso_paypal_api.user: 'my-api-use' -siso_paypal_api.password: 'paypal api password' -siso_paypal_api.signature: 'paypal-api-signature' -``` - -## Changing the logo for [[= product_name_com =]] - -If you want to change the logo in the Back Office, provide a link to the assets located in the `web/*` folder: - -``` yaml -# Logo for the shop -siso_core.default.logo_image: /bundles/silversolutionseshop/img/logo-advanced.png -# Logo used for invoices -siso_core.default.invoice_logo: /bundles/silversolutionseshop/img/invoice_logo.png -# Logo used for the email header -siso_core.default.email_header: /bundles/silversolutionseshop/img/email-header.png -``` diff --git a/docs/guide/shop_configuration/storing_sessions_in_memcache.md b/docs/guide/shop_configuration/storing_sessions_in_memcache.md deleted file mode 100644 index 1b7637df64..0000000000 --- a/docs/guide/shop_configuration/storing_sessions_in_memcache.md +++ /dev/null @@ -1,37 +0,0 @@ -# Storing sessions in Memcache [[% include 'snippets/commerce_badge.md' %]] - -You can configure storing sessions in Memcache, preferably using stash. -This way you don't mix session stash cache with other stash caches, such as SPI, translation, and navigation. - -You can also configure a cache server for the failover session. - -## Step 1. Configure Memcache servers - -Add a new stash service to stash configuration in `ezpublish_env.yml`: - -``` yaml -session: - drivers: [ Memcache ] - inMemory: true - registerDoctrineAdapter: false - registerSessionHandler: true - Memcache: - prefix_key: harmony_ - retry_timeout: 1 - servers: - - - server: 127.0.0.1 - port: 11211 - - - server: 127.0.0.1 - port: 11212 -``` - -## Step 2. Configure session handler - -Configure the session handler in `config_env.yml`: - -``` yaml -session: - handler_id: stash.adapter.session.session_cache -``` diff --git a/docs/guide/shop_features/catalog.md b/docs/guide/shop_features/catalog.md deleted file mode 100644 index 7ae2fde8a6..0000000000 --- a/docs/guide/shop_features/catalog.md +++ /dev/null @@ -1,45 +0,0 @@ -# Catalog [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] generates the shop catalog automatically from the products and content in the installation or an ERP/PIM system. - -The source of products depends on the data provider you have configured. - -## Data providers - -Data providers define where product information is stored. - -You can use one of two data providers: the content model data provider and the eContent data provider. - -### Content model data provider - -When you use the content model data provider, each product is a Content item, stored in the Repository. - -Use this data provider when you have no PIM system and a limited number of products (up to 20.000). - -You can create and edit all products directly in the Back Office. - -Importing products when using this data provider is time-consuming. - -### eContent data provider - -Use the eContent data provider when you have a PIM system or an ERP which provides product information. -This information can be imported quickly from the relevant system. - -The catalog can contain more than 1 million products. - -The products cannot be edited in the Back Office. - -eContent offers a staging feature and enhanced catalog segmentation and allows imports during production and switching catalogs. - -### Changing data providers - -See [Switching the data provider](../data_providers/data_providers#switching-the-data-provider.md) -to learn how to change the data provider. - -## Product categories - -![product catalog](img/product_catalog_2.png) - -A category represents a product group. [[= product_name_com =]] enables showing a product category page using different layouts (by default sub-categories only, products only or both on the entry page of a category). In the Back Office you can also configure if bestsellers of the group should be displayed. - -![both](img/catalog_categories_and_products.png) diff --git a/docs/guide/shop_features/content_management.md b/docs/guide/shop_features/content_management.md deleted file mode 100644 index bef8ec8c9c..0000000000 --- a/docs/guide/shop_features/content_management.md +++ /dev/null @@ -1,74 +0,0 @@ -# Content management [[% include 'snippets/commerce_badge.md' %]] - -## Product - -[[= product_name_com =]] offers a built-in Product Content Type. - -Any content of this type can be automatically used for all shop functionalities, -such as adding to basket, price calculation and ordering, -and can be embedded on the front end with quick ordering buttons. - -A Product contains the following Fields: - -|Name | Identifier | Type | Description | -|---|---|---|---| -|Productname | `ses_name` | `ezstring` | Main name of the product. Used to create the URL | -|Product type | `ses_type` | `ezselection` | | -|SKU | ses_sku | `ezstring` | Unique Stock keeping unit | -|Subtitle | `ses_subtitle` | `ezstring` | Additional product name | -|Short description | `ses_short_description` | `ezrichtext` | Short product description | -|Long description | `ses_long_description` | `ezrichtext` | Long product description | -|Specifications | `ses_specifications` | `sesspecificationstype` | A set of product specification values. They are indexed in the search engine and can be used for faceted search | -|EAN | `ses_ean` | `ezstring` | European Article Number | -|Variants | `ses_variants` | `uivarvarianttype` | [Product variants](#product-variants) | -|Manufacturer SKU | `ses_manufacturer_sku` | `ezstring` | SKU of the product as assigned by the manufacturer | -|Unit price | `ses_unit_price` | `ezstring` | Product price | -|Product image | `ses_image_main` | ezimage | Main product image | -|Manufacturer | `ses_manufacturer` | ezstring | Manufacturer name | -|Color | `ses_color` | `ezstring` | Product color | -|Technical specification | `ses_specification` | `eztext` | Technical product description | -|Video | `ses_video` | `ezstring` | Link to a product video | -|Add. Product image 1-4 | `ses_image_1` | `ezimage` | Up to four additional images | -|Currency | `ses_currency` | `ezstring` | Default product currency | -|VAT Code | `ses_vat_code` | `sesselection` | One of predefined VAT rates | -|Product Type | `ses_product_type` | `ezstring` | Product type used for grouping products in comparison | -|Packaging unit | `ses_packaging_unit` | `ezstring` | Product packaging unit | -|Min order quantity | `ses_min_order_quantity` | `ezstring` | Minimum quantity that can be ordered | -|Max order quantity | `ses_max_order_quantity` | `ezstring` | Maximum quantity that can be ordered | -|Unit | `ses_unit` | `ezstring` | Product unit | -|Stock numeric | `ses_stock_numeric` | `ezstring` | | -|Discontinued | `ses_discontinued` | `ezboolean` | Flag to indicate if the product is discontinued | -|Tags | `tags` | `ezkeyword` | Product keywords | - -### Custom product Content Type - -`Ez5CatalogDataProvider` defines which Content Type is treated as products and which Field in Product Content items -is treated as SKU: - -``` php -const EZ_PRODUCT_CONTENT_TYPE_IDENTIFIER = 'ses_product'; -const EZ_PRODUCT_SKU_FIELD_IDENTIFIER = 'ses_sku'; -``` - -If you replace the built-in product with a custom Content Type, you need to replace these constants. - -You also need to configure the Content Type to be treated as `createOrderableProductNode`: - -``` yaml -parameters.silver_eshop.default.catalog_factory.<content_type_identifier>: createOrderableProductNode -``` - -## Product variants - -A product can have different variants, corresponding for example to different colors or sizes. - -You can select and preview variants on the product's page. -Variants can have one or two levels. -When you choose a first attribute, the shop narrows down the options for the second level. - -![Product detail](img/product_detail.png) - -## Product type - -A product type represents a collection of very similar products that differ only in some characteristics. -It is used to show a list of products in a tabular way, every product can be added to the basket directly from the overview page. diff --git a/docs/guide/shop_features/erp_integration.md b/docs/guide/shop_features/erp_integration.md deleted file mode 100644 index cd5a237fa5..0000000000 --- a/docs/guide/shop_features/erp_integration.md +++ /dev/null @@ -1,42 +0,0 @@ -# ERP integration [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_exp =]] can be connected to ERP systems. Out of the box it offers Web.Connectors for SAP, Microsoft Dynamics NAV and AX. -The product provides an open interface which can be adapted to other ERP systems as well. - -Existing ERP customers can automatically create an account in the shop without waiting for confirmation from the administrator. -The shop updates customer data from the ERP in real time - -The shop can update stock and price information with data from the ERP in real time, including complex price rules. -Orders are directly transferred to the ERP. - -The shop requests orders, delivery notes, invoices and credit memos from the ERP. - -## Stock information - -[[= product_name_exp =]] requests real-time stock information from the ERP -and notifies the customer if the stock is lower than the required quantity. -It is possible to display the real stock as a numeric value as well. - -![](img/stock_info_in_basket.png) - -## Configuration of price providers - -The shop owner can select systems used for calculating prices. - -A fallback price provider (e.g. using imported prices) can be configured. It is used if the ERP is not available. - -![](img/price_providers.png) - -## ERP fallback - -[[= product_name_exp =]] supports fallback scenarios for the most important processes in case the connection to the ERP is not available: - -- Caching latest customer data after login. -- Fallback price engine. The customer is informed if the prices and stock are not up to date. -- Storing an order in the shop. It is transmitted to the ERP when the system is available again. - -## Monitoring - -You can review all communication (request- and response messages) sent between shop and ERP system. - -You can also check the efficiency of the connection between the shop and the ERP system, including the number of the different requests per date and time. diff --git a/docs/guide/shop_features/img/addresses.png b/docs/guide/shop_features/img/addresses.png deleted file mode 100644 index 9a19525fd9..0000000000 Binary files a/docs/guide/shop_features/img/addresses.png and /dev/null differ diff --git a/docs/guide/shop_features/img/autosuggest.png b/docs/guide/shop_features/img/autosuggest.png deleted file mode 100644 index 3ce561b1f4..0000000000 Binary files a/docs/guide/shop_features/img/autosuggest.png and /dev/null differ diff --git a/docs/guide/shop_features/img/catalog_categories_and_products.png b/docs/guide/shop_features/img/catalog_categories_and_products.png deleted file mode 100644 index db65641e20..0000000000 Binary files a/docs/guide/shop_features/img/catalog_categories_and_products.png and /dev/null differ diff --git a/docs/guide/shop_features/img/comparison_list.png b/docs/guide/shop_features/img/comparison_list.png deleted file mode 100644 index 0bf55891b1..0000000000 Binary files a/docs/guide/shop_features/img/comparison_list.png and /dev/null differ diff --git a/docs/guide/shop_features/img/manage_addresses.png b/docs/guide/shop_features/img/manage_addresses.png deleted file mode 100644 index d75e79c39e..0000000000 Binary files a/docs/guide/shop_features/img/manage_addresses.png and /dev/null differ diff --git a/docs/guide/shop_features/img/navigation.png b/docs/guide/shop_features/img/navigation.png deleted file mode 100644 index 2c2b11cd5d..0000000000 Binary files a/docs/guide/shop_features/img/navigation.png and /dev/null differ diff --git a/docs/guide/shop_features/img/price_mgmt.png b/docs/guide/shop_features/img/price_mgmt.png deleted file mode 100644 index 1d37cd0c14..0000000000 Binary files a/docs/guide/shop_features/img/price_mgmt.png and /dev/null differ diff --git a/docs/guide/shop_features/img/price_providers.png b/docs/guide/shop_features/img/price_providers.png deleted file mode 100644 index 271b9e913c..0000000000 Binary files a/docs/guide/shop_features/img/price_providers.png and /dev/null differ diff --git a/docs/guide/shop_features/img/product_catalog_2.png b/docs/guide/shop_features/img/product_catalog_2.png deleted file mode 100644 index ede1154b98..0000000000 Binary files a/docs/guide/shop_features/img/product_catalog_2.png and /dev/null differ diff --git a/docs/guide/shop_features/img/product_detail.png b/docs/guide/shop_features/img/product_detail.png deleted file mode 100644 index 255e0bdd06..0000000000 Binary files a/docs/guide/shop_features/img/product_detail.png and /dev/null differ diff --git a/docs/guide/shop_features/img/quickorder.png b/docs/guide/shop_features/img/quickorder.png deleted file mode 100644 index fad6875cc9..0000000000 Binary files a/docs/guide/shop_features/img/quickorder.png and /dev/null differ diff --git a/docs/guide/shop_features/img/quickorder_upload.png b/docs/guide/shop_features/img/quickorder_upload.png deleted file mode 100644 index 244eb44902..0000000000 Binary files a/docs/guide/shop_features/img/quickorder_upload.png and /dev/null differ diff --git a/docs/guide/shop_features/img/registration_advanced.png b/docs/guide/shop_features/img/registration_advanced.png deleted file mode 100644 index 18db9b7059..0000000000 Binary files a/docs/guide/shop_features/img/registration_advanced.png and /dev/null differ diff --git a/docs/guide/shop_features/img/search_1.png b/docs/guide/shop_features/img/search_1.png deleted file mode 100644 index f3f2692548..0000000000 Binary files a/docs/guide/shop_features/img/search_1.png and /dev/null differ diff --git a/docs/guide/shop_features/img/stock_info_in_basket.png b/docs/guide/shop_features/img/stock_info_in_basket.png deleted file mode 100644 index f61338d6ba..0000000000 Binary files a/docs/guide/shop_features/img/stock_info_in_basket.png and /dev/null differ diff --git a/docs/guide/shop_features/img/stock_management.png b/docs/guide/shop_features/img/stock_management.png deleted file mode 100644 index 9a7c19dcdb..0000000000 Binary files a/docs/guide/shop_features/img/stock_management.png and /dev/null differ diff --git a/docs/guide/shop_features/img/stored_basket.png b/docs/guide/shop_features/img/stored_basket.png deleted file mode 100644 index 880d923cb6..0000000000 Binary files a/docs/guide/shop_features/img/stored_basket.png and /dev/null differ diff --git a/docs/guide/shop_features/img/user_information.png b/docs/guide/shop_features/img/user_information.png deleted file mode 100644 index 3a4c098a58..0000000000 Binary files a/docs/guide/shop_features/img/user_information.png and /dev/null differ diff --git a/docs/guide/shop_features/img/wishlist.png b/docs/guide/shop_features/img/wishlist.png deleted file mode 100644 index a9d83acc5f..0000000000 Binary files a/docs/guide/shop_features/img/wishlist.png and /dev/null differ diff --git a/docs/guide/shop_features/payment_and_shipping.md b/docs/guide/shop_features/payment_and_shipping.md deleted file mode 100644 index d966df3a7a..0000000000 --- a/docs/guide/shop_features/payment_and_shipping.md +++ /dev/null @@ -1,27 +0,0 @@ -# Payment and shipping [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] offers the following payment methods: - -- invoice -- PayPal Express -- Ogone/Ingenico -- Telecash - -Payment is based on a standard Symfony bundle. You can also integrate other payment providers. - -The payment and shipping methods can be enabled or disabled per SiteAccess. - -## Shipping costs - -[[= product_name_com =]] offers a flexible way to define shipping costs, if they are not set in the ERP system. - -Shipping costs can be set up per: - -- currency -- shipping method -- country -- state -- ZIP code (e.g. for exceptions such as delivery to islands) -- size of the basket (including free of freight rules) - -If no shipping costs are defined for a given country, shipping method, currency and value of goods, a fallback cost from the configuration is used. diff --git a/docs/guide/shop_features/pricing.md b/docs/guide/shop_features/pricing.md deleted file mode 100644 index 389d28d0f5..0000000000 --- a/docs/guide/shop_features/pricing.md +++ /dev/null @@ -1,38 +0,0 @@ -# Pricing [[% include 'snippets/commerce_badge.md' %]] - -You can set up prices manually per product or per product variant. -Every price can contain an offer price and a base price. If an offer price is set, both are displayed. - -In addition, prices can vary per customer group. - -![Price management](img/price_mgmt.png) - -## Currencies - -The currency is configured per country in the [configuration settings](../shop_configuration/shop_configuration.md#currencies-used-per-country). - -If a product has a price for a currency this price is displayed in the shop. - -If there is no price set in a shop for the given currency, [[= product_name_com =]] offers two options that can be set in the shop configuration: - -- Calculate the price for the requested currency using the base price defined in the product (using the base currency setup for the installation) and an exchange rate defined in the configuration -- Display an error in the frontend that no price is available - -## Import/export stock and price information - -You can upload and download stock and price information using a CSV file. - -## Vouchers - -Using an ERP system you can create vouchers containing discount conditions like minimum basket value or discountable products. -Individual vouchers can be valid for a given number of uses, or for a set time period. -Personalized vouchers are only valid for one-time use. - -## Stock - -You can manage stock per product and product variant. - -![Stock management](img/stock_management.png) - -In addition you can add a stock text (e.g. indicating when the product will be available). -After an order is placed the stock is reduced by the number of products bought by the customer. diff --git a/docs/guide/shop_features/search.md b/docs/guide/shop_features/search.md deleted file mode 100644 index 4ff6365f5c..0000000000 --- a/docs/guide/shop_features/search.md +++ /dev/null @@ -1,11 +0,0 @@ -# Search [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] uses Elasticsearch or Solr to index products together with content. - -![Search](img/search_1.png) - -## Autosuggestion - -When searching on the front end, autosuggestions are offered for products, categories, downloads and content. - -![Autosuggest](img/autosuggest.png) diff --git a/docs/guide/shop_features/shop_features.md b/docs/guide/shop_features/shop_features.md deleted file mode 100644 index 07cacfb499..0000000000 --- a/docs/guide/shop_features/shop_features.md +++ /dev/null @@ -1,58 +0,0 @@ -# Shop features [[% include 'snippets/commerce_badge.md' %]] - -## Shop navigation - -Shop navigation is generated automatically from Content items under the root Location (Location ID 2). -By default these include the product catalog and content relating to legal documents, -but you can add any other content to it. -The order of the navigation items can be defined by the editor in the Back Office using the priority field. - -![Default navigation](img/navigation.png) - -## Product comparison - -The customer can add products to a comparison list and access it from the "My shop" menu. -The comparison list automatically groups products per product category and product type (so it is impossible to compare products belonging to different types). -The customer can change the order of the sorting by dragging and can display only differences of the products. - -![Product comparison](img/comparison_list.png) - -## Wishlist and stored baskets - -A logged-in customer can store products for later without adding them to the basket. -The customer can also add products to a personal wishlist or to a named, stored basket. - -![Wishlist](img/wishlist.png) - -![Stored baskets](img/stored_basket.png) - -Products that are not in the catalog are automatically marked as "not available". - -## Quickorder - -B2B customers can quickly order products by providing their SKU numbers. - -![](img/quickorder.png) - -They can also upload a CSV file containing a list of SKUs and quantities. - -![](img/quickorder_upload.png) - -## Order history - -When an ERP system is connected, the customer can browse and search past orders, invoices, delivery notes, and credit memos. -This applies to all channels of making an order, including phone, fax, email, or online shop. -The documents for the chosen dates are fetched in real time. -If the ERP provides a tracking link, this information is displayed as well. -The customer can order the same products again. - -## GDPR compliance - -[[= product_name_com =]] offers tools to set up a shop that is compliant with GDPR (General Data Protection Regulation): - -- Prepared articles with dummy text for T&C, data privacy and cancellation policy -- Important legal documents (e.g. T&C or data privacy) can be linked using permanent URLs -- Checkboxes in checkout and registration to confirm the T&C, cancellation policy and data privacy -- Double opt-in registration process -- Cancellation policy text included in order confirmation emails -- Cookie consent with a link to data privacy to inform the user about used cookies diff --git a/docs/guide/shop_features/user_management.md b/docs/guide/shop_features/user_management.md deleted file mode 100644 index c53bba7d96..0000000000 --- a/docs/guide/shop_features/user_management.md +++ /dev/null @@ -1,47 +0,0 @@ -# User management [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] by default adds the Shop users User Group, containing subgroups Business customers and Private customers. - -These groups are assigned Roles that allow them to use the shop as customers. - -You can get information about a User's last orders and sales statistics. If a user has a current basket it is displayed as well. - -![](img/user_information.png) - -## Roles and Policies - -The built-in Roles than allow using the shop are: - -- Ecommerce anonymous -- Ecommerce checkout -- Ecommerce registered users - -[[= product_name_com =]] adds [special shop-related Policies](../permissions.md#available-policies). - -## User registration - -A user can register as private, business or existing customer. - -When registering as a business customer, you need to provide additional information such as the VAT number (checked for EU VAT codes using an online service (VIES)) or a PDF with company registration. - -![](img/registration_advanced.png) - -A newly registered account is not active by default. The shop owner or administrator must enable it first. - -### Existing customers - -Customers who are set up in the connected ERP can activate their [[= product_name_com =]] accounts themselves by entering their customer number and one invoice number. -The data is sent to the ERP. If the data in ERP and the entered data match, the customer gets access to the shop immediately. -A User Content item is created automatically with the customer number from the ERP. - -## Account management - -The customer can access **My account** on the front page to manage their account, including changing account data, password and addresses. - -### Addresses - -![](img/addresses.png) - -The customer can manage their delivery addresses in the address book. The addresses are offered for selection during checkout process. - -![](img/manage_addresses.png) diff --git a/docs/guide/shop_process.md b/docs/guide/shop_process.md new file mode 100644 index 0000000000..09b195835b --- /dev/null +++ b/docs/guide/shop_process.md @@ -0,0 +1,14 @@ +--- +description: The shop process of Ibexa DXP covers various steps of making a transaction from listing available products, through adding products to a cart, to checkout and confirmation. +--- + +[[= cards([ +"guide/basket/basket", +"guide/basket/wishlist_and_stored_baskets", +"guide/checkout/checkout", +"guide/payment/payment", +"guide/pricing/price_engine", +"guide/quick_order/quick_order", +"guide/order_history/order_history", +"guide/vouchers/vouchers" +], force_version="3.3") =]] \ No newline at end of file diff --git a/docs/guide/shop_templates/design_engine.md b/docs/guide/shop_templates/design_engine.md deleted file mode 100644 index 202ab72825..0000000000 --- a/docs/guide/shop_templates/design_engine.md +++ /dev/null @@ -1,51 +0,0 @@ -# Design engine in the shop [[% include 'snippets/commerce_badge.md' %]] - -!!! tip - - See [Design engine](../design_engine.md) for more information - about using the design engine in [[= product_name =]]. - -[[= product_name_com =]] comes with the configured `base_design` and `base_theme` which use the existing standard templates used with the template resolver. - -## SiteAccess configuration - -You can configure designs per SiteAccess or SiteAccess group: - -``` yaml -siteaccess: - list: - - de - - en - - import - - site - - admin - groups: - site_group: [de, en, import, site] - admin_group: [admin] - default_siteaccess: en -site_group: - design: base_design -admin_group: - design: admin -``` - -## Activation - -If the template resolver of [[= product_name_com =]] is disabled (standard), the design engine of [[= product_name =]] is automatically activated: - -``` yaml -siso_tools.default.template_resolver.enabled: false -``` - -## Template theme paths - -All [[= product_name_com =]] bundles contain an `ez_design.yml` file which is used to define the `templates_theme_path` to the templates. -Without the template theme path, the templates are not recognized by the design engine: - -``` yaml -design_list: - base_design: [base_theme] -templates_theme_paths: - base_theme: - - '%kernel.root_dir%/../vendor/silversolutions/silver.someBundle/Resources/views' -``` diff --git a/docs/guide/shop_templates/product_images.md b/docs/guide/shop_templates/product_images.md deleted file mode 100644 index ce199d9ba0..0000000000 --- a/docs/guide/shop_templates/product_images.md +++ /dev/null @@ -1,52 +0,0 @@ -# Product images and assets [[% include 'snippets/commerce_badge.md' %]] - -Product images and assets can be taken from the content model (if this data provider is used). -External images can also be stored in the file system. - -If images come from the file system, they have to be stored in the following folders: - -- Product images in `web/var/assets/product_images` -- Product group images `web/var/assets/product_group_images` - -To avoid too many files in one directory, subfolders are used. - -For example, images for a product with SKU `D4241` are stored in the following file system folders: - -``` -web/var/assets/product_images/D/4/D4142_picture.jpg -web/var/assets/product_images/D/4/D4142_picture_2.jpg -``` - -When using a cluster, this folder has to be mounted as an NFS shared or cluster FS file system. - -These folders are used as source folders for all assets. - -Images are resized automatically when they are used for the first time. -The different image variations are stored in `web/var/ecommerce/storage/<image_class>/<subfolders>`: - -``` -web/var/ecommerce/storage//images/image_zoom/D/4/1/-D4142_picture_2_image_zoom.jpg -web/var/ecommerce/storage//images/image_zoom/D/4/1/-D4142_picture_image_zoom.jpg -web/var/ecommerce/storage//images/thumb_big/D/4/1/-D4142_picture_2_thumb_big.jpg -web/var/ecommerce/storage//images/thumb_big/D/4/1/-D4142_picture_thumb_big.jpg -web/var/ecommerce/storage//images/thumb_medium/D/4/1/-D4142_picture_thumb_medium.jpg -web/var/ecommerce/storage//images/thumb_medium/D/4/1/589768-D4142_thumb_medium.jpg -web/var/ecommerce/storage//images/thumb_smaller/D/4/1/-D4142_picture_2_thumb_smaller.jpg -web/var/ecommerce/storage//images/thumb_smaller/D/4/1/-D4142_picture_thumb_smaller.jpg -``` - -Image paths are cached in order to avoid accessing the file system in production mode. -The cache uses stash. - -## Remove stash cache for images and one SKU - -``` php - /** @var $catalogElement \Silversolutions\Bundle\EshopBundle\Catalog\CatalogElement */ - $catalogElement = $catalogService->getDataProvider()->fetchElementBySku( - $sku, - array(), - $ezHelper->getCurrentLanguageCode() - ); - $assetService = $this->get('silver_catalog.asset_service'); - $assetService->removeAssetCache($catalogElement); -``` diff --git a/docs/guide/shop_templates/reusable_address_template.md b/docs/guide/shop_templates/reusable_address_template.md deleted file mode 100644 index 6d13c1b436..0000000000 --- a/docs/guide/shop_templates/reusable_address_template.md +++ /dev/null @@ -1,28 +0,0 @@ -# Reusable address template [[% include 'snippets/commerce_badge.md' %]] - -The address template is used to display addresses in a more flexible way. - -- `EshopBundle\Resources\views\parts\address.html.twig` -- `EshopBundle\Resources\views\parts\address.txt.twig` - -To override the template inside a project, create templates inside your project bundle that follow the same structure and have the same name. - -## Configuration - -|Parameter|Description| -|--- |--- | -|`address`|Address to be displayed. Must be type of Party object (e.g. `buyerAddress`)| -|`class`|Class used for styling (e.g. `styled_list`)| -|`displayEmail`|If set to true, the email address is displayed in template. Default value is `true`.| -|`displayPhone`|If set to true, the phone number is displayed in template. Default value is `true`.| -|`raw`|If set to `true`, the template is displayed without formatting. If set to `false` the template is displayed in an unordered list. Default value is `false`.| - -## Example - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:address.html.twig', {'class' : 'styled_list', 'address' : buyerAddress, 'displayEmail' : false, 'displayPhone' : false}) }} - -{{ include('SilversolutionsEshopBundle:parts:address.html.twig', {'address' : address, 'displayEmail' : false, 'displayPhone' : false, 'raw' : true}) }} - -{{ include('SilversolutionsEshopBundle:parts:address.txt.twig', {'address' : deliveryAddress, 'displayEmail' : false}) }} -``` diff --git a/docs/guide/shop_templates/reusable_message_template.md b/docs/guide/shop_templates/reusable_message_template.md deleted file mode 100644 index e41b9fa851..0000000000 --- a/docs/guide/shop_templates/reusable_message_template.md +++ /dev/null @@ -1,129 +0,0 @@ -# Reusable message template [[% include 'snippets/commerce_badge.md' %]] - -The message template renders inline messages within content pages. - -`EshopBundle/Resources/views/parts/message.html.twig` - -## Parameters - -- `content` (required) - Content that is displayed inside a message box. Can be mixed with any type of HTML or text. -- `close` - Flag that indicates if you can close the message. Default: `false`. -- `attrs` - Array of any HTML attributes that is allowed for a `<div>` element as well as some special attributes. - -Common attributes: - -``` -'class': 'alert|success|info|warning|secondary', -'id: 'message-id', -'data-custom-attr': 'data custom value', -'style': 'display: none;' -``` - -You can pass a CSS class that will style message. Currently the following classes are supported: - -- alert (for alerts/errors) -- success -- info -- warning -- secondary - -You can pass multiple class names if required, e.g.:`'class': 'alert radius u-margin-top-1x'` - -## Examples - -### Success message - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': 'hello world', - 'attrs': { - 'class': 'success' - } }) }} -``` - -### Alert message with a close icon - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': 'This is an error message', - 'close': true, - 'attrs': { - 'class': 'alert' - } }) }} -``` - -### Notice message with multiline content - -``` html+twig -// setting the message content -{% set noticeMsg %} - {% for n in notice %} - <p>{{ n }}</p> - {% endfor %} -{% endset %} - -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': noticeMsg, // using the noticeMsg variable - 'attrs': { - 'class': 'info' - } }) }} -``` - -### Warning message with `st_translate()` as content - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': 'search_result_empty'|st_translate('search'), - 'attrs': { - 'class': 'warning' - } }) }} -``` - -### Alert message with an extra attribute for Behat testing - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': 'Your Basket is empty'|st_translate ~ '!', - 'attrs': { - 'class': 'alert', - 'behat': st_tag('message', 'error', 'exists', '', '') - } }) }} -``` - -### Alert message with extra CSS classes and inline styling - -``` html+twig -{{ include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': "Start date must be lower than end date."|st_translate(), - 'attrs': { - 'class': 'alert js-order-history-dates-invalid', - 'style': 'display: none;' - } }) }} -``` - -### Behat extra information - -Message elements are rendered with a data tag followed by the Behat `attrs` parameters separated by `-` - -The following example checks for a success message with the following configuration: - -``` html+twig -// Twig file with success message: -include('SilversolutionsEshopBundle:parts:message.html.twig'|st_resolve_template, { - 'content': s, - 'close': true, - 'attrs': { - 'class': 'success', - 'behat': st_tag('message', 'success', 'exists', '', '') -``` - -``` -// Behat file FeatureContext.php -// This method will check for existing message of a type specified -// Given $messageType = 'success' -public function iShouldSeeAMessageOfType($messageType) - { - $xpath = sprintf("//*[@data-tag-message-%s-exists]", $messageType); - return $this->iShouldSeeAnElementWithXpath($xpath); - } -``` diff --git a/docs/guide/shop_templates/set_up_new_project_design.md b/docs/guide/shop_templates/set_up_new_project_design.md deleted file mode 100644 index a13bd1723a..0000000000 --- a/docs/guide/shop_templates/set_up_new_project_design.md +++ /dev/null @@ -1,62 +0,0 @@ -# Setting up new project design [[% include 'snippets/commerce_badge.md' %]] - -1\. Add an `ez_design.yml` file to your project bundle. - -!!! caution - - Don't remove the `base_theme` when adding a `project_theme` to the project design. - -``` yaml -design_list: - project_design: [project_theme, base_theme] -templates_theme_paths: - project_theme: - - '%kernel.root_dir%/../src/ProjectBundle/Resources/views' -``` - -2\. Prepend `ez_design.yml` so the configuration can be used by the design engine. - -In `src/ProjectBundle/DependencyInjection/ProjectExtension.php`: - -``` -<?php -namespace ProjectBundle\DependencyInjection; - -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; -use Symfony\Component\Yaml\Yaml; - -/** - * This is the class that loads and manages your bundle configuration - * - * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} - */ -class ProjectExtension extends Extension implements PrependExtensionInterface -{ - /** - * @param ContainerBuilder $container - */ - public function prepend( ContainerBuilder $container ) - { - $config = Yaml::parse(file_get_contents( __DIR__ . '/../Resources/config/ez_design.yml' )); - $container->prependExtensionConfig( 'ezdesign', $config ); - } -} -``` - -3\. Add `project_design` either to a SiteAccess or SiteAccess group. - -``` yaml -# Siteaccess configuration, with one siteaccess per default -siteaccess: - list: - - de - - en - groups: - site_group: [de, en] - default_siteaccess: en - -site_group: - design: project_design -``` diff --git a/docs/guide/shop_templates/shop_templates.md b/docs/guide/shop_templates/shop_templates.md deleted file mode 100644 index 0fe4904894..0000000000 --- a/docs/guide/shop_templates/shop_templates.md +++ /dev/null @@ -1,22 +0,0 @@ -# Shop templates [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] offers additional Twig functions and filters you can use in templates. - -## Template resolver - -[[= product_name_com =]] comes with a [template resolver](template_resolver.md) system which enables overriding templates on a SiteAccess and design base. - -## Design engine - -Instead of the template resolver you can use the [design engine](../design_engine.md). -See [Integration of the design engine](design_engine.md) for more information. - -## Shop templates - -Built-in templates for the shop are stored in multiple bundles - -``` -vendor/silversolutions/silver.customercenter/src/Siso/Bundle/CustomerCenterBundle/Resources/views -vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views -vendor/silversolutions/silver.orderhistory/src/Siso/Bundle/OrderHistoryBundle/Resources/views -``` diff --git a/docs/guide/shop_templates/st_tag_selector.md b/docs/guide/shop_templates/st_tag_selector.md deleted file mode 100644 index 93caf6b7f6..0000000000 --- a/docs/guide/shop_templates/st_tag_selector.md +++ /dev/null @@ -1,129 +0,0 @@ -# st_tag selector [[% include 'snippets/commerce_badge.md' %]] - -Template operator `st_tag` creates a custom HTML attribute that enables you to quickly find an HTML tag with this attribute. - -This is especially helpful for Behat testing, because `st_tag` provides consistent naming convention for page selectors. - -### Example - -Twig template: - -``` html+twig -<p {{ st_tag('product', 'price', 'read') }}>1.865,00 €</p> -``` - -is rendered to HTML: - -``` html+twig -<p data-tag-product-price-read>1.865,00 €</p> -``` - -## Enabling tagging - -To enable tagging, define a dynamic SiteAccess-aware parameter `siso_tools.default.tag-prefix`: - -``` -siso_tools.default.tag-prefix: data-tag -``` - -If you set an empty value to this parameter, then all tags are hidden. -It is important, for example, in `prod` environment to remove Behat tags. - -## Implementation - -Tagging is implemented with a template operator `st_tag`. - -The operator takes the following parameters: - -|Parameter|Required|Type| Description|Example| -|--- |--- |--- |--- |---| -|Module/Component|required |string | Identifies module/component/page that you are working on.|`product`, `form`| -|Element (target)|required |string | Identifies specific field that you are searching for.|`price`, `field`, `text`, `label`| -|Action|required |string | Identifies desired action for the selector.|`read` - reads to value of text node</br>`fill`/`write` - fills in form field</br>`click`/`follow`/`submit` - clicking on buttons, following links| -|ID|optional |number, string | Identifies a unique element that occurs more than once on a page. Helpful when searching for specific element in collections, e.g. product list, multiple images, buttons, etc.|`1200`, `button_action`| -|Value|optional |mix | If you don't want to take the value from the wrapper because it contains special characters or format, you can use this parameter to set a standard format. Can be helpful for example for getting prices.|`1600.00` (instead of `1.600,00 €`)| - -!!! caution - - Don't use these attributes in CSS (for styling) or JavaScript (for selecting, hooks, etc.). - These attributes have different and specific purpose. - In addition, in production mode they can be disabled, which breaks the selectors. - -## Examples - -### Read price - -``` html+twig -<p {{ st_tag('product', 'price', 'read') }}>1.865,00 €</p> -``` - -Output: - -``` html+twig -<p data-tag-product-price-read>1.865,00 €</p> -``` - -### Read price with custom value - -``` html+twig -<p {{ st_tag('product', 'price', 'read', '', '1865.00') }}>1.865,00 €</p> -``` - -Output: - -``` html+twig -<p data-tag-product-price-read="1865.00">1.865,00 €</p> -``` - -### Follow/click a text link - -``` html+twig -<a href="/link-to-product.html" {{ st_tag('product', 'anchor', 'follow', '1200', '') }}>Paprika3</p> -``` - -Output: - -``` html+twig -<a href="/link-to-product.html" data-tag-product-anchor-follow-1200>Paprika3</p> -``` - -### Fill out form field - -``` html+twig -<input name="quantity" id="quantity" value="" {{ st_tag('form', 'input', 'write', 'quantity', '') }}> -``` - -Output: - -``` html+twig -<input name="quantity" id="quantity" value="" data-tag-form-input-write-quantity> -``` - -### Check if there is a specific message on the page (e.g. after an Ajax response) - -``` html+twig -<div class="message notice" {{ st_tag('message', 'notice', 'read', '', '') }}>Message content -``` - -Output: - -``` html+twig -<div class="message notice" data-tag-message-notice-read>Message content -``` - -### Submit buttons or links - -!!! caution - - Always use the same naming for buttons which submit data. - A Behat test or Google tag manager doesn't recognize whether it is a button, a link or an input field which sends the form. - -``` xml -<button name="add_to_basket" type="submit" value="Kaufen" class="button add_to_basket float_right"> {{ st_tag('product', 'order', 'submit', '', '') }} -``` - -Output: - -``` xml -<input name="quantity" id="quantity" value="" data-tag-product-order-submit> -``` diff --git a/docs/guide/shop_templates/template_resolver.md b/docs/guide/shop_templates/template_resolver.md deleted file mode 100644 index b14941eeae..0000000000 --- a/docs/guide/shop_templates/template_resolver.md +++ /dev/null @@ -1,187 +0,0 @@ -# Template resolver [[% include 'snippets/commerce_badge.md' %]] - -Template resolver enables overriding templates. -It is an alternative to the [Symfony override functionality](https://symfony.com/doc/3.4/templating/overriding.html). - -The template resolver goes through all configured bundles and designs (if specified), looking for the requested template. -If it can't find the requested template, the template resolver takes this template or the default one. - -An override is activated by a template resolver configuration. You can configure: - -- list of bundles -- list of designs (folders with templates) -- default template - -## Overriding templates - -In this example you have a project with a bundle `src/Client/Bundle/WebsiteBundle`, a SiteAccess `website_de_de` -and you want to override [[= product_name_com =]] bundle templates. - -### Step 1. Change `config_{env}.yml` or `parameters.yml` - -The template resolver is disabled in the default configuration and has to be enabled: - -``` yaml -parameters: - # 1. Enable template resolver - siso_tools.website_de_de.template_resolver.enabled: true # true|false - - # 2. Define that your bundle is able to override templates - siso_tools.website_de_de.template_resolver.bundles: [ClientWebsiteBundle] -``` - -!!! note - - The template resolver uses dynamic configuration of [the ConfigResolver](../config_dynamic.md#configresolver). - - If you want to have one configuration for all SiteAccesses, use the `default` scope: - - ``` yaml - siso_tools.default.template_resolver.enabled: false - ``` - -### Step 2. Create new templates - -To override templates, you have to reflect the structure of [[= product_name_com =]] vendor bundles, e.g.: - -| | | -| ----------------- | --- | -| Original template | `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views/Catalog/catalog.html.twig` | -| New template | `/src/Client/Bundle/WebsiteBundle/Resources/views/Catalog/catalog.html.twig` | - -In this example the template from `ClientWebsiteBundle` overrides `catalog.html.twig` from `SilversolutionsEshopBundle`. - -## Overriding templates using designs - -Design is an additional folder in your `Resources/views`. -Templates in the design folder have to reflect the structure of the bundle or bundles that you want to override. - -In this example you prepare templates for a website redesign with a provisional name `new_redesign`. - -### Step 1. Change `config_{env}.yml` or `parameters.yml` - -``` yaml -parameters: - siso_tools.website_de_de.template_resolver.enabled: true # true|false - siso_tools.website_de_de.template_resolver.bundles: [ClientWebsiteBundle] - - # Define design name - siso_tools.website_de_de.template_resolver.designs: [new_redesign] -``` - -### Step 2. Create new templates in the design folder - -Create new folder `Resources/views/designs/new_redesign` in your bundle and put new templates there: - -| | | -| ----------------- | ------ | -| Original template | `vendor/silversolutions/silver.e-shop/src/Silversolutions/Bundle/EshopBundle/Resources/views/Catalog/catalog.html.twig` | -| New template | `src/Client/Bundle/WebsiteBundle/Resources/views/designs/new_redesign/Catalog/catalog.html.twig` | - -In this example the template from the design `new_redesign` in the `ClientWebsiteBundle` overrides `catalog.html.twig` from `SilversolutionsEshopBundle`. - -Templates in design folders always have precedence over other templates. - -If you have both `new_redesign/Catalog/catalog.html.twig` and `Catalog/catalog.html.twig`, the first one is used. - -## Configuration - -| Parameter | Type | Description | Default | Example | -| ------------ | ------- | ----------------- | ------------ | -------------- | -| `siso_tools.<scope>.template_resolver.enabled` | bool (`true`\|`false`) | Enable or disable template resolver | `true` | `true` | -| `siso_tools.<scope>.template_resolver.bundles` | array | List of bundle names | `[ ]` (all bundles) | `[Bundle1, Bundle2, Bundle3]` | -| `siso_tools.<scope>.template_resolver.designs` | array | List of bundle names | `[ ]` (none) | `[Design1, Design2]` | -| `siso_tools.<scope>.template_resolver.default_template` | string | Default template to return if nothing is found | `SisoToolsBundle:TemplateResolver:default.html.twig` | `SisoToolsBundle:TemplateResolver:default.html.twig` | - -## Developing with the template resolver - -### Use in templates - -To make a template overridable, use the template filter `st_resolve_template` with its name. - -``` html+twig -{% extends "SilversolutionsEshopBundle::pagelayout.html.twig"|st_resolve_template %} -<div class="grid_4"> - {{ include('SilversolutionsEshopBundle:parts:address.html.twig'|st_resolve_template, {'class' : 'styled_list', 'address' : buyerAddress, 'displayEmail' : false, 'displayPhone' : false}) }} -``` - -!!! note - - You cannot include blocks with the [`use`](http://twig.sensiolabs.org/doc/tags/use.html) tag - because of specific implementation of this tag. - - Because `use` statements are resolved independently of the context passed to the template, - the template reference cannot be an expression. - -### Use in PHP code - -#### Use in controllers - -To make using of the template resolver in controllers easier, you can inherit the controller from -`Silversolutions\Bundle\EshopBundle\Controller\BaseController`. -In this case the `render` and `renderView` methods get rendered with a template using `TemplateResolver`, i.e. overridden template. - -``` php -use Silversolutions\Bundle\EshopBundle\Controller\BaseController; - -class ExampleController extends BaseController -{ - public function showAction(Request $request) - { - return $this->render( - 'SilversolutionsEshopBundle:Basket:show.html.twig', - array( - 'param' => 'example_param' - ) - ); - } -} -``` - -#### General usage - -Alternatively, you can inject the template resolver in your service (or take it from container) and resolve the template there: - -``` php -/** @var TemplateResolverServiceInterface $templateResolverService */ -$templateResolverService = $this->get('siso_tools.template_resolver'); - -$resolvedTemplate = $templateResolverService->resolve('YourBundle:new_redesign/Catalog/catalog.html.twig'); -``` - -## Debugging - -You can get information about the template resolver configuration and overridden templates in the SilverSolutions tab of the Symfony debug toolbar: - -![](../img/search_19.png) - -For example: - -![](../img/search_20.png) - -## Limitations - -Using the template resolver has the following limitations: - -- `pagelayout.html.twig` cannot be resolved by the template resolver -- it is not possible to override templates included with the Twig `use` operator. -- it is not possible to use the template resolver in the configuration, -so for overriding standard templates, for example full eZ templates, you need to override the configuration: - -``` yaml -ezpublish: - system: - <scope>: - location_view: - full: - silverModuleRuleset: - #change the template name here - template: SilversolutionsEshopBundle::st_module.html.twig - match: - Identifier\ContentType: [st_module] - silverEshopProductCatalogRuleset: - #change the template name here - template: SilversolutionsEshopBundle::ses_productcatalog.html.twig - match: - Identifier\ContentType: [ses_productcatalog] -``` diff --git a/docs/guide/shop_templates/twig_extension.md b/docs/guide/shop_templates/twig_extension.md deleted file mode 100644 index 02f2d6c2d4..0000000000 --- a/docs/guide/shop_templates/twig_extension.md +++ /dev/null @@ -1,126 +0,0 @@ -# Twig extension [[% include 'snippets/commerce_badge.md' %]] - -[[= product_name_com =]] offers the following Twig functions and filter for use in templates: - -## Functions - -|Name|Parameters|Description| -|--- |--- |--- | -|`ses_render_field`|`CatalogElement $catalogElement`</br>`string $fieldIdentifier = ''`</br>`array $params = array()`|Renders a CatalogElement containing a Field| -|`ses_navigation`|`array $params = array()`|Returns navigation node tree| -|`ses_product`|`array $params = array()`|Returns a product node</br>`{% set product = ses_product({'sku': line.sku, 'variantCode': line.variantCode }) %}`| -|`ses_variant_product_by_sku`|`string $sku`|Fetches a VariantProductNode for the given SKU| -|`ses_assets_main_image`|`CatalogElement $catalogElement`</br>`string $productId = null`|Returns the main image of CatalogElement or null| -|`ses_assets_image_list`|`CatalogElement $catalogElement`</br>`$productId = null`|Returns the list of images for a CatalogElement or an empty array| -|`ses_variants_sort`|`VariantProductNode $node`</br>`$order = array()`|Returns all product variants in a given order| -|`ses_render_price`|`CatalogElement $catalogElement`</br>`PriceField $field`</br>`array $params = array()`|Renders a PriceField| -|`ses_render_stock`|`StockField $field = null,`</br>`array $params = array()`|Renders a StockField| -|`ses_basket`|-|Gets the basket of the current user| -|`ses_wish_list`|-|Gets the wishlist of the current user| -|`ses_total_comparison`|-|Gets total basket lines array for basket flyout| -|`ses_check_product_in_comparison`|`$sku`</br>`$variantCode = null`|Returns true if product with the given SKU and variant code is already in the product comparison| -|`ses_check_product_in_wish_list`|`$sku`</br>`$variantCode = null`|Returns true if product with the given SKU and variant code is already in the wishList| -|`ses_pagination`|`Pagerfanta $pagerfanta`</br>`$viewName = null`</br>`array $options = array()`|Renders pagination| -|`get_stored_baskets`|-|Returns stored baskets for the current user| -|`ses_user_menu`|-|Returns rendered user menu</br>Combines parts of all configured bundles| -|`ses_track_basket`|`Basket $basket`</br>`$mode`</br>`array $params = array()`|Returns a list with rendered template snippets for basket tracking| -|`ses_track_product`|`ProductNode $catalogElement`</br>`$mode`</br>`array $params = array()`|Returns a list with rendered template snippets for product tracking| -|`ses_track_base`|`array $params = array()`|Returns a list with rendered template snippets for general tracking| -|`get_search_query`|-|Returns search query| -|`get_siteaccess_locale`|`$siteAccessName = null`|Returns the Symfony locale that matches the given SiteAccess. If no SiteAccess name is given, the current SiteAccess is used.</br>`{% set locale = get_siteaccess_locale('ger') %}`| -|`get_characteristics_b2b`|`VariantProductNode $catalogElement`</br>`array $order = array()`|Returns characteristic sorted for B2B| -|`has_user_role`|`$module`</br>`$function`|Checks if the user has a Role| -|`get_parent_product_catalog`|`CatalogElement $catalogElement`|Fetches the parent product catalog element for the `$identifier`| -|`get_data_location_ids`|`$object`|Returns a comma-separated string of `data_locations` of the given element.</br>All parent Locations including the element Location are returned.| -|`set_bold_text`|`$needle`</br>`$haystack`|Returns HTML with added `<strong>` tag(s) to needle occurrences in haystack.</br>Supports multiple words.| -|`has_subcategory`|`$locationId`</br>`$offset`</br>`$limit|Checks if the current category has subcategories| -|`get_customer_sku`|`$sku`</br>`$customerNumber`|Returns customer SKU for the given SKU with customer number| -|`get_sku`|`$customerSku`</br>`$customerNumber = null`|Returns SKU for th given customer SKU with customer number| -|`get_search_result_template`|`$catalogElement`|Selects the correct template according to catalog element type| -|`ses_fetch_content_by_identifier`|`string $contentType`</br>`string $identifier`|Fetches a Content item of the specific type and a Field `identifier` with a specific value| -|`ses_config_parameter`|`$code, $domain, $scope = null`|Returns a SiteAccess parameter from the configuration| -|`st_imageconverter`|`Abstract $imageFieldstring $size`|Provides an image in the required resolution| -|`st_siteaccess_lang`|`string $siteAccessName`|Returns the prioritiest language for given siteaccess| -|`st_tag`|`$component`</br>`$target`</br>`$action`</br>`$id = null`</br>`$value = null`|Returns the selector tag for Behat tests or for Google tag management| - -## Filters - -|Name|Parameters|Description| -|--- |--- |--- | -|date_format|`$date`</br>`$inputFormat = 'Y-m-d'`</br>`$locale = 'en'`</br>`$dateType = '\IntlDateFormatter::GREGORIAN'`</br>`$timeType = '\IntlDateFormatter::NONE'`</br>`$formatPattern = 'dd.MM.yyyy'`|Formats a date value| -|price_format|`$priceValue`</br>`$currency`</br>`$locale`|Formats a price value</br>default parameters in `silver.eshop.yml`</br>`silver_eshop.default.price_format_currency: EUR`</br>`silver_eshop.default.price_format_locale: en`| -|youtube_video_id|`$youtubeUrl`|Returns a video ID from a Youtube URL| -|shipping|`Basket $basket`|Returns the shipping costs from the basket or null| -|code_label|`$code,`</br>`$context = null`|Returns the translated code label. This can be e.g. country code or a different code.</br>If the context is `country`, first Symfony Intl Bundle is used to resolve the country code to a country name, otherwise just the translation for the code is returned| -|basket_discounts|`Basket $basket`|Returns the discounts from the basket| -|basket_add_costs|`Basket $basket`|Returns the additional costs from the basket| -|basket_add_lines|`Basket $basket`|Returns the additional lines from the basket| -|code_label|`$code`</br>`$context = null`|Returns the translated code label.</br>If the context is `country`, first Symfony Intl Bundle is used to resolve the country code to country name| -|sort_characteristic_codes|`array $characteristicCodes`</br>`$characteristicIndex`|By default the list of all variant codes is sorted alphabetically in the ASC order| -|sort_characteristics|`array $characteristics`</br>`$type`</br>`array $order = array()`|Returns sorted characteristics| -|ses_format_args|`$args`|Formats exception arguments| -|truncate|`$text`</br>`$limit`</br>`$break = ' '`</br>`$pad = '...'`|Truncates the given text</br>`$break` can be used to indicate where the truncate should break, e.g. words (`$break = ' '`), sentences (`$break = '.'`)| -|st_siteaccess_path|`string $urlPath`</br>`string $siteAccessName = null`|Formats the given URL path into a SiteAccess path, e.g. `home` -> `/de/home`| -|st_siteaccess_url|`string $urlPath`</br>`string $siteAccessName = null`|Formats the given URL path into a SiteAccess URL</br>e.g. `home` -> <your domain>/en/home| -|st_translate|`$messageOrCode`</br>`$context = ''`</br>`$array $parameters = array()`</br>`$domain = null`</br>`$siteaccess = null`|Returns translation for `$messageOrCode`| -|st_resolve_template|`$templateName, $theme = null`|Returns resolved template name with `TemplateResolverService`| - -## Global variables - -There is a central global variable `ses` provided by the `EshopBundle`. -This variable is an instance of the `GlobalVariables` class which provides methods and is inherited from the Symfony global variable class. -All methods from the global template variable `app` are also available in `ses`. - -| | | -| --------- | -------------------------------------------- | -| Class | `GlobalVariables` | -| Namespace | `Silversolutions\Bundle\EshopBundle\Twig` | - -### Methods for global variable `ses` - -#### `getProfile()` - -Aliases: - -- `getProfileData()` -- `getCustomerProfileData()` - -This method returns the current customer profile. - -``` html+twig -Current customer number: {{ ses.profile.sesUser.customerNumber }} -``` - -- `ses` - global Twig variable -- `profile` - `public method GlobalVariables::getProfile()` -- `sesUser` - `SesUser` from `CustomerProfileData` returned by `getProfile()` -- `customerNumber` - readable variable from `SesUser->customerNumber` - -#### `getDefaultBuyerAddress()` - -This method returns the default BuyerParty. - -``` html+twig -{% set buyerAddress = ses.defaultBuyerAddress %} -Street name of buyer address: {{ buyerAddress.PostalAddress.StreetName }} -``` - -#### `getDefaultDeliveryParty()` - -This method returns the default DeliveryParty. - -``` html+twig -{% set deliveryAddress = ses.defaultDeliveryParty %} -Street name of default delivery address: {{ buyerAddress.PostalAddress.StreetName }} -``` - -#### `getDeliveryParty($deliveryPartyId = null)` - -This method returns `DeliveryParty` by `$deliveryPartyId`. If no `$deliveryPartyId` is given, all parties will be returned - -``` html+twig -{% set deliveryAddresses = ses.deliveryParty %} -{% for address in deliveryAddresses %} - Street Number {{ loop.index }}: {{ deliveryAddress.PostalAddress.StreetName }} -{% endfor %} -``` diff --git a/docs/guide/shop_translations.md b/docs/guide/shop_translations.md index 4c89842b94..e2776435d9 100644 --- a/docs/guide/shop_translations.md +++ b/docs/guide/shop_translations.md @@ -1,8 +1,13 @@ -# Shop translations [[% include 'snippets/commerce_badge.md' %]] +--- +description: Translate the shop interface by editing special text modules in the Back Office. +edition: commerce +--- + +# Shop translations You can use special translation Content items called "text modules" to create translations of the interface. The translation service first checks if a Content item with a specific identifier exists and then returns the text attribute of this object. -If it does not find any translations, the [standard Symfony translation service](http://symfony.com/doc/3.4/book/translation.html) is used. +If it does not find any translations, the [standard Symfony translation service]([[= symfony_doc =]]/book/translation.html) is used. ## Twig filter @@ -42,7 +47,7 @@ The translation service can use the given SiteAccess to specify the language or ### Pluralisation -To handle plurals in translations, use [Symfony pluralization](https://symfony.com/doc/3.4/translation.html#pluralization). +To handle plurals in translations, use [Symfony pluralization]([[= symfony_doc =]]/translation.html#pluralization). ## Translation in PHP code diff --git a/docs/guide/site_factory.md b/docs/guide/site_factory.md deleted file mode 100644 index f0ea4a671f..0000000000 --- a/docs/guide/site_factory.md +++ /dev/null @@ -1,342 +0,0 @@ -# Site Factory [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -Site Factory is a site management User Interface, integrated with Admin UI. -It enables you to configure sites without editing: - -- SiteAccess configuration -- multisite configuration - -After [clean installation](../getting_started/install_ez_platform.md) the Site Factory will be disabled. -If you are not working on a clean installation, follow [Upgrading eZ Platform to v3](../upgrading/5_upgrade_database.md#site-factory-upgrade). -This results in the following message on the **Site** tab: -"There is a design configuration error, and you will not be able to create a new site. Please update the configuration." -If you plan to use Site Factory you need to enable and configure it. - -To enable or disable Site Factory, follow respectively: - -- [Enable Site Factory section](#enable-site-factory) -- [Disable Site Factory section](#disable-site-factory) - -## Enable Site Factory - -To enable Site Factory you need to set `enabled` to `true` in `config/packages/ezplatform_site_factory.yaml`. - -### Configure designs - -Next, configure Site Factory by adding empty SiteAccess groups, only one empty group is mandatory. -The number of empty SiteAccess groups must be equal to the number of templates that you want to have when you create the new site. - -In this example, you add two SiteAccess groups (`example_site_factory_group_1` and `example_site_factory_group_2`) that correspond to the two templates (`ez_site1` and `ez_site2`) that you will add in the next step. -Under these groups you configure all the settings that do not expose the UI, e.g. the Content view. - -Add groups in `config/packages/ezplatform.yaml`: - -```yaml -ezplatform: - siteaccess: - list: [site] - groups: - site_group: [site] - example_site_factory_group_1: [] - example_site_factory_group_2: [] - - system: - example_site_factory_group_1: - example_site_factory_group_2: -``` - -Uncomment the SiteAccess matcher by removing the comment from `EzSystems\EzPlatformSiteFactory\SiteAccessMatcher` matcher under: - -```yaml -ezplatform: - siteaccess: - match: - # '@EzSystems\EzPlatformSiteFactory\SiteAccessMatcher': ~ -``` - -`ezdesign` defines templates for your sites, so add them before continuing. -Next, add the configuration for `ezdesign` on the same level as `ezplatform`: - -```yaml -ezdesign: - design_list: - example_1: [example_1_template] - example_2: [example_2_template] -``` - -Finally, configure designs for empty SiteAccess groups: - -``` -ezplatform: - system: - example_site_factory_group_1: - design: example_1 - example_site_factory_group_2: - design: example_2 -``` - -### Add templates configuration - -Add thumbnails and names for your templates in `config/packages/ezplatform_site_factory.yaml` -It will connect SiteAccesses with the templates. - -```yaml -ez_platform_site_factory: - templates: - ez_site1: - siteaccess_group: example_site_factory_group_1 - name: example_site_1 - thumbnail: /path/to/image/example-thumbnail_1.png - ez_site2: - siteaccess_group: example_site_factory_group_2 - name: example_site_2 - thumbnail: /path/to/image/example-thumbnail_2.png -``` - -You can check the results of your work in the Back Office by going to the **Site** tab and selecting the **List** icon. - -There, you should be able to add a new site and choose a design for it. - -### Define domains - -To be able to see your site online you need to define a domain for it. - -!!! caution "Define domain for production environment" - - These steps are for `dev` environment only. - If you want to define domains in production environment, you will need to configure Apache or Nginx by yourself. - -In the `.env` file change line 2 to: `COMPOSE_FILE=doc/docker/base-dev.yml:doc/docker/multihost.yml` - -Take a look into the `doc/docker/multihost.yml` file. -Here you will define your domains. -To add a new domain you must add it in `command:` and under frontend and backend aliases as shown in the example below. - -```yaml hl_lines="3 6 11" -services: - web: - command: /bin/bash -c "cd /var/www && cp -a doc/nginx/ez_params.d /etc/nginx && bin/vhost.sh --host-name=site.example.com --host-alias='admin.example.com test.example.com' --template-file=doc/nginx/vhost.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" - networks: - frontend: - aliases: - - site.example.com - - admin.example.com - - test.example.com - backend: - aliases: - - site.example.com - - admin.example.com - - test.example.com -``` - -Next, you must define the domains in `etc/hosts`: - -`0.0.0.0 site.example.com admin.example.com test.example.com www.admin.example.com` - -Then, run `docker-compose up`: - -```bash -export COMPOSE_FILE="doc/docker/base-dev.yml:doc/docker/multihost.yml" -docker-compose up -``` - -Your sites should be now visible under: - -``` -http://site.example.com:8080/ -http://admin.example.com:8080/ -http://localhost:8080/ -http://test.example.com:8080/ -``` - -![Site Factory enabled](img/site_factory_site_list.png "Site Factory enabled") - -### Define site directory - - You can adjust the place where the directory of the new site will be created. - By default the Location for the site directories is defined in bundle configuration `src/bundle/Resources/config/default_settings.yaml`: - - ``` yaml - parameters: - ezsettings.default.site_factory.sites_location_id: 2 - ``` - -To change it to e.g. [[= product_name =]], go to `config/packages/ezplatform_site_factory.yaml`, and add the following parameter: - -``` yaml -parameters: - ezsettings.default.site_factory.sites_location_id: 42 -``` - -Now, all new directories will be created under [[= product_name =]]. - -### Provide access - -The Site Factory is set up, now you can provide sufficient permissions to the Users. - -Set the below Policies to allow Users to: - -- `site/view` - enter the Site Factory interface -- `site/create` - create sites -- `site/edit` - edit sites -- `site/change_status` - change status of the public accesses to `Live` or `Offline` -- `site/delete` - delete sites - -For full documentation on how Permissions work and how to set them up, see [the Permissions section](permissions.md). - -To learn how to use Site Factory, see [User documentation.](https://doc.ezplatform.com/projects/userguide/en/latest/site_organization/site_factory/#site-factory) - -## Configuration - -### Configure parent Location - -You can define the parent Location for a new site in the configuration. -Each new site is created in the designated Location. - -To define a parent Location, add a new configuration key to the template definition. -Each template is assigned its own Location. -This can be either a Location ID (e.g. `62`), or a recommended remote Location ID (e.g. `1548b8cd8dd4c6b5082e566615d45e91`). - -Add the configuration key to your template: - -```yaml hl_lines="7 12" -ez_platform_site_factory: - templates: - ez_site1: - siteaccess_group: example_site_factory_group_1 - name: example_site_1 - thumbnail: /path/to/image/example-thumbnail_1.png - parent_location_id: 62 - ez_site2: - siteaccess_group: example_site_factory_group_2 - name: example_site_2 - thumbnail: /path/to/image/example-thumbnail_2.png - parent_location_remote_id: 1548b8cd8dd4c6b5082e566615d45e91 -``` - -Now, you can see the path to the new site's parent Location under design selection. -If you have sufficient permissions you can change the defined Location during site creation. -If the parent Location is not defined, you have to choose it from Universal Discovery Widget. - -### Configure Site skeleton - -The Site skeleton enables you to copy an entire content structure of the site design to the defined Location. - -Site skeleton copying is a one-off operation, it only happens during the site creation process. -After that, you cannot copy the Site skeleton again, for example in the edit view. - -You can create as many skeletons as you need and assign them to templates. -Remember that one template can only have one Site skeleton assigned. - -If the design does not have a defined Site skeleton, a directory of the new site is created in a standard Site Factory process. - -!!! caution "Update from v3.0" - - To be able to create a Location for the Site skeletons, run the [update procedure](../updating/4_update_3.1.md#site-factory). - - Now, you should be able to see **Site skeletons** tab under **Admin** tab. The tab is only visible after the update procedure. - -To define a Site skeleton, add a new configuration key to the template definition. -This can be either a Location ID (e.g. `5966`), or a recommended remote Location ID (e.g. `3bed95afb1f8126f06a3c464e461e1ae66`). - -Add the configuration key to your template: - -```yaml hl_lines="7 12" -ez_platform_site_factory: - templates: - ez_site1: - siteaccess_group: example_site_factory_group_1 - name: example_site_1 - thumbnail: /path/to/image/example-thumbnail_1.png - site_skeleton_id: 5966 - ez_site2: - siteaccess_group: example_site_factory_group_2 - name: example_site_2 - thumbnail: /path/to/image/example-thumbnail_2.png - site_skeleton_remote_id: 3bed95afb1f8126f06a3c464e461e1ae66 -``` - -Now, you can choose a design with a defined Site skeleton, and decide if you want to use its skeleton by toggling **Generate site using site skeleton**. - -### Configure User Group skeletons - -With User Group skeletons you can define Policies and Limitations that apply to selected groups of users who can access the site. - -You can create many User Group skeletons and associate them with many templates. -One template can have many User Group skeletons assigned. - -To create a User Group skeleton, first go to **Admin** -> **Site skeletons** and add a User Group to a list of available skeletons. -Then, review the detailed information of the newly created User Group skeleton, -copy the Location ID or the the Location remote ID value, and add a configuration key to the template definition: - -```yaml -ez_platform_site_factory: - templates: - <site_name>: - # ... - user_group_skeleton_ids: [ <id_skeleton1>, <id_skeleton2>, ... ] - user_group_skeleton_remote_ids: [ <id_skeleton3>, <id_skeleton4>, ... ] -``` - -Manage the permissions associated to the User Group skeleton by [assigning Roles](https://doc.ibexa.co/projects/userguide/en/latest/site_organization/organizing_the_site/#managing-permissions). -Make sure that the Roles that you assign to the User Group skeleton do not contain Location-based Limitations. -User Group skeletons cannot contain individual User Content items either. - -User Group skeletons are retained after deleting the site. - -### Define Roles to be automatically updated when a site is created - -Role definitions can contain user/login Policies with Limitations that limit user access to certain sites. -To avoid the need to add the new SiteAccess to Limitations for all Roles, -you can decide that the Roles you select are automatically updated when the site is created. - -In `config/packages/ezplatform_site_factory.yaml`, add a list of Roles which will have access to the frontend when a site is created in Site Factory. - -For example: - -``` yaml -ez_platform_site_factory: - # ... - enabled: true - update_roles: [Anonymous, Administrator] -``` - -For more information about Roles and policies, see [Permissions](permissions.md). - -## Disable Site Factory - -Enabled Site Factory may cause following performance issues: - -- Config Resolver will look for SiteAccesses in the database -- Site Factory matchers will be connected to the database in search for new SiteAccesses - -You can disable Site Factory to boost Config Resolver performance. -Keep in mind that with disabled Site Factory you will not be able to add new sites or use existing ones. - -1\. In `config/packages/ezplatform_site_factory.yaml` change enabled to `false`. - -2\. In `config/packages/ezplatform.yaml` comment the `ezplatform.siteaccess.match: '@EzSystems\EzPlatformSiteFactory\SiteAccessMatcher': ~` if it is uncommented. - -3\. Remove separate connection to database in `config/packages/doctrine.yaml`. - -``` yaml -doctrine: - dbal: - connections: - ... - # This connection is dedicated for SiteFactory to avoid known issues - site_factory: -``` - -4\. Remove separate cache pool in `config/packages/cache.yaml`. - -``` yaml -framework: - cache: - ... - pools: - # This pool should be used only by SiteFactory bundle - site_factory_pool: -``` - -The Site Factory should be disabled. diff --git a/docs/guide/siteaccess.md b/docs/guide/siteaccess.md deleted file mode 100644 index d3d42e301f..0000000000 --- a/docs/guide/siteaccess.md +++ /dev/null @@ -1,629 +0,0 @@ -# SiteAccess - -## Introduction - -[[= product_name =]] enables you to maintain multiple sites in one installation using a feature called **SiteAccesses**. - -In short, a SiteAccess is a set of configuration settings that is used when you reach the site through a specific address. -When the user visits the site, the system analyzes the URI and compares it to rules specified in the configuration. If it finds a set of fitting rules, this SiteAccess is used. - -Settings defined per SiteAccess may include, among others, database, language or `var` directory. -When that SiteAccess is used, they override the default configuration. - -### SiteAccesses use cases - -Typical uses of a SiteAccess are: - -- different language versions of the same site identified by a uri part; one SiteAccess for one language -- two different versions of a website: one SiteAccess with a public interface for visitors and one with a restricted interface for administrators - -!!! note "SiteAccess switching [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]]" - - If you need to change between SiteAccesses in Site mode, do not use any functions in the page itself (for example, a language switcher). This may cause unexpected errors. Instead, switch between SiteAccesses using the SiteAccess bar above the page. - -#### `admin` SiteAccess - -The back-office UI of [[= product_name =]] is housed in a predefined `admin` SiteAccess in `admin_group`. - -If you have a multisite setup with a separate back-office interface for each site, -you need to create your own admin SiteAccesses and add them to this group. In cases where the sites are on separate databases they will need their own [repository](configuration.md#configuration-examples) (including their own storage and search connection), var dir, [cache pool](persistence_cache.md#persistence-cache-configuration), and ideally also separate Varnish/Fastly config for each site individually. - -### Selecting SiteAccesses - -A SiteAccess is selected using one or more matchers – rules based on the uri or its parts. Example matching criteria are elements of the uri, host name (or its parts), port number, etc. - -For detailed information on how SiteAccess matchers work, see [SiteAccess Matching](siteaccess_matching.md). - -!!! note - - A SiteAccess that you define for a site by following the [configuration](#configuring-siteaccesses) - is always treated with higher priority than a SiteAccess created by using the Site Factory. - For example, if you define a French site within a YAML file, and another user creates a site that - uses the `fr` path in Site Factory, the other user's site is ignored by the matchers. - -## Configuring SiteAccesses - -You configure SiteAccess in your config files (e.g. `ezplatform.yaml`) under the `ezplatform.siteacess` keys. -The required elements of the configuration are: - -#### `list` - -Lists all SiteAccesses in the installation. - -#### `default_siteaccess` - -Identifies which SiteAccess will be used by default when no other is specified. - -#### `groups` (optional) - -Collects SiteAccesses into groups that can be used later for configuration. - -#### `match` - -The rule or set of rules by which SiteAccesses are matched. See [SiteAccess matching](siteaccess_matching.md) for more information. - -### SiteAccess selection in Page Builder [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] - -To define which SiteAccesses are available in the submenu in Page Builder, use the following configuration. -`siteaccess_list` is an array of SiteAccess identifiers: - -``` yaml -ezplatform: - system: - admin: - page_builder: - siteaccess_list: [site, de, fr, no] - de: - page_builder: - siteaccess_list: [site, de] -``` - -If you are using multiple domains, list all domains for an admin SiteAccess under `siteaccess_hosts`: - -``` yaml -ezpublish: - system: - admin: - page_builder: - siteaccess_list: [site, de, fr, no] - siteaccess_hosts: - - my_domain.com - - another_domain.org -``` - -### Settings per SiteAccess - -Various system settings can be set per SiteAccess or SiteAccess group under the `ezplatform.system` key. These settings include languages or the `var` directory. - -### Multilanguage sites - -A site has content in two languages: English and Norwegian. It has one URI per language: `http://example.com/eng` and `http://example.com/nor`. Uri parts of each language (eng, nor) are mapped to a *SiteAccess*, commonly named like the URI part: `eng`, `nor`. Using semantic configuration, each of these SiteAccesses can be assigned a prioritized list of languages it should display: - -- The English site would display content in English and ignore Norwegian content; -- The Norwegian site would display content in Norwegian but also in English *if it does not exist in Norwegian*. - -Such configuration would look like this: - -``` yaml -ezplatform: - siteaccess: - # There are two SiteAccesses - list: [eng, nor] -  - # eng is the default one if no prefix is specified - default_siteaccess: eng - - # the first URI of the element is used to find a SiteAccess with a similar name - match: - URIElement: 1 - - -ezplatform: - # root node for configuration per SiteAccess - system: - # Configuration for the 'eng' SiteAccess - eng: - languages: [eng-GB] - nor: - languages: [nor-NO, eng-GB] -``` - -!!! note - - A new SiteAccess is recognized by the system, but an Anonymous User will not have read access to it until it is [explicitly given via the Admin > Roles panel](permissions.md#use-cases). Without read access the Anonymous User will simply be directed to the default login page. - -### Defining SiteAccess name - -In order to simplify the interface and create a better editorial experience, you can "hide" - the SiteAccess code and substitute it with a human-readable name of the website e.g. `Tasteful Planet`, `Page EN`. - -List of interfaces where you can apply SiteAccess names: - -- Page Builder (SiteAccess switcher in the top navigation) - -- [Content Preview](https://doc.ezplatform.com/projects/userguide/en/latest/creating_content_basic/#previewing-content) (SiteAccess switcher in the dropdown menu) - -- Page creation modal window (when coming from Content Structure) - -You can also translate SiteAccess names. Displayed names depend on the selected language of the administration interface. - -To define translation you need to put them in YAML file with correct language code e.g. `translations/ezplatform_siteaccess.en.yaml`: - -```yaml -en: Tasteful Planet -fr: Tasteful Planet France -``` - -## Scope - -Configuration is resolved depending on scope. It gives the opportunity to define settings for a given SiteAccess, for instance like in the [legacy INI override system](http://doc.ez.no/eZ-Publish/Technical-manual/4.x/Concepts-and-basics/Configuration). - -The available scopes are: - -1. `global` -2. SiteAccess -3. SiteAccess group -4. `default` - -The scopes are applied in the order presented. This means that `global` overrides all other scopes. -If `global` is not defined, the configuration will then try to match a SiteAccess, and then a SiteAccess group. -Finally, if no other scope is matched, `default` will be applied. - -In short: if you want a match that will always apply, regardless of SiteAccesses use `global`. -To define a fallback, use `default`. - -``` yaml -ezplatform: - system: - global: - # If set, this value will be used regardless of any other var_dir configuration - #var_dir: var/global - site: - # This var_dir will be used for the 'site' SiteAccess - var_dir: var/site - site_group: - # This will be overwritten by the SiteAccess above, since the SiteAccess has precedence - var_dir: var/group - default: - # This value will only be used if there is no global, SiteAccess or SiteAccess group defined - var_dir: var/site -``` - -Be aware that the `default` scope concerns both back and front views. -For example, the following configuration defines both the front template for articles -and the template used in the Back Office, unless other templates are configured for specific a SiteAccess or SiteAccess group: - -``` yaml -ezpublish: - system: - default: - content_view: - full: - article: - template: full/article.html.twig - match: - Identifier\ContentType: [article] -``` - -Note that you should avoid defining a setting twice within the same scope, as this will cause a [silent failure](https://github.com/symfony/symfony/issues/11538). - -This mechanism is not limited to [[= product_name =]] internal settings (the `ezsettings` namespace) and is applicable for specific needs (bundle-related, project-related, etc.). - -Always prefer semantic configuration especially for internal eZ settings. -Manually editing internal eZ settings is possible, but at your own risk, as unexpected behavior can occur. - -## Cross-SiteAccess links - -When using the [multisite](multisite.md) feature, it is sometimes useful to be able to generate cross-links between different sites within one installation. -This allows you to link different resources referenced in the same content repository, but configured independently with different tree roots. - -``` html+twig -<!--Twig example--> -{# Linking a location #} -<a href="{{ url( 'ez_urlalias', {'locationId': 42, 'siteaccess': 'some_siteaccess_name'} ) }}">{{ ez_content_name( content ) }}</a> - -{# Linking a regular route #} -<a href="{{ url( "some_route_name", {"siteaccess": "some_siteaccess_name"} ) }}">Hello world!</a> -``` - -See [ez\_urlalias](twig_functions_reference.md#ez_urlalias) for more information about linking to a Location. - -``` php -namespace App\Controller; - -use eZ\Bundle\EzPublishCoreBundle\Controller as BaseController; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; - -class MyController extends BaseController -{ - public function fooAction() - { - // ... - - $location = $this->getRepository()->getLocationService()->loadLocation( 123 ); - $locationUrl = $this->generateUrl( - $location, - [ 'siteaccess' => 'some_siteaccess_name' ], - UrlGeneratorInterface::ABSOLUTE_PATH - ); - - $regularRouteUrl = $this->generateUrl( - 'some_route_name', - [ 'siteaccess' => 'some_siteaccess_name' ], - UrlGeneratorInterface::ABSOLUTE_PATH - ); - - // ... - } -} -``` - -!!! note "Important" - - As SiteAccess matchers can involve hosts and ports, it is **highly recommended** to generate cross-SiteAccess links in an absolute form (e.g. using `ez_url()` Twig helper). - -#### Troubleshooting - -- The **first matcher succeeding always wins**, so be careful when using *catch-all* matchers like `URIElement`. -- If the passed SiteAccess name is not valid, an `InvalidArgumentException` will be thrown. -- If the matcher used to match the provided SiteAccess doesn't implement `VersatileMatcher`, the link will be generated for the current SiteAccess. -- When using `Compound\LogicalAnd`, all inner matchers **must match**. If at least one matcher doesn't implement `VersatileMatcher`, it will fail. -- When using `Compound\LogicalOr`, the first inner matcher succeeding will win. - -#### Under the hood - -To implement this feature, a new `VersatileMatcher` was added to allow SiteAccess matchers to be able to *reverse-match*. -All existing matchers implement this new interface, except the regexp-based matchers which have been deprecated. - -The SiteAccess router has been added a `matchByName()` method to reflect this addition. Abstract URLGenerator and `DefaultRouter` have been updated as well. - -!!! note - - SiteAccess router public methods have also been extracted to a new interface, `SiteAccessRouterInterface`. - -#### Navigating between SiteAccesses - limitations - -There are two known limitations to moving between SiteAccesses in [[= product_name_exp =]]'s Pages: - -1. On a Page you can encounter a 404 error when clicking a relative link which points to a different SiteAccess (if the Content item being previewed does not exist in the previously used SiteAccess). This is because detecting SiteAccesses when navigating in preview is not functional yet. This is a known limitation that is awaiting resolution. - -1. When navigating between SiteAccesses in the back office using the top bar, you are always redirected to the main page, not to the Content item you started from. - -## Injecting SiteAccess - -SiteAccess is exposed in the Dependency Injection Container as the `@ezpublish.siteaccess` service, so it can be injected into any custom service. - -The `@ezpublish.siteaccess` service, if needed, must be injected using setter injection. It comes from the fact that SiteAccess matching -is done in a `kernel.request` event listener, so when injected into a constructor, it might not be initialized properly. - -To ensure proper contract, the `eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware` interface can be implemented on a custom service. - -**Example** - -Let's define a simple service which depends on the Repository's ContentService and the current SiteAccess. - -```yaml -services: - App\MyService: - arguments: ['@ezpublish.api.service.content'] - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] -``` - -```php -<?php - -namespace App; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; - -class MyService implements SiteAccessAware -{ - /** - * @var \eZ\Publish\API\Repository\ContentService - */ - private $contentService; - - /** - * @var \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - private $siteAccess; - - public function __construct(ContentService $contentService ) - { - $this->contentService = $contentService; - } - - public function setSiteAccess(SiteAccess $siteAccess = null) - { - $this->siteAccess = $siteAccess; - } -} -``` - -## Exposing SiteAccess-aware configuration for your bundle - -The [Symfony Config component](https://symfony.com/doc/5.0/components/config.html) makes it possible to define *semantic configuration*, exposed to the end developer. -This configuration is validated by rules you define, e.g. validating type (string, array, integer, boolean, etc.). -Usually, once validated and processed, this semantic configuration is then mapped to internal *key/value* parameters stored in the `ServiceContainer`. - -[[= product_name =]] uses this for its core configuration, but adds another configuration level, the **SiteAccess**. -For each defined SiteAccess, you need to be able to use the same configuration tree in order to define SiteAccess-specific config. - -These settings then need to be mapped to SiteAccess-aware internal parameters that you can retrieve via the `ConfigResolver`. -For this, internal keys need to follow the format `<namespace>.<scope>.<parameter_name>`. where: - -- `namespace`is specific to your app or bundle -- `scope` is the SiteAccess, SiteAccess group, `default` or `global` -- `parameter_name` is the actual setting *identifier* - -For more information on ConfigResolver, namespaces and scopes, see [[[= product_name =]] configuration basics](../guide/configuration.md). - -The goal of this feature is to make it easy to implement a SiteAccess-aware semantic configuration and its mapping to internal config for any [[= product_name =]] bundle developer. - -### Semantic configuration parsing - -An abstract `Configuration` class has been added, simplifying the way to add a SiteAccess settings tree like the following in `config/packages/ezplatform.yaml`: - -``` yaml -acme_example: - system: - <siteaccess>: - foo: bar - setting_a: - number: 456 - enabled: true - - <siteaccess_group>: - foo: baz - setting_a: - string: foobar - number: 123 - enabled: false -``` - -The fully qualified name of the class is `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration`. -All you have to do is to extend it and use `$this->generateScopeBaseNode()`: - -``` php hl_lines="17" -<?php - -namespace Acme\ExampleBundle\DependencyInjection; - -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration as SiteAccessConfiguration; -use Symfony\Component\Config\Definition\Builder\NodeBuilder; -use Symfony\Component\Config\Definition\Builder\TreeBuilder; - -class Configuration extends SiteAccessConfiguration -{ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root( 'acme_example' ); - - // $systemNode will then be the root of SiteAccess-aware settings. - $systemNode = $this->generateScopeBaseNode($rootNode); - $systemNode - ->scalarNode( 'foo' )->isRequired()->end() - ->arrayNode( 'setting_a' ) - ->children() - ->scalarNode( "string" )->end() - ->integerNode( "number" )->end() - ->booleanNode( "enabled" )->end() - ->end() - ->end(); - - return $treeBuilder; - } -} -``` - -!!! note - - Default name for the *SiteAccess root node* is `system`, but you can customize it. - To do this, pass the name you want to use as a second argument of `$this->generateScopeBaseNode()`. - -### Mapping to internal settings - -Semantic configuration must always be mapped to internal key/value settings within the `ServiceContainer`. -This is usually done in the DIC extension. - -For SiteAccess-aware settings, new `ConfigurationProcessor` and `Contextualizer` classes have been introduced to ease the process. - -``` php -<?php - -namespace Acme\ExampleBundle\DependencyInjection; - -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Loader; - -/** - * This is the class that loads and manages your bundle configuration - * - * To learn more see {@link http://symfony.com/doc/5.0/cookbook/bundles/extension.html} - */ -class AcmeExampleExtension extends Extension -{ - public function load(array $configs, ContainerBuilder $container) - { - $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); - - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load( 'default_settings.yaml' ); - - // "acme_example" will be the namespace as used in ConfigResolver format. - $processor = new ConfigurationProcessor($container, 'acme_example'); - $processor->mapConfig( - $config, - // Any kind of callable can be used here. - // It will be called for each declared scope/SiteAccess. - function ($scopeSettings, $currentScope, ContextualizerInterface $contextualizer) - { - // Will map the "foo" setting to "acme_example.<$currentScope>.foo" container parameter - // It will then be possible to retrieve this parameter through ConfigResolver in the application code: - // $helloSetting = $configResolver->getParameter( 'foo', 'acme_example' ); - $contextualizer->setContextualParameter('foo', $currentScope, $scopeSettings['foo']); - } - ); - - // Now map "setting_a" and ensure the key defined for "my_siteaccess" overrides the one for "my_siteaccess_group" - // It is done outside the closure as it is needed only once. - $processor->mapConfigArray('setting_a', $config); - } -} -``` - -!!! tip - - You can map simple settings by calling `$processor->mapSetting()`, without having to call `$processor->mapConfig()` with a callable. - - ``` php - $processor = new ConfigurationProcessor($container, 'acme_example'); - $processor->mapSetting('foo', $config); - ``` - -!!! caution "Important" - - Always ensure you have defined and loaded default settings. - -In `@AcmeExampleBundle/Resources/config/default_settings.yaml`: - -``` yaml -parameters: - acme_example.default.foo: bar - acme_example.default.setting_a: - string: ~ - os_types: [windows] - number: 0 - enabled: false - language: php -``` - -#### Merging hash values between scopes - -When you define a hash as semantic config, you sometimes don't want the SiteAccess settings to replace the default or group values, -but *enrich* them by appending new entries. This is possible by using `$processor->mapConfigArray()`, -which needs to be called outside the closure (before or after), in order to be called only once. - -Consider the following default config in `default_settings.yaml`: - -``` yaml -parameters: - acme_example.default.setting_a: - string: ~ - os_types: [windows] - number: 0 - enabled: false - language: php -``` - -And then this semantic config in `ezplatform.yaml`: - -``` yaml -acme_example: - system: - siteaccess_group: - setting_a: - string: foobar - number: 123 - - # Assuming "siteaccess1" is part of "siteaccess_group" - siteaccess1: - setting_a: - number: 456 - enabled: true - language: javascript -``` - -What you want here is that keys defined for `setting_a` are merged between default/group/SiteAccess, like this: - -``` yaml -parameters: - acme_example.siteaccess1.setting_a: - string: foobar - os_types: [windows] - number: 456 - enabled: true - language: javascript -``` - -##### Merge from second level - -In the example above, entries were merged in respect to the scope order of precedence. However, if you define the `planets` key for `siteaccess1`, it will completely override the default value since the merge process is done at only 1 level. - -You can add another level by passing `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` as an option (third argument) to `$contextualizer->mapConfigArray()`. - -In `default_settings.yaml`: - -``` yaml -parameters: - acme_example.default.setting_a: - string: ~ - os_types: [windows] - number: 0 - enabled: false - language: [php] -``` - -Semantic config (`ezplatform.yaml` / `config.yaml`): - -``` yaml -acme_example: - system: - siteaccess_group: - setting_a: - string: foobar - os_types: [macos, linux] - number: 123 - - # Assuming "siteaccess1" is part of "siteaccess_group" - siteaccess1: - setting_a: - number: 456 - enabled: true - language: [javascript, python] -``` - -Result of using `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` option: - -``` yaml -parameters: - acme_example.siteaccess1.setting_a: - string: foobar - os_types: [windows, macos, linux] - number: 456 - enabled: true - language: [php, javascript, python] -``` - -There is also another option, `ContextualizerInterface::UNIQUE`, -to be used when you want to ensure your array setting has unique values. It will only work on normal arrays though, not hashes. - -##### Limitations - -A few limitations exist with this scope hash merge: - -- Semantic setting name and internal name will be the same (like `foo_setting` in the examples above). -- Applicable to first level semantic parameter only (i.e. settings right under the SiteAccess name). -- Merge is not recursive. Only second level merge is possible by using `ContextualizerInterface::MERGE_FROM_SECOND_LEVEL` option. - -### Dedicated mapper object - -Instead of passing a callable to `$processor->mapConfig()`, an instance of `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface` can be passed. - -This can be useful if you have a lot of configuration to map and don't want to pollute your DIC extension class (better for maintenance). - -#### Merging hash values between scopes - -As specified above, `$contextualizer->mapConfigArray()` is not to be used within the *scope loop*, like for simple values. -When using a closure/callable, you usually call it before or after `$processor->mapConfig()`. -For mapper objects, a dedicated interface can be used: `HookableConfigurationMapperInterface`, -which defines 2 methods: `preMap()` and `postMap()`. diff --git a/docs/guide/siteaccess_matching.md b/docs/guide/siteaccess_matching.md deleted file mode 100644 index b0ca869a4d..0000000000 --- a/docs/guide/siteaccess_matching.md +++ /dev/null @@ -1,408 +0,0 @@ -# SiteAccess Matching - -SiteAccess matching is done through `eZ\Publish\MVC\SiteAccess\Matcher` objects. You can configure this matching and even develop custom matchers. - -To be usable, every SiteAccess must be provided a matcher. - -You can configure SiteAccess matching in your main `config/packages/ezplatform.yaml`: - -``` yaml -ezplatform: - siteaccess: - default_siteaccess: ezdemo_site - list: - - ezdemo_site - - eng - - fre - - fr_eng - - ezdemo_site_admin - groups: - ezdemo_site_group: - - ezdemo_site - - eng - - fre - - fr_eng - admin_group: - - ezdemo_site_admin - match: - Map\URI: - ezdemo_site: ezdemo_site - eng: eng - fre: fre - fr_eng: fr_eng - ezdemo_site_admin: ezdemo_site_admin -``` - -You need to set several parameters: - -- `ezplatform.siteaccess.default_siteaccess` is the default SiteAccess that will be used if matching was not successful. This ensures that a SiteAccess is always defined. -- `ezplatform.siteaccess.list` is the list of all available SiteAccesses in your website. -- `ezplatform.siteaccess.groups` defines which groups SiteAccesses belong to. This is useful when you want to mutualize settings between several SiteAccesses and avoid config duplication. Siteaccess groups are treated the same as regular SiteAccesses as far as configuration is concerned. A SiteAccess can be part of several groups. A SiteAccess configuration has always precedence on the group configuration. - -!!! caution "admin_group" - - Do not remove or rename `admin_group` group. It is used to distinguish common SiteAccesses from admin ones. In case of multisite with multiple Admin Panels, remember to add any additional admin SiteAccesses to this group. - -- `ezplatform.siteaccess.match` holds the matching configuration. It consists in a hash where the key is the name of the matcher class. If the matcher class doesn't start with a **\\** , it will be considered relative to `eZ\Publish\MVC\SiteAccess\Matcher` (e.g. `Map\Host` will refer to  `eZ\Publish\MVC\SiteAccess\Matcher\Map\Host`) - -Every custom matcher can be specified with a fully qualified class name (e.g. `\My\SiteAccess\Matcher`) or by a service identifier prefixed by @ (e.g. `@my_matcher_service`). - -- In the case of a fully qualified class name, the matching configuration will be passed in the constructor. -- In the case of a service, it must implement `eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher`. The matching configuration will be passed to `setMatchingConfiguration()`. - -!!! note - - Make sure to type the matcher in correct case. If it is in wrong case like "Uri" instead of "URI," it will work well on systems like macOS X because of their case-insensitive file system, but will fail when you deploy it to a Linux server. This is a known artifact of [PSR-0 autoloading](http://www.php-fig.org/psr/psr-0/) of PHP classes. - -## Available matchers - -- [URIElement](#urielement) -- [URIText](#uritext) -- [HostElement](#hostelement) -- [HostText](#hosttext) -- [Map\Host](#maphost) -- [Map\URI](#mapuri) -- [Map\Port](#mapport) -- [Regex\Host](#regexhost) (deprecated) -- [Regex\URI](#regexuri) (deprecated) - -### URIElement - -Maps a URI element to a SiteAccess. This is the default matcher used when choosing URI matching in setup wizard. - -**Configuration.** The element number you want to match (starting from 1) - -``` yaml -ezplatform: - siteaccess: - match: - URIElement: 1 -``` - -*Important:* When using a value > 1, the matcher will concatenate the elements with `_`. - -**Example.** URI: `/ezdemo_site/foo/bar` - -Element number: 1; Matched SiteAccess: `ezdemo_site` - -Element number: 2; Matched SiteAccess: `ezdemo_site_foo` - -### URIText - -Matches URI using pre and/or post sub-strings in the first URI segment. - -**Configuration.** The prefix and/or suffix (none are required) - -``` yaml -ezplatform: - siteaccess: - match: - URIText: - prefix: foo - suffix: bar -``` - -**Example.** URI: `/footestbar/my/content` - -Prefix: `foo`; Suffix: `bar`; Matched SiteAccess: `test` - -### HostElement - -Maps an element in the host name to a SiteAccess. - -**Configuration.** The element number you want to match (starting from 1) - -``` yaml -ezplatform: - siteaccess: - match: - HostElement: 2 -``` - -**Example.** Host name: `www.example.com` - -Element number: 2; Matched SiteAccess: `example` - -### HostText - -Matches a SiteAccess in the host name, using pre and/or post sub-strings. - -**Configuration.** The prefix and/or suffix (none are required) - -``` yaml -ezplatform: - siteaccess: - match: - HostText: - prefix: www. - suffix: .com -``` - -**Example.** Host name: `www.foo.com` - -Prefix: `www.`; Suffix: `.com`; Matched SiteAccess: `foo` - -### Map\\Host - -Maps a host name to a SiteAccess. - -**Configuration.** A hash map of host/siteaccess - -``` yaml -ezplatform: - siteaccess: - match: - Map\Host: - www.foo.com: foo_front - adm.foo.com: foo_admin - www.bar-stuff.fr: bar_front - adm.bar-stuff.fr: bar_admin -``` - -**Example.** Map: - -- `www.foo.com` => `foo_front` -- `admin.foo.com` => `foo_admin` - -Host name: `www.example.com` - -Matched SiteAccess: `foo_front` - -!!! note - - If you encounter problems with the `Map\Host` matcher, make sure that your installation is - [properly configured to use token-based authentication](../releases/ez_platform_v2.4.md#update-ez-enterprise-v24-to-v242). - -### Map\\URI - -Maps a URI to a SiteAccess. - -**Configuration.** A hash map of URI/siteaccess - -```yaml -ezplatform: - siteaccess: - match: - Map\URI: - ezdemo: ezdemo_site - ezadmin: ezdemo_site_admin -``` - -**Example.** URI: `/ezdemo/my/content` - -Map: - -- `ezdemo` => `ezdemo_site` -- `ezadmin` => `ezdemo_site_admin` - -Matched SiteAccess: `ezdemo_site` - -### Map\\Port - -Maps a port to a SiteAccess. - -**Configuration.** A hash map of Port/siteaccess - -``` yaml -ezplatform: - siteaccess: - match: - Map\Port: - 80: foo - 8080: bar -``` - -**Example.** URL: `http://ezpublish.dev:8080/my/content` - -Map: - -- `80`: `foo` -- `8080`: `bar` - -Matched SiteAccess: `bar` - -### Regex\\Host - -!!! caution - - This matcher is deprecated. - -Matches against a regexp and extracts a portion of it. - -**Configuration.** The regexp to match against and the captured element to use - -``` yaml -ezplatform: - siteaccess: - match: - Regex\Host: - regex: '^(\\w+_sa)$' - # Default is 1 - itemNumber: 1 -``` - -**Example.** Host name: `example_sa` - -regex: `^(\\w+)_sa$` - -itemNumber: 1 - -Matched SiteAccess: `example` - -### Regex\\URI - -!!! caution - - This matcher is deprecated. - -Matches against a regexp and extracts a portion of it. - -**Configuration.** The regexp to match against and the captured element to use - -``` yaml -ezplatform: - siteaccess: - match: - Regex\URI: - regex: '^/foo(\\w+)bar' - # Default is 1 - itemNumber: 1 -``` - -**Example.** URI: `/footestbar/something` - -regex: `^/foo(\\w+)bar`; itemNumber: 1 - -Matched SiteAccess: `test` - -## Custom matchers - -Beside the built-in matchers, you can also use your own services to match SiteAcceses: - -``` yaml -ezplatform: - siteaccess: - list: [site] - groups: - site_group: [site] - default_siteaccess: site - match: - '@App\Matcher\MySiteaccessMatcher': ~ -``` - -The service must be tagged with `ezplatform.siteaccess.matcher` -and must implement `eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher` -(and `eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher` if you want to use reverse matching). - -## Compound SiteAccess matcher - -The Compound SiteAccess matcher enables you to combine several matchers together, for example: - -- `http://example.com/en` matches site\_en (match on host=example.com *and* URIElement(1)=en) -- `http://example.com/fr` matches site\_fr (match on host=example.com *and* URIElement(1)=fr) -- `http://admin.example.com` matches site\_admin (match on host=admin.example.com) - -Compound matchers correspond to the legacy **host\_uri** matching feature. - -They are based on logical combinations, or/and, using logical compound matchers: - -- `Compound\LogicalAnd` -- `Compound\LogicalOr` - -Each compound matcher will specify two or more sub-matchers. A rule will match if all the matchers combined with the logical matcher are positive. The example above would have used `Map\Host` and `Map\Uri`, combined with a `LogicalAnd`. When both the URI and host match, the SiteAccess configured with "match" is used. - -``` yaml -ezplatform: - siteaccess: - match: - Compound\LogicalAnd: - # Nested matchers, with their configuration. - # No need to specify their matching values (true is enough). - site_en: - matchers: - Map\URI: - en: true - Map\Host: - example.com: true - match: site_en - site_fr: - matchers: - Map\URI: - fr: true - Map\Host: - example.com: true - match: site_fr - Map\Host: - admin.example.com: site_admin -``` - -## Matching by request header - -It is possible to define which SiteAccess to use by setting an `X-Siteaccess` header in your request. This can be useful for REST requests. - -In such a case, `X-Siteaccess` must be the *SiteAccess name* (e.g. `ezdemo_site`). - -## Matching by environment variable - -It is also possible to define which SiteAccess to use directly via an **EZPUBLISH\_SITEACCESS** environment variable. - -This is recommended if you want to get performance gain since no matching logic is done in this case. - -You can define this environment variable directly from your web server configuration: - -``` xml -<!--Apache VirtualHost example--> -# This configuration assumes that mod_env is activated -<VirtualHost *:80> - DocumentRoot "/path/to/ezpublish5/web/folder" - ServerName example.com - ServerAlias www.example.com - SetEnv EZPUBLISH_SITEACCESS ezdemo_site -</VirtualHost> -``` - -!!! tip - - You can also do it via PHP-FPM configuration file, if you use it. See  [PHP-FPM documentation](http://php.net/manual/en/install.fpm.configuration.php#example-60) for more information. - -!!! note "Precedence" - - The precedence order for SiteAccess matching is the following (the first matched wins): - - 1. Request header - 1. Environment variable - 1. Configured matchers - -## URILexer and semanticPathinfo - -In some cases, after matching a SiteAccess, it is necessary to modify the original request URI. This is for example needed with URI-based matchers since the SiteAccess is contained in the original URI and is not part of the route itself. - -The problem is addressed by *analyzing* this URI and by modifying it when needed through the **URILexer** interface. - -``` php -// URILexer interface - -/** - * Interface for SiteAccess matchers that need to alter the URI after matching. - * This is useful when you have the SiteAccess in the URI like "/<siteaccessName>/my/awesome/uri" - */ -interface URILexer -{ - /** - * Analyses $uri and removes the SiteAccess part, if needed. - * - * @param string $uri The original URI - * @return string The modified URI - */ - public function analyseURI( $uri ); - /** - * Analyses $linkUri when generating a link to a route, in order to have the SiteAccess part back in the URI. - * - * @param string $linkUri - * @return string The modified link URI - */ - public function analyseLink( $linkUri ); -} -``` - -Once modified, the URI is stored in the ***semanticPathinfo*** request attribute, and the original pathinfo is not modified. diff --git a/docs/guide/templates.md b/docs/guide/templates.md deleted file mode 100644 index 315dfa75d1..0000000000 --- a/docs/guide/templates.md +++ /dev/null @@ -1,735 +0,0 @@ -# Templates - -## Templating basics - -To apply a template to any part of your webpage, you need three (optionally four) elements: - -1. An entry in the configuration that defines which template should be used in what situation -1. The template file itself -1. Assets used by the template (for example, CSS or JS files, images, etc.) -1. *Optionally:* A custom controller used when the template is read which allows you more detailed control over the page. - -Each template must be mentioned in a configuration file together with a definition of the situation in which it is used. You can use the `ezplatform.yaml` file located in the `config/packages` folder, or create your own separate configuration file in that folder that will list all your templates. - -!!! note - - If you create a new configuration file outside the `packages` folder, you will need to import it by including an import statement in `ezplatform.yaml`. Add the following code at the beginning of `ezplatform.yaml`: - - ``` yaml - imports: - - { resource: ../<your_file_name>.yaml } - ``` - -!!! tip - - If you are using the recommended .yaml files for configuration, here are the basic rules for this format: - - - The configuration is based on pairs of a key and its value, separated by a colon, presented in the following form: `key: value`. - - The value of the key may contain further keys, with their values containing further keys, and so on. - - This hierarchy is marked using indentation – each level lower in the hierarchy must be indented in comparison with its parent. - -### Template configuration - -A short configuration file can look like this: - -``` yaml -# Sample configuration file -ezplatform: - system: - site_group: - user: - layout: pagelayout.html.twig - content_view: - full: - article: - template: full/article.html.twig - match: - Identifier\ContentType: [article] - blog_post: - controller: App\Controller\BlogController::showBlogPostAction - template: full/blog_post.html.twig - match: - Identifier\ContentType: [blog_post] - line: - article: - template: line/article.html.twig - match: - Identifier\ContentType: [article] -``` - -This is what individual keys in the configuration mean: - -- `ezplatform` and `system` are obligatory at the start of any configuration file which defines views. -- `site_group` defines the scope for which the configuration will be used. `site_group` is a SiteAccess group used out of the box for all front-end SiteAccesses. -See [Scope](siteaccess.md#scope) for other available keys. -- `user` and `layout` point to the main template file that is used in any situation where no other template is defined. All other templates extend this one. -- `content_view` defines the view provider. - -!!! note - - In earlier version `location_view` was used as the view provider. It has been deprecated since eZ Platform 1.x. - -- `full` and `line` determine the kind of view to be used (see below). -- `article` and `blog_post` are the keys that start the configuration for one individual case of using a template. You can name these keys any way you want, and you can have as many of them as you need. -- `template` names the template to be used in this case, including the folder it is stored in (starting from `app/Resources/views`). -- `controller` defines the controller to be used in this case. Optional, if this key is absent, the default controller is used. -- `match` defines the situation in which the template will be used. There are different criteria which can be used to "match" a template to a situation, for example a Content Type, a specific Location ID, Section, etc. You can view the full list of matchers here: [View provider configuration](content_rendering.md#configuring-views-the-viewprovider). You can specify more than one matcher for any template; the matchers will be linked with an AND operator. - -In the example above, three different templates are mentioned, two to be used in the full view, and one in the line view. -Notice that two separate templates are defined for the `article` Content Type. -They use the same matcher, but will be used in different situations – one when an Article is displayed in the full view, and one in the line view. -Templates for each of the view types are located in different folders. -The line template will also make use of a custom controller, while the remaining cases will employ the default one. - -##### Full, line and other views - -Each Content item can be rendered differently, using different templates, depending on the type of view it is displayed in. -The default, built-in views are: - -- **full** – used when the Content item is displayed by itself, as a full page -- **line** – used when it is displayed as an item in the list, for example a listing of contents of a folder -- **embed** – used when one Content item is embedded in another, as a block -- **embed-inline** – used when a Content item is embedded inline in another block - -Other, custom view types can be created, used for example for [embedding one Content item in another](#embedding-content-items), but only these four have built-in controllers in the system. -For more details, see [View provider configuration](content_rendering.md#configuring-views-the-viewprovider). - -### Template file - -Templates in [[= product_name =]] are written in the Twig templating language. - -!!! note "Twig templates in short" - - At its core, a Twig template is an HTML frame of the page that will be displayed. Inside this frame you define places (and manners) in which different parts of your Content items will be displayed (rendered). - - Most of a Twig template file can look like an ordinary HTML file. This is also where you can define places where Content items or their Fields will be embedded. - -The configuration described above lets you select one template to be used in a given situation, but this does not mean you are limited to only one template file per case. It is possible to include other templates in the main template file. For example, you can have a single template for the footer of a page and include it in many other templates. Such templates do not need to be mentioned in the configuration .yaml file. - -!!! tip - - See [Including Templates](http://symfony.com/doc/5.0/book/templating.html#including-templates) in Symfony documentation for more information on including templates. - -The main template for your webpage is placed in a pagelayout. -You can define the pagelayout per SiteAccess using the `ezplatform.system.<SiteAccess>.pagelayout` setting. -This template will be used by default for those parts of the website where no other templates are defined. - -A `pagelayout.html.twig` file exists already in Demo Bundles, but if you are using a clean installation, you need to create it from scratch. This file is typically located in the `templates` folder. - -Any further templates will extend and modify this one, so they need to start with a line like this: - -``` html+twig -{% extends "pagelayout.html.twig" %} -``` - -Templates can be extended using a Twig [`block`](http://twig.sensiolabs.org/doc/functions/block.html) tag. This tag lets you define a named section in the template that will be filled in by the child template. For example, you can define a "title" block in the main template. Any child template that extends it can also contain a "title" block. In this case the contents of the block from the child template will be placed inside this block in the parent template (and override what was inside this block): - -``` html+twig -<!--pagelayout.html.twig--> -{# ... #} - <body> - {% block title %} - <h1>Default title</h1> - {% endblock %} - </body> -{# ... #} -``` - -``` html+twig -<!--child.html.twig--> -{% extends "pagelayout.html.twig" %} -{% block title %} - <h1>Specific title</h1> -{% endblock %} -``` - -In the simplified example above, when the `child.html.twig` template is used, the "title" block from it will be placed in and will override the "title" block from the main template – so "Specific title" will be displayed instead of "Default title." - -!!! tip - - Alternatively, you can place templates inside one another using the [`include`](http://twig.sensiolabs.org/doc/functions/include.html)function. - - See [http://twig.sensiolabs.org/doc/templates.html\#](http://twig.sensiolabs.org/doc/templates.html) for detailed documentation on how to use Twig. - -##### Embed content in templates - -Now that you know how to create a general layout with Twig templates, let's take a look at the ways in which you can render content inside them. - -There are several ways of placing Content items or their Fields inside a template. You can do it using one of the [Twig functions described in detail here](twig_functions_reference.md). - -As an example, let's look at one of those functions: [ez\_render\_field](twig_functions_reference.md#ez_render_field). It renders one selected Field of the Content item. In its simplest form this function can look like this: - -``` html+twig -{{ ez_render_field( content, 'description' ) }} -``` - -This renders the value of the Field with identifier "description" of the current Content item (signified by "content"). You can additionally choose a special template to be used for this particular Field: - -``` html+twig -{{ ez_render_field( - content, - 'description', - { 'template': 'fields/description.html.twig' } - ) }} -``` - -!!! note - - As you can see in the case above, templates can be created not only for whole pages, but also for individual Fields. - -Another way of embedding Content items is using the `render_esi` function (which is not an eZ-specific function, but a Symfony standard). This function lets you easily select a different Content item and embed it in the current page. This can be used, for instance, if you want to list the children of a Content item in its parent. - -``` html+twig -{{ render_esi(controller('ez_content::viewAction', {locationId: 33, viewType: 'line'} )) }} -``` - -This example renders the Content item with Location ID 33 using the line view. To do this, the function applies the `ez_content::viewAction` controller. This is the default controller for rendering content, but can be substituted here with any custom controller of your choice. - -#### Assets - -Asset files such as CSS stylesheets, JS scripts or image files can be defined in the templates and need to be included in the directory structure in the same way as with any other web project. Assets are placed in the `public/` folder in your installation. - -Instead of linking to stylesheets or embedding images like usually, you can use the [`asset`](http://symfony.com/doc/5.0/book/templating.html#linking-to-assets) function. - -#### Controller - -While it is possible to template a whole website using only Twig, a custom PHP controller gives many more options of customizing the behavior of the pages. - -See [Custom rendering logic](controllers.md#custom-rendering-logic) for more information. - -## Rendering Rich Text - -### Alignment of images - -Images that have been added to Rich Text with alignment set have one of following classes: `align-left`, `align-right`, or `align-center`. - -By default, these classes have no specific styling, so for the alignment to work properly, you need to add them to your styles, for example: - -``` css -.align-left { - float: left; - padding-right: 20px; -} - -.align-right { - float: right; - padding-left: 20px; -} - -.align-center { - text-align: center; - padding-bottom: 10px; -} -``` - -## Rendering Content items - -By default (without any configuration), a Content item is rendered without any template. -By creating multiple templates and configuring them properly, you can configure the platform to render Content items differently depending on the scenario. - -### Content item Fields - -A view template receives the requested Content item, holding all Fields. -In order to display the Fields' value the way you want, you can either manipulate the Field value object itself, or use a custom template. - -#### Getting raw Field value - -As you have access to the Content item in the template, you can use [its public methods](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Repository/Values/Content/Content.php) to access all the information you need. You can also use the `ez_field_value` helper to get the [Field's value only](twig_functions_reference.md#ez_field_value). It will return the correct language if there are several, based on language priorities. - -``` html+twig -{# With the following, myFieldValue will be in the Content item's main language #} -{# It will also takes languages you provided to API on retrieval into account #} -{% set myFieldValue = content.getFieldValue( 'some_field_identifier' ) %} - -{# Here myTranslatedFieldValue will be in the current language if a translation is available (read from SiteAccess configuration). If not, the Content item's main language will be used #} -{% set myTranslatedFieldValue = ez_field_value( content, 'some_field_identifier' ) %} -``` - -#### Rendering Content items on full page - -To render a Content item on a full page, first you need to create a `templates/full/article.html.twig` template: - -``` html+twig -<div> - {# 'ez_render_field' is one of the available Twig functions. - It will render the 'body' Field of the current 'content' #} - {{ ez_render_field(content, 'body') }} -</div> -``` - -Next, you need to provide the [template configuration](#template-configuration). -You can place the config in the `config/packages/` folder in either of two places: a new configuration file or the pre-existing `ezplatform.yaml` file. -In this case you'll use the latter. - -In `ezplatform.yaml`, under the `ezpublish` and `system` keys, add the following config: - -``` yaml -# 'default' is the SiteAccess. -default: - # 'content_view' indicates that you will be defining view configuration. - content_view: - # 'full' is the type of view to use. Defining other view types is described below. - full: - # Here starts the entry for our view. You can give it any name you want, as long as it is unique. - article: - # This is the path to the template file, relative to the 'templates' folder. - template: full/article.html.twig - # This identifies the situations when the template will be used. - match: - # The template will be used when the Content Type of the content is 'article'. - Identifier\ContentType: [article] -``` -Pay attention to indentation – `default` should be indented relative to `system`. -Use `match` to identify not only the Content Type, but also the scenario for using the template. -For details, see [Matchers](content_rendering.md#view-matchers). - -At this point all Content items that are articles should render using the new template. -If you do not see changes, clear the cache by running: `php bin/console cache:clear`. - -#### Using the Field Type's template block - -All built-in Field Types come with [their own Twig template.](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig) -You can render any Field using this default template using the `ez_render_field()` helper. - -``` html+twig -{{ ez_render_field( content, 'some_field_identifier' ) }} -``` - -You can use this helper to render various Content item Fields. -This, paired with the fact that each Content item can have multiple Fields and you can render them differently, offers more rendering options. - -To see it in practice, extend the `templates/full/article.html.twig` template: - -``` html+twig -{# This renders the content name of the article #} -<h1>{{ ez_content_name(content) }}</h1> -<div> - {# Here you add a rendering of a different Field, 'intro' #} - <b>{{ ez_render_field(content, 'intro') }}</b> -</div> -<div> - {{ ez_render_field(content, 'body') }} -</div> -``` - -For more details on the `ez_render_field()` helper, see [Twig functions reference guide](twig_functions_reference.md#ez_render_field). - -You can also use other [Twig functions](twig_functions_reference.md), for example [`ez_field_value`](twig_functions_reference.md#ez_field_value), which renders the value of the Field without a template. - -!!! tip - - As this makes use of reusable templates, **using `ez_render_field()` is the recommended way and is to be considered the best practice**. - -### Content name - -The **name** of a Content item is its generic "title", generated by the repository based on the Content Type's naming pattern. It often takes the form of a normalized value of the first field, but might be a concatenation of several fields. There are 2 different ways to access this special property: - -- Through the name property of ContentInfo (not translated). -- Through VersionInfo with the TranslationHelper (translated). - -#### Translated name - -The *translated name* is held in a `VersionInfo` object, in the `names` property which consists of a hash indexed by locale. You can easily retrieve it in the right language via the `TranslationHelper` service. - -``` html+twig -{# Languages provided to API will be taken into account so you can do this: #} -<h2>Translated Content name: {{ content.name }}</h2> - -{# In earlier versions the same is available using Twig translation helper ez_content_name() #} -<h2>Translated Content name: {{ ez_content_name( content ) }}</h2> -<h3>Also works from ContentInfo: {{ ez_content_name( content.contentInfo ) }}</h3> -``` - -The helper will by default follow the prioritized languages order. If there is no translation for your prioritized languages, the helper will always return the name in the main language. - -You can also **force a locale** in a second argument: - -``` html+twig -{# Force fre-FR locale. #} -<h2>{{ ez_content_name( content, 'fre-FR' ) }}</h2> -``` - -!!! note "Name property in ContentInfo" - - This property is the actual content name, but **in the main language only** (so it is not translated). - - ``` html+twig - <h2>Content name: {{ content.contentInfo.name }}</h2> - ``` - - In PHP that would be: - - ``` php - $contentName = $content->getContentInfo->getName(); - ``` - - So make sure to use `$content->getName() or $versionInfo->getName()`, which takes translations into account. - -### Embedding images - -The Rich Text Field allows you to embed other Content items within the Field. - -Content items that are identified as images will be rendered in the Rich Text Field using a dedicated template. - -You can determine which Content Types will be treated as images and rendered using this template in the `ezplatform.content_view.image_embed_content_types_identifiers` parameter. By default it is set to cover the Image Content Type, but you can add other types that you want to be treated as images, for example: - -``` yaml -parameters: - ezplatform.content_view.image_embed_content_types_identifiers: [image, photo, banner] -``` - -The template that is used when rendering embedded images can be set in the `ezplatform.default_view_templates.content.embed_image` container parameter: - -``` yaml -parameters: - ezplatform.default_view_templates.content.embed_image: content/view/embed/image.html.twig -``` - -### Adding Links - -#### Links to other Locations - -Linking to other Locations is done with the `ez_path()` Twig helper (or `ez_url()` if you want to generate absolute URLs). When you pass it the Location object, `ez_path()` will generate the URL alias. - -``` html+twig -{# Assuming "location" variable is a valid eZ\Publish\API\Repository\Values\Content\Location object #} -<a href="{{ ez_path( location ) }}">Some link to a Location</a> -``` - -If you don't have the Location object, but only its ID, you can generate the URL alias the following way: - -``` html+twig -<a href="{{ path( "ez_urlalias", {"locationId": 123} ) }}">Some link to a Location, with its ID only</a> -``` - -!!! tip - - Instead of pointing to a specific Content item by its Location ID, you can also use here a variable. - For more details, see [this example in the Demo Bundle.](https://github.com/ezsystems/ezplatform-demo/blob/e15b93ade4b8c1f9084c5adac51239d239f9f7d8/app/Resources/views/full/blog.html.twig#L25) - - -You can also use the Content item's ID. In that case the generated link will point to the Content item's main Location. - -``` html+twig -<a href="{{ path( "ez_urlalias", {"contentId": 456} ) }}">Some link from a contentId</a> -``` - -!!! note "Under the hood" - - In the back end, `ez_path()` uses the Router to generate links. - - This makes it also easy to generate links from PHP, via the `router` service. - -See also: [Cross-SiteAccess links](siteaccess.md#cross-siteaccess-links) - -### Embedding Content items - -To render an embedded Content from a Twig template, you can do one of the following things: - -- use [`ez_render` Twig helper](#using-ez_render-twig-helpers) -- do a [subrequest with the `ez_content` controller](#using-the-ez_content-controller) - -#### Using `ez_render` Twig helpers - -You can use the `ez_render()`, `ez_render_content()`, and `ez_render_location()` functions -to render the provided Content item. -It is rendered by default with the `embed` view. - -`ez_render_content()` and `ez_render_location()` take the Content object and the Location object -as parameters, respectively. - -You can also use `ez_render()`, which automatically selects and uses either of those functions depending on the provided parameter. - -``` html+twig -{{ ez_render(content) }} -{{ ez_render(location) }} - -{{ ez_render_content(content) }} -{{ ez_render_location(location) }} -``` - -You can also specify one of the available [rendering methods](twig_functions_reference.md#rendering-methods): - -``` html+twig -{{ ez_render(location, {method: "esi"}) }} -``` - -To change the [view type](#full-line-and-other-views), provide it in an optional parameter: - -``` html+twig -{{ ez_render(location, {method: "esi", viewType: "line"}) }} -``` - -#### Using the `ez_content` controller - -This controller is exactly the same as [the ViewController presented above](content_rendering.md#the-viewcontroller). It has one main `viewAction` that renders a Content item. - -You can use this controller from templates with the following syntax: - -``` html+twig -{{ render(controller("ez_content::viewAction", {"contentId": 123, "viewType": "line"})) }} -``` - -The example above renders the Content item whose ID is **123** with the view type **line**. - -Referencing the `ez_content` controller follows the syntax of *controllers as a service*, [as explained in Symfony documentation](http://symfony.com/doc/5.0/cookbook/controller/service.html). - -##### Available arguments - -As with any controller, you can pass arguments to `ez_content::viewAction` to fit your needs. -You must provide `contentId` (and, optionally, `locationId`) for the action to work. - -|Name|Description|Type|Default value| -|---|---|---|---| -|`contentId`|ID of the Content item you want to render. Can be used together with `locationId`, if the Location belongs to that Content item.|integer|Location's Content item, if defined| -|`locationId`|ID of the Location you want to render. Can be used together with `contentId`, if the Location belongs to that Content item.|integer|Content item's main location, if defined| -|`viewType`|The view type you want to render your Content item/Location in. Will be used by the ViewManager to select a corresponding template, according to defined rules. </br>Example: full, line, my_custom_view, etc.|string|full| -|`layout`|Indicates if the sub-view needs to use the main layout (see [available variables in a view template](content_rendering.md#available-variables))|boolean|false| -|`params`|Hash of variables you want to inject to sub-template, key being the exposed variable name.|hash|empty hash| - -For example: - -``` html+twig -{{ render( - controller( - "ez_content::viewAction", - { - "contentId": 123, - "viewType": "line", - "params": { "some_variable": "some_value" } - } - ) -) }} -``` - -### Listing Content item children - -For details on listing children of a Content item, for example all content contained in a folder, see [Displaying children of a Content item](displaying_children_of_a_content_item.md). - -### Non-content related Query Types - -If you use [Query Types](controllers.md#query-controller) that do not make use of the current content or Location -(e.g. rendering a list of latest blog posts, or a menu), -you can render them in your templates using `ez_render_<type>_query` (e.g. `ez_render_content_query`) Twig functions -or their ESI equivalents: `ez_render_content_query_esi`. - -``` html+twig -{{ ez_render_content_query({ - 'query': { - 'query_type': 'LatestContent', - 'assign_results_to': 'latest_articles' - }, - 'template': 'latest/latest.html.twig', -}) }} -``` - -You can also set pagination using the Twig function: - -``` html+twig -{{ ez_render_location_query({ - 'query': { - 'query_type': 'LatestContent', - 'assign_results_to': 'latest_articles' - }, - 'pagination': { - 'enabled': true, - 'limit': 5, - 'page_param': 'page' - }, - 'template': 'latest/latest.html.twig', -}) }} -``` - -Using this function does not require adding anything to your content view configuration. - -#### Rendering and cache - -##### ESI - -Just like for regular Symfony controllers, you can take advantage of [ESI](https://symfony.com/doc/5.0/http_cache/esi.html) and use different cache levels: - -``` html+twig -{{ render_esi(controller("ez_content::viewAction", {"contentId": 123, "viewType": "line"})) }} -``` - -Only scalar variables (not objects) can be sent via `render_esi`. - -## Rendering search results - -You can set a template to be used for search results under the `search_view` key -and set the pagination limit under `search.pagination`: - -``` yaml -system: - <siteaccess>: - search_view: - full: - search: - template: 'custom_search_template.html.twig' - match: true - search: - pagination: - limit: 12 -``` - -## Rendering in preview - -When previewing content in the back office, the draft view is rendered using the [PreviewController](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/MVC/Symfony/Controller/Content/PreviewController.php). - -The first draft of a yet unpublished Content item does not have a Location, because Locations are only assigned when content is published. -To enable rendering in such cases, the PreviewController [creates a temporary virtual Location](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Helper/PreviewLocationProvider.php#L65). -This Location has some of the properties of the future Location, such as the parent Location ID. -However, it does not fully replace a normal Location. - -If the rendering template refers directly to the Location ID of the content, an error will occur. -To avoid such situations, you can check if the Location is virtual using the `location.isDraft` flag in Twig templates, for example: - -``` html+twig -{% if not location.isDraft %} - <a href="{{ ez_path(location) }}">{{ ez_content_name(content) }}</a> -{% endif %} -``` - -## Exposing additional variables - -### Contextual Twig variables - -You can create custom Twig variables for use in templates. - -The variables can be set per SiteAccess, or per content view. - -``` yaml -ezplatform: - system: - <siteaccess>: - twig_variables: - my_custom_variable: variable_value -``` - -You can access this variable directly in all templates in that SiteAccess: - -``` html+twig -{{ my_custom_variable }} -``` - -Variables set per content view will only be available when this view is matched: - -``` yaml -ezplatform: - system: - <siteaccess>: - full: - article: - template: 'full/article.html.twig' - params: - my_custom_variable: variable_value - match: - Identifier\ContentType: article -``` - -Custom variables can be nested. -You can use Expression language to access values such as: - -``` yaml -article: - template: 'full/article.html.twig' - params: - my_custom_variable: - content_type_name: "@=content.contentType.identifier" -``` - -``` html+twig -{{ my_custom_variable.content_type_name }} -``` - -!!! caution - - It is possible to overwrite an existing parameter, so it is good practice to avoid existing - parameter names such as `content` or `location`. - - Use `{{ dump() }}` to view the list of all parameters available in the given template. - -### Dynamic variable injection - -#### Custom variable providers - -In your templates you can use Twig variables coming from custom variable providers. - -``` php -<?php -declare(strict_types=1); - -namespace App\Provider; - -use \eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\SPI\MVC\View\VariableProvider; - -class MyVariableProvider implements VariableProvider -{ - public function getTwigVariables(View $view, array $options = []): object - { - return (object)[ - 'my_variable' => 'Value of ' . $this->getIdentifier(), - ]; - } - - public function getIdentifier(): string - { - return 'my_variable_provider'; - } -} -``` - -Register the provider as a service: - -``` yaml -App\Provider\MyVariableProvider: - autoconfigure: true -``` - -``` yaml -article: - template: 'full/article.html.twig' - params: - provided_variable: "@=twig_variable_provider('my_variable_provider').my_variable" -``` - -#### Injecting variables through event - -You can dynamically inject variables in content view templates by listening to the `ezpublish.pre_content_view` event. - -The event listener method receives an [`eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent`](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/MVC/Symfony/Event/PreContentViewEvent.php) object. - -The following example injects `my_variable` and `my_array` variables in all content view templates. - -``` php -<?php -namespace App\EventListener; - -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; - -class PreContentViewListener -{ - public function onPreContentView(PreContentViewEvent $event) - { -  // Get content view object and inject whatever you need. - // You may also add custom business logic here. - $contentView = $event->getContentView(); - $contentView->addParameters( - [ - 'my_variable' => 'my_value', - 'my_array' => [ 'value1', 'value2', 'value3' ] - ] - ); - } -} -``` - -Service configuration: - -``` yaml -services: - App\EventListener\PreContentViewListener: - tags: - - {name: kernel.event_listener, event: ezpublish.pre_content_view, method: onPreContentView} -``` diff --git a/docs/guide/twig_functions_reference.md b/docs/guide/twig_functions_reference.md deleted file mode 100644 index f82e829645..0000000000 --- a/docs/guide/twig_functions_reference.md +++ /dev/null @@ -1,671 +0,0 @@ -# Twig Functions Reference - -!!! note "Symfony and Twig template functions/filters/tags" - - For the template functionality provided by Symfony Framework, see [Symfony Twig Extensions Reference page](http://symfony.com/doc/5.0/reference/twig_reference.html). For those provided by the underlying Twig template engine, see [Twig Reference page](http://twig.sensiolabs.org/documentation#reference). - -In addition to the [native functions provided by Twig](http://twig.sensiolabs.org/doc/functions/index.html), [[= product_name =]] offers the following: - -|Twig function|Description| -|-------------|-----------| -|[`ez_content_name`](#ez_content_name)|Displays a Content item's name in the current language.| -|[`ez_field_description`](#ez_field_description)|Returns the description from the Field definition of a Content item's Field in the current language.| -|[`ez_field_name`](#ez_field_name)|Returns the name from the Field definition of a Content item's Field in the current language.| -|[`ez_field_value`](#ez_field_value)|Returns a Content item's Field value in the current language.| -|[`ez_field`](#ez_field)|Returns a Field from a Content item in the current language.| -|[`ez_file_size`](#ez_file_size)|Returns the size of a file as string.| -|[`ez_content_field_identifier_first_filled_image`](#ez_content_field_identifier_first_filled_image)|Returns the identifier of the first image field that is not empty.| -|[`ez_full_datetime`](#ez_full_datetime-ez_full_date-ez_full_time)|Outputs date and time in full format.| -|[`ez_full_date`](#ez_full_datetime-ez_full_date-ez_full_time)|Outputs date in full format.| -|[`ez_full_time`](#ez_full_datetime-ez_full_date-ez_full_time)|Outputs time in full format.| -|[`ez_image_alias`](#ez_image_alias)|Displays a selected variation of an image.| -|[`ez_field_is_empty`](#ez_field_is_empty)|Checks if a Content item's Field value is considered empty in the current language.| -|[`ez_short_datetime`](#ez_short_datetime-ez_short_date-ez_short_time)|Outputs date and time in short format.| -|[`ez_short_date`](#ez_short_datetime-ez_short_date-ez_short_time)|Outputs date in short format.| -|[`ez_short_time`](#ez_short_datetime-ez_short_date-ez_short_time)|Outputs time in short format.| -|[`ez_render`](#ez_render)|Renders the indicated Content item.| -|[`ez_render_content`](#ez_render_content)|Renders the indicated Content item.| -|[`ez_render_location`](#ez_render_location)|Renders the Content item in the indicated Location.| -|[`ez_render_content_query`](#ez_render_content_query)|Renders results of a non-content related query.| -|[`ez_render_location_query`](#ez_render_location_query)|Renders results of a non-content related Location query.| -|[`ez_render_field`](#ez_render_field)|Displays a Content item's Field value, taking advantage of the template block exposed by the Field Type used.| -|[`ez_urlalias`](#ez_urlalias)|It is a special route name for generating URLs for a Location from the given parameters.| - -### `ez_content_name` - -#### Description - -`ez_content_name()` is a Twig helper which displays a Content item's name in the current language. - -If the Content item does not have a translation in the current language, the name in the main language is always returned. This behavior is identical when forcing a language. - -If languages were specified during retrieval of the Content item, you can render name directly using `$content->getName()` (Twig: `content.name`) and it will take the prioritised languages into account. If not, it falls back to the main language, just like `ez_content_name()` does. For usage with ContentInfo, see examples below. - -#### Prototype and Arguments - -`ez_content_name ( eZ\Publish\API\Repository\Values\Content\Content content [, string forcedLanguage ] ) : string` - -`ez_content_name ( contentInfo [, string forcedLanguage ] ) : string` - -| Argument name | Type | Description | -|---------------|------|-------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo ` | Content or ContentInfo object the displayable field belongs to.| -| `forcedLanguage` | `string` | Locale you want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) | - -#### Usage - -``` html+twig -<h2>Content name in current language: {{ ez_content_name( content ) }}</h2> -<h2>Content name in current language, from ContentInfo: {{ ez_content_name( content.contentInfo ) }}</h2> -<h2>Content name in French (forced): {{ ez_content_name( content, "fre-FR" ) }}</h2> -``` - -#### Equivalent PHP code - -##### Getting the translated name for a Content item - -``` php -// Assuming you're in a controller action -$translationHelper = $this->get( 'ezpublish.translation_helper' ); -  -// From Content -$translatedContentName = $translationHelper->getTranslatedContentName( $content ); -// From ContentInfo -$translatedContentName = $translationHelper->getTranslatedContentNameByContentInfo( $contentInfo ); -``` - -##### Forcing a specific language - -``` php -// Assuming you're in a controller action -$translatedContentName = $this->get( 'ezpublish.translation_helper' )->getTranslatedName( $content, 'fre-FR' ); -``` - -### `ez_field_description` - -#### Description - -`ez_field_description()` is a Twig helper which returns the description from the Field definition of a Content item's Field in the current language. - -This can be useful when you don't want to use a sub-request and custom controller to be able to display this information. - -If the Content item does not have a translation in the current language, the main language will be used. This behavior is identical when forcing a language using **forcedLanguage**. - -#### Prototype and Arguments - -`ez_field_description ( Content|ContentInfo content, string fieldDefIdentifier [, string forcedLanguage ] ) : string|null` - -| Argument name | Type | Description | -|---------------|------|-------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo ` | Content/ContentInfo object the **fieldDefIdentifier** belongs to. | -| `fieldDefIdentifier` | `string` | Identifier of the Field you want to get the Field definition description from. | -| `forcedLanguage` | `string` | Language you want to force (e.g. "eng-US"), otherwise takes prioritized languages from SiteAccess settings. | - -#### Usage - -``` html+twig -<p id="ez-content-article-title-description">{{ ez_field_description( content, "title" ) }}</p> -``` - -### `ez_field_name` - -#### Description - -`ez_field_name()` is a Twig helper which returns the name from the Field definition of a Content item's Field in the current language. - -This can be useful when you don't want to use a sub-request and custom controller to be able to display this information. - -If the Content item does not have a translation in the current language, the main language will be used. This behavior is identical when forcing a language using **forcedLanguage**. - -#### Prototype and Arguments - -`ez_field_name ( Content|ContentInfo content, string fieldDefIdentifier [, string forcedLanguage ] ) : string|null` - -| Argument name | Type | Description | -|---------------|------|-------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` or `eZ\Publish\API\Repository\Values\Content\ContentInfo` | Content / ContentInfo object the **fieldDefIdentifier** belongs to. | -| `fieldDefIdentifier` | `string` | Identifier of the Field you want to get the Field definition name from. | -| `forcedLanguage` | `string` | Language you want to force (e.g. "`jpn-JP`"), otherwise takes prioritized languages from SiteAccess settings. | - -#### Usage - -``` html+twig -<label for="ez-content-article-title">{{ ez_field_name( content, "title" ) }}</label> -``` - -### `ez_field_value` - -#### Description - -`ez_field_value()` is a Twig helper which returns a Content item's Field value in the current language. - -This can be useful when you don't want to use [`ez_render_field`](#ez_render_field) and manage the rendering by yourself. - -If the Content item does not have a translation in the current language, the main language will be used. This behavior is identical when forcing a language using **forcedLanguage**. - -!!! tip - - If languages were specified during retrieval of the Content item, you can get field value directly using `content->getFieldValue('title')` and it will take the prioritised languages into account. If not, it falls back to the main language, just like **ez\_field\_value()** does. - -#### Prototype and Arguments - -`ez_field_value ( eZ\Publish\API\Repository\Values\Content\Content content, string fieldDefIdentifier [, string forcedLanguage ] ): eZ\Publish\Core\FieldType\Value` - -| Argument name | Type | Description | -|----------------------|----------------------------------------------------|--------------------------------------------------------------------------------------------------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Field referred to with `fieldDefIdentifier` belongs to. | -| `fieldDefIdentifier` | `string` | Identifier of the Field you want to get the value from. | -| `forcedLanguage` | `string` | Locale you want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) | - -#### Usage - -``` html+twig -<h2>My title value: {{ ez_field_value( content, "title" ) }}</h2> -``` - -### `ez_field` - -#### Description - -`ez_field()` is a Twig helper which returns a Field in the current language. The Field gives you access to the Field value, as well as the Field's definition identifier and Field Type identifier. - -!!! tip - - Other Twig helpers are available to display specific information of the Field; they all start with `ez_field_`. - -If the Content item does not have a translation in the current language, the main language will be used. This behavior is identical when forcing a language using **forcedLanguage**. - -!!! tip - - If languages were specified during retrieval of the Content item, you can get the Field directly using `content->getField('title')` and it will take the prioritised languages into account. If not, it falls back to the main language, just like **ez\_field()** does. - -#### Prototype and Arguments - -`ez_field ( eZ\Publish\API\Repository\Values\Content\Content content, string fieldDefIdentifier [, string forcedLanguage ] ) : eZ\Publish\API\Repository\Values\Content\Field` - -| Argument name | Type | Description | -|----------------------|----------------------------------------------------|--------------------------------------------------------------------------------------------------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Field referred to with `fieldDefIdentifier` belongs to. | -| `fieldDefIdentifier` | `string` | Identifier of the Field you want to get the value from. | -| `forcedLanguage` | `string` | Locale you want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) | - -#### Usage - -``` html+twig -<h2>My title's id: {{ ez_field( content, "title" ).id }}</h2> -``` - -### `ez_file_size` - -#### Description - -`ez_file_size()` is a Twig helper (Twig filter) which is mostly a byte calculator. It will convert a number from byte to the correct suffix (from B to EB). The output pattern will also vary with the current language of the SiteAccess (e.g. choosing between coma or point pattern). - -It returns a string. - -!!! note - - The byte factor is 1000 instead of 1024 to be more familiar for users. - -#### Prototype and Arguments - -`integer number_of_bytes|ez_file_size( integer number_of_decimal )` - -| Argument name | Type | Description | -|---------------------|-----------|--------------------------------------------------| -| `number_of_bytes` | `integer` | The number in byte you want to convert | -| `number_of_decimal` | `integer` | The number of decimal you want the output to have | - -#### Usage - -``` html+twig -{{ 42698273|ez_file_size( 3 ) }} //Output with French SiteAccess : 42,698 MB - -{{ 42698273|ez_file_size( 4 ) }} //Output with English SiteAccess : 42.6983 MB -``` - -### `ez_content_field_identifier_first_filled_image` - -#### Description - -`ez_content_field_identifier_first_filled_image` is a Twig helper which returns the identifier of the first image field that is not empty. - -It can be used for example to identify the first image in an article to render it in an embed or line view. - -#### Prototype and Arguments - -`ez_content_field_identifier_first_filled_image` ( eZ\Publish\API\Repository\Values\Content\Content content ) : string` - -| Argument name | Type | Description | -|---------------|----------------------------------------------------|-----------------------------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item the Fields belong to | - -### `ez_full_datetime`, `ez_full_date`, `ez_full_time` - -These Twig filters are used to [format date and time](../extending/extending_date_and_time.md). -The formats are defined in [user preferences](config_back_office.md#date-and-time-formats). - -| Twig filter | Description | -|-------------|-------------| -| `ez_full_datetime` | outputs date and time in full format | -| `ez_full_date` | outputs date in full format | -| `ez_full_time` | outputs time in full format | - -The filters accept `\DateTimeInterface` as argument. -If the argument is null, the filter returns the current date and time in the selected format. - -For example `{{ contentInfo.publishedDate|ez_full_datetime }}` will return `03 May 2019 23:03`. - -The filters also accept an optional `timezone` parameter for displaying date and time in a chosen time zone. - -### `ez_icon_path` - -`ez_icon_path()` is a Twig helper that generates a path to the selected icon from an [icon set](config_back_office.md#icon-sets). - -#### Prototype and Arguments - -`ez_icon_path ( string icon [, string set = null ] ) : string` - -| Argument name | Type | Description | -| ----- | ----- | ----- | -| `icon` | string | Identifier of an icon in the icon set. | -| `set` | string | Identifier of the configured icon set. If empty, the default icon set is used. | - -#### Usage - -``` html+twig -<svg class="ez-icon ez-icon--medium ez-icon--light"> - <use xlink:href="{{ ez_icon_path('edit', 'my_icons') }}"></use> -</svg> -``` - -### `ez_image_alias` - -#### Description - -`ez_image_alias()` is a Twig helper that displays a selected variation (alias) of an image. - -#### Prototype and Arguments - -`ez_image_alias ( eZ\Publish\API\Repository\Values\Content\Field field, eZ\Publish\API\Repository\Values\Content\VersionInfo versionInfo, string variantName ) : \eZ\Publish\SPI\Variation\Values\Variation|null` - -| Argument name | Type | Description | -|---------------|--------------------------------------------------------|-------------------------------------------| -| `field` | `eZ\Publish\API\Repository\Values\Content\Field` | The image Field | -| `versionInfo` | `eZ\Publish\API\Repository\Values\Content\VersionInfo` | The VersionInfo that the Field belongs to | -| `variantName` | `string` | Name of the image variation to be used. To display original image alias, use `original` as an image variation. | - -See [images](images.md) for more information about image variations. - -### `ez_field_is_empty` - -#### Description - -`ez_field_is_empty()` is a Twig helper which checks if a Content item's Field value is considered empty in the current language. - -It returns a Boolean value (`true` or `false`). - -If the Content item does not have a translation in the current language, the main language will be used. This behavior is identical when forcing a language using **forcedLanguage**. - -#### Prototype and Arguments - -`ez_field_is_empty ( eZ\Publish\API\Repository\Values\Content\Content content, eZ\Publish\API\Repository\Values\Content\Field|string fieldDefIdentifier [, string forcedLanguage ] ) : bool` - -| Argument name | Type | Description | -|---------------|------|-------------| -| `content` | `eZ\Publish\API\Repository\Values\Content\Content` | Content item the displayed Field belongs to. | -| `fieldDefIdentifier` | `eZ\Publish\API\Repository\Values\Content\Field or string` | The Field you want to check or its identifier. | -| `forcedLanguage` | `string` | Locale you want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) | - -#### Usage - -##### Using the Field identifier as parameter - -``` html+twig -{# Display "description" Field if not empty #} -{% if not ez_field_is_empty( content, "description" ) %} - <div class="description"> - {{ ez_render_field( content, "description" ) }} - </div> -{% endif %} -``` - -##### Using the Field as parameter - -``` html+twig -{# Display "description" field if not empty #} -{% if not ez_field_is_empty( content, field ) %} - <div class="description"> - {{ ez_render_field( content, field.fieldDefIdentifier ) }} - </div> -{% endif %} -``` - -##### Checking if Field exists before use - -``` html+twig -{# Display "description" field if it exists and is not empty #} -{% if content.fields.description is defined and not ez_field_is_empty( content, "description" ) %} - <div class="description"> - {{ ez_render_field( content, "description" ) }} - </div> -{% endif %} -``` - -### `ez_render` - -#### Description - -`ez_render()` is a Twig helper that renders the indicated Content item. -It is rendered by default with the `embed` view. - -The helper automatically selects and uses either [`ez_render_content()`](#ez_render_content) -or [`ez_render_location()`](#ez_render_location) depending on the provided parameter. - -#### Prototype and Arguments - -`ez_render ( eZ\Publish\API\Repository\Values\Content\Content content| - eZ\Publish\API\Repository\Values\Content\Location location - [, string method, - string viewType] ) : string` - -|Argument name|Type|Description| -|------|------|------| -|`content`|`eZ\Publish\API\Repository\Values\Content\Content`|Content item to render.| -|`location`|`eZ\Publish\API\Repository\Values\Content\Location`|Location of the Content item to render.| -|`method`|`string`|[Rendering method](#rendering-methods). One of: `direct`, inline`, `esi`, `ssi`.| -|`viewType`|`string`|[View type](templates.md#full-line-and-other-views).| - -#### Rendering methods - -The following rendering methods are available out of the box for the `ez_render()`, `ez_render_content()`, -and `ez_render_location()` functions: - -- `direct` - (default) renders the Content item without using a request -- `inline` - Symfony inline rendering method, sends a request to the server and inserts the response -- `esi` - uses the Symfony [Edge Side Include mechanism](https://symfony.com/doc/current/http_cache/esi.html) to render the correct tag that is handled by reverse proxy -- `ssi` - uses the Symfony [Server Side Include mechanism](https://symfony.com/doc/current/http_cache/ssi.html) to render the correct tag that is handled by the web server - -### `ez_render_content` - -#### Description - -`ez_render_content()` is a Twig helper that renders the indicated Content item. -It is rendered by default using the `embed` view. - -#### Prototype and Arguments - -`ez_render_content ( eZ\Publish\API\Repository\Values\Content\Content content [, string method, string viewType] ) : string` - -|Argument name|Type|Description| -|------|------|------| -|`content`|`eZ\Publish\API\Repository\Values\Content\Content`|Content item to render.| -|`method`|`string`|[Rendering method](#rendering-methods). One of: `direct`, inline`, `esi`, `ssi`.| -|`viewType`|`string`|[View type](templates.md#full-line-and-other-views).| - -### `ez_render_location` - -#### Description - -`ez_render_location()` is a Twig helper that renders the Content item from the indicated Location. -It is rendered by default using the `embed` view. - -#### Prototype and Arguments - -`ez_render_location ( eZ\Publish\API\Repository\Values\Content\Location location [, string method, string viewType] ) : string` - -|Argument name|Type|Description| -|------|------|------| -|`location`|`eZ\Publish\API\Repository\Values\Content\Location`|Location of the Content item to render.| -|`method`|`string`|[Rendering method](#rendering-methods). One of: `direct`, inline`, `esi`, `ssi`.| -|`viewType`|`string`|[View type](templates.md#full-line-and-other-views).| - -### `ez_render_content_query` - -#### Description - -`ez_render_content_query` is a Twig helper that renders the results of a [non-content related query made by using a Query Type](templates.md#non-content-related-query-types). - -#### Prototype and Arguments - -|Argument name|Type|Description| -|------|------|------| -|`options`|array|Available options are: `query`, `pagination`, `template`.| - -### `ez_render_location_query` - -#### Description - -`ez_render_location_query` is a Twig helper that renders the results of a [non-content related Location query made by using a Query Type](templates.md#non-content-related-query-types). - -#### Prototype and Arguments - -|Argument name|Type|Description| -|------|------|------| -|`options`|array|Available options are: `query`, `pagination`, `template`.| - -### `ez_render_field` - -#### Description - -`ez_render_field()` is a Twig helper that displays a Content item's Field value, taking advantage of the template block exposed by the Field Type used. - -Template blocks for built-in Field Types [reside in EzPublishCoreBundle](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig). - -See section of [Using the Field Type's template block](templates.md#using-the-field-types-template-block) for more information. - -#### Prototype and Arguments - -`ez_render_field ( eZ\Publish\API\Repository\Values\Content\Content content, string fieldDefinitionIdentifier [, hash params] ) : string` - -|Argument name|Type|Description| -|------|------|------| -|`content`|`eZ\Publish\API\Repository\Values\Content\Content`|Content item the displayable field belongs to.| -|`fieldDefinitionIdentifier`|`string`|The identifier the Field is referenced by.| -|`params`|`hash`|Hash of parameters that will be passed to the template block.</br>By default you can pass 2 entries:</br>`lang` (to override the current language, must be a valid locale with xxx-YY format)</br>`template` (to override the template to use, see below)</br>`attr` (hash of HTML attributes you want to add to the inner markup)</br>parameters (arbitrary parameters to pass to the template block)</br></br>Some Field Types might expect specific entries under the `parameters` key, like the [MapLocation Field Type](../api/field_type_reference.md#maplocation-field-type). - -#### Override a Field template block - -If you do not want to use the built-in Field template block, -you can override it by specifying your own template. -You can do this [inline](#inline-override) when calling `ez_render_field()`, -or [globally](#global-override) by prepending a Field template to use by the helper. - -Your template block must comply to a regular Field Type template block, [as explained in the Field Type documentation](../api/field_type_form_and_template.md). - -##### Inline override - -You can use the template you need by filling the `template` entry in the `params` argument. - -``` html+twig -{{ ez_render_field(  - content,  - 'my_field_identifier', - { 'template': 'fields/my_field_template.html.twig' } - ) }} -``` - -This code will load `my_field_template.html.twig` located in `templates/fields/`. - -``` html+twig -{# Assuming "my_field_identifier" from the template above example is an ezkeyword field. #} -{% block ezkeyword_field %} - {% apply spaceless %} - {% if field.value.values|length() > 0 %} - <ul> - {% for keyword in field.value.values %} - <li>{{ keyword }}</li> - {% endfor %} - </ul> - {% endif %} - {% endapply %} -{% endblock %} -``` - -!!! tip "Overriding a block and calling the parent" - - When overriding a Field template block, it is possible to call its parent. - For this, you need to import the original template horizontally (without inheritance), - using the [`use` Twig tag](http://twig.sensiolabs.org/doc/tags/use.html). - - ``` html+twig - {# templates/fields/my_field_template.html.twig #} - {# Assuming "my_field_identifier" from above template example is an ezkeyword field. #} -   - {% use "@EzPublishCore/content_fields.html.twig" with ezkeyword_field as base_ezkeyword_field %} -   - {# Surround base block with a simple div #} - {% block ezkeyword_field %} - <div class="ezkeyword"> - {{ block("base_ezkeyword_field") }} - </div> - {% endblock %} - ``` - -##### Inline override using current template - -If you want to override a specific Field template only once -(e.g. because your override would be only valid in your current template), -you can specify the current template to be the source of the Field block. - -``` html+twig -{% extends "pagelayout.html.twig" %} - -{% block content %} - {# Note that "tags" is a field using ezkeyword fieldType #} - <div class="tags">{{ ez_render_field( content, "tags" , { "template": _self } ) }}</div> -{% endblock %} - -{# Here begins the inline block for my ezkeyword Field #} -{% block ezkeyword_field %} - {% apply spaceless %} - {% if field.value.values|length() > 0 %} - <ul> - {% for keyword in field.value.values %} - <li>{{ keyword }}</li> - {% endfor %} - </ul> - {% endif %} - {% endapply %} -{% endblock %} -``` - -!!! caution "Limitation" - - **Using `_self` will only work if your current template is extending another one.** - - This is basically the same limitation as for [Symfony form themes](https://symfony.com/doc/5.0/form/form_themes.html). - -##### Global override - -If you want to override a Field template every time it occurs, -you can append it to the Field templates list. - -``` yaml -ezplatform: - system: - my_siteaccess: - field_templates: - -  - template: fields/my_field_template.html.twig - # Priority is optional (default is 0). The higher it is, the higher your template gets in the list. - priority: 10 -``` - -It will then be used every time the Field is rendered with `ez_render_field()`. - -!!! tip - - Because built-in Field templates have `priority` of `0`, you need to set yours to a higher value to override them. - -The content of the template must be placed in a Twig block corresponding to the Field Type's internal name -(e.g. `{% block ezstring_field %}`) for `ezstring`. - -The template must also extend `EzPublishCore/content_fields.html.twig`. - -``` html+twig -{% extends "@EzPublishCore/content_fields.html.twig" %} - -{% block ezstring_field %} - {# template content here #} -{% endblock %} -``` - -### `ez_short_datetime`, `ez_short_date`, `ez_short_time` - -These Twig filters are used to [format date and time](../extending/extending_date_and_time.md). -The formats are defined in [user preferences](config_back_office.md#date-and-time-formats). - -| Twig filter | Description | -|-------------|-------------| -| `ez_short_datetime` | outputs date and time in short format | -| `ez_short_date` | outputs date in short format | -| `ez_short_time` | outputs time in short format | - -The filters accept `\DateTimeInterface` as argument. -If the argument is null, the filter returns the current date and time in the selected format. - -For example `{{ contentInfo.publishedDate|ez_full_datetime }}` will return `03 May 2019 23:03`. - -The filters also accept an optional `timezone` parameter for displaying date and time in a chosen time zone. - -### `ez_urlalias` - -#### Description - -`ez_urlalias` is a not a real Twig helper, but a special route name for generating URLs for a Location from the given parameters. - -#### Prototype and Arguments - -`ez_path( eZ\Publish\API\Repository\Values\Content\Location| - \eZ\Publish\API\Repository\Values\Content\Content| - \eZ\Publish\API\Repository\Values\Content\ContentInfo| - \eZ\Publish\API\Repository\Values\Content\Location| - \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference name [, array parameters ] [, bool absolute ] ) : string` - -|Argument name|Type|Description| -|------|------|------| -|`name`|`string | `\eZ\Publish\API\Repository\Values\Content\Location`</br>`\eZ\Publish\API\Repository\Values\Content\Content`</br>`\eZ\Publish\API\Repository\Values\Content\ContentInfo`</br>`\eZ\Publish\API\Repository\Values\Content\Location`</br>`\eZ\Publish\Core\MVC\Symfony\Routing\RouteReference`|The name of the route, Location or Content instance| -|`parameters`|`array`|A hash of parameters:</br>`locationId`</br>`contentId`| -|`absolute`|`boolean`|Whether to generate an absolute URL| - -#### Working with Location - -Linking to other Locations is fairly easy and is done with the `ez_path()` Twig helper (or `ez_url()` if you want to generate absolute URLs). You just have to pass it the Location object and `ez_path()` will generate the URLAlias for you. - -``` html+twig -{# Assuming "location" variable is a valid eZ\Publish\API\Repository\Values\Content\Location object #} -<a href="{{ ez_path( location ) }}">Some link to a location</a> -``` - -#### I don't have the Location object - -##### Generating a link from a Location ID - -``` html+twig -<a href="{{ path( "ez_urlalias", {"locationId": 123} ) }}">Some link to a location, with its Id only</a> -``` - -##### Generating a link from a Content ID - -``` html+twig -<a href="{{ path( "ez_urlalias", {"contentId": 456} ) }}">Some link from a contentId</a> -``` - -!!! note - - Links generated from a Content ID will point to its main location. - -#### Error management - -For a Location alias set up a 301 redirect to the Location's current URL when: - -1. the alias is historical -1. the alias is a custom one with forward flag true -1. the requested URL does not match the one loaded (case-sensitively) - -!!! note "Under the hood" - - In the back end, `ez_path()` uses the Router to generate links. - - This makes it also easy to generate links from PHP, via the `router` service. diff --git a/docs/guide/url_management.md b/docs/guide/url_management.md index 28e5dbb7cd..7c3264a434 100644 --- a/docs/guide/url_management.md +++ b/docs/guide/url_management.md @@ -1,3 +1,7 @@ +--- +description: Manage URL aliases and wildcards, and validate external URLs. +--- + # URL management You can manage external URL addresses and URL wildcards in the Back Office, **Admin** tab, the **URL Management** node. @@ -19,7 +23,7 @@ Edit the entry to update the URL address in all the occurrences throughout the w ## External URL validation -You can validate all the addresses from the URL table by executing the `ezplatform:check-urls` command. +You can validate all the addresses from the URL table by executing the `ibexa:check-urls` command. It validates the links by accessing them one by one and updates the value in the Last checked field. If a broken link is found, its status is set to "invalid". @@ -31,12 +35,12 @@ The following protocols are currently supported: ### Enabling automatic URL validation -To enable automatic URL validation, set up cron to run the `ezplatform:check-urls` command periodically. +To enable automatic URL validation, set up cron to run the `ibexa:check-urls` command periodically. For example, to check links every week, add the following script: ``` -echo '0 0 * * 0 cd [path-to-ezplatform]; php bin/console ezplatform:check-urls --quiet --env=prod' > ezp_cron.txt +echo '0 0 * * 0 cd [path-to-ezplatform]; php bin/console ibexa:check-urls --quiet --env=prod' > ezp_cron.txt ``` Next, append the new cron to user's crontab without destroying existing crons. @@ -91,7 +95,7 @@ For details, see the tables below. |--------------------|---------------------------------------------------------------------|---------------| | enabled | Enables link validation. | true | -For more information about ezPlatform configuration, see [Configuration](configuration.md). +For more information about ezPlatform configuration, see [Configuration](configuration/configuration.md). ### Custom protocol support @@ -138,6 +142,19 @@ The `scheme` attribute is mandatory and has to correspond to the name of the pro You can define URL aliases for individual Content items, for example, when you reorganize the content, and want to provide users with continuity. For each URL alias definition the history of changes is preserved, so that users who have bookmarked the URL addresses of content items can still find he information they desire. +!!! note + + Make sure that you correctly define languages used by the site in the configuration + (under the `ezplatform.system.<scope>.languages` key). + Otherwise, redirections for the renamed Content with translations in multiple + languages may fail to work properly. + +!!! caution "Legacy storage engine limitation" + + The [Legacy storage engine](../api/field_type_storage.md#legacy-storage-engine) does not archive URL aliases, which initially + had the same name in multiple languages. + For more information, see [the Jira ticket](https://issues.ibexa.co/browse/EZP-31818). + URL aliases are not SiteAccess-aware. When creating an alias, you can select a SiteAccess to base it on. If the SiteAccess root path (configured in `content.tree_root.location_id`) is different than the default, the prefix path that results from the configured content root is prepended to the final alias path. @@ -178,7 +195,7 @@ To add commands to an existing group, provide the group name and list the comman ### Regenerating URL aliases -You can use the `ezplatform:urls:regenerate-aliases` command to regenerate all URL aliases. +You can use the `ibexa:urls:regenerate-aliases` command to regenerate all URL aliases. After the command is applied, old aliases redirect to the new ones. Use it when: @@ -195,13 +212,13 @@ Use it when: Execute the following command to regenerate aliases: ``` bash -bin/console ezplatform:urls:regenerate-aliases +bin/console ibexa:urls:regenerate-aliases ``` You can also extend the command with the following parameters: - `--iteration-count` — Defines how many Locations are processed at once to reduce memory usage -- `--location-id` — Regenerates URL addresses for specific Locations only, e.g. `ezplatform:urls:regenerate-aliases --location-id=1 --location-id=2` +- `--location-id` — Regenerates URL addresses for specific Locations only, e.g. `ibexa:urls:regenerate-aliases --location-id=1 --location-id=2` ## URL wildcards diff --git a/docs/guide/user_generated_content.md b/docs/guide/user_generated_content.md index 2ae44fcb5c..b594cc484e 100644 --- a/docs/guide/user_generated_content.md +++ b/docs/guide/user_generated_content.md @@ -1,23 +1,22 @@ -# User-Generated Content - -## Creating content +--- +description: You can enable users to create new content in the Repository by using forms available in the front end of the site. +--- -[[= product_name =]] comes with content edition features via the Symfony stack. They are meant to allow the implementation of user-generated content from the front end, without entering the PlatformUI back end. - -### Creating a Content item without using a draft +# User-Generated Content -The `/content/create/nodraft` route shows a Content item creation form for a given Content Type: +[[= product_name =]] comes with content edition features via the Symfony stack. +They are meant to allow the implementation of user-generated content from the front end, without entering the Back Office. -### Creating a new draft +## Creating a new draft -The `content/create/draft` route allows you to create a new draft for the selected Content item. -Provide it with the content ID as an argument. +The `content/create/draft` route enables you to create a new draft for the selected Content item. +Pass the ID of the Content item as an argument. For example, `content/create/draft/59` creates a new draft of the Content item with ID 59. -### Creating a content item without using a draft +## Creating a Content item without using a draft -The `/content/edit/nodraft` route shows a Content item creation form for a given Content Type: +The `/content/edit/nodraft` route shows a Content item creation form for a given Content Type: | Argument | Type | Description | |-------------------------|-----------|----------------------------------------------------------------------------| @@ -25,9 +24,9 @@ The `/content/edit/nodraft` route shows a Content item creation form for a give | `languageCode` | `string` | Language code the Content item must be created in. Example: `eng-GB` | | `parentLocationId` | `integer` | ID of the Location the Content item must be created in. Example: `2` | -This means that `/content/create/nodraft/folder/eng-GB/2` will enable you to create a Folder in English as a child of Location with ID 2. +This means that `/content/create/nodraft/folder/eng-GB/2` enables you to create a Folder in English as a child of Location with ID 2. -For now a limited subset of Field Types is supported: +A limited subset of Field Types is supported: - `TextLine` - `TextBlock` @@ -41,9 +40,9 @@ For now a limited subset of Field Types is supported: - `Float` - `URL` -### Editing a Content item +## Editing a Content item -To edit an existing draft, use the `/content/edit/draft/` +To edit an existing draft, use the `/content/edit/draft/` route, with the following arguments: | Argument | Type | Description | |-------------------------|-----------|--------------------------------------------------------------------------| @@ -53,7 +52,7 @@ To edit an existing draft, use the `/content/edit/draft/` For example, `/content/edit/draft/1/5/eng-GB` enables you to edit draft 5 of Content item 1 in English. -### Content editing templates +## Content editing templates You can use custom templates for the content editing forms. diff --git a/docs/guide/user_management/delegate_function.md b/docs/guide/user_management/delegate_function.md index eeb87bfd7f..f9e4b0c378 100644 --- a/docs/guide/user_management/delegate_function.md +++ b/docs/guide/user_management/delegate_function.md @@ -1,4 +1,4 @@ -# Delegate function [[% include 'snippets/commerce_badge.md' %]] +# Delegate function You can delegate your operations in the shop to another user. This user can then take over as if they had a different customer number. diff --git a/docs/guide/user_management/login_and_registration.md b/docs/guide/user_management/login_and_registration.md index d1ea2ecfe6..05e2316d16 100644 --- a/docs/guide/user_management/login_and_registration.md +++ b/docs/guide/user_management/login_and_registration.md @@ -1,18 +1,16 @@ -# Login and registration [[% include 'snippets/commerce_badge.md' %]] +--- +description: Configure registering and activating customers. +--- -## Login - -In [[= product_name_com =]], users can log in not only with their user name or email, but also with the customer number. +# Login and registration -To enable logging in with customer number, use the `enable_customer_number_login` configuration parameter: +## Login -``` yaml -siso_core.default.enable_customer_number_login: true -``` +In [[= product_name =]], users can log in with their user name or email. ## Registration -[[= product_name_com =]] provides different registration options for private and business customers. +[[= product_name =]] provides different registration options for private and business customers. ### Private customers @@ -48,7 +46,7 @@ The shop owner checks the provided data and creates a customer record in the ERP The shop checks this data by sending a request to the ERP. There are two options: - activate a business account - the customer is created using their customer number and can immediately see their special discounts in the shop. - - create the main contact in Customer Center - if Customer Center is enabled, the company is created in the shop, and the account is created as the main contact. + - in [[= product_name_com =]], create the main contact in Customer Center. If Customer Center is enabled, the company is created in the shop, and the account is created as the main contact. ## Configuration diff --git a/docs/guide/user_management/login_via_external_service.md b/docs/guide/user_management/login_via_external_service.md new file mode 100644 index 0000000000..c11134585b --- /dev/null +++ b/docs/guide/user_management/login_via_external_service.md @@ -0,0 +1,73 @@ +--- +description: Allow users to log in to Ibexa DXP through external services by using OAuth2. +--- + +# Add login through external service + +To add an option to log in to the system through an external service, you can use [OAuth2](oauth.md) to authorize your users. + +The example below shows how to add a **Log in with Google** option to the Back Office. + +## Configure OAuth2 client + +Configure the OAuth2 client in `config/packages/knpu_oauth2_client.yaml`: + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/packages/knpu_oauth2_client.yaml') =]] +``` + +## Enable OAuth authentication + +Enable OAuth2 authentication through Google for the `site` SiteAccess: + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/packages/oauth.yaml') =]] +``` + +## Configure firewall + +Add the `Ibexa\Platform\OAuth2Client\Security\Authenticator\OAuth2Authenticator` guard authenticator +to your firewall configuration in `config/packages/security.yaml` +and ensure that the `ibexa.oauth2.connect` route is accessible by an anonymous user: + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/packages/security.yaml', 20, 36) =]] +``` + +## Implement a resource owner mapper + +Create a resource owner mapper for Google login in `src/OAuth/GoogleResourceOwnerMapper.php`. +The mapper extends [`ResourceOwnerToExistingOrNewUserMapper`](oauth.md#resource-owner-mappers), +which enables it to create a new user in the Repository if the user does not exist yet. + +The mapper loads a user (line 50) or creates a new one (line 60), +based on the information from `resourceOwner`, that is the OAuth provider. + +The new user name is set with a `google:` prefix (lines 18, 105), to avoid conflicts with users registered in a regular way. + +``` php hl_lines="18 50 60 105" +[[= include_file('code_samples/user_management/oauth_google/src/OAuth/GoogleResourceOwnerMapper.php') =]] +``` + +Configure the service by using the `ibexa.oauth2_client.resource_owner_mapper` tag: + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/services.yaml', 33, 36) =]] +``` + +## Add template + +To add a **Log in with Google** button to your Back Office login form, create the following template file +in `templates/themes/admin/account/login/oauth2_login.html.twig`: + +``` html+twig +[[= include_file('code_samples/user_management/oauth_google/templates/themes/admin/account/login/oauth2_login.html.twig') =]] +``` + +Finally, add the template to the login form by using the `login-form-after` [component](../../extending/custom_components.md): + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/services.yaml', 37, 43) =]] +``` + +![Log in to the Back Office with Google](../img/log_in_via_google.png) diff --git a/docs/guide/user_management/oauth.md b/docs/guide/user_management/oauth.md new file mode 100644 index 0000000000..82d7dbfb1c --- /dev/null +++ b/docs/guide/user_management/oauth.md @@ -0,0 +1,68 @@ +--- +description: OAuth2 allows you to securely connect to external services, among others enabling login via external services. +--- + +# OAuth authentication + +You can use OAuth2 authentication to securely connect to external services. + +[[= product_name =]] uses an integration with [`knpuniversity/oauth2-client-bundle`](https://github.com/knpuniversity/oauth2-client-bundle) +to provide OAuth2 authentication. + +To enable OAuth2, you need to: + +- [enable and configure an OAuth2 provider](#oauth2-provider-configuration) +- [add a guard authentication to your firewall configuration](#firewall-configuration) +- [select one of the existing resource owner mappers, or implement your own one](#resource-owner-mappers) + +## OAuth2 provider configuration + +To configure the OAuth2 provider, add it under the `oauth2` key in SiteAccess-aware configuration, for example: + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/packages/oauth.yaml') =]] +``` + +Details of the configuration depend on the OAuth2 provider that you want to use. +For sample configurations for different providers, +see [`knpuniversity/oauth2-client-bundle` configuration](https://github.com/knpuniversity/oauth2-client-bundle#configuration). + +## Firewall configuration + +Firewall configuration is located in `config/packages/security.yaml` under `security.firewalls`. +The `guard.authenticators` setting specifies the [Guard authenticators]([[= symfony_doc =]]/security/guard_authentication.html) to use. + +``` yaml +[[= include_file('code_samples/user_management/oauth_google/config/packages/security.yaml', 20, 36) =]] +``` + +## Resource owner mappers + +Resource owner mappers map the data received from the OAuth2 provider to user information in the Repository. + +Resource owner mappers must implement the `Ibexa\Platform\Contracts\OAuth2Client\ResourceOwner\ResourceOwnerMapper` interface. +There are four existing implementations of `ResourceOwnerMapper`: + +- `ResourceOwnerToExistingUserMapper` is the base class that is extended by the following mappers: + - `ResourceOwnerIdToUserMapper` does not create a new user, but loads a user (resource owner) based on their identifier. + - `ResourceOwnerEmailToUserMapper` does not create a new user, but loads a user (resource owner) based on their email. +- `ResourceOwnerToExistingOrNewUserMapper` checks if the user exists and loads them if they do. +If they do not, the mapper creates a new user in the Repository. + +To use `ResourceOwnerToExistingOrNewUserMapper` you need to extend it in your custom mapper. + +See [Adding login through external service](login_via_external_service.md) for an example of creating a mapper +that extends `ResourceOwnerToExistingOrNewUserMapper`. + +!!! tip "OAuth User Content Type" + + When you implement your own mapper for external login, + it is good practice to create a special User Content Type for users registered in this way. + + This is because users who register through an external service do not have a separate password in the system. + Instead, they log in by their external service's password. + + To avoid issues with password restrictions in the built-in User Content Type, + create a special Content Type (for example, "OAuth User"), without restrictions on the password. + + This new Content Type must also contain the User (`ezuser`) Field. diff --git a/docs/guide/user_management/token.md b/docs/guide/user_management/token.md index 25133dc35a..12c065403f 100644 --- a/docs/guide/user_management/token.md +++ b/docs/guide/user_management/token.md @@ -1,6 +1,10 @@ -# Token [[% include 'snippets/commerce_badge.md' %]] +--- +description: A token is used in the registration process to send out unique emails to activate accounts. +--- -[[= product_name_com =]] uses the token system in the registration process to create a double opt-in possibility. +# Token + +[[= product_name =]] uses the token system in the registration process to create a double opt-in possibility. The token service can generate a unique token that is valid for a given time. After user registration, a token is created and stored in the database. diff --git a/docs/guide/user_management/user_management.md b/docs/guide/user_management/user_management.md index 2dbe6e8663..0f9c1af3a7 100644 --- a/docs/guide/user_management/user_management.md +++ b/docs/guide/user_management/user_management.md @@ -1,3 +1,7 @@ +--- +description: Set up user login methods, password rules, and customize user authentication. +--- + # User management ## Passwords @@ -11,11 +15,43 @@ To change password, the user must have the `user/password` permission. When the user requests a reset of a forgotten password, an email is sent to them with a token. It allows them to create a new password. +For information about how to create and configure the template, see [Add forgot password option](../content_rendering/layout/add_forgot_password.md) + The template for this email is located in `templates/Security/mail/forgot_user_password.html.twig` in `ezsystems/ezplatform-user`. -You can [customize it according to your needs](#customize-login-form). +You can [customize it according to your needs](../content_rendering/layout/add_login_form.md#customize-login-form). The validity of the password recovery token can be set using the `ezplatform.system.<siteaccess>.security.token_interval_spec` parameter. -By default it is set to `PT1H` (one hour). +By default, it is set to `PT1H` (one hour). + +### Revoking passwords + +In case of a security situation such as a data leakage, you may need to force users to change their passwords. +You can do it with the help of the `ibexa:user:expire-password` command, +which revokes the passwords for specific users, User Groups or users belonging to the chosen Content Type. + +To select which users to revoke passwords for, use one of the following options with the command: + +- `--user-id|-u` - the ID of the user. Accepts multiple user IDs +- `--user-group-id|-ug` - the ID of the User Group. Accepts multiple group IDs +- `--user-content-type-identifier|-ct` - the identifier of the user Content Type. Accepts multiple Content Types + +You can use the following additional options with the command: + +- `--force|-f` - commits the change, otherwise the command only performs a dry run +- `--iteration-count|-c` - defines how many users are fetched at once. Lowering this value helps with memory issues +- `--password-ttl|-t` - number of days after which new passwords expire. Used when the command enables password expiration for user Content Types that do not use it yet. + +For example, to revoke the passwords of all users of the `user` Content Type, run: + +``` bash +php bin/console ibexa:user:expire-password --user-content-type-identifier=user --force +``` + +To perform a dry run (without saving the results) of revoking passwords of all users from User Group 13, run: + +``` bash +php bin/console ibexa:user:expire-password --user-group-id=13 +``` ## Password rules @@ -33,7 +69,7 @@ To access the password settings: !!! tip There can be other Content Types that function as users, beyond the built-in User Content Type. - For details, see [User Identifiers](../config_repository.md#user-identifiers). + For details, see [User Identifiers](../configuration/config_repository.md#user-identifiers). ### Password attributes @@ -59,7 +95,11 @@ The notification will be displayed in the Back Office after login and in the Use You can set a rule that the password cannot be reused. You set it for the User Content Type in the **User account (ezuser)** Field Type's settings. -When this is set, the user will not be able to set a password that had been in use before. +When this is set, the user cannot type in the same password when it expires. +It has to be changed to a new one. + +This only checks the new password against the current one. +A password that has been used before can be used again. This rule is valid by default when password expiration is set. @@ -95,7 +135,7 @@ security: You can customize per User Field whether the email address used as a login method must be unique or not. To check that all existing User accounts have unique emails, -run the `ezplatform:user:audit_database` command. +run the `ibexa:user:audit-database` command. It will list all User accounts with duplicate emails. !!! caution @@ -106,7 +146,7 @@ It will list all User accounts with duplicate emails. This may happen if more than one account uses the same email address. Login through the User name will still be available. - To resolve the issues, run `ezplatform:user:audit_database` + To resolve the issues, run `ibexa:user:audit-database` and manually modify accounts that have duplicate emails. ### Login rules @@ -121,7 +161,7 @@ set `[a-z]+$` as **Username pattern**: ![Setting a User name pattern](../img/username_pattern.png) To check that all existing User accounts have names that fit the current pattern, -run the `ezplatform:user:audit_database` command. +run the `ibexa:user:audit-database` command. It will check all User accounts in the database and list those that do not fit the pattern. ## Registering new users @@ -140,161 +180,6 @@ ezplatform: group_id: <userGroupContentId> ``` -### Registration form templates - -You can use custom templates for the registration form and registration confirmation page. - -The templates are defined with the following configuration: - -``` yaml -ezplatform: - system: - default: - user_registration: - templates: - form: user/registration_form.html.twig - confirmation: user/registration_confirmation.html.twig -``` - -With this configuration you place the templates in `templates/user/registration_form.html.twig` and `templates/user/registration_confirmation.html.twig`. - -Example registration form: - -``` html+twig -{% extends no_layout is defined and no_layout == true ? view_base_layout : page_layout %} -{% block content %} - <section class="ez-content-edit"> - {{ form_start(form) }} - - {% for fieldForm in form.fieldsData %} - {% set fieldIdentifier = fieldForm.vars.data.fieldDefinition.identifier %} - <div class="col-md-6"> - {{ form_widget(fieldForm.value, { - 'contentData': form.vars.data - }) }} - </div> - {%- do fieldForm.setRendered() -%} - {% endfor %} - - <div class="row"> - <div class="col-md-4 col-md-offset-4"> - {{ form_widget(form.register, {'attr': { - 'class': 'btn btn-block btn-primary' - }}) }} - </div> - </div> - - {{ form_end(form) }} - </section> -{% endblock %} -``` - -Example confirmation form: - -``` html+twig -{% extends no_layout is defined and no_layout == true ? view_base_layout : page_layout %} -{% block content %} - <h1>Your account has been created</h1> - <p class="user-register-confirmation-message"> - Thank you for registering an account. You can now <a href="{{ path('login') }}">login</a>. - </p> -{% endblock %} -``` - -### Customize login form - -You can use a custom template for example to display information about password expiration -or to customize [other user management templates](#other-user-management-templates). - -If you need only to change a template, you can use the following configuration: - -```yaml -ezpublish: - system: - my_siteaccess: - user: - login_template: '@ezdesign/Security/login.html.twig' -``` - -In case of more advanced template customization, you can use a subscriber, -for example in `src/EventSubscriber/LoginFormViewSubscriber.php`: - -``` php hl_lines="23 35 40 42" -<?php - -declare(strict_types=1); - -namespace App\EventSubscriber; - -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\View\LoginFormView; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\Security\Core\Exception\CredentialsExpiredException; - -final class LoginFormViewSubscriber implements EventSubscriberInterface -{ - /** - * Returns an array of events this subscriber wants to listen to. - * - * @return string[] - */ - public static function getSubscribedEvents(): array - { - return [ - MVCEvents::PRE_CONTENT_VIEW => 'onPreContentView', - ]; - } - - public function onPreContentView(PreContentViewEvent $event): void - { - $view = $event->getContentView(); - - if (!($view instanceof LoginFormView)) { - return ; - } - - $view->addParameters([ - 'foo' => 'foo', - 'bar' => 'bar' - ]); - - if ($view->getLastAuthenticationException() instanceof CredentialsExpiredException) { - // View with instruction to unlock account - $view->setTemplateIdentifier('login/expired_credentials.html.twig'); - } - } -} -``` - -In the provided example, in line 23, the `PRE_CONTENT_VIEW` event is used -(for details, see [eZ Publish Core events](../content_rendering.md#ez-publish-core)). -You can also pass additional parameters to the view (line 35). -In this case, at the instance of exception (line 40), the subscriber displays the `expired_credentials.html.twig` template (line 42). - -Remember to provide a template and point to it in the subscriber -(in this case, in `templates/login/expired_credentials.html.twig`): - -```html+twig -{% extends '@ezdesign/Security/base.html.twig' %} - -{%- block content -%} - <h2 class="ez-login__header"> - {{ 'authentication.credentials_expired'|trans|desc('Your password has expired') }} - </h2> - <p> - {{ 'authentication.credentials_expired.message'|trans|desc( - 'For security reasons, your password has expired and needs to be changed. An email has been sent to you with instructions.' - ) }} - </p> - <p> - <a href="{{ path('ezplatform.user.forgot_password') }}" class="btn btn-primary ez-btn ez-btn--login"> - {{ 'authentication.credentials_expired.reset_password'|trans|desc('Reset password') }} - </a> - </p> -{%- endblock -%} -``` - ### Other user management templates You can also modify the following form templates: @@ -334,9 +219,9 @@ ezsettings.<siteaccess>.user_settings.templates.list ezsettings.<siteaccess>.user_settings.templates.update ``` -## Authenticating user with multiple user providers +## Authenticate user with multiple user providers -Symfony provides native support for [multiple user providers](https://symfony.com/doc/5.0/security/multiple_user_providers.html). +Symfony provides native support for [multiple user providers]([[= symfony_doc =]]/security/multiple_user_providers.html). This makes it easy to integrate any kind of login handlers, including SSO and existing third party bundles (e.g. [FR3DLdapBundle](https://github.com/Maks3w/FR3DLdapBundle), [HWIOauthBundle](https://github.com/hwi/HWIOAuthBundle), [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), [BeSimpleSsoAuthBundle](http://github.com/BeSimple/BeSimpleSsoAuthBundle), etc.). However, to be able to use *external* user providers with [[= product_name =]], a valid Platform user needs to be injected into the Repository. @@ -359,7 +244,7 @@ This token holds a `UserWrapped` instance which contains the originally matche Note that the *API user* is mainly used for permission checks against the repository and thus stays *under the hood*. -### Customizing the User class +### Customize the User class It is possible to customize the user class used by extending `ezpublish.security.login_listener` service, which defaults to `eZ\Publish\Core\MVC\Symfony\Security\EventListener\SecurityListener`. @@ -387,7 +272,7 @@ security: Symfony\Component\Security\Core\User\User: plaintext ``` -### Implementing the listener +### Implement the listener In the `config/services.yaml` file: diff --git a/docs/guide/vouchers/voucher_templates.md b/docs/guide/vouchers/voucher_templates.md index b6b6c7a18c..13989d4ed6 100644 --- a/docs/guide/vouchers/voucher_templates.md +++ b/docs/guide/vouchers/voucher_templates.md @@ -1,4 +1,8 @@ -# Voucher templates [[% include 'snippets/commerce_badge.md' %]] +--- +edition: commerce +--- + +# Voucher templates Vouchers are rendered in the `vouchers` Twig block that is included in `Silversolutions/Bundle/EshopBundle/Resources/views/Basket/show.html.twig`. diff --git a/docs/guide/vouchers/vouchers.md b/docs/guide/vouchers/vouchers.md index 8e92f3422f..f6533126aa 100644 --- a/docs/guide/vouchers/vouchers.md +++ b/docs/guide/vouchers/vouchers.md @@ -1,4 +1,10 @@ -# Vouchers [[% include 'snippets/commerce_badge.md' %]] +--- +description: Manage vouchers and provide your customers with discounts. + +edition: commerce +--- + +# Vouchers [[= product_name_com =]] supports vouchers that are managed in ERP. The customer can enter a voucher number in the basket. Then the voucher is sent to the ERP and, if it is valid, the customer gets a discount. diff --git a/docs/guide/workflow.md b/docs/guide/workflow.md deleted file mode 100644 index 6e6214d735..0000000000 --- a/docs/guide/workflow.md +++ /dev/null @@ -1,163 +0,0 @@ -# Editorial workflow - -The workflow functionality passes a Content item version through a series of stages. - -For example, an editorial workflow can pass a Content item from draft stage through design and proofreading. - -You can define different workflow in configuration. The workflow is permission-aware. - -## Workflow configuration - -Each workflow consists of stages and transitions between them. - -The following configuration defines a workflow where you can pass a draft to proofreading, and then to final approval. -The workflow is defined in the `config/packages/ezplatform.yaml` configuration file. - -``` yaml hl_lines="16 17 18 30 31 32 33 34 35 36 37" -ezplatform: - system: - # Workflow configuration is SiteAccess-aware - default: - workflows: - # Identifier of the workflow - custom_workflow: - name: Custom Workflow - matchers: - # Which Content Types can use this workflow, optional - content_type: article - # Which status of the Content item can use this workflow, optional. Available statuses are draft and published. - content_status: draft - # All stages the content goes through - stages: - draft: - label: Draft - color: '#f15a10' - proofread: - label: Proofread - color: '#5a10f1' - done: - label: Done - color: '#301203' - # Content items in this stage don't appear on My dashboard and in Review Queue. - last_stage: true - initial_stage: draft - # Available transitions between stages - transitions: - to_proofread: - from: draft - to: proofread - label: To proofreading - color: '#8888ba' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' - reviewers: - required: true - back_to_draft: - reverse: to_proofread - label: Back to draft - color: '#cb8888' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' - done: - from: proofread - to: done - label: Done - color: '#88ad88' - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#comment' -``` - -Each stage in the workflow has an identifier and can be assigned a label and a color (lines 16-18). - -Each transition also has an identifier. It must state between which stages it transitions, or be marked as `reverse` of a different transition. - -Transitions can also have labels, colors, and icons (lines 33-35). -If you don't define a custom color for a transition, a default setting will be used (`$ez-color-base-light`, i.e. `#878787`). - -You can require that a reviewer has to be selected when content is sent through a transition (lines 36-37). -The user will then not have the option to send the Content item without selecting a reviewer: - -![Required reviewer option in a workflow](img/workflow_reviewers.png) - -!!! note - - If a User does not have the permissions to view or edit the Content item, - you cannot select them as a reviewer. - -You can view all configured workflows in the Admin Panel by selecting **Workflow**. - -![Workflow in Admin Panel](img/workflow_panel.png) - -## Publishing content with workflow - -You can automatically publish a Content item once it goes through a specific transition. -To do so, configure the `publish` action for the transition: - -``` yaml -custom_workflow: - # ... - transitions: - # ... - done: - from: proofread - to: done - label: Done - color: '#88ad88' - actions: - publish: ~ -``` - -## Sending notifications - -When an editor selects a reviewer in the Back Office, your configuration can send a notification to the reviewer: - -``` yaml -custom_workflow: - # ... - stages: - # ... - proofread: - label: Proofread - color: '#5a10f1' - actions: - notify_reviewer: ~ -``` - -The notification is displayed in the user menu: - -![Notification about content to review](img/workflow_notification.png) - -## Permissions - -You can limit access to workflows at stage and transition level. -Use the `workflow/change_stage` Policy to grant a User permission to change stages in a specific workflow. - -This Policy can be limited with the [Workflow Transition Limitation](limitation_reference.md#workflow-transition-limitation) to only allow sending content in the allowed transition. - -For example, using the example above, a `workflow/change_stage` Policy with `WorkflowTransitionLimitation` set to `To Proofreading` -will allow the Technical team to send content to proofreading after they are done with technical review. - -You can also use the [Workflow Stage Limitation](limitation_reference.md#workflow-stage-limitation) together with the `content/edit` and `content/publish` Policy to limit the ability to edit content in specific stages. -For example, you can use it to only allow Technical team to edit content in the `technical` stage. - -## Workflow service - -Workflow makes use of the Symfony [Workflow Component](https://symfony.com/doc/5.0/components/workflow.html), -but special [[= product_name =]] treatment is covered in the Workflow service. - -The service implements the following methods: - -- `start` - places a Content item in a workflow -- `apply` - performs a transition -- `can` - checks if a transition is possible - -!!! tip - - The methods `apply` and `can` are the same as in Symfony Workflow, but the implementation in Workflow Service - extends them, for example by providing messages. - -You can also use the following methods to read information about workflow from the database: - -- `loadWorkflowMetadataForContent` - reads all workflow information about a Content item (as `WorkflowMetadata`) -- `loadWorkflowMetadataOriginatedByUser` - reads all workflow actions performed by the provided user (as `WorkflowMetadata`) -- `loadAllWorkflowMetadata` - reads all workflow information from the system - -`\EzSystems\EzPlatformWorkflow\Value\WorkflowMetadata` object contains all information about a workflow, such as ID, name, transitions and current stage. -`\EzSystems\EzPlatformWorkflow\Value\WorkflowMetadata::$workflow` gives you direct access to native Symfony Workflow object. diff --git a/docs/guide/workflow/add_custom_workflow_action.md b/docs/guide/workflow/add_custom_workflow_action.md new file mode 100644 index 0000000000..679911a913 --- /dev/null +++ b/docs/guide/workflow/add_custom_workflow_action.md @@ -0,0 +1,87 @@ +--- +description: Add custom actions that are performed during specific workflow transitions. +--- + +# Add custom workflow action + +Built-in workflow actions enable you to [automatically publish a Content item](workflow.md#content-publishing) +or to [send a notification to reviewers](workflow.md#notifications). + +You can also create custom actions that are called when content reaches a specific stage +or goes through a transition in a workflow. + +The following example shows how to configure two custom actions that send customized notifications. + +## Configure custom action + +Configure the first custom action in the following way: + +``` yaml hl_lines="15-18" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 0, 5) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 23, 36) =]] +``` + +The configuration indicates the name of the custom action (`legal_transition_action`). +`data` contains additional data that is passed to the action. In this case, it is a message to display. + +## Create event listener + +To define what the action does, create an event listener `src/EventListener/LegalTransitionListener.php`: + +``` php hl_lines="27 36" +[[= include_file('code_samples/workflow/custom_workflow/src/EventListener/LegalTransitionListener.php') =]] +``` + +This listener displays a notification bar at the bottom of the page when a Content item goes through the `to_legal` transition. + +The content of the notification is the message configured in `actions.legal_transition_action.data`. +To get it, access the metadata for this transition through `getActionMetadata()` (line 27). + +Register the listener as a service (in `config/services.yaml`): + +``` yaml +[[= include_file('code_samples/workflow/custom_workflow/config/custom_services.yaml', 0, 4) =]] +``` + +## Use custom transition value + +Line 36 in the listener above sets a custom result value for the transition. +You can use this value in other stages and transitions for this Content item, for example: + +``` yaml hl_lines="10 11" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 42, 53) =]] +``` + +The action indicated here is performed only if the result from the `legal_transition_action` is set to `true`. +Then, the following `src/EventListener/ApprovedTransitionListener` is called: + +``` php hl_lines="27" +[[= include_file('code_samples/workflow/custom_workflow/src/EventListener/ApprovedTransitionListener.php') =]] +``` + +Register this listener as a service: + +``` yaml +[[= include_file('code_samples/workflow/custom_workflow/config/custom_services.yaml', 0, 1) =]][[= include_file('code_samples/workflow/custom_workflow/config/custom_services.yaml', 4, 7) =]] +``` + +This listener also displays a notification, but in this case its content is taken from the message +that the user types when choosing the `Done` transition. + +The message is contained in the context of the action. + +`$event->getContext()` (line 27) gives you access to the context. +The context contains: + +- `$workflowId` - the ID of the current workflow +- `$message` - content of the user's message when sending the Content item through the transitions +- `$reviewerId` - ID of the user who was selected as a reviewer +- `$result` - an array of transition actions performed so far + +You can also modify the context using the `setContext()` method. +For example, you can override the message typed by the user: + +``` php +$new_context = $context; +$new_context['message'] = "This article went through proofreading"; +$event->setContext($new_context); +``` diff --git a/docs/guide/workflow/workflow.md b/docs/guide/workflow/workflow.md new file mode 100644 index 0000000000..60c4e9e508 --- /dev/null +++ b/docs/guide/workflow/workflow.md @@ -0,0 +1,200 @@ +--- +description: Workflow controls how Content items pass between stages and allows setting up editorial flows, for example for reviews and proofreading. +--- + +# Workflow + +The workflow functionality passes a Content item version through a series of stages. + +For example, an editorial workflow can pass a Content item from draft stage through +design and proofreading. + +By default, [[= product_name =]] comes pre-configured with a Quick Review workflow. +You can disable the default workflow and define different workflows in configuration. +Workflows are permission-aware. + +## Workflow configuration + +Each workflow consists of stages and transitions between them. + +The following example configuration defines a workflow where you can optionally pass a draft to be checked by the legal team. + +![Diagram of custom workflow](../img/workflow_custom_diagram.png) + +``` yaml +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 0, 32) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 37, 50) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 53, 61) =]] +``` + +### Matchers + +Matchers define when the workflow is used. Their configuration is optional. + +`content_type` contains an array of Content Type identifiers that use this workflow. + +`content_status` lists the statuses of Content items which fall under this workflow. The available values are: `draft` and `published`. + +``` yaml +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 6, 9) =]] +``` + +### Stages + +Each stage in the workflow has an identifier and can have a label and a color. + +The optional `last_stage` key indicates that content in this stage does not appear on the dashboard or in Review Queue. + +One stage, listed under `initial_stage`, is the one that the workflow starts with. + +``` yaml hl_lines="13 14" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 9, 23) =]] +``` + +### Transitions + +Each transition has an identifier and can have a label, a color, and an icon. + +A transition must state between which stages it transitions (lines 3-4), +or be `reverse` to a different transition (line 9). + +``` yaml hl_lines="3 4 9" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 23, 30) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 37, 42) =]] +``` + +### Reviewers + +When moving a Content item through a transition, the user can select a reviewer. +Assigning a reviewer is mandatory if you set `reviewers.required` to `true` for this transition. + +To be able to search for users for review, the user must have the content/read Policy without any Limitation, +or with a Limitation that allows reading users. +This means that, in addition to your own settings for this Policy, +you must add the /Users subtree to the Limitation and add Users in the [Content Type Limitation](limitation_reference.md#content-type-limitation). + +``` yaml hl_lines="8 9" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 23, 32) =]] +``` + +#### Notifications + +To ensure that the assigned reviewers get a notification of a transition, configure the `actions.notify_reviewer` action for a stage. + +``` yaml hl_lines="4 5" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 13, 18) =]] +``` + +The notification is displayed in the user menu: + +![Notification about content to review](../img/workflow_notification.png) + +#### Draft locking + +You can configure draft assignment in a way that when a user sends a draft to review, +only the first editor of the draft can either edit the draft or unlock it for editing, and no +other user can take it over. + +Use the [Version Lock Limitation](../limitation_reference.md#version-lock-limitation), +set to "Assigned only", together with the `content/edit` and `content/unlock` +Policies to prevent users from editing and unlocking drafts that are locked +by another user. + +### Content publishing + +You can automatically publish a Content item once it goes through a specific transition. +To do so, configure the `publish` action for the transition: + +``` yaml hl_lines="7 8" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 53, 61) =]] +``` + +### Disable Quick Review + +You can disable the default workflow, for example, if your project does not use +workflows, or Quick Review entries clog your database: + +``` yaml +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 0, 4) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 62, 66) =]] +``` + +## Custom actions + +Besides the built-in actions of publishing content and notifying the reviewers, you can also [create custom workflow actions](add_custom_workflow_action.md). + +## Workflow event timeline + +Workflow event timeline displays workflow transitions. + +You can also use it to render custom entries in the timeline, for example system alerts on workflows. + +### Custom entry type + +To add a custom entry type, create a custom class extending `EzSystems\EzPlatformWorkflow\WorkflowTimeline\Value\AbstractEntry`. +Use an `EzSystems\EzPlatformWorkflow\Event\TimelineEvents::COLLECT_ENTRIES` event to add your entries to the timeline. + +### Custom templates + +To provide custom templates for new event timeline entries, use the following configuration: + +``` yaml +ezplatform: + system: + default: + workflows_config: + timeline_entry_templates: + - { template: '@EzPlatformWorkflow/ezplatform_workflow/timeline/entries.html.twig', priority: 10 } +``` + +The template has to provide a block named `ez_workflow_timeline_entry_{ENTRY_IDENTIFIER}`. + +## Permissions + +You can limit access to workflows at stage and transition level. + +The `workflow/change_stage` Policy grants permission to change stages in a specific workflow. + +You can limit this Policy with the [Workflow Transition Limitation](../limitation_reference.md#workflow-transition-limitation) +to only allow sending content in the selected transition. + +For example, by using the example above, a `workflow/change_stage` Policy +with `WorkflowTransitionLimitation` set to `Approved by legal` allows a legal team to send content forward +after they are done with their review. + +You can also use the [Workflow Stage Limitation](../limitation_reference.md#workflow-stage-limitation) +together with the `content/edit` and `content/publish` Policies to limit the ability to edit content in specific stages. +For example, you can use it to only allow a legal team to edit content in the `legal` stage. + +## Workflow service + +Workflow uses the Symfony [Workflow Component]([[= symfony_doc =]]/components/workflow.html), +extended in the workflow service. + +The service implements the following methods: + +- `start` - places a Content item in a workflow +- `apply` - performs a transition +- `can` - checks if a transition is possible + +The methods `apply` and `can` are the same as in Symfony Workflow, +but the implementation in workflow service extends them, for example by providing messages. + +For examples of using the Workflow Service, see [PHP API documentation](../../api/public_php_api_managing_repository.md#workflow). + +## Validation + +### Validate form before workflow transition + +By default, sending content to the next stage of the workflow does not validate the form in UI, +so with the publish action, the form is not verified for errors in UI. +However, during the publish action, the sent form is validated in the service. + +Therefore, if there are any errors in the form, you return to the edit page but errors aren't triggered, +which can be confusing when you have two or more tabs. + +To enable form validation in UI before sending it to the next stage of the workflow, +add `validate: true` to the transitions of the stage. +In the example below the form is validated in two stages:` to_legal` and `done`: + +``` yaml hl_lines="14 27" +[[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 23, 42) =]][[= include_file('code_samples/workflow/custom_workflow/config/packages/workflows.yaml', 54, 62) =]] +``` + +You can check validation for a particular stage of the workflow even if the stage doesn't have any actions. diff --git a/docs/guidelines/Introduction.md b/docs/guidelines/Introduction.md deleted file mode 100644 index 3be2f4074b..0000000000 --- a/docs/guidelines/Introduction.md +++ /dev/null @@ -1,9 +0,0 @@ -# User Interface Guidelines - -This is a resource for developers, product managers, and designers, providing a unified language to build and customize eZ Platform admin user interface (aka Admin UI). We use it to simplify how we can build and offer a consistent interface across our content management platform. - -This section provides information about the specific User Interface components and resources that eZ Platform is using: - -- If you're interested in how to add a specific component, check them out under **Components**. -- For design aspects, you'll find under **Resources** the three main ones we are using: [Typography](resources/typography.md), [Colors](resources/colors.md) and [Icons](resources/icons.md) -- Last but not least, take a look at our **Design Principles** in case you want to know the [Accessibility standards](design_principles/Accessibility.md) we are applying. diff --git a/docs/guidelines/components/badges.md b/docs/guidelines/components/badges.md deleted file mode 100644 index 12297d97fa..0000000000 --- a/docs/guidelines/components/badges.md +++ /dev/null @@ -1,79 +0,0 @@ -# Badges - -We use badges to signify important information for users in the UI. - -##<div class="mgt-3 header-line">Badge with icon</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgt-minus-5 mgb-3">Badge with icon</div>** -<div class="ez-guidelines-badges mgt-3"> -[[code_example {html} -<span class="badge badge-warning ez-badge"> - <svg class="ez-icon ez-icon--small"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#warning"></use> - </svg> - Development -</span> -code_example]] -</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Contextual variations</div>** -<div class="ez-guidelines-badges ez-guidelines-badges--sample mgt-3"> -[[code_example {html} -<span class="badge badge-info ez-badge">GPL</span> -<span class="badge badge-warning ez-badge"> - <svg class="ez-icon ez-icon--small"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#warning"></use> - </svg> - Development -</span> -<span class="badge badge-danger ez-badge"> - <svg class="ez-icon ez-icon--small"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#warning"></use> - </svg> - Trial -</span> -code_example]] -</div> - -##<div class="mgt-3 header-line">Small badges</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgt-minus-5 mgb-3">Small badges</div>** -<div class="ez-guidelines-badges"> -[[code_example {html} -<span class="badge badge-secondary ez-badge ez-badge--small">Draft</span> -code_example]] -</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Contextual variations</div>** -<div class="ez-guidelines-badges"> -[[code_example {html} -<span class="badge badge-secondary ez-badge ez-badge--small">Draft</span> -<span class="badge badge-secondary ez-badge ez-badge--small">Quick review</span> -<span class="badge badge-secondary ez-badge ez-badge--small">Publish</span> -<span class="badge badge-warning ez-badge ez-badge--small">VIEWING</span> -code_example]] -</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Contextual variations - Editing view specific case</div>** -<div class="ez-guidelines-badges--sample-contextual"> -[[code_example {html} -<div class="ez-details-items"> -<span class="ez-details-items__connector">Editing:</span> -<span class="ez-badge ez-badge--small ez-details-items__pill ez-details-items__pill--content-type"> - <svg class="ez-icon ez-icon--small"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#article"></use> - </svg> - Article -</span> -<span class="ez-details-items__connector ez-details-items__connector--small">in</span> -<span class="ez-badge ez-badge--small ez-details-items__pill ez-details-items__pill--language">English (United Kingdom)</span> -<span class="ez-details-items__connector ez-details-items__connector--small">under</span> -<span class="ez-badge ez-badge--small ez-details-items__pill ez-details-items__pill--location">Home</span> -</div> -code_example]] -</div> diff --git a/docs/guidelines/components/buttons.md b/docs/guidelines/components/buttons.md deleted file mode 100644 index 1ee551bde5..0000000000 --- a/docs/guidelines/components/buttons.md +++ /dev/null @@ -1,152 +0,0 @@ -# Buttons - -We use button as main element for click function. Use a disabled attribute `disabled="disabled"` when a button can't be clicked. - -##<div class="mgt-3 header-line">Basic buttons</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgt-minus-5 mgb-3">Primary button</div>** -[[code_example {html} -<button type="button" class="btn btn-primary font-weight-bold">Confirm selection</button> -code_example]] - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Disabled state</div>** -[[code_example {html} -<button type="button" class="btn btn-primary font-weight-bold" disabled="disabled">Confirm selection</button> -code_example]] - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Neutral button</div>** -[[code_example {html} -<button type="button" class="btn btn-dark">Cancel</button> -code_example]] - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Secondary button</div>** -[[code_example {html} -<button type="button" class="btn btn-secondary">Save</button> -code_example]] - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Negative button</div>** -[[code_example {html} -<button type="button" class="btn btn-danger font-weight-bold">Send to trash</button> -code_example]] - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Outline button</div>** -[[code_example {html} -<button type="button" class="btn btn-outline-secondary">Select content item</button> -code_example]] - -##<div class="mgt-3 header-line">Set of two buttons</div> -<div class="mgt-minus-3 mgb-4">When combining two buttons together emphasize the preferred primary action button with a bolded font-weight.</div> -<div class="ez-guidelines-buttons__two-buttons ez-guidelines-sample"> -[[code_example {html} -<button type="button" class="btn btn-dark">Cancel</button> -<button type="button" class="btn btn-danger font-weight-bold">Send to trash</button> -code_example]] -<div class="ez-guidelines-sample__correct-block">Yes</div> -</div> - -### -<div class="ez-guidelines-buttons__two-buttons ez-guidelines-sample ez-guidelines-sample-negative mgb-1"> -[[code_example {html} -<button type="button" class="btn btn-dark">Cancel</button> -<button type="button" class="btn btn-danger">Send to trash</button> -code_example]] -<div class="ez-guidelines-sample__correct-block ez-guidelines-sample__correct-block-negative">No</div> -</div> - -##<div class="mgt-5 header-line">Wide buttons</div> -<div class="mgt-minus-3 mgb-4">Add class `ez-btn--wide` when in need of a wider button in the UI.</div> -[[code_example {html} -<button type="button" class="btn btn-primary ez-btn--wide font-weight-bold">Send for review</button> -code_example]] - -##<div class="mgt-5 header-line">Buttons with icons</div> -<div class="mgt-minus-3 mgb-4">Add the icon you want to your action button and specify the classes you need for it.</div> -<div class="ez-guidelines-buttons__button-icon"> -[[code_example {html} -<button type="button" class="btn btn-primary"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> - Create -</button> -code_example]] -</div> - -!!! note - We have used here a colored icon `ez-icon--light` as an example. If you want to dig more into icons check [Icons](../resources/icons.md) within Resources. - -##<div class="mgt-3 header-line">Buttons in menu bars</div> -<div class="mgt-minus-3 mgb-4">When adding a new action button that is part of the menu bars, `ez-side-menu` and `ez-context-menu`, we use the following two options: buttons and links that look like buttons as well.</div> - -###<div class="mgt-minus-5"></div> -**<div class="mgb-3">Buttons within Discovery menu bar</div>** -<div class="mgt-minus-3 mgb-4">Buttons within the Discovery menu bar, `ez-side-menu`, are styled as follows:</div> -<div class="ez-guidelines-buttons__side-menu"> -[[code_example {html} -<button type="button" class="btn btn-dark btn-block"> - <svg class="ez-icon ez-icon-browse"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#browse"></use> - </svg> - Browse -</button> -code_example]] -</div> - -###<div></div> -**<div class="mgt-minus-1 mgb-3">Link Buttons within Discovery menu bar</div>** -<div class="mgt-minus-3 mgb-4">Links that look like buttons within the Discovery menu bar, `ez-side-menu`, have the same styling as buttons.</div> -<div class="ez-guidelines-buttons__side-menu btn-block--white"> -[[code_example {html} -<a type="button" class="btn btn-dark btn-block"> - <svg class="ez-icon ez-icon-search"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#search"></use> - </svg> - Search -</a> -code_example]] -</div> - -###<div></div> -**<div class="mgt-minus-1 mgb-3">Buttons within Context menu bar</div>** -<div class="mgt-minus-3 mgb-4">Buttons within the Context menu bar, `ez-context-menu`, allow users to interact with their specific content item. They are styled as secondary buttons.</div> -<div class="ez-guidelines-buttons__side-menu"> -[[code_example {html} -<button type="button" class="btn btn-secondary btn-block"> - <svg class="ez-icon ez-icon-publish"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> - </svg> - Publish -</button> -code_example]] -</div> - -##<div class="mgt-3 header-line">Buttons in tables</div> -<div class="mgt-minus-3 mgb-4">When adding a new action button that is part of content listed within tables, `ez-table-header`, we use the following two options: buttons that are part of the header and the ones that are included within table rows.</div> - -###<div class="">Button in table header</div> -<div class="ez-guidelines-buttons__table-header"> -[[code_example {html} -<button type="button" class="btn btn-primary"> - <svg class="ez-icon ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> -</button> -code_example]] -</div> - -###<div class="mgt-3">Button in table rows</div> -<div class="ez-guidelines-buttons__table-row"> -[[code_example {html} -<button type="button" class="btn btn-icon"> - <svg class="ez-icon ez-icon-edit"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#edit"></use> - </svg> -</button> -code_example]] -</div> diff --git a/docs/guidelines/components/form_components.md b/docs/guidelines/components/form_components.md deleted file mode 100644 index b03b31c05b..0000000000 --- a/docs/guidelines/components/form_components.md +++ /dev/null @@ -1,135 +0,0 @@ -# Form components - -We list here all the basic form components that eZ Platform uses. - -Remember to add the `type` you may want for extending or customizing the form you are working on. - -##<div class="mgt-3 header-line">Label</div> -[[code_example {html} -<div class="form-group"> - <label class="ez-label">Identifier</label> -</div> -code_example]] - -##<div class="mgt-3 header-line">Label & Input box</div> -[[code_example {html} -<div class="form-group"> - <label for="example_location_update" class="ez-label">Identifier</label> - <input type="text" id="example_location_update" name="example_location_update" class="form-control"> -</div> -code_example]] - -##<div class="mgt-3 header-line">Input number</div> -<div class="ez-guidelines-formcomponent-number"> -[[code_example {html} -<div class="form-group"> - <label for="example_number" class="ez-label">Number</label> - <input type="number" id="example_number" name="example_number" class="form-control"> -</div> -code_example]] -</div> - -##<div class="mgt-3 header-line">Select list</div> -<div class="ez-guidelines-formcomponent"> -[[code_example {html} -<div class="form-group"> - <label for="example_order_by" class="ez-label">Order by</label> - <select id="example_order_by" name="example_order_by" class="form-control"> - <option>Content name</option> - <option>Location priority</option> - <option>Modification date</option> - <option>Publication date</option> - <option>Location path</option> - <option>Section identifier</option> - <option>Location depth</option> - <option>Location ID</option> - <option>Content ID</option> - </select> -</div> -code_example]] -</div> - -##<div class="mgt-3 header-line">Multiple Select list</div> -<div class="ez-guidelines-formcomponent-multiple"> -[[code_example {html} -<div class="form-group"> - <label for="example_role_assignment" class="ez-label">Group</label> - <select id="example_role_assignment" name="example_role_assignment" class="form-control" multiple="multiple"> - <option>Administrator Users</option> - <option>Anonymous Users</option> - <option>App Contributors</option> - <option>Editors</option> - <option>Members</option> - <option>Users</option> - </select> -</div> -code_example]] -</div> - -##<div class="mgt-3 header-line">Radio buttons & Checkboxes</div> -[[code_example {html} -<div class="form-check form-check-inline"> - <input type="radio" id="example_radio-button_one" name="example_radio-button" class="form-check-input"> - <label class="form-check-label radio-inline" for="example_radio-button_one">No limitations</label> -</div> -<div class="form-check form-check-inline"> - <input type="radio" id="example_radio-button_two" name="example_radio-button" class="form-check-input"> - <label class="form-check-label radio-inline" for="example_radio-button_two">No limitations</label> -</div> -code_example]] - -**<div class="mgt-3 mgb-3">Radio buttons group example:</div>** -<div class="ez-guidelines-formcomponent-radioExample"> -[[code_example {html} -<div class="form-check form-check-inline"> - <input type="radio" id="example_radio-option_one" name="example_radio-button" class="form-check-input"> - <label class="form-check-label radio-inline" for="example_radio-option_one">No limitations</label> -</div> -<div class="example-group"> - <div class="form-check form-check-inline"> - <input type="radio" id="example_radio-option_two" name="example_radio-button" class="form-check-input"> - <label class="form-check-label radio-inline" for="example_radio-option_two">Sections</label> - </div> - <select id="example_role_assignment" name="example_role_assignment" class="form-control" multiple="multiple"> - <option>Setup</option> - <option>Form</option> - <option>Media</option> - <option>Content</option> - <option>Extra section</option> - <option>Users</option> - </select> -</div> -<div class="example-group"> - <div class="form-check form-check-inline"> - <input type="radio" id="example_radio-button_three" name="example_radio-button" class="form-check-input"> - <label class="form-check-label radio-inline" for="example_radio-option_three">Subtree</label> - </div> - <button type="button" class="btn btn-secondary"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon--select-subtree"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#relations"></use> - </svg> - Select Subtree - </button> -</div> -code_example]] -</div> - -**<div class="mgt-5 mgb-3">Checkboxes single example:</div>** -[[code_example {html} -<div class="form-check form-check-inline"> - <input type="checkbox" id="example_checkbox-single" name="example_checkbox-single" class="form-check-input"> - <label class="form-check-label checkbox-inline" for="example_checkbox-single">Container</label> -</div> -code_example]] - -**<div class="mgt-5 mgb-3">Checkboxes group example:</div>** -[[code_example {html} -<div class="form-check form-check-inline"> - <input type="checkbox" id="example_checkbox-one" name="example_checkbox-one" class="form-check-input"> - <label class="form-check-label checkbox-inline" for="example_checkbox-one">Option 1</label> -</div> -<div class="form-check form-check-inline"> - <input type="checkbox" id="example_checkbox-two" name="example_checkbox-two" class="form-check-input"> - <label class="form-check-label checkbox-inline" for="example_checkbox-two">Option 2</label> -</div> -code_example]] \ No newline at end of file diff --git a/docs/guidelines/components/modals.md b/docs/guidelines/components/modals.md deleted file mode 100644 index cb18f308c3..0000000000 --- a/docs/guidelines/components/modals.md +++ /dev/null @@ -1,205 +0,0 @@ -# Modals - -<div>We use modals as components for displaying specific information directly related to the content item currently checked which needs interaction from the user.</div> - -##<div class="mgt-3">Introduction</div> -<div class="mgt-minus-3 mgb-5">Right below you have a modal (with interaction removed). This use case contains the three standard elements that we usually add to it: `modal-header`, `modal-body`, and `modal-footer`. All of our modals include a `modal-header`s with dismiss actions as standard best practice. Often (but not always) they also have explicit dismiss action buttons.</div> - -!!! note - The objective of this component is to showcase `modal` structures. Content inside them is representative, but not an accurate representation of a real use case. - - -**<div class="mgb-3">Modal sample</div>** -<div class="ez-guidelines-modals ez-guidelines-modals__sample mgt-3 mgb-5"> -[[code_example {html} -<div class="modal ez-modal ez-modal--send-to-trash" tabindex="-1" role="dialog"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <svg class="ez-icon ez-icon--medium" aria-hidden="true"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#discard"></use> - </svg> - </button> - </div> - <div class="modal-body"> - <p class="ez-modal-body__main">Are you sure you want to send this content item to trash?</p> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-dark">Cancel</button> - <button type="button" class="btn btn-danger font-weight-bold">Send to trash</button> - </div> - </div> - </div> -</div> -code_example]] -</div> - -!!! note - Remember when combining two buttons together to emphasize the preferred primary action button. We add class `font-weight-bold` for the primary button. Check more in [Buttons - Set of two buttons](buttons.md#set-of-two-buttons). - -##<div class="mgt-3 header-line">Send to Trash modals</div> -<div class="mgt-minus-3 mgb-5">Removing and deleting content are important actions in our application. Whereas in the former case the Content item is later retrievable from the Trash, in the second case the Content item is completely erased. Given the significant effects that these actions can have, their purpose have to be highlighted with a specific group of modals.</div> - -**<div class="mgb-3">Send to Trash</div>** -<div class="mgt-minus-3 mgb-5">Use it when removing content created by the user that will be sent to Trash.</div> -<div class="ez-guidelines-modals ez-guidelines-modals__send-to-trash mgt-3 mgb-5"> -[[code_example {html} -<!-- Button trigger modal --> -<button type="button" class="btn btn-secondary btn-modal-launcher" data-toggle="modal" data-target="#trash-location-modal">Launch Send to Trash modal</button> - -<!-- Modal --> -<div class="modal fade ez-modal ez-modal--send-to-trash show" id="trash-location-modal" tabindex="-1" role="dialog"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <svg class="ez-icon ez-icon--medium" aria-hidden="true"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#discard"></use> - </svg> - </button> - </div> - <div class="modal-body"> - <p class="ez-modal-body__main">Are you sure you want to send this content item to trash?</p> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-danger font-weight-bold">Send to Trash</button> - </div> - </div> - </div> -</div> -code_example]] -</div> - -All Send to Trash modals should have the same styling in order to help users quickly identify what the modal's message is about. We don't add `.modal-title` to the header and we position `.modal-footer` set of two buttons to the right. -##<div class="mgt-minus-2"></div> -<div class="ez-guidelines-sample ez-guidelines-sample--images"> -![Content structure](../img/UIG_Component_Modal_correct.png) -<div class="ez-guidelines-sample__correct-block">Yes</div> -</div> -##<div class="mgt-minus-2"></div> -<div class="ez-guidelines-sample ez-guidelines-sample--images ez-guidelines-sample-negative"> -![Content structure](../img/UIG_Component_Modal_wrong.png) -<div class="ez-guidelines-sample__correct-block ez-guidelines-sample__correct-block-negative">No</div> -</div> - -##<div class="mgt-3 header-line">Modals with headers</div> -<div class="mgt-minus-3">We recommend using them when you have to display listed information in tables and you want users to interact with the information contained within that specific table through action buttons (either the whole row or specific buttons). Hence, there is no need to add more interaction buttons, like in `.modal-footer`.</div> - -##<div class="mgt-minus-2"></div> -<div class="ez-guidelines-modals ez-guidelines-modals__with-header mgt-3"> -[[code_example {html} -<!-- Button trigger modal --> -<button type="button" class="btn btn-secondary btn-modal-launcher" data-toggle="modal" data-target="#view-notifications">Launch modal with header</button> - -<!-- Modal --> -<div class="modal fade ez-notifications-modal show" id="view-notifications" tabindex="-1" role="dialog" aria-modal="true"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title" data-notifications-total="(1)">Notifications </h5> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <svg class="ez-icon ez-icon--medium" aria-hidden="true"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#discard"></use> - </svg> - </button> - </div> - <div class="modal-body"> - <div class="ez-notifications-modal__spinner"> - <svg class="ez-icon ez-icon--medium" aria-hidden="true"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#spinner"></use> - </svg> - </div> - <div class="ez-notifications-modal__results"> - <table class="table n-table--notifications"> - <thead> - <tr> - <th>Type</th> - <th>Description</th> - <th>Date</th> - </tr> - </thead> - <tbody class="n-table__body"> - <tr class="n-notifications-modal__item fw-notification"> - <td class="n-notifications-modal__type"> - <span class="type__icon"> - <svg class="ez-icon ez-icon--review"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#review"></use> - </svg> - </span> - <span class="type__text"> - Content review request - </span> - </td> - <td class="n-notifications-modal__description"> - <p class="description__title"> - From: - <span class="description__title__item">Administrator user</span> - </p> - <p class="description__text">Can you check this? Thxs!</p> - </td> - <td class="n-notifications-modal__time"> - Sep 13, 2018, 2:58 PM - </td> - </tr> - </tbody> - </table> - </div> - </div> - </div> - </div> -</div> -code_example]] -</div> - -##<div class="mgt-3 header-line">Modals with headers and footers</div> -<div class="mgt-minus-3">We display them when users need to add or modify specific settings of a content item. This modal includes buttons within its `.modal-footer` due to the need of having an action button that submits changes added or defined.</div> - -##<div class="mgt-minus-2"></div> -<div class="ez-guidelines-modals ez-guidelines-modals__with-header-footer mgt-3"> -[[code_example {html} -<!-- Button trigger modal --> -<button type="button" class="btn btn-secondary btn-modal-launcher" data-toggle="modal" data-target="#ez-modal--custom-url-alias">Launch modal with header and footer</button> - -<!-- Modal --> -<div class="modal fade ez-modal ez-translation show" id="ez-modal--custom-url-alias" tabindex="-1" role="dialog" aria-modal="true"> - <div class="modal-dialog" role="document"> - <div class="modal-content"> - <div class="modal-header"> - <h3 class="modal-title">Create new translation</h3> - <button type="button" class="close" data-dismiss="modal" aria-label="Close"> - <svg class="ez-icon ez-icon--medium" aria-hidden="true"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#discard"></use> - </svg> - </button> - </div> - <div class="modal-body"> - <span class="ez-translation__title">Select a language for your new translation</span> - <div id="add-translation_language" class="ez-translation__language-wrapper"> - <div class="radio ez-translation__input-wrapper"> - <label class="ez-translation__label">French</label> - </div> - <div class="radio ez-translation__input-wrapper"> - <label class="ez-translation__label">German</label> - </div> - <div class="radio ez-translation__input-wrapper"> - <label class="ez-translation__label">Norwegian</label> - </div> - </div> - <span class="ez-translation__title">Base this translation on an existing translation</span> - <div id="add-translation_base_language" class="ez-translation__language-wrapper"> - <div class="radio ez-translation__input-wrapper"> - <label class="ez-translation__label">English (United Kingdom)</label> - </div> - </div> - </div> - <div class="modal-footer justify-content-center"> - <button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button> - <button type="button" class="btn btn-primary font-weight-bold" type="submit" name="custom_url_add[add]" disabled="true">Create</button> - </div> - </div> - </div> -</div> -code_example]] -</div> diff --git a/docs/guidelines/components/pagination.md b/docs/guidelines/components/pagination.md deleted file mode 100644 index 98a9480e5d..0000000000 --- a/docs/guidelines/components/pagination.md +++ /dev/null @@ -1,27 +0,0 @@ -# Pagination - -This is a component directly linked to tables. - -Every view has its own offset spacing defined. You can set it with [Bootstrap's spacing utilities](https://getbootstrap.com/docs/4.2/utilities/spacing/) according to the position of the table in the UI and if there is more content below the pagination component. - -##<div class="mgt-3 header-line">Pagination</div> -<div class="ez-guidelines-pagination"> -[[code_example {html} -<div class="row justify-content-center align-items-center mb-2 ez-pagination__spacing"> - <span class="ez-pagination__text"> - Viewing <strong>10</strong> out of <strong>11</strong> items - </span> -</div> -<div class="row justify-content-center align-items-center ez-pagination__btn mb-5"> - <ul class="pagination ez-pagination"> - <li class="page-item prev disabled"><span class="page-link">Back</span></li> - <li class="page-item active"><span class="page-link">1 <span class="sr-only">(current)</span></span></li> - <li class="page-item"><a class="page-link" href="#">2</a></li> - <li class="page-item next"><a class="page-link" href="#" rel="next">Next</a></li> - </ul> -</div> -code_example]] -</div> - -!!! note - Code example's background has been colored to show Pagination's full features. diff --git a/docs/guidelines/components/switchers.md b/docs/guidelines/components/switchers.md deleted file mode 100644 index fd8aeb3159..0000000000 --- a/docs/guidelines/components/switchers.md +++ /dev/null @@ -1,74 +0,0 @@ -# Switchers -We list here all the switchers that eZ Platform uses. - -##<div class="mgt-3 header-line">Switcher</div> -<div class="ez-guidelines-switcher"> -<p class="mgt-minus-3 mgb-3">We created `switcher` as a SASS @mixin function (check [`ezplatform-admin-ui/src/bundle/Resources/public/scss/_mixins.scss`](https://github.com/ezsystems/ezplatform-admin-ui/blob/master/src/bundle/Resources/public/scss/_mixins.scss)).</p> -<p class="mgb-5">Specify its size in the @mixin function: `@include checkbox-switcher($size);` in the corresponding `.scss` stylesheet. By default its size is set to 2rem.</p> -<div class="ez-guidelines-switcher"> -[[code_example {html} -<div class="ez-data-source"> - <label class="ez-data-source__label"> - <input class="ez-data-source__input" type="checkbox"> - <span class="ez-data-source__indicator"></span> - </label> -</div> -code_example]] -</div> - -##<div class="mgt-3 header-line">Switcher with icons</div> -####<div>**Primary color**</div> -<div class="ez-guidelines-switcher-icons ez-guidelines-switcher-icons--primary"> -[[code_example {html} -<label class="ez-checkbox-icon ez-page-info-bar-switcher is-checked"> - <svg class="ez-icon ez-icon--view"> - <use xlink:href="../../ez-icons.svg#view"></use> - </svg> - <svg class="ez-icon ez-icon--view-hide"> - <use xlink:href="../../ez-icons.svg#edit"></use> - </svg> - <input class="ez-checkbox-icon__checkbox" type="checkbox"> -</label> -code_example]] -</div> - -####<div class="mgt-5">**Secondary color**</div> -<div class="ez-guidelines-switcher-icons"> -[[code_example {html} -<label class="ez-checkbox-icon is-checked"> - <svg class="ez-icon ez-icon--view"> - <use xlink:href="../../ez-icons.svg#view"></use> - </svg> - <svg class="ez-icon ez-icon--view-hide"> - <use xlink:href="../../ez-icons.svg#view-hide"></use> - </svg> - <input class="ez-checkbox-icon__checkbox" type="checkbox"> -</label> -code_example]] -</div> - -!!! note - When using switchers with icons don't forget to add the corresponding `JS` script linked to the actions you want to trigger. - -##<div class="mgt-3 header-line">Preview switcher</div> -<div class="ez-guidelines-switcher-preview"> -[[code_example {html} -<div class="ez-preview-switcher"> - <button class="ez-preview-switcher__action ez-preview-switcher__action--selected"> - <svg class="ez-icon"> - <use xlink:href="../../ez-icons.svg#view-desktop"></use> - </svg> - </button> - <button class="ez-preview-switcher__action"> - <svg class="ez-icon"> - <use xlink:href="../../ez-icons.svg#view-tablet"></use> - </svg> - </button> - <button class="ez-preview-switcher__action"> - <svg class="ez-icon"> - <use xlink:href="../../ez-icons.svg#view-mobile"></use> - </svg> - </button> -</div> -code_example]] -</div> diff --git a/docs/guidelines/components/tables.md b/docs/guidelines/components/tables.md deleted file mode 100644 index 55468db6d3..0000000000 --- a/docs/guidelines/components/tables.md +++ /dev/null @@ -1,206 +0,0 @@ -# Tables - -We use tables as main element for displaying content information. - -##<div class="mgt-3 header-line">Basic tables</div> -**<div class="mgt-minus-2 mgb-3">Basic table</div>** -<div class="ez-guidelines-tables__with-header mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">System URL</div> -</div> -<table class="table"> - <thead> - <tr> - <th>URL</th> - <th>Language</th> - </tr> - </thead> - <tbody> - <tr> - <td>/places-tastes</td> - <td>English (United Kingdom)</td> - </tr> - </tbody> -</table> -code_example]] -</div> - -**<div class="mgb-3">Basic table with no content</div>** -<div class="ez-guidelines-tables__with-header mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">Reverse relations (content items using News)</div> -</div> -<p class="ez-table-no-content">This content item has no reverse relations</p> -code_example]] -</div> - -**<div class="mgb-3">Basic table and actions buttons in Header</div>** -<div class="ez-guidelines-tables__with-header mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">Translation manager</div> - <div> - <button type="button" class="btn btn-primary"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> - </button> - <button type="button" class="btn btn-danger" disabled="disabled"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-trash"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#trash"></use> - </svg> - </button> - </div> -</div> -<table class="table"> - <thead> - <tr> - <th></th> - <th>Language name</th> - <th>Language code</th> - </tr> - </thead> - <tbody> - <tr> - <td class="ez-checkbox-cell"> - <input type="checkbox"> - </td> - <td>English (United Kingdom)</td> - <td>eng-GB</td> - </tr> - </tbody> -</table> -code_example]] -</div> - -**<div class="mgb-3">Basic table and action buttons in header and within rows</div>** -<div class="ez-guidelines-tables__with-header ez-guidelines-tables__with-header--action-btn mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">Archived versions</div> - <div> - <button type="button" class="btn btn-danger" disabled="disabled"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-trash"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#trash"></use> - </svg> - </button> - </div> -</div> -<table class="table"> - <thead> - <tr> - <th></th> - <th>Version</th> - <th>Modified language</th> - <th>Contributor</th> - <th>Created</th> - <th>Last saved</th> - <th></th> - </tr> - </thead> - <tbody> - <tr> - <td class="ez-table__cell ez-table__cell--has-checkbox"> - <input type="checkbox"> - </td> - <td class="ez-table__cell">1</td> - <td class="ez-table__cell">English (United Kingdom)</td> - <td class="ez-table__cell">Administrator User</td> - <td class="ez-table__cell">May 03, 2019 15:05</td> - <td class="ez-table__cell">May 03, 2019 15:05</td> - <td class="ez-table__cell ez-table__cell--has-action-btns text-right"> - <button type="button" class="btn btn-icon mx-2 ez-btn--content-edit" title="Restore Archived Version"> - <svg class="ez-icon ez-icon-edit"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#archive-restore"></use> - </svg> - </button> - </td> - </tr> - </tbody> -</table> -code_example]] -</div> - -##<div class="mgt-3 header-line">Secondary table</div> -<div class="ez-guidelines-tables__with-header mgb-5"> -[[code_example {html} -<div class="ez-table-header ground-base"> - <div class="ez-table-header__headline">Location Content Swap</div> -</div> -<table class="table ez-table--no-border"> - <tbody> - <tr> - <td>Swap the content item at this location with another - <button type="button" class="btn btn-outline-secondary ml-5">Select content item</button> - </td> - </tr> - </tbody> -</table> -code_example]] -</div> - -##<div class="mgt-3 header-line">Notifications table</div> -<div class="ez-guidelines-tables__notifications mgb-5"> -[[code_example {html} -<table class="table n-table--notifications"> - <thead> - <tr> - <th>Type</th> - <th>Description</th> - <th>Date</th> - </tr> - </thead> - <tbody class="n-table__body"> - <tr class="n-notifications-modal__item fw-notification"> - <td class="n-notifications-modal__type"> - <span class="type__icon"> - <svg class="ez-icon ez-icon--review"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#review"></use> - </svg> - </span> - <span class="type__text"> - Content review request - </span> - </td> - <td class="n-notifications-modal__description"> - <p class="description__title"> - From: - <span class="description__title__item">Administrator user</span> - </p> - <p class="description__text">Can you check this? Thxs!</p> - <span class="description__read-more">Read more »</span> - </td> - <td class="n-notifications-modal__time"> - September 13, 2018 14:58 - </td> - </tr> - </tbody> -</table> -code_example]] -</div> - -!!! Note - This table is displayed within `.n-notifications-modal` modal. Styling relies on this class. - - -##<div class="mgt-3 header-line">List table</div> -<div class="ez-guidelines-tables__list mgb-5"> -[[code_example {html} -<table class="table ez-table ez-table--list"> - <thead> - <tr> - <th colspan="2"> - Composer - </th> - </tr> - </thead> - <tbody> - <tr> - <td>Minimum stability</td> - <td>stable</td> - </tr> - </tbody> -</table> -code_example]] diff --git a/docs/guidelines/components/tabs.md b/docs/guidelines/components/tabs.md deleted file mode 100644 index 2e7f9f02ca..0000000000 --- a/docs/guidelines/components/tabs.md +++ /dev/null @@ -1,61 +0,0 @@ -# Tabs - -We use Tabs component to offer structured views of information to users. - -###<div class="mgt-1">Standard tabs</div> -<div class="mgt-2 ez-guidelines-tabs"> -[[code_example {html} -<ul class="nav nav-tabs ez-tabs" role="tablist"> - <li class="nav-item"> - <a class="nav-link active" data-toggle="tab" href="#ez-tab-location-view-content" role="tab" aria-controls="ez-tab-location-view-content" aria-expanded="1">View</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-details" role="tab" aria-controls="ez-tab-location-view-details" aria-expanded="1">Details</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-versions" role="tab" aria-controls="ez-tab-location-view-versions" aria-expanded="1">Versions</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-locations" role="tab" aria-controls="ez-tab-location-view-locations" aria-expanded="1">Locations</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-relations" role="tab" aria-controls="ez-tab-location-view-relations" aria-expanded="1">Relations</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-translations" role="tab" aria-controls="ez-tab-location-view-translations" aria-expanded="1">Translations</a> - </li> - <li class="nav-item"> - <a class="nav-link" data-toggle="tab" href="#ez-tab-location-view-urls" role="tab" aria-controls="ez-tab-location-view-urls" aria-expanded="1">URL</a> - </li> -</ul> -code_example]] -</div> - -!!! note - Standard tabs component will have a darker background, `ez-header`, in the application that will enhance active tab's color contrast. - -###<div class="mgt-1">My dashboard tabs</div> -<div class="mgt-2 ez-guidelines-tabs ez-guidelines-tabs--dashboard"> -[[code_example {html} -<ul class="nav nav-tabs ez-tabs" role="tablist" id="ez-tab-list-dashboard-my"> - <li class="nav-item"> - <a class="nav-link active" id="ez-tab-label-dashboard-my" data-toggle="tab" href="#ez-tab-dashboard-my-my-drafts" role="tab" aria-controls="ez-tab-dashboard-my-my-drafts" aria-expanded="1">Drafts</a> - </li> - <li class="nav-item"> - <a class="nav-link" id="ez-tab-label-dashboard-my" data-toggle="tab" href="#ez-tab-dashboard-my-my-scheduled" role="tab" aria-controls="ez-tab-dashboard-my-my-scheduled" aria-expanded>My scheduled</a> - </li> - <li class="nav-item"> - <a class="nav-link" id="ez-tab-label-dashboard-my" data-toggle="tab" href="#ez-tab-dashboard-my-my-content" role="tab" aria-controls="ez-tab-dashboard-my-my-content" aria-expanded>Content</a> - </li> - <li class="nav-item"> - <a class="nav-link" id="ez-tab-label-dashboard-my" data-toggle="tab" href="#ez-tab-dashboard-my-my-media" role="tab" aria-controls="ez-tab-dashboard-my-my-media" aria-expanded>Media</a> - </li> - <li class="nav-item"> - <a class="nav-link" id="ez-tab-label-dashboard-my" data-toggle="tab" href="#ez-tab-dashboard-my-my-drafts-under-review" role="tab" aria-controls="ez-tab-dashboard-my-my-drafts-under-review" aria-expanded>Drafts under review</a> - </li> -</ul> -code_example]] -</div> - -!!! note - Specific styling classes for **My dashboard** tabs component will be applied automatically. diff --git a/docs/guidelines/components/tooltips.md b/docs/guidelines/components/tooltips.md deleted file mode 100644 index 5168e2b1c0..0000000000 --- a/docs/guidelines/components/tooltips.md +++ /dev/null @@ -1,149 +0,0 @@ -# Tooltips - -We use Tooltips to provide more information for the users to identify an element, such as a description of its function, and guide them to take action. - -**<div class="mgt-3">How they work</div>** -To add a new tooltip you have to add a `title` attribute with the custom tooltip text in the HTMLnode that should to have a tooltip. - -```html -<button type="button" class="class" title="your custom tooltip text">click me</button> -``` -Additionally, you can add following attributes: - -- `data-extra-classes` - an additional class for tooltip container `.tooltip` -- `data-tooltip-container-selector` - a css selector for a tooltip container (Bootstrap tooltip `data-container` attribute) - -Example of a tooltip with additional attributes: - -```html -<button type="button" class="class" title="your custom tooltip text" data-extra-classes="additional_class" data-tooltip-container-selector="selector"> - click me -</button> -``` - -You can also add tooltip helpers to the JavaScript `eZ.helpers` object: - -- `eZ.helpers.tooltips.parse(optional HTMLnode)` - creates a tooltip. Equivalent of `$(selector).tooltip()`. HTMLnode will execute `querySelectorAll` on this object, a `window.document` is default value. -- `eZ.helpers.tooltips.hideAll(optional HTMLnode)` - closes all tooltips. Equivalent of `$(selector).tooltip('hide')`. HTMLnode will execute `querySelectorAll` on this object, a `window.document` is default value. - -A tooltip is displayed automatically when the user hovers the pointer over an action button, and removed when the user clicks the control or move the mouse. The tooltip can also be triggered by focusing on that specific element with the keyboard (tab key), or tapping on it. - -!!! note - Our application relies on [Bootstrap's tooltips](https://getbootstrap.com/docs/4.1/components/tooltips/). Check out their documentation for basic aspects regarding Tooltips configuration. - -**<div class="mgt-3">Behavior & motion</div>** -A tooltip is displayed upon hovering over an action button. It shows up over 150ms and fades out over 75ms. - -##<div class="mgt-3 header-line">Examples</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgt-minus-5 mgb-3">Tooltips in buttons with icons</div>** -<div class="ez-guidelines-tooltips-sample"> -[[code_example {html} -<button type="button" class="btn btn-primary" title="Select content"> - <svg class="ez-icon ez-icon--medium ez-icon--light"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#relations"></use> - </svg> - Select content -</button> -<button type="button" class="btn ez-btn btn-secondary" title="Add author"> - <svg class="ez-icon ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> -</button> -<button type="button" class="btn ez-btn btn-dark" title="Open new tab"> - <svg class="ez-icon ez-icon-open-newtab"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#open-newtab"></use> - </svg> -</button> -code_example]] -</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Tooltips in Table header</div>** -<div class="ez-guidelines-tables__with-header mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">Translation manager</div> - <div> - <button type="button" class="btn btn-primary" title="Add translation"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> - </button> - <button type="button" class="btn btn-danger" disabled="disabled"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-trash"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#trash"></use> - </svg> - </button> - </div> -</div> -<table class="table"> - <thead> - <tr> - <th></th> - <th>Language name</th> - <th>Language code</th> - </tr> - </thead> - <tbody> - <tr> - <td class="ez-checkbox-cell"> - <input type="checkbox"> - </td> - <td>English (United Kingdom)</td> - <td>eng-GB</td> - </tr> - </tbody> -</table> -code_example]] -</div> - -###<div class="mgt-minus-2"></div> -**<div class="mgb-3">Tooltips in Table rows</div>** -<div class="ez-guidelines-tables__with-header ez-guidelines-tables__with-header--action-btn mgb-5"> -[[code_example {html} -<div class="ez-table-header"> - <div class="ez-table-header__headline">Archived versions</div> - <div> - <button type="button" class="btn btn-danger" disabled="disabled"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-trash"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#trash"></use> - </svg> - </button> - </div> -</div> -<table class="table"> - <thead> - <tr> - <th></th> - <th>Version</th> - <th>Modified language</th> - <th>Contributor</th> - <th>Created</th> - <th>Last saved</th> - <th></th> - </tr> - </thead> - <tbody> - <tr> - <td class="ez-table__cell ez-table__cell--has-checkbox"> - <input type="checkbox"> - </td> - <td class="ez-table__cell">1</td> - <td class="ez-table__cell">English (United Kingdom)</td> - <td class="ez-table__cell">Administrator User</td> - <td class="ez-table__cell">May 03, 2019 15:05</td> - <td class="ez-table__cell">May 03, 2019 15:05</td> - <td class="ez-table__cell ez-table__cell--has-action-btns text-right"> - <button type="button" class="btn btn-icon mx-2 ez-btn--content-edit" title="Restore archived version"> - <svg class="ez-icon ez-icon-edit"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#archive-restore"></use> - </svg> - </button> - </td> - </tr> - </tbody> -</table> -code_example]] -</div> \ No newline at end of file diff --git a/docs/guidelines/design_principles/Accessibility.md b/docs/guidelines/design_principles/Accessibility.md deleted file mode 100644 index 388e35a3ce..0000000000 --- a/docs/guidelines/design_principles/Accessibility.md +++ /dev/null @@ -1,23 +0,0 @@ -# Accessibility - -### Overview and limitations - -The overall accessibility of any project built with eZ Platform depends in large part on the author's markup, additional styling, and scripting they've included. However, provided that these have been implemented correctly, it should be perfectly possible to create websites and applications with Bootstrap that fulfill WCAG 2.0 (A/AA/AAA), Section 508 and similar accessibility standards and requirements. - -### Markup - -This documentation aims to provide developers with best practice examples to demonstrate the use of eZ Platform itself and illustrate appropriate semantic markup, including ways in which potential accessibility concerns can be addressed. - -### Interactive components - -Regarding interactive components — such as modal dialogs, dropdown menus and custom tooltips — we rely on Bootstrap's framework that designed them to work for touch, mouse and keyboard users. Through the use of relevant WAI-ARIA roles and attributes, these components should also be understandable and operable using assistive technologies (such as screen readers). - -As standard practice for users of assistive technologies, as well as for other users using keyboard for website navigation, we add <code>tab-index="-1"</code> to a HTML element/node wherever needed, when a click handler had been attached to it. It's applicable to all other HTML elements different than <code>button</code> and <code>a</code>. - -For more information regarding this topic, please check [Bootstrap's Accessibility](https://getbootstrap.com/docs/4.0/getting-started/accessibility/) section. - -### Color contrast - -All color combinations have been previously tested to meet at least one or more of the minimum ratios that the [WCAG 2.0](https://www.w3.org/TR/WCAG20/) specifies for text and background color combinations. This compliance will help users who are colorblind or have low vision to better interact with our app, as well as it will benefit usability and readability for all users. - -When planning a new color combination, check beforehand compliance with these standards. We suggest [Colour Contrast Analyser](https://www.paciellogroup.com/resources/contrastanalyser/) app. diff --git a/docs/guidelines/design_principles/Philosophy.md b/docs/guidelines/design_principles/Philosophy.md deleted file mode 100644 index a19b825db2..0000000000 --- a/docs/guidelines/design_principles/Philosophy.md +++ /dev/null @@ -1,43 +0,0 @@ -# Philosophy - -The design philosophy underpins everything we do as a Product and Engineering Teams. It informs the way our products look, the way they behave and the way we work as a team. - -### Modern - -We design and build a new generation of content management software. This has been the result of marathon 3 years of collaboration. - -### Simple - -Our interface is the result of our vision: to deliver tools that simplify the way people — developers, designers, editors and marketers — interact with content. - -### Content-centric - -eZ's content engine provides a solid mechanism to reuse content. Across channels and contexts, users can easily repackage content for simple integration, and continuous optimization. - -### Multi - Platform / Channel / Language - -A flexible UI that accounts for different languages as well as format time and date conventions to support global variations. - -### Expert / Beginner - -To keep our focus on improving the experience of who ever uses eZ, from developers and designers to content editors, marketers and end users. - -### Expandable - -eZ Platform is designed to be extended by developers with new features and functionality. The content editing interface is open and allows developers to create custom features. - -### Flexible - -Opportunities for customization are limitless, so customers that use eZ are well positioned to continually adapt to their end users' ever-changing needs. - -### User-centered - -The experience must be personal and adapt to the constantly changing expectations of the end user. A great experience enhances the efficiency of any project. - -### Open sourced - -Members of the Community play a key role in the continuous development of eZ Platform. We are committed to try to handle incoming community contributions. - -### Last... - -But not least. We put quality first. diff --git a/docs/guidelines/ez-icons.svg b/docs/guidelines/ez-icons.svg deleted file mode 100644 index 2e912f590a..0000000000 --- a/docs/guidelines/ez-icons.svg +++ /dev/null @@ -1,711 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> - <symbol id="about-info" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18 24.333c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2v0c1.105 0 2 0.895 2 2v0zM17.333 18.973v-1.9c2.907-0.667 4-3.593 4-5.63 0-2.32-1.42-5.777-5.333-5.777-2.79 0-4.747 1.787-5.24 4.777-0.011 0.065-0.018 0.14-0.018 0.217 0 0.737 0.597 1.334 1.334 1.334 0.66 0 1.209-0.48 1.316-1.11l0.001-0.008c0.417-2.543 2.067-2.543 2.607-2.543 2.503 0 2.667 2.597 2.667 3.11-0.029 0.608-0.167 1.177-0.395 1.697l0.012-0.031c-0.44 0.98-1.167 1.437-2.283 1.437-0.736 0-1.333 0.597-1.333 1.333v0 3.083c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 8.837 7.163 16 16 16v0c8.833-0.009 15.991-7.167 16-15.999v-0.001zM29.333 16c0 7.364-5.97 13.333-13.333 13.333s-13.333-5.97-13.333-13.333c0-7.364 5.97-13.333 13.333-13.333v0c7.364 0 13.333 5.97 13.333 13.333v0z"></path> - </symbol> - <symbol id="about" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20.667 18.667h-1.333v-8.667c0-0.736-0.597-1.333-1.333-1.333v0h-6.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.333v7.333h-1.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM15.333 11.333h1.333v7.333h-1.333zM18 6c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2v0c1.105 0 2 0.895 2 2v0zM6.667 32c-0.231-0-0.447-0.058-0.637-0.16l0.007 0.004c-0.422-0.23-0.703-0.669-0.703-1.175 0-0.001 0-0.001 0-0.002v0-5.333h-2.333c-0.029 0.001-0.064 0.002-0.098 0.002-1.603 0-2.903-1.3-2.903-2.903 0-0.035 0.001-0.069 0.002-0.103l-0 0.005v-19.333c-0.001-0.029-0.002-0.064-0.002-0.098 0-1.603 1.3-2.903 2.903-2.903 0.035 0 0.069 0.001 0.103 0.002l-0.005-0h26.027c0.015-0 0.033-0 0.051-0 1.614 0 2.923 1.309 2.923 2.923 0 0.027-0 0.054-0.001 0.081l0-0.004v19.333c0.001 0.029 0.002 0.064 0.002 0.098 0 1.603-1.3 2.903-2.903 2.903-0.035 0-0.069-0.001-0.103-0.002l0.005 0h-11.93l-9.667 6.443c-0.207 0.14-0.462 0.223-0.737 0.223h-0zM3 2.667c-0.263 0-0.333 0.070-0.333 0.333v19.333c0 0.263 0.070 0.333 0.333 0.333h3.667c0.736 0 1.333 0.597 1.333 1.333v0 4.177l7.927-5.287c0.208-0.14 0.463-0.223 0.739-0.223 0 0 0.001 0 0.001 0h12.333c0.253 0 0.333-0.080 0.333-0.333v-19.333c0-0.307-0.14-0.333-0.307-0.333z"></path> - </symbol> - <symbol id="airtime" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.293 19.35c-0.735-0.001-1.331-0.598-1.331-1.333 0-0.367 0.148-0.699 0.388-0.94l-0 0c1.367-1.384 2.211-3.287 2.211-5.387s-0.844-4.003-2.212-5.387l0.001 0.001c-0.254-0.243-0.412-0.585-0.412-0.964 0-0.736 0.597-1.333 1.333-1.333 0.381 0 0.725 0.16 0.968 0.417l0.001 0.001c1.846 1.866 2.987 4.433 2.987 7.267s-1.141 5.401-2.988 7.268l0.001-0.001c-0.242 0.243-0.576 0.393-0.946 0.393-0 0-0.001 0-0.001 0h0zM10.683 19c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0c-1.391-1.385-2.252-3.302-2.252-5.42 0-2.088 0.836-3.981 2.193-5.361l-0.001 0.001c0.236-0.241 0.382-0.571 0.382-0.935 0-0.737-0.597-1.334-1.334-1.334-0.373 0-0.709 0.153-0.951 0.399l-0 0c-1.823 1.862-2.949 4.413-2.949 7.228 0 2.854 1.157 5.439 3.028 7.309l0 0c0.241 0.241 0.574 0.389 0.942 0.389s0.7-0.149 0.942-0.389l-0 0zM27.237 23.060c2.94-2.896 4.762-6.92 4.762-11.37 0-4.413-1.792-8.408-4.689-11.297l-0-0c-0.243-0.255-0.586-0.414-0.966-0.414-0.736 0-1.333 0.597-1.333 1.333 0 0.381 0.159 0.724 0.415 0.967l0.001 0.001c2.413 2.406 3.907 5.734 3.907 9.41 0 3.708-1.519 7.061-3.968 9.471l-0.002 0.002c-0.242 0.241-0.391 0.575-0.391 0.943s0.149 0.702 0.391 0.943v0c0.241 0.242 0.575 0.391 0.943 0.391s0.702-0.149 0.943-0.391v0zM6.667 23.047c0.237-0.24 0.383-0.571 0.383-0.935 0-0.373-0.153-0.71-0.399-0.951l-0-0c-2.449-2.412-3.967-5.765-3.967-9.471 0-3.677 1.493-7.005 3.907-9.412l0-0c0.228-0.239 0.368-0.563 0.368-0.919 0-0.736-0.597-1.333-1.333-1.333-0.356 0-0.679 0.139-0.918 0.366l0.001-0.001c-2.897 2.889-4.689 6.883-4.689 11.297 0 4.45 1.822 8.474 4.76 11.368l0.002 0.002c0.241 0.242 0.575 0.391 0.943 0.391s0.702-0.149 0.943-0.391v0zM20 11.687c0-0.001 0-0.003 0-0.005 0-2.209-1.791-4-4-4s-4 1.791-4 4c0 1.731 1.1 3.206 2.639 3.762l0.028 0.009v15.213c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-15.213c1.565-0.565 2.665-2.037 2.667-3.766v-0z"></path> - </symbol> - <symbol id="align-center" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.667 30.667h-25.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h25.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM23.333 22.667c0-0.736-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333v0zM26.667 16c0-0.736-0.597-1.333-1.333-1.333v0h-18.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h18.667c0.736 0 1.333-0.597 1.333-1.333v0zM23.333 9.333c0-0.736-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333v0zM30 2.667c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="align-justify" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.667 4h-25.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h25.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM30 9.333c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0zM30 16c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0zM30 22.667c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0zM30 29.333c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="align-left" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.667 30.667h-25.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h25.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM16.917 22.667c0-0.736-0.597-1.333-1.333-1.333v0h-12.25c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12.25c0.736 0 1.333-0.597 1.333-1.333v0zM23.333 16c0-0.736-0.597-1.333-1.333-1.333v0h-18.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h18.667c0.736 0 1.333-0.597 1.333-1.333v0zM16.667 9.333c0-0.736-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333v0zM30 2.667c0-0.736-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h25.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="align-right" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M2 29.333c0-0.736 0.597-1.333 1.333-1.333v0h25.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-25.333c-0.736 0-1.333-0.597-1.333-1.333v0zM16.417 24h12.25c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-12.25c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0zM10 17.333h18.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-18.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0zM16.667 10.667h12c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0zM3.333 4h25.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-25.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0z"></path> - </symbol> - <symbol id="approved" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16.177 22c-.357 0-.681-.141-.92-.37L7.08 13.817a1.333 1.333 0 1 1 1.84-1.926l7.23 6.907L29.717 5.065a1.334 1.334 0 1 1 1.9 1.874l-14.49 14.667a1.33 1.33 0 0 1-.949.397h-.001zM32 16c0-1.289-.151-2.542-.435-3.743l.022.11a1.333 1.333 0 1 0-2.585.61l-.001-.007c.224.915.353 1.966.353 3.047 0 7.364-5.97 13.333-13.333 13.333S2.688 23.38 2.688 16.017c0-7.364 5.97-13.333 13.333-13.333a13.28 13.28 0 0 1 8.493 3.054l-.022-.018a1.333 1.333 0 0 0 1.702-2.051l-.003-.002A15.907 15.907 0 0 0 16.001.002c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16V16z"></path> - </symbol> - <symbol id="archive-restore" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M11.333 22.667c0-0.736 0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-6.667c-0.736 0-1.333-0.597-1.333-1.333v0zM31.813 11.333l-4.147 7.030v11.71c-0.004 1.065-0.868 1.927-1.933 1.927-0.001 0-0.002 0-0.004 0h-19.46c-0.001 0-0.002 0-0.003 0-1.065 0-1.93-0.862-1.933-1.926v-11.71l-4.147-7.030c-0.159-0.217-0.255-0.489-0.255-0.784 0-0.736 0.597-1.333 1.333-1.333 0.537 0 1.001 0.318 1.212 0.776l0.003 0.008 3.947 6.667h19.147l3.947-6.667c0.214-0.466 0.678-0.784 1.215-0.784 0.736 0 1.333 0.597 1.333 1.333 0 0.295-0.096 0.567-0.258 0.788l0.003-0.004zM25 19.333h-18v10h18zM21.28 4.393l-3.717-3.727c-0.399-0.402-0.951-0.65-1.562-0.65s-1.163 0.249-1.562 0.65l-0 0-3.72 3.727c-0.239 0.241-0.387 0.573-0.387 0.94 0 0.737 0.597 1.334 1.334 1.334 0.37 0 0.705-0.151 0.947-0.394l0-0 2.053-2.067v9.127c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.13l2.053 2.070c0.242 0.243 0.577 0.394 0.947 0.394 0.737 0 1.334-0.597 1.334-1.334 0-0.367-0.148-0.699-0.387-0.94l0 0z"></path> - </symbol> - <symbol id="article" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M8.667 16.667c0-0.736 0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-6.667c-0.736 0-1.333-0.597-1.333-1.333v0zM10 22.667h12c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0zM22 24.667h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16z"></path> - </symbol> - <symbol id="assign-section" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg"> - <path d="M70,96A16,16,0,1,1,86,80,16,16,0,0,1,70,96Zm0-24a8,8,0,1,0,8,8A8,8,0,0,0,70,72ZM42,16A16,16,0,1,1,26,0,16,16,0,0,1,42,16ZM84.69,47,74.4,56.32a6.59,6.59,0,0,1-8.8,0L55.31,47A4,4,0,1,1,60.69,41L66,45.86V23.91A4,4,0,0,0,61.87,20H38a4,4,0,0,1,0-8H61.87A12,12,0,0,1,74,23.91v22L79.31,41A4,4,0,1,1,84.69,47Z"/> - </symbol> - <symbol id="assign-user" viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg"> - <path d="M50,34a6.45,6.45,0,0,1-1.7,4.4L39,48.69A4,4,0,0,1,33,43.31L37.86,38h-11A2.86,2.86,0,0,0,24,40.85V64.53a16,16,0,1,1-8,0V40.85A10.87,10.87,0,0,1,26.85,30h11L33,24.69A4,4,0,1,1,39,19.31L48.32,29.6A6.52,6.52,0,0,1,50,34ZM90.55,61.64A6,6,0,0,1,85.79,64H58.23a6,6,0,0,1-4.75-2.34A7.1,7.1,0,0,1,52.12,56a14.45,14.45,0,0,1,.29-1.91c.19-.84,2.92-13,5.28-16.63a13,13,0,0,1,9.78-5.78,16,16,0,1,1,8.64.1c3.34.46,7.73,2.07,9.94,6.11,2.76,4.15,5.62,16.83,5.65,17a2,2,0,0,1,.06.34l.09.67A7.09,7.09,0,0,1,90.55,61.64ZM72,24.36a8.18,8.18,0,1,0-8-8.18A8.1,8.1,0,0,0,72,24.36ZM83.76,56c-1.1-4.87-3.37-12.25-4.35-13.64a4,4,0,0,1-.34-.56C78.16,40,75,39.65,73.9,39.65H68.43a5.09,5.09,0,0,0-4,2.2c-.84,1.3-2.91,8.25-4.17,14l0,.1v0Z"/> - </symbol> - <symbol id="author" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M10.667 16c-4.418 0-8-3.582-8-8s3.582-8 8-8c4.418 0 8 3.582 8 8v0c0 4.418-3.582 8-8 8v0zM10.667 2.667c-2.946 0-5.333 2.388-5.333 5.333s2.388 5.333 5.333 5.333c2.946 0 5.333-2.388 5.333-5.333v0c0-2.946-2.388-5.333-5.333-5.333v0zM12 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-8c-0.005-0.015-0.008-0.033-0.008-0.052s0.003-0.036 0.008-0.053l-0 0.001c0.020-0.073 0.035-0.159 0.040-0.247l0-0.003c0.017-0.207 0.048-0.396 0.092-0.579l-0.005 0.026v-0.040c0.707-2.873 1.943-6.697 2.54-7.517 1.173-1.58 3.297-1.537 3.31-1.537h3.103c0.371 0.004 0.731 0.035 1.083 0.089l-0.043-0.005c0.058 0.009 0.125 0.014 0.193 0.014 0.737 0 1.334-0.597 1.334-1.334 0-0.669-0.492-1.222-1.133-1.319l-0.007-0.001c-0.432-0.065-0.934-0.105-1.445-0.11l-0.005-0h-3c-0.41 0-3.6 0-5.52 2.63-1.29 1.757-2.87 8.037-2.977 8.437-0.071 0.265-0.129 0.583-0.161 0.907l-0.002 0.026c-0.040 0.178-0.063 0.383-0.063 0.593 0 0.647 0.217 1.243 0.581 1.72l-0.005-0.007c0.477 0.625 1.222 1.025 2.060 1.027h8.030c0.736 0 1.333-0.597 1.333-1.333v0zM16.263 31.883l6.023-2.703c0.262-0.122 0.473-0.316 0.613-0.557l0.003-0.007 5.877-10.427c0.108-0.189 0.171-0.414 0.171-0.655 0-0.739-0.599-1.338-1.338-1.338-0.498 0-0.933 0.272-1.163 0.676l-0.003 0.007-5.667 10.047-3.937 1.773-0.49-4.48 5.573-9.94c0.107-0.188 0.17-0.413 0.17-0.653 0-0.739-0.599-1.337-1.337-1.337-0.498 0-0.933 0.273-1.163 0.677l-0.003 0.007-5.783 10.333c-0.096 0.18-0.153 0.393-0.153 0.62 0 0.056 0.004 0.112 0.010 0.166l-0.001-0.006 0.737 6.727c0.054 0.431 0.306 0.793 0.66 0.997l0.006 0.003c0.191 0.111 0.42 0.177 0.664 0.177 0.198 0 0.386-0.043 0.554-0.12l-0.008 0.003zM20.187 25.423l5.617-10c0.107-0.188 0.17-0.413 0.17-0.653 0-0.739-0.599-1.337-1.337-1.337-0.498 0-0.933 0.273-1.163 0.677l-0.003 0.007-5.61 10.003c-0.108 0.188-0.171 0.413-0.171 0.654 0 0.497 0.272 0.93 0.675 1.16l0.007 0.003c0.188 0.107 0.413 0.17 0.652 0.17 0.498 0 0.932-0.273 1.161-0.677l0.003-0.007zM30.637 9.583l-4.857-2.637c-0.361-0.203-0.793-0.322-1.253-0.322-0.257 0-0.505 0.037-0.739 0.107l0.018-0.005c-0.582 0.17-1.053 0.558-1.328 1.068l-0.006 0.012-0.773 1.507c-0.096 0.18-0.152 0.393-0.152 0.619 0 0.503 0.278 0.94 0.689 1.168l0.007 0.003 6.903 3.747c0.183 0.101 0.401 0.16 0.633 0.16 0.001 0 0.002 0 0.004 0h-0c0.138-0.001 0.271-0.021 0.396-0.059l-0.010 0.003c0.348-0.11 0.628-0.349 0.79-0.659l0.003-0.007 0.79-1.507c0.58-1.113 0.087-2.547-1.117-3.197z"></path> - </symbol> - <symbol id="b2b" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 2.333c-0.736 0-1.333 0.597-1.333 1.333v0 2h-2.333l-5.067-2.523c-0.175-0.090-0.381-0.143-0.6-0.143h-5.333c-0.001 0-0.003 0-0.004 0-0.368 0-0.701 0.149-0.942 0.39v0l-2.273 2.277h-10.113v-2c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 17.417c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2h0.88c-0.008 0.077-0.012 0.166-0.012 0.256 0 0.735 0.298 1.401 0.779 1.884l-0-0 5.833 5.863c0.483 0.484 1.15 0.783 1.888 0.783 0.436 0 0.847-0.105 1.211-0.29l-0.015 0.007 1.123 1.15c0.568 0.574 1.356 0.93 2.226 0.93 0.001 0 0.003 0 0.004 0h-0c0.488-0.008 0.946-0.13 1.35-0.341l-0.017 0.008c0.925-0.459 1.614-1.276 1.894-2.265l0.006-0.025c0.040-0.075 0.107-0.13 0.188-0.153l0.002-0.001c1.204-0.37 2.129-1.31 2.47-2.497l0.006-0.026c0.038-0.076 0.106-0.133 0.188-0.156l0.002-0c1.204-0.37 2.129-1.31 2.47-2.497l0.006-0.026c0.019-0.076 0.072-0.137 0.142-0.166l0.002-0.001c0.712-0.256 1.269-0.792 1.547-1.473l0.006-0.017c0.112-0.283 0.177-0.611 0.177-0.953v-0h2.31v2c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-17.41c0-0.736-0.597-1.333-1.333-1.333v0zM13.21 24l-0.797 0.803-0.017 0.020-0.333 0.333-5.86-5.843 1.173-1.183 5.837 5.87zM24.333 19.047c-0.85 0.336-1.491 1.038-1.738 1.906l-0.005 0.020c-0.101 0.362-0.389 0.638-0.75 0.722l-0.007 0.001c-0.919 0.309-1.622 1.036-1.891 1.949l-0.005 0.021c-0.101 0.362-0.389 0.638-0.75 0.722l-0.007 0.001c-0.919 0.3-1.625 1.018-1.904 1.923l-0.006 0.021c-0.076 0.284-0.271 0.512-0.527 0.631l-0.006 0.002c-0.046 0.019-0.099 0.029-0.155 0.029-0.128 0-0.243-0.056-0.321-0.145l-0-0-1.057-1.077c0.422-0.47 0.68-1.094 0.68-1.779 0-0.738-0.299-1.405-0.783-1.888l-0-0-5.833-5.86c-0.483-0.484-1.151-0.784-1.888-0.784s-1.405 0.3-1.888 0.784l-0 0-0.157 0.153h-2.667v-8.067h7.46l-1.74 1.747c-0.184 0.188-0.314 0.429-0.365 0.698l-0.001 0.009c-0.043 0.24-0.383 2.397 0.977 3.753 0.583 0.58 1.667 1.197 3.577 0.847 0.278-0.051 0.52-0.183 0.707-0.37l3.72-3.727 7.223 7.26c0.069 0.141 0.121 0.305 0.146 0.478l0.001 0.009zM25.87 16.437l-7.923-7.977c-0.242-0.244-0.577-0.394-0.947-0.394s-0.705 0.151-0.947 0.394l-4.333 4.37c-0.067 0.015-0.145 0.023-0.224 0.023-0.228 0-0.441-0.070-0.617-0.189l0.004 0.002c-0.169-0.248-0.27-0.555-0.27-0.886 0-0.052 0.002-0.103 0.007-0.154l-0.001 0.006 5.933-5.967h4.463l5.050 2.54c0.166 0.080 0.362 0.127 0.568 0.127 0.011 0 0.022-0 0.034-0l-0.002 0h2.667v8.077z"></path> - </symbol> - <symbol id="back" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 14.667h-18.85l5.183-5.777c0.21-0.235 0.339-0.548 0.339-0.89 0-0.739-0.599-1.339-1.339-1.339-0.397 0-0.754 0.173-0.999 0.447l-0.001 0.001-6.27 7c-0.453 0.491-0.73 1.149-0.73 1.873 0 0.006 0 0.012 0 0.018v-0.001c-0 0.006-0 0.014-0 0.021 0 0.719 0.276 1.374 0.728 1.864l-0.002-0.002 6.273 7.007c0.246 0.276 0.603 0.449 1 0.449 0.739 0 1.339-0.599 1.339-1.339 0-0.342-0.128-0.655-0.34-0.891l0.001 0.001-5.19-5.777h18.857c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="back-current-date" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 26.507c0.002 0.055 0.003 0.12 0.003 0.185 0 2.096-1.276 3.894-3.093 4.659l-0.033 0.012c-0.846 0.379-1.831 0.611-2.867 0.636l-0.009 0h-1.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.333c0.55 0 3.333-0.17 3.333-2.827 0-2.477-3.197-2.507-3.333-2.507h-9.127l2.067 2.053c0.243 0.242 0.394 0.577 0.394 0.947 0 0.737-0.597 1.334-1.334 1.334-0.367 0-0.699-0.148-0.94-0.387l0 0-3.727-3.72c-0.403-0.398-0.652-0.951-0.652-1.562s0.249-1.163 0.652-1.561l0-0 3.727-3.717c0.241-0.239 0.573-0.387 0.94-0.387 0.737 0 1.334 0.597 1.334 1.334 0 0.37-0.151 0.705-0.394 0.947l-2.070 2.053h9.13c1.028 0.024 2.001 0.235 2.894 0.6l-0.054-0.020c2.037 0.84 3.16 2.47 3.16 4.593zM26.627 1h-1.293c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.287c0.029 0.022 0.048 0.057 0.048 0.096 0 0.005-0 0.010-0.001 0.015l0-0.001v6.557h-24v-6.6c-0-0.001-0-0.002-0-0.003 0-0.035 0.028-0.063 0.063-0.063 0 0 0 0 0 0v0h1.27c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.27c-1.508 0.002-2.73 1.225-2.73 2.733 0 0 0 0 0 0v0 22.523c0 0 0 0 0 0 0 1.513 1.224 2.74 2.736 2.743h7.264c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-7.263c-0.039-0.002-0.070-0.034-0.070-0.073 0-0.001 0-0.002 0-0.004v0-13.257h24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.223c0-0.009 0-0.020 0-0.030 0-1.503-1.207-2.724-2.705-2.746l-0.002-0zM18 3.667h-6.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM24 6.667c0 0.002 0 0.003 0 0.005 0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-0.97 0.517-1.818 1.291-2.285l0.012-0.007v-3.047c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 3c0.82 0.466 1.363 1.332 1.363 2.326 0 0.002 0 0.005 0 0.007v-0zM10.333 6.667c0 0.002 0 0.003 0 0.005 0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-0.97 0.518-1.818 1.291-2.285l0.012-0.007v-3.047c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 3c0.819 0.466 1.363 1.332 1.363 2.326 0 0.002 0 0.005 0 0.007v-0z"></path> - </symbol> - <symbol id="banner" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 7h-6.863l-6.267-5.040c-0.226-0.182-0.517-0.292-0.833-0.292s-0.607 0.11-0.836 0.294l0.003-0.002-6.337 5.040h-6.867c-1.105 0-2 0.895-2 2v0 14c0 1.105 0.895 2 2 2v0h28c1.105 0 2-0.895 2-2v0-14c0-1.105-0.895-2-2-2v0zM16.030 4.707l2.85 2.293h-5.73zM29.333 22.333h-26.667v-12.667h26.667zM24 15.333h-16c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h16c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM20.667 19.333h-9.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h9.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0z"></path> - </symbol> - <symbol id="bestseller" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.487 26l-3.91-6.777c1.513-1.986 2.423-4.502 2.423-7.231 0-6.627-5.373-12-12-12s-12 5.373-12 12c0 2.729 0.911 5.245 2.445 7.26l-0.021-0.029-3.91 6.777c-0.112 0.191-0.179 0.421-0.179 0.667 0 0.736 0.597 1.333 1.333 1.333 0.088 0 0.174-0.009 0.257-0.025l-0.008 0.001 4.063-0.78 1.353 3.907c0.171 0.49 0.607 0.845 1.131 0.896l0.005 0h0.127c0.49-0.001 0.919-0.266 1.15-0.66l0.003-0.006 4.25-7.333h0.033l4.233 7.333c0.235 0.401 0.663 0.666 1.153 0.667h0.127c0.523-0.058 0.951-0.411 1.117-0.887l0.003-0.009 1.357-3.907 4.063 0.78c0.074 0.014 0.158 0.023 0.245 0.023 0.736 0 1.333-0.597 1.333-1.333 0-0.245-0.066-0.475-0.182-0.672l0.003 0.006zM16 2.667c5.155 0 9.333 4.179 9.333 9.333s-4.179 9.333-9.333 9.333c-5.155 0-9.333-4.179-9.333-9.333v0c0-5.155 4.179-9.333 9.333-9.333v0zM10.897 27.473l-0.777-2.24c-0.186-0.527-0.68-0.897-1.26-0.897-0.088 0-0.175 0.009-0.259 0.025l0.008-0.001-2.333 0.447 2.057-3.573c1.33 1.11 2.929 1.945 4.683 2.395l0.083 0.018zM23.39 24.36c-0.075-0.015-0.162-0.024-0.25-0.024-0.58 0-1.074 0.37-1.257 0.888l-0.003 0.009-0.777 2.24-2.213-3.827c1.841-0.467 3.444-1.302 4.796-2.429l-0.019 0.016 2.063 3.573zM21.47 11l-2.29 2.28 0.487 3.287c0.005 0.030 0.008 0.065 0.008 0.1 0 0.368-0.298 0.667-0.667 0.667-0.003 0-0.005 0-0.008-0h0c-0.122-0-0.237-0.032-0.337-0.088l0.003 0.002-2.75-1.56-2.787 1.487c-0.091 0.049-0.199 0.078-0.313 0.078-0.368 0-0.667-0.298-0.667-0.667 0-0.041 0.004-0.080 0.011-0.119l-0.001 0.004 0.573-3.263-2.237-2.333c-0.116-0.12-0.187-0.283-0.187-0.463 0-0.337 0.249-0.615 0.573-0.66l0.004-0 3.11-0.43 1.43-2.94c0.111-0.224 0.337-0.375 0.599-0.375 0.268 0 0.5 0.159 0.606 0.387l0.002 0.004 1.367 2.97 3.1 0.513c0.253 0.038 0.457 0.215 0.535 0.449l0.001 0.005c0.018 0.058 0.029 0.125 0.029 0.195 0 0.184-0.075 0.351-0.196 0.472v0z"></path> - </symbol> - <symbol id="block-add" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18.667 28H1.334a1.333 1.333 0 0 1-1.333-1.333V1.334C.001.598.598.001 1.334.001h25.333C27.403.001 28 .598 28 1.334v17.333a1.333 1.333 0 0 1-2.666 0v-16H2.667v22.667h16a1.333 1.333 0 0 1 0 2.666zm12-2.667H28v-2.667a1.333 1.333 0 0 0-2.666 0v2.667h-2.667a1.333 1.333 0 0 0 0 2.666h2.667v2.667a1.333 1.333 0 0 0 2.666 0v-2.667h2.667a1.333 1.333 0 0 0 0-2.666z"></path> - </symbol> - <symbol id="block-invisible" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.88 24.887c-0.027 0.057-0.063 0.113-0.097 0.17s-0.020 0.037-0.033 0.053v0c-3.143 4.36-6.51 6.557-10.047 6.557-0.71-0.002-1.4-0.085-2.061-0.242l0.061 0.012c-0.598-0.141-1.035-0.669-1.035-1.3 0-0.737 0.597-1.334 1.334-1.334 0.106 0 0.209 0.012 0.307 0.036l-0.009-0.002c2.94 0.667 5.88-0.863 8.753-4.57-0.263-0.403-0.527-0.787-0.787-1.14-0.154-0.215-0.246-0.482-0.246-0.772 0-0.736 0.597-1.333 1.333-1.333 0.427 0 0.806 0.2 1.050 0.512l0.002 0.003c0.47 0.633 0.94 1.333 1.4 2.1v0c0 0.005 0 0.011 0 0.017s-0 0.012-0 0.017l0-0.001c0.035 0.057 0.066 0.123 0.091 0.192l0.002 0.008c0.012 0.033 0.023 0.075 0.032 0.117l0.001 0.006c0 0.043 0.027 0.083 0.033 0.127s0 0.090 0 0.133 0 0.083 0 0.127 0 0.077 0 0.117c-0.002 0.049-0.009 0.096-0.021 0.141l0.001-0.004c0 0.040-0.023 0.077-0.037 0.117s-0.010 0.093-0.030 0.137zM22.88 17.127c-7-1.173-11.813 6.173-12 6.487 0 0 0 0.033-0.023 0.047s-0.047 0.087-0.067 0.13c-0.015 0.029-0.030 0.067-0.044 0.105l-0.002 0.008c0 0.040-0.023 0.083-0.033 0.127s-0.020 0.083-0.027 0.123 0 0.087 0 0.133 0 0.080 0 0.12 0 0.090 0 0.137 0 0.080 0.023 0.117 0.027 0.087 0.040 0.13l0.050 0.12s0 0.037 0.023 0.053c0.471 0.835 0.952 1.55 1.482 2.224l-0.028-0.037c0.246 0.317 0.628 0.519 1.057 0.519 0.737 0 1.334-0.597 1.334-1.334 0-0.308-0.105-0.592-0.28-0.818l0.002 0.003c-0.333-0.423-0.59-0.81-0.793-1.12 1.217-1.563 4.633-5.333 8.853-4.643 0.062 0.010 0.134 0.016 0.208 0.016 0.736 0 1.333-0.597 1.333-1.333 0-0.654-0.471-1.199-1.093-1.312l-0.008-0.001zM29.67 17.127c-0.246-0.278-0.603-0.452-1.001-0.452-0.335 0-0.64 0.123-0.874 0.327l0.002-0.001-5.3 4.567c-0.343-0.148-0.743-0.235-1.163-0.235-1.657 0-3 1.343-3 3 0 0 0 0.001 0 0.002v-0c0.002 0.266 0.037 0.523 0.101 0.768l-0.005-0.021-5.3 4.587c-0.348 0.244-0.572 0.643-0.572 1.095 0 0.736 0.597 1.333 1.333 1.333 0.387 0 0.735-0.165 0.978-0.427l0.001-0.001 5.3-4.567c0.343 0.148 0.743 0.235 1.163 0.235 1.657 0 3-1.343 3-3 0-0 0-0.001 0-0.002v0c-0.002-0.266-0.037-0.523-0.101-0.768l0.005 0.021 5.3-4.587c0.278-0.246 0.452-0.603 0.452-1.001 0-0.333-0.122-0.637-0.324-0.871l0.001 0.002zM18 3.667h-6.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM26.627 1h-1.293c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.287c0.029 0.022 0.048 0.057 0.048 0.096 0 0.005-0 0.010-0.001 0.015l0-0.001v6.557h-24v-6.6c-0-0.001-0-0.002-0-0.003 0-0.035 0.028-0.063 0.063-0.063 0 0 0 0 0 0v0h1.27c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.27c-1.508 0.002-2.73 1.225-2.73 2.733 0 0 0 0 0 0v0 22.523c0 0 0 0 0 0 0 1.513 1.224 2.74 2.736 2.743h5.264c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5.263c-0.039-0.002-0.070-0.034-0.070-0.073 0-0.001 0-0.002 0-0.004v0-13.257h24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.223c0-0.009 0-0.020 0-0.030 0-1.503-1.207-2.724-2.705-2.746l-0.002-0zM9 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM23 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007z"></path> - </symbol> - <symbol id="block-visible-recurring" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.947 21.087l-0.863 2.957c-0.106 0.355-0.346 0.641-0.659 0.806l-0.007 0.003c-0.177 0.092-0.386 0.147-0.608 0.147-0.001 0-0.001 0-0.002 0h0c-0.005 0-0.012 0-0.018 0-0.163 0-0.319-0.028-0.465-0.080l0.010 0.003-2.787-1c-0.494-0.203-0.836-0.68-0.836-1.237 0-0.726 0.579-1.316 1.301-1.333l0.002-0c-1.032-1.066-2.475-1.727-4.073-1.727-1.842 0-3.478 0.879-4.513 2.24l-0.010 0.014c-0.249 0.335-0.643 0.55-1.087 0.55-0.294 0-0.565-0.094-0.787-0.253l0.004 0.003c-0.33-0.246-0.541-0.634-0.541-1.072 0-0.29 0.093-0.559 0.25-0.778l-0.003 0.004c1.543-2.041 3.965-3.346 6.693-3.346 2.627 0 4.971 1.21 6.505 3.104l0.012 0.016c0.226-0.432 0.672-0.722 1.185-0.722 0.136 0 0.267 0.020 0.391 0.058l-0.009-0.003c0.563 0.164 0.967 0.676 0.967 1.282 0 0.13-0.019 0.255-0.053 0.374l0.002-0.009zM30.11 26.803c-0.218-0.157-0.49-0.251-0.784-0.251-0.444 0-0.837 0.214-1.083 0.544l-0.003 0.004c-1.032 1.372-2.657 2.25-4.488 2.25-0.013 0-0.025-0-0.038-0h0.002c-0.003 0-0.007 0-0.010 0-1.595 0-3.035-0.659-4.065-1.719l-0.001-0.001c0.724-0.016 1.306-0.606 1.306-1.333 0-0.557-0.342-1.034-0.827-1.234l-0.009-0.003-2.787-1c-0.134-0.049-0.288-0.078-0.449-0.078-0.223 0-0.433 0.055-0.618 0.152l0.007-0.003c-0.321 0.169-0.561 0.455-0.664 0.801l-0.002 0.009-0.863 2.957c-0.031 0.107-0.049 0.231-0.049 0.358 0 0.605 0.404 1.117 0.956 1.279l0.009 0.002c0.112 0.032 0.24 0.052 0.372 0.053h0.001c0.001 0 0.002 0 0.003 0 0.515 0 0.962-0.292 1.184-0.719l0.003-0.007c1.547 1.913 3.893 3.126 6.523 3.126 2.718 0 5.133-1.295 6.662-3.302l0.015-0.020c0.158-0.216 0.252-0.487 0.252-0.78 0-0.444-0.217-0.838-0.552-1.080l-0.004-0.003zM18 3.667h-6.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM26.627 1h-1.293c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.287c0.029 0.022 0.048 0.057 0.048 0.096 0 0.005-0 0.010-0.001 0.015l0-0.001v6.557h-24v-6.6c-0-0.001-0-0.002-0-0.003 0-0.035 0.028-0.063 0.063-0.063 0 0 0 0 0 0v0h1.27c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.27c-1.508 0.002-2.73 1.225-2.73 2.733 0 0 0 0 0 0v0 22.523c0 0 0 0 0 0 0 1.513 1.224 2.74 2.736 2.743h9.264c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-9.263c-0.039-0.002-0.070-0.034-0.070-0.073 0-0.001 0-0.002 0-0.004v0-13.257h24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.223c0-0.009 0-0.020 0-0.030 0-1.503-1.207-2.724-2.705-2.746l-0.002-0zM9 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM23 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007z"></path> - </symbol> - <symbol id="block-visible" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18 3.667h-6.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM26.627 1h-1.293c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.287c0.029 0.022 0.048 0.057 0.048 0.096 0 0.005-0 0.010-0.001 0.015l0-0.001v6.557h-24v-6.6c-0-0.001-0-0.002-0-0.003 0-0.035 0.028-0.063 0.063-0.063 0 0 0 0 0 0v0h1.27c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.27c-1.508 0.002-2.73 1.225-2.73 2.733 0 0 0 0 0 0v0 22.523c0 0 0 0 0 0 0 1.513 1.224 2.74 2.736 2.743h5.264c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5.263c-0.039-0.002-0.070-0.034-0.070-0.073 0-0.001 0-0.002 0-0.004v0-13.257h24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.223c0-0.009 0-0.020 0-0.030 0-1.503-1.207-2.724-2.705-2.746l-0.002-0zM23 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM9 4.36v-3.027c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM31.9 25.17c0.014-0.033 0.029-0.074 0.041-0.116l0.002-0.008c0-0.043 0.023-0.083 0.033-0.127s0-0.083 0-0.123 0-0.087 0-0.13 0-0.087 0-0.13 0-0.080 0-0.123-0.020-0.083-0.033-0.127c-0.014-0.050-0.029-0.091-0.046-0.131l0.003 0.007c0-0.037-0.033-0.073-0.050-0.113s-0.047-0.080-0.073-0.12c-0.011-0.022-0.023-0.040-0.037-0.057l0 0.001c-3.313-4.467-6.907-6.667-10.667-6.533-5.857 0.2-9.8 6-10.167 6.573v0l-0.023 0.033-0.037 0.080c-0.025 0.038-0.049 0.083-0.071 0.13l-0.003 0.007c0 0.040-0.023 0.080-0.037 0.12s-0.027 0.083-0.037 0.127 0 0.087 0 0.13 0 0.083 0 0.127 0 0.083 0 0.127 0 0.087 0 0.13 0.023 0.083 0.037 0.127 0.023 0.080 0.037 0.12c0.024 0.053 0.049 0.098 0.076 0.141l-0.003-0.004 0.037 0.080 0.023 0.033c0.367 0.557 4.31 6.373 10.167 6.573h0.333c3.667 0 7.14-2.197 10.36-6.54 0.013-0.017 0.026-0.035 0.036-0.055l0.001-0.002c0.027-0.040 0.050-0.080 0.073-0.12s0.010-0.070 0.023-0.107zM21.167 29.333c-3.563-0.117-6.397-3.227-7.52-4.667 1.117-1.437 3.933-4.537 7.51-4.667 2.62-0.087 5.257 1.48 7.827 4.667-2.563 3.18-5.19 4.753-7.817 4.667zM24.333 24.667c0 1.657-1.343 3-3 3s-3-1.343-3-3c0-1.657 1.343-3 3-3v0c1.657 0 3 1.343 3 3v0z"></path> - </symbol> - <symbol id="blog_post" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 32h-18.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h18.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32.040 4.49c0 0 0 0.001 0 0.001 0 0.371-0.152 0.707-0.397 0.949l-0 0-13.957 13.737c-0.033 0.033-0.073 0.057-0.107 0.087 0.062 0.151 0.098 0.327 0.098 0.511 0 0.063-0.004 0.125-0.012 0.186l0.001-0.007-0.8 4.8c-0.092 0.528-0.484 0.945-0.991 1.071l-0.009 0.002-11.2 2.8c-0.101 0.027-0.217 0.043-0.337 0.043-0.736 0-1.333-0.597-1.333-1.333 0-0.12 0.016-0.236 0.045-0.346l-0.002 0.009 2.8-11.2c0.128-0.516 0.545-0.908 1.065-0.999l0.008-0.001 4.8-0.8c0.064-0.011 0.137-0.016 0.212-0.016 0.214 0 0.416 0.049 0.597 0.137l-0.008-0.004 0.050-0.057 13.957-13.727c0.24-0.237 0.571-0.383 0.935-0.383s0.695 0.146 0.935 0.383l3.253 3.196c0.245 0.242 0.397 0.578 0.397 0.949 0 0.004-0 0.008-0 0.012v-0.001zM14.907 20.19l-3.43-3.43-3.263 0.543-1.42 5.697 2.207-2.21c0.228-0.189 0.524-0.303 0.847-0.303 0.736 0 1.333 0.597 1.333 1.333 0 0.323-0.114 0.618-0.305 0.849l0.002-0.002-2.21 2.207 5.683-1.42zM28.807 4.49l-1.353-1.333-13 12.803 1.333 1.333c0.008-0.011 0.017-0.021 0.027-0.030l0-0z"></path> - </symbol> - <symbol id="blog" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 22.667c-0.736 0-1.333-0.597-1.333-1.333v0c-0.011-10.305-8.362-18.655-18.666-18.667h-0.001c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0c11.777 0.013 21.32 9.557 21.333 21.332v0.001c0 0.736-0.597 1.333-1.333 1.333v0zM26.28 21.12c0.032-0.348 0.050-0.754 0.050-1.163 0-3.586-1.403-6.844-3.689-9.256l0.006 0.006c-2.895-3.088-6.994-5.018-11.543-5.040h-0.104c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h0.103c3.778 0.019 7.179 1.619 9.577 4.173l0.007 0.007c1.842 1.936 2.976 4.561 2.976 7.451 0 0.322-0.014 0.641-0.042 0.957l0.003-0.041c-0.004 0.038-0.006 0.081-0.006 0.125 0 0.695 0.532 1.266 1.211 1.328l0.005 0c0.018 0.002 0.039 0.003 0.060 0.003s0.042-0.001 0.063-0.003l-0.003 0c0.694-0.001 1.263-0.531 1.326-1.208l0-0.005zM20.973 20.453c0.187-2.12-0.667-4.38-2.333-6.2-1.87-2.037-4.473-3.253-6.973-3.253-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0c1.763 0 3.637 0.893 5 2.387 1.17 1.28 1.77 2.79 1.667 4.163-0.004 0.037-0.006 0.080-0.006 0.123 0 0.69 0.525 1.258 1.197 1.326l0.006 0h0.117c0.695-0.001 1.265-0.533 1.326-1.212l0-0.005zM16.79 21.93l-0.943 5.667c-0.092 0.528-0.484 0.945-0.991 1.071l-0.009 0.002-13.18 3.29c-0.101 0.027-0.217 0.043-0.337 0.043-0.736 0-1.333-0.597-1.333-1.333 0-0.12 0.016-0.236 0.045-0.346l-0.002 0.009 3.293-13.19c0.128-0.516 0.545-0.908 1.065-0.999l0.008-0.001 5.667-0.943c0.066-0.012 0.142-0.018 0.22-0.018 0.369 0 0.702 0.15 0.944 0.392l5.18 5.193c0.242 0.241 0.392 0.575 0.392 0.944 0 0.078-0.007 0.154-0.019 0.227l0.001-0.008zM14.047 22.167l-4.213-4.213-4.12 0.687-1.92 7.693 3.207-3.21c0.228-0.189 0.524-0.303 0.847-0.303 0.736 0 1.333 0.597 1.333 1.333 0 0.323-0.114 0.618-0.305 0.849l0.002-0.002-3.21 3.207 7.667-1.92z"></path> - </symbol> - <symbol id="bold" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.113 16.823c-0.538-0.696-1.195-1.272-1.945-1.703l-0.032-0.017c0.452-0.359 0.845-0.761 1.185-1.207l0.012-0.017c1.033-1.357 1.56-3.080 1.56-5.123 0-3.067-1.207-5.353-3.583-6.79-2.19-1.323-5.503-1.967-10.13-1.967h-9.513c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h11.437c3.737 0 6.763-0.843 9-2.51 2.367-1.757 3.563-4.223 3.563-7.33 0-2.16-0.523-3.97-1.553-5.337zM14.18 2.667c4.070 0 7 0.533 8.753 1.583 1.563 0.947 2.293 2.377 2.293 4.507 0 1.467-0.333 2.613-1 3.507-0.59 0.788-1.463 1.337-2.465 1.497l-0.022 0.003h-15.74v-11.097zM23.513 27.35c-1.77 1.333-4.263 2-7.413 2h-10.1v-12.92h15.15c0.122 0.078 0.265 0.137 0.418 0.169l0.008 0.001c1.607 0.333 2.75 0.96 3.407 1.827s1 2.153 1 3.733c0.017 2.25-0.797 3.947-2.467 5.19z"></path> - </symbol> - <symbol id="bookmark-active" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.933 12.12c-0.161-0.469-0.564-0.815-1.056-0.892l-0.008-0.001-9.433-1.447-4.237-9.013c-0.217-0.455-0.672-0.764-1.2-0.767h-0c-0.53 0-0.989 0.31-1.203 0.759l-0.003 0.008-4.223 9-9.44 1.443c-0.499 0.078-0.902 0.424-1.061 0.884l-0.003 0.009c-0.044 0.127-0.070 0.273-0.070 0.425 0 0.363 0.145 0.692 0.38 0.932l-0-0 6.87 7.037-1.617 9.947c-0.011 0.064-0.017 0.137-0.017 0.212 0 0.736 0.597 1.333 1.333 1.333 0.236 0 0.458-0.061 0.65-0.169l-0.007 0.003 8.41-4.65 8.423 4.667c0.186 0.104 0.408 0.166 0.644 0.166 0.736 0 1.333-0.597 1.333-1.333 0-0.076-0.006-0.151-0.019-0.224l0.001 0.008-1.62-9.953 6.86-7.027c0.235-0.24 0.38-0.569 0.38-0.932 0-0.152-0.025-0.298-0.072-0.434l0.003 0.009z"></path> - </symbol> - <symbol id="bookmark-manager" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.783 25.843l1.573-1c.383-.235.637-.649.643-1.122V1.333C28.999.597 28.402 0 27.666 0H8.212a5.213 5.213 0 0 0-5.213 5.192v21.645c0 .12-.02.24-.02.36.02 2.65 2.46 4.803 5.463 4.803h19.223a1.333 1.333 0 0 0 .718-2.456l-.005-.003-1.587-1a1.492 1.492 0 1 1-.008-2.692l.009-.004zm-18.34 3.49c-1.507 0-2.777-.977-2.777-2.147s1.27-2.133 2.777-2.133h15.443c-.389.606-.621 1.346-.621 2.14s.231 1.534.63 2.156l-.01-.016zm0-6.946h-.024c-1 0-1.942.247-2.768.682l.032-.016V5.19a2.54 2.54 0 0 1 2.54-2.523H26.333v19.72zm13.36-10.72l-2.29 2.28.487 3.287a.667.667 0 0 1-.659.767h-.008a.688.688 0 0 1-.337-.088l.003.002-2.75-1.56-2.787 1.487a.667.667 0 0 1-.969-.708l-.001.004.573-3.263-2.237-2.333a.666.666 0 0 1 .39-1.123h.003l3.107-.43 1.43-2.94a.668.668 0 0 1 .602-.38h.026-.001.007c.268 0 .499.158.605.386l.002.004 1.333 2.977 3.1.513a.67.67 0 0 1 .532.449l.001.005a.665.665 0 0 1-.163.666z"></path> - </symbol> - <symbol id="bookmark" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.077 32c-0.237-0.001-0.46-0.062-0.654-0.17l0.007 0.004-8.43-4.66-8.417 4.667c-0.186 0.104-0.407 0.166-0.644 0.166-0.736 0-1.333-0.597-1.333-1.333 0-0.075 0.006-0.148 0.018-0.22l-0.001 0.008 1.617-9.947-6.863-7.053c-0.235-0.24-0.38-0.569-0.38-0.932 0-0.152 0.025-0.298 0.072-0.434l-0.003 0.009c0.161-0.469 0.564-0.815 1.056-0.892l0.008-0.001 9.433-1.437 4.223-9c0.217-0.46 0.677-0.773 1.21-0.773 0.001 0 0.002 0 0.003 0h-0c0.528 0.003 0.983 0.312 1.197 0.759l0.003 0.008 4.237 9 9.433 1.447c0.645 0.101 1.133 0.653 1.133 1.318 0 0.363-0.145 0.692-0.38 0.932l-6.86 7.040 1.627 9.947c0.011 0.065 0.018 0.14 0.018 0.217 0 0.736-0.596 1.332-1.331 1.333h-0zM16 24.333c0 0 0.001 0 0.001 0 0.237 0 0.459 0.062 0.652 0.17l-0.007-0.003 6.687 3.67-1.293-7.9c-0.011-0.065-0.018-0.14-0.018-0.217 0-0.362 0.144-0.69 0.378-0.93l-0 0 5.52-5.667-7.587-1.15c-0.445-0.072-0.811-0.355-0.997-0.742l-0.003-0.008-3.333-7.087-3.333 7.080c-0.188 0.396-0.554 0.681-0.992 0.752l-0.008 0.001-7.567 1.15 5.523 5.667c0.242 0.241 0.391 0.575 0.391 0.944 0 0.069-0.005 0.137-0.015 0.204l0.001-0.007-1.3 7.903 6.667-3.667c0.183-0.102 0.401-0.162 0.633-0.163h0z"></path> - </symbol> - <symbol id="browse" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.167 29.333h-1.5c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.49c0.053-0.003 0.101-0.021 0.141-0.050l-0.001 0c0.023-0.020 0.037-0.049 0.037-0.082 0-0 0-0.001 0-0.001v0-14.533h-26.667v14.55c0.010 0.067 0.067 0.117 0.135 0.117 0.005 0 0.010-0 0.015-0.001l-0.001 0h1.49c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-1.49c-0.004 0-0.009 0-0.013 0-1.541 0-2.792-1.244-2.803-2.782v-15.884c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 15.867c0 0.002 0 0.004 0 0.005 0 0.77-0.315 1.466-0.823 1.968l-0 0c-0.512 0.511-1.219 0.827-1.999 0.827-0.004 0-0.008 0-0.012-0h0.001zM32 7.333v-1.467c-0.015-1.586-1.305-2.867-2.893-2.867-0.007 0-0.014 0-0.021 0h-16.349l-1.18-1.743c-0.524-0.763-1.392-1.257-2.375-1.257-0.003 0-0.006 0-0.008 0h-6.29c-0.006-0-0.013-0-0.020-0-1.573 0-2.85 1.269-2.863 2.839v4.495c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4.493c0.015-0.099 0.099-0.174 0.201-0.174 0.005 0 0.011 0 0.016 0.001l-0.001-0h6.29c0.002-0 0.004-0 0.006-0 0.068 0 0.129 0.033 0.167 0.083l0 0.001 1.577 2.333c0.243 0.354 0.646 0.583 1.102 0.583 0.002 0 0.003 0 0.005 0h17.056c0.005-0 0.010-0.001 0.016-0.001 0.117 0 0.214 0.087 0.231 0.199l0 0.001v1.467c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM23.94 29.72l-3.433-3.41c0.778-1.105 1.243-2.478 1.243-3.961 0-0.006 0-0.011-0-0.017v0.001c0-0.005 0-0.011 0-0.017 0-3.881-3.146-7.027-7.027-7.027s-7.027 3.146-7.027 7.027c0 3.881 3.146 7.027 7.027 7.027 1.444 0 2.786-0.435 3.902-1.182l-0.025 0.016 3.46 3.437c0.241 0.239 0.573 0.387 0.94 0.387 0.737 0 1.334-0.597 1.334-1.334 0-0.37-0.151-0.705-0.394-0.947l-0-0zM10.333 22.333c0.067-2.365 2-4.256 4.375-4.256s4.308 1.891 4.375 4.25l0 0.006c0 0.002 0 0.005 0 0.008 0 1.191-0.484 2.27-1.267 3.049l-0 0c-0.793 0.794-1.889 1.285-3.1 1.285-2.406 0-4.359-1.939-4.383-4.339l-0-0.002z"></path> - </symbol> - <symbol id="bubbles" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.963 27.39c-0.736 0-1.333-0.597-1.333-1.333v0-6.033l-1.963-0.023c-1.468-0.035-2.647-1.223-2.667-2.691l-0-0.002v-14.613c0.033-1.474 1.219-2.661 2.69-2.693l0.003-0h21.827c1.427 0 2.48 1.133 2.48 2.693v14.62c0 1.537-1.113 2.687-2.57 2.687h-9.513l-8.073 7.063c-0.235 0.202-0.543 0.326-0.88 0.327h-0zM2.667 2.723v14.59c0.011 0.015 0.027 0.025 0.046 0.027l0 0 3.263 0.030c0.736 0 1.333 0.597 1.333 1.333v0 4.42l6.23-5.457c0.233-0.206 0.54-0.332 0.876-0.333h9.917v-14.667h-21.567c-0.040 0.007-0.075 0.027-0.1 0.056l-0 0zM26.037 32c-0.005 0-0.012 0-0.018 0-0.339 0-0.648-0.126-0.883-0.335l0.001 0.001-7.667-7.020-2.787 0.020c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0l3.3-0.043c0.014-0.001 0.031-0.001 0.049-0.001 0.336 0 0.642 0.127 0.873 0.335l-0.001-0.001 5.833 5.333v-4.333c0-0.736 0.597-1.333 1.333-1.333v0l3.287-0.033s0.023-0.020 0.023-0.027v-14.203c-0.699-0.048-1.248-0.626-1.248-1.333 0-0.738 0.598-1.336 1.336-1.336 0.031 0 0.062 0.001 0.092 0.003l-0.004-0h0.217c1.467 0 2.273 0.803 2.273 2.26v14.637c-0.016 1.461-1.181 2.644-2.632 2.69l-0.004 0-2 0.020v6.033c0 0.736-0.597 1.333-1.333 1.333v0z"></path> - </symbol> - <symbol id="button" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.63 18.667h-9.297c-2.946 0-5.333-2.388-5.333-5.333v0-5.333c0-2.946 2.388-5.333 5.333-5.333v0h21.333c2.946 0 5.333 2.388 5.333 5.333v0 4.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-4.667c0-1.473-1.194-2.667-2.667-2.667v0h-21.333c-1.473 0-2.667 1.194-2.667 2.667v0 5.333c0 1.473 1.194 2.667 2.667 2.667v0h9.297c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM31.473 19.573l-11.333-8.633c-0.224-0.177-0.511-0.284-0.823-0.284-0.736 0-1.333 0.597-1.333 1.333 0 0.073 0.006 0.145 0.017 0.215l-0.001-0.008 2.057 13.82c0.068 0.442 0.345 0.807 0.726 0.997l0.008 0.003c0.169 0.086 0.368 0.137 0.579 0.137 0.003 0 0.005 0 0.008-0h-0c0.004 0 0.010 0 0.015 0 0.239 0 0.464-0.063 0.658-0.174l-0.007 0.003 3.507-1.96 2.183 3.667c0.223 0.444 0.674 0.743 1.195 0.743 0.736 0 1.333-0.597 1.333-1.333 0-0.287-0.091-0.553-0.245-0.77l0.003 0.004-2.143-3.61 3.457-1.923c0.411-0.232 0.684-0.667 0.684-1.165 0-0.432-0.206-0.816-0.524-1.060l-0.003-0.002z"></path> - </symbol> - <symbol id="captcha" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M11.227 18.333c0.073 0.2 0.14 0.377 0.197 0.523s0.127 0.303 0.207 0.467l1 0.14 0.177 0.48c-0.112 0.067-0.241 0.123-0.378 0.164l-0.012 0.003c-0.167 0.057-0.333 0.107-0.533 0.16s-0.38 0.090-0.57 0.123l-0.51 0.077h-0.47c-0.152-0.184-0.285-0.394-0.389-0.619l-0.007-0.018c-0.097-0.21-0.203-0.467-0.333-0.77h-0.067c0.006 0.048 0.009 0.103 0.009 0.159 0 0.132-0.019 0.26-0.055 0.381l0.002-0.009c-0.050 0.177-0.137 0.329-0.254 0.454l0.001-0.001c-0.132 0.142-0.293 0.255-0.474 0.33l-0.009 0.003c-0.203 0.078-0.438 0.124-0.683 0.124-0.015 0-0.031-0-0.046-0.001l0.002 0c-0.001 0-0.001 0-0.002 0-0.317 0-0.623-0.051-0.908-0.146l0.020 0.006c-0.308-0.104-0.574-0.238-0.818-0.404l0.012 0.007c-0.255-0.172-0.474-0.37-0.663-0.595l-0.004-0.005c-0.188-0.226-0.341-0.489-0.447-0.775l-0.006-0.018c-0.099-0.221-0.156-0.479-0.156-0.75 0-0.095 0.007-0.189 0.021-0.281l-0.001 0.010c0.068-0.288 0.25-0.525 0.495-0.664l0.005-0.003c0.334-0.189 0.725-0.323 1.14-0.378l0.016-0.002c0.535-0.086 1.174-0.148 1.823-0.172l0.031-0.001c-0.152-0.658-0.402-1.239-0.74-1.758l0.013 0.021c-0.238-0.335-0.624-0.55-1.060-0.55-0.013 0-0.025 0-0.038 0.001l0.002-0c-0.013-0-0.027-0.001-0.042-0.001-0.25 0-0.489 0.050-0.706 0.142l0.012-0.004-0.043 1.667h-0.607c-0.123-0.118-0.239-0.241-0.347-0.37l-0.006-0.007c-0.12-0.14-0.24-0.3-0.357-0.473-0.107-0.14-0.217-0.3-0.319-0.465l-0.014-0.025c-0.085-0.131-0.173-0.289-0.254-0.451l-0.013-0.029c0.319-0.265 0.705-0.462 1.128-0.563l0.019-0.004c0.482-0.131 1.035-0.207 1.606-0.207 0.021 0 0.043 0 0.064 0l-0.003-0c0.023-0 0.049-0.001 0.076-0.001 0.448 0 0.88 0.064 1.29 0.182l-0.033-0.008c0.363 0.106 0.679 0.267 0.958 0.475l-0.008-0.005c0.255 0.192 0.47 0.422 0.64 0.683l0.006 0.010c0.159 0.246 0.301 0.529 0.41 0.827l0.010 0.030c0.203 0.55 0.373 1.050 0.51 1.493s0.317 0.913 0.487 1.39zM8.51 19.393c0.013 0.001 0.029 0.001 0.044 0.001 0.152 0 0.294-0.041 0.416-0.113l-0.004 0.002c0.105-0.068 0.191-0.156 0.255-0.26l0.002-0.004c0.058-0.095 0.095-0.21 0.1-0.332l0-0.002c0-0.12 0-0.223 0-0.317l-0.5-1.37c-0.365 0.002-0.722 0.031-1.070 0.085l0.040-0.005c-0.216 0.026-0.409 0.109-0.569 0.232l0.003-0.002c-0.109 0.094-0.177 0.232-0.177 0.386 0 0.005 0 0.010 0 0.015l-0-0.001c0.006 0.212 0.051 0.411 0.127 0.594l-0.004-0.011c0.109 0.326 0.296 0.601 0.541 0.811l0.002 0.002c0.209 0.174 0.481 0.28 0.778 0.28 0.005 0 0.011-0 0.016-0h-0.001zM13.37 16.743c0.203 0.22 0.366 0.48 0.475 0.767l0.005 0.016-0.243 0.29-2.367-2 0.243-0.29c0.12 0.023 0.243 0.053 0.367 0.090 0.147 0.048 0.269 0.099 0.387 0.158l-0.017-0.008 3.64-4.333c-0.085-0.105-0.161-0.223-0.222-0.349l-0.005-0.011c-0.043-0.086-0.091-0.196-0.132-0.309l-0.008-0.024 0.243-0.293 4 3.363c-0.097 0.122-0.193 0.231-0.294 0.334l0.001-0.001-0.333 0.333c-0.117 0.113-0.237 0.223-0.353 0.333s-0.233 0.2-0.333 0.287l-0.313-0.263 0.407-1.2-1.487-1.247-1.697 2.013 1.050 0.88 0.84-0.3 0.23 0.193c-0.093 0.15-0.197 0.303-0.313 0.457s-0.237 0.307-0.36 0.453-0.263 0.293-0.393 0.43-0.243 0.253-0.353 0.353l-0.233-0.2 0.163-0.893-1.050-0.873zM21.933 13.527l-0.47-0.17c0.047-0.14 0.103-0.303 0.167-0.487s0.133-0.377 0.207-0.58 0.15-0.4 0.23-0.597 0.163-0.37 0.247-0.527c0.475-0.107 1.020-0.169 1.58-0.169 0.061 0 0.122 0.001 0.183 0.002l-0.009-0c0.74 0.019 1.441 0.166 2.089 0.418l-0.042-0.014c0.45 0.148 0.839 0.367 1.176 0.648l-0.006-0.005c0.245 0.206 0.428 0.478 0.524 0.788l0.003 0.012c0.029 0.111 0.046 0.238 0.046 0.369 0 0.187-0.034 0.366-0.096 0.531l0.003-0.010c-0.133 0.35-0.315 0.652-0.543 0.914l0.003-0.004c-0.432 0.498-0.948 0.911-1.528 1.219l-0.029 0.014c-0.557 0.313-1.209 0.608-1.889 0.846l-0.088 0.027c-0.538 0.2-1.237 0.41-1.951 0.584l-0.142 0.029-1.897 0.45-1.067 0.16-0.040 0.047 0.9 0.233 2.37 0.863 1.803-1.093 0.467 0.17c-0.133 0.203-0.293 0.437-0.467 0.703s-0.37 0.533-0.56 0.803-0.373 0.523-0.55 0.767-0.333 0.433-0.45 0.577l-6.32-2.297 0.643-0.75c0.393-0.093 0.843-0.21 1.35-0.333s1.037-0.287 1.593-0.463 1.123-0.373 1.703-0.59 1.127-0.45 1.667-0.703c0.558-0.268 1.029-0.543 1.477-0.851l-0.037 0.024c0.41-0.274 0.764-0.588 1.071-0.943l0.006-0.007c0.303-0.302 0.49-0.719 0.49-1.181 0-0.013-0-0.025-0-0.038l0 0.002c-0.040-0.373-0.307-0.667-0.803-0.833-0.118-0.046-0.265-0.090-0.416-0.125l-0.024-0.005c-0.14-0.027-0.303-0.050-0.49-0.067zM30.667 25.333h-29.333c-0.736 0-1.333-0.597-1.333-1.333v0-16c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 16c0 0.736-0.597 1.333-1.333 1.333v0zM2.667 22.667h26.667v-13.333h-26.667z"></path> - </symbol> - <symbol id="caret-back" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18.667 24c-0.001 0-0.001 0-0.002 0-0.326 0-0.625-0.117-0.857-0.312l0.002 0.002-8-6.69c-0.292-0.246-0.477-0.612-0.477-1.022 0-0.005 0-0.010 0-0.016v0.001c0.001-0.411 0.188-0.778 0.481-1.022l0.002-0.002 8-6.633c0.233-0.204 0.541-0.329 0.877-0.329 0.736 0 1.333 0.597 1.333 1.333 0 0.425-0.198 0.803-0.508 1.047l-0.003 0.002-6.767 5.61 6.773 5.667c0.298 0.246 0.487 0.616 0.487 1.030 0 0.736-0.597 1.333-1.333 1.333-0.004 0-0.007 0-0.011-0h0.001z"></path> - </symbol> - <symbol id="caret-down" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M15.967 22.667c-0.41-0.002-0.775-0.189-1.018-0.481l-0.002-0.002-6.633-8c-0.177-0.224-0.284-0.511-0.284-0.823 0-0.736 0.597-1.333 1.333-1.333 0.4 0 0.759 0.176 1.003 0.455l0.001 0.002 5.61 6.767 5.667-6.773c0.246-0.293 0.613-0.478 1.023-0.478 0.737 0 1.335 0.598 1.335 1.335 0 0.327-0.118 0.627-0.313 0.859l0.002-0.002-6.69 8c-0.246 0.292-0.612 0.477-1.022 0.477-0.004 0-0.008-0-0.012-0h0.001z"></path> - </symbol> - <symbol id="caret-next" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M13.333 24c-0.001 0-0.001 0-0.002 0-0.736 0-1.333-0.597-1.333-1.333 0-0.41 0.185-0.777 0.477-1.022l0.002-0.002 6.773-5.667-6.767-5.617c-0.28-0.246-0.456-0.604-0.456-1.004 0-0.736 0.597-1.333 1.333-1.333 0.312 0 0.599 0.107 0.826 0.286l-0.003-0.002 8 6.633c0.295 0.245 0.482 0.612 0.483 1.023v0c0 0.004 0 0.010 0 0.015 0 0.409-0.184 0.776-0.475 1.020l-0.002 0.002-8 6.703c-0.227 0.185-0.52 0.297-0.839 0.297-0.006 0-0.013-0-0.019-0h0.001z"></path> - </symbol> - <symbol id="caret-up" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M9.333 20c-0.736-0-1.333-0.597-1.333-1.333 0-0.324 0.116-0.621 0.308-0.852l-0.002 0.002 6.633-8c0.245-0.294 0.61-0.481 1.020-0.483h0c0.005-0 0.012-0 0.018-0 0.409 0 0.776 0.184 1.020 0.475l0.002 0.002 6.703 8c0.194 0.23 0.311 0.53 0.311 0.857 0 0.737-0.598 1.335-1.335 1.335-0.41 0-0.777-0.185-1.022-0.476l-0.002-0.002-5.667-6.773-5.63 6.767c-0.246 0.296-0.615 0.483-1.027 0.483h-0z"></path> - </symbol> - <symbol id="caret-expanded" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.833 24h-.002c-.326 0-.625-.117-.857-.312l.002.002-8-6.69a1.332 1.332 0 0 1-.477-1.022v-.005a1.33 1.33 0 0 1 .497-1.031l.003-.002 8-6.633a1.333 1.333 0 0 1 1.702 2.051l-.003.002-6.783 5.61 6.773 5.667A1.332 1.332 0 0 1 23.842 24h-.011.001zm-6.643-.477a1.33 1.33 0 0 0-.164-1.878l-.002-.002-6.773-5.667 6.767-5.61a1.333 1.333 0 1 0-1.684-2.059l.001-.001-8 6.633c-.294.245-.481.61-.483 1.02v.032c0 .402.178.762.459 1.006l.002.001 8 6.703a1.33 1.33 0 0 0 1.878-.164l.002-.002z"></path> - </symbol> - <symbol id="cart-upload" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.333 29.333c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0zM10.667 26.667c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM26.333 20.667c0.582-0.001 1.076-0.374 1.257-0.894l0.003-0.009 4.333-12.667c0.061-0.148 0.097-0.319 0.097-0.499 0-0.736-0.597-1.333-1.333-1.333-0.608 0-1.121 0.407-1.281 0.963l-0.002 0.009-4.027 11.763h-13.427l-4.027-11.763c0.001-0.003 0.001-0.006 0.001-0.010s-0-0.007-0.001-0.010l0 0-2-5.333c-0.19-0.519-0.68-0.883-1.255-0.883-0.002 0-0.003 0-0.005 0h-3.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.41l1.667 4.45 4.12 12.037-2.65 4.127c-0.132 0.204-0.211 0.453-0.211 0.72 0 0.736 0.596 1.332 1.331 1.333h18.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-15.89l1.283-2zM23.947 4.393l-3.717-3.727c-0.398-0.403-0.951-0.652-1.562-0.652s-1.163 0.249-1.561 0.652l-0 0-3.72 3.727c-0.239 0.241-0.387 0.573-0.387 0.94 0 0.737 0.597 1.334 1.334 1.334 0.37 0 0.705-0.151 0.947-0.394l0-0 2.053-2.067v9.127c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.13l2.053 2.070c0.242 0.243 0.577 0.394 0.947 0.394 0.737 0 1.334-0.597 1.334-1.334 0-0.367-0.148-0.699-0.387-0.94l0 0z"></path> - </symbol> - <symbol id="cart-wishlist" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.333 29.333c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0zM10.667 26.667c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM26.333 20.667c0 0 0.001 0 0.001 0 0.582 0 1.078-0.373 1.259-0.894l0.003-0.009 0.613-1.8c0.046-0.129 0.072-0.277 0.072-0.432 0-0.736-0.597-1.333-1.333-1.333-0.582 0-1.077 0.373-1.259 0.892l-0.003 0.009-0.307 0.9h-13.427l-3.42-10h4.053c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5l-1.667-4.467c-0.195-0.511-0.68-0.867-1.249-0.867-0.002 0-0.003 0-0.005 0h-3.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.41l1.667 4.45 4.12 12.037-2.65 4.127c-0.132 0.204-0.211 0.453-0.211 0.72 0 0.736 0.596 1.332 1.331 1.333h18.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-15.89l1.283-2zM24.833 14.373c1.197-0.957 7.167-5.923 7.167-9.667 0.001-0.033 0.001-0.071 0.001-0.109 0-1.406-0.647-2.662-1.66-3.484l-0.008-0.007c-0.896-0.691-2.034-1.107-3.27-1.107-0.032 0-0.064 0-0.095 0.001l0.005-0c-0.036-0.001-0.078-0.001-0.12-0.001-1.074 0-2.065 0.351-2.867 0.944l0.013-0.009c-0.788-0.584-1.78-0.935-2.854-0.935-0.042 0-0.084 0.001-0.126 0.002l0.006-0c-0.017-0-0.036-0-0.056-0-1.239 0-2.38 0.419-3.29 1.123l0.012-0.009c-1.021 0.829-1.668 2.084-1.668 3.491 0 0.038 0 0.077 0.001 0.115l-0-0.006c0 3.737 5.97 8.703 7.167 9.667 0.226 0.182 0.517 0.293 0.833 0.293s0.607-0.11 0.836-0.294l-0.003 0.002zM22.667 3.637c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0c0-0.767 1.073-0.97 1.64-0.97 0.012-0 0.025-0 0.039-0 0.613 0 1.179 0.202 1.635 0.542l-0.007-0.005c0.41 0.331 0.67 0.834 0.67 1.398 0 0.039-0.001 0.078-0.004 0.117l0-0.005c0.027 1.62-2.867 4.777-5.307 6.887-3.667-3.123-5.333-5.863-5.333-6.887-0.002-0.033-0.003-0.073-0.003-0.112 0-0.564 0.26-1.067 0.667-1.395l0.003-0.003c0.449-0.335 1.015-0.537 1.628-0.537 0.014 0 0.027 0 0.041 0l-0.002-0c0.593 0 1.667 0.203 1.667 0.97z"></path> - </symbol> - <symbol id="cart" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.333 20.667c0.582-0.001 1.076-0.374 1.257-0.894l0.003-0.009 4.333-12.667c0.045-0.128 0.071-0.276 0.071-0.43 0-0.736-0.596-1.332-1.331-1.333h-23.077l-1.667-4.467c-0.195-0.511-0.68-0.867-1.249-0.867-0.003 0-0.005 0-0.008 0h-3.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.41l1.667 4.45 4.12 12.037-2.65 4.127c-0.132 0.204-0.211 0.453-0.211 0.72 0 0.736 0.596 1.332 1.331 1.333h18.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-15.89l1.283-2zM8.533 8h20.267l-3.42 10h-13.427zM13.333 29.333c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0zM26.333 29.333c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0z"></path> - </symbol> - <symbol id="catalog" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.667 0h-22.667c-1.105 0-2 0.895-2 2v0 1.78c-0.752 0.141-1.423 0.393-2.032 0.74l0.032-0.017c-0.411 0.233-0.684 0.668-0.684 1.167 0 0.739 0.599 1.337 1.337 1.337 0.24 0 0.465-0.063 0.66-0.174l-0.007 0.003c0.19-0.113 0.411-0.214 0.642-0.293l0.025-0.007v2.583c-0.752 0.141-1.423 0.393-2.032 0.74l0.032-0.017c-0.411 0.233-0.684 0.668-0.684 1.167 0 0.739 0.599 1.337 1.337 1.337 0.24 0 0.465-0.063 0.66-0.174l-0.007 0.003c0.19-0.113 0.411-0.214 0.642-0.293l0.025-0.007v2.583c-0.752 0.141-1.423 0.393-2.032 0.74l0.032-0.017c-0.411 0.233-0.684 0.668-0.684 1.167 0 0.738 0.599 1.337 1.337 1.337 0.24 0 0.465-0.063 0.66-0.174l-0.007 0.003c0.19-0.113 0.411-0.214 0.642-0.293l0.025-0.007v2.583c-0.752 0.141-1.423 0.393-2.032 0.74l0.032-0.017c-0.411 0.233-0.684 0.668-0.684 1.167 0 0.738 0.599 1.337 1.337 1.337 0.24 0 0.465-0.063 0.66-0.174l-0.007 0.003c0.19-0.113 0.411-0.214 0.642-0.293l0.025-0.007v2.583c-0.752 0.141-1.423 0.393-2.032 0.74l0.032-0.017c-0.411 0.233-0.684 0.668-0.684 1.167 0 0.738 0.599 1.337 1.337 1.337 0.24 0 0.465-0.063 0.66-0.174l-0.007 0.003c0.19-0.113 0.411-0.214 0.642-0.293l0.025-0.007v2.103c0 1.105 0.895 2 2 2v0h22.8c1.105 0 2-0.895 2-2v0-28c0-1.105-0.895-2-2-2v0zM28 29.333h-21.333v-1.443c0.265 0.079 0.492 0.173 0.708 0.287l-0.021-0.010c0.187 0.106 0.411 0.169 0.65 0.169 0.738 0 1.336-0.598 1.336-1.336 0-0.499-0.274-0.934-0.679-1.163l-0.007-0.003c-0.578-0.307-1.248-0.546-1.954-0.683l-0.045-0.007v-2.587c0.265 0.079 0.492 0.173 0.708 0.287l-0.021-0.010c0.187 0.106 0.411 0.169 0.65 0.169 0.738 0 1.336-0.598 1.336-1.336 0-0.499-0.274-0.934-0.679-1.163l-0.007-0.003c-0.578-0.307-1.248-0.546-1.954-0.683l-0.045-0.007v-2.587c0.265 0.079 0.492 0.173 0.708 0.287l-0.021-0.010c0.187 0.106 0.411 0.169 0.65 0.169 0.738 0 1.336-0.598 1.336-1.336 0-0.499-0.274-0.934-0.679-1.163l-0.007-0.003c-0.578-0.307-1.248-0.546-1.954-0.683l-0.045-0.007v-2.587c0.265 0.079 0.492 0.173 0.708 0.287l-0.021-0.010c0.187 0.106 0.411 0.169 0.65 0.169 0.738 0 1.336-0.598 1.336-1.336 0-0.499-0.274-0.934-0.679-1.163l-0.007-0.003c-0.578-0.307-1.248-0.546-1.954-0.683l-0.045-0.007v-2.587c0.265 0.079 0.492 0.173 0.708 0.287l-0.021-0.010c0.187 0.106 0.411 0.169 0.65 0.169 0.738 0 1.336-0.598 1.336-1.336 0-0.499-0.274-0.934-0.679-1.163l-0.007-0.003c-0.578-0.307-1.248-0.546-1.954-0.683l-0.045-0.007v-1.143h21.4zM24 16h-10.667c-0.736 0-1.333-0.597-1.333-1.333v0-8c0-0.736 0.597-1.333 1.333-1.333v0h10.667c0.736 0 1.333 0.597 1.333 1.333v0 8c0 0.736-0.597 1.333-1.333 1.333v0zM14.667 13.333h8v-5.333h-8z"></path> - </symbol> - <symbol id="category" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.377 8.793c-0.020-0.021-0.042-0.041-0.065-0.059l-0.001-0.001c0.123-0.413 0.203-0.683 0.22-0.743 0.088-0.251 0.139-0.541 0.139-0.842 0-0.693-0.268-1.323-0.707-1.793l0.001 0.002c-0.383-0.417-3.73-4.097-4.113-4.52-0.455-0.516-1.118-0.841-1.856-0.841-0.328 0-0.641 0.064-0.927 0.18l0.017-0.006c-0.333 0.127-6 2.057-9.733 3.333-0.222 0.074-0.411 0.2-0.559 0.362l-0.001 0.001-9.537 10.503c-2.393 2.63-2.587 6.54-0.433 8.927l6.463 7c0.927 1.044 2.271 1.7 3.769 1.703h0.017c1.809-0.053 3.418-0.867 4.524-2.133l0.006-0.007 5.733-6.297c0.213-0.237 0.343-0.551 0.343-0.897 0-0.742-0.601-1.343-1.343-1.343-0.397 0-0.753 0.172-0.999 0.445l-0.001 0.001-5.703 6.293c-0.624 0.736-1.531 1.215-2.551 1.27l-0.009 0c-0.722-0.002-1.368-0.322-1.807-0.827l-0.002-0.003-6.463-7c-1.193-1.333-1-3.767 0.433-5.333l9.303-10.26c2-0.667 8.387-2.85 9.407-3.21 0.6 0.667 3.687 4.057 4.050 4.45-0 0.005-0.001 0.011-0.001 0.017s0 0.012 0.001 0.017l-0-0.001c-0.097 0.3-1.41 4.803-2.267 7.77-0.043 0.125-0.067 0.268-0.067 0.418 0 0.736 0.597 1.333 1.333 1.333 0.622 0 1.144-0.426 1.292-1.002l0.002-0.009 1.070-3.667c0.91 1.86 0.877 4.037-0.23 5.333-0.517 0.598-1.276 0.974-2.124 0.974-0.012 0-0.023-0-0.035-0l0.002 0c-1.167 0-2.39-0.617-3.353-1.693-0.992-1.094-1.599-2.554-1.599-4.155 0-0.265 0.017-0.526 0.049-0.783l-0.003 0.031c0.137 0.055 0.295 0.087 0.461 0.087 0.277 0 0.534-0.089 0.742-0.239l-0.004 0.003c0.728-0.516 1.197-1.354 1.197-2.303 0-0.011-0-0.022-0-0.032v0.002c0-1.473-1.194-2.667-2.667-2.667s-2.667 1.194-2.667 2.667v0c0 0.001 0 0.002 0 0.004 0 0.176 0.015 0.349 0.043 0.518l-0.003-0.018c0.058 0.311 0.217 0.578 0.442 0.772l0.002 0.001c-0.667 2.533 0.023 5.63 2.020 7.863 1.47 1.653 3.417 2.61 5.34 2.61 0.016 0 0.034 0 0.053 0 1.645 0 3.119-0.734 4.111-1.893l0.006-0.007c2.323-2.707 1.99-7.227-0.76-10.307z"></path> - </symbol> - <symbol id="checkbox-multiple" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 25h-14.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h14.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM12 28v-9.333c0-0.736-0.597-1.333-1.333-1.333v0h-9.333c-0.736 0-1.333 0.597-1.333 1.333v0 9.333c0 0.736 0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 20h6.667v6.667h-6.667zM30.667 10h-14.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h14.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM12 13.333v-9.333c0-0.736-0.597-1.333-1.333-1.333v0h-9.333c-0.736 0-1.333 0.597-1.333 1.333v0 9.333c0 0.736 0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 5.333h6.667v6.667h-6.667zM5.543 11c0.231-0.003 0.434-0.123 0.552-0.304l0.002-0.003 2.133-3.333c0.066-0.102 0.105-0.226 0.105-0.36 0-0.369-0.299-0.669-0.669-0.669-0.236 0-0.443 0.122-0.562 0.306l-0.002 0.003-1.583 2.48-0.633-0.937c-0.122-0.178-0.324-0.294-0.553-0.294-0.369 0-0.667 0.299-0.667 0.668 0 0.139 0.043 0.269 0.116 0.376l-0.002-0.002 1.22 1.777c0.122 0.178 0.323 0.293 0.552 0.293 0 0 0.001 0 0.001 0h-0z"></path> - </symbol> - <symbol id="checkbox" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.333 26.667h-18.667c-0.736 0-1.333-0.597-1.333-1.333v0-18.667c0-0.736 0.597-1.333 1.333-1.333v0h18.667c0.736 0 1.333 0.597 1.333 1.333v0 18.667c0 0.736-0.597 1.333-1.333 1.333v0zM8 24h16v-16h-16zM14.533 21.333c0.4-0.005 0.757-0.186 0.998-0.468l0.002-0.002 6.8-8c0.135-0.205 0.216-0.457 0.216-0.727 0-0.736-0.597-1.333-1.333-1.333-0.339 0-0.648 0.127-0.884 0.335l0.001-0.001-5.847 6.843-2.82-3.137c-0.247-0.275-0.603-0.448-1-0.448-0.741 0-1.341 0.6-1.341 1.341 0 0.344 0.129 0.657 0.342 0.895l-0.001-0.001 3.857 4.263c0.245 0.271 0.598 0.44 0.99 0.44 0.004 0 0.007 0 0.011-0h-0.001z"></path> - </symbol> - <symbol id="checkmark" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M11.913 27.333c-0.369-0.001-0.702-0.151-0.943-0.393l-0-0-10.583-10.667c-0.225-0.238-0.363-0.56-0.363-0.914 0-0.736 0.597-1.333 1.333-1.333 0.358 0 0.684 0.141 0.923 0.372l-0-0 9.607 9.667 17.807-19c0.244-0.26 0.59-0.421 0.973-0.421 0.737 0 1.335 0.598 1.335 1.335 0 0.354-0.138 0.675-0.362 0.914l0.001-0.001-18.753 20c-0.236 0.263-0.574 0.432-0.951 0.443l-0.002 0z"></path> - </symbol> - <symbol id="circle-caret-down" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM17 22.19l6.703-8c0.194-0.23 0.311-0.53 0.311-0.857 0-0.737-0.598-1.335-1.335-1.335-0.41 0-0.777 0.185-1.022 0.476l-0.002 0.002-5.667 6.773-5.63-6.767c-0.246-0.28-0.604-0.456-1.004-0.456-0.736 0-1.333 0.597-1.333 1.333 0 0.312 0.107 0.599 0.286 0.826l-0.002-0.003 6.633 8c0.245 0.294 0.61 0.481 1.020 0.483h0c0.005 0 0.012 0 0.018 0 0.409 0 0.776-0.184 1.020-0.475l0.002-0.002z"></path> - </symbol> - <symbol id="circle-caret-left" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM19.69 23.523c0.194-0.23 0.312-0.53 0.312-0.857 0-0.41-0.185-0.777-0.476-1.021l-0.002-0.002-6.773-5.667 6.767-5.61c0.312-0.246 0.51-0.625 0.51-1.049 0-0.736-0.597-1.333-1.333-1.333-0.337 0-0.644 0.125-0.879 0.33l0.002-0.001-8 6.633c-0.294 0.244-0.48 0.608-0.483 1.016v0.001c-0 0.004-0 0.010-0 0.015 0 0.409 0.184 0.776 0.475 1.020l0.002 0.002 8 6.703c0.23 0.194 0.53 0.312 0.857 0.312 0.41 0 0.777-0.185 1.021-0.476l0.002-0.002z"></path> - </symbol> - <symbol id="circle-caret-right" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM14.19 23.69l8-6.69c0.292-0.246 0.477-0.612 0.477-1.022 0-0.005-0-0.010-0-0.016v0.001c-0.001-0.411-0.188-0.778-0.481-1.022l-0.002-0.002-8-6.633c-0.224-0.177-0.511-0.284-0.823-0.284-0.736 0-1.333 0.597-1.333 1.333 0 0.4 0.176 0.759 0.455 1.003l0.002 0.001 6.767 5.61-6.773 5.667c-0.293 0.246-0.478 0.613-0.478 1.023 0 0.737 0.598 1.335 1.335 1.335 0.327 0 0.627-0.118 0.859-0.313l-0.002 0.002z"></path> - </symbol> - <symbol id="circle-caret-up" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM10.36 19.517l5.61-6.767 5.667 6.773c0.246 0.293 0.613 0.478 1.023 0.478 0.737 0 1.335-0.598 1.335-1.335 0-0.327-0.118-0.627-0.313-0.859l0.002 0.002-6.683-8c-0.246-0.292-0.612-0.477-1.022-0.477-0.004 0-0.008 0-0.012 0h0.001c-0.41 0.002-0.775 0.189-1.018 0.481l-0.002 0.002-6.633 8c-0.204 0.233-0.329 0.541-0.329 0.877 0 0.736 0.597 1.333 1.333 1.333 0.425 0 0.803-0.198 1.047-0.508l0.002-0.003z"></path> - </symbol> - <symbol id="circle-close" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM17.887 16l4.39-4.39c0.241-0.241 0.391-0.575 0.391-0.943 0-0.737-0.597-1.334-1.334-1.334-0.368 0-0.702 0.149-0.943 0.391v0l-4.39 4.39-4.39-4.39c-0.241-0.241-0.575-0.391-0.943-0.391-0.737 0-1.334 0.597-1.334 1.334 0 0.368 0.149 0.702 0.391 0.943v0l4.39 4.39-4.39 4.39c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391l4.39-4.39 4.39 4.39c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="circle-create" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM22.667 14.667h-5.333v-5.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 5.333h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h5.333v5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-5.333h5.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="clipboard" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26 0h-20c-1.473 0-2.667 1.194-2.667 2.667v0 26.667c0 1.473 1.194 2.667 2.667 2.667v0h20c1.473 0 2.667-1.194 2.667-2.667v0-26.667c0-1.473-1.194-2.667-2.667-2.667v0zM20.917 2.667l-0.77 2.667h-8.277l-0.75-2.667zM6 29.333v-26.667h2.35l1.227 4.36c0.162 0.566 0.675 0.973 1.283 0.973h10.287c0.001 0 0.002 0 0.002 0 0.605 0 1.115-0.402 1.279-0.954l0.002-0.009 1.263-4.37h2.307v26.667zM14.333 22.667c-0.323 0-0.618-0.115-0.849-0.305l0.002 0.002-4.333-3.55c-0.299-0.247-0.488-0.617-0.488-1.032 0-0.737 0.598-1.335 1.335-1.335 0.322 0 0.618 0.114 0.849 0.305l-0.002-0.002 3.457 2.84 6.823-5.923c0.216-0.158 0.488-0.253 0.781-0.253 0.736 0 1.333 0.597 1.333 1.333 0 0.357-0.14 0.681-0.368 0.92l0-0.001-7.667 6.667c-0.232 0.205-0.538 0.331-0.873 0.333h-0z"></path> - </symbol> - <symbol id="collection" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M11.393 30.667H4.606a3.273 3.273 0 0 1-3.273-3.273v-6.787a3.273 3.273 0 0 1 3.273-3.273h6.787a3.273 3.273 0 0 1 3.273 3.273v6.787a3.273 3.273 0 0 1-3.273 3.273zM4.607 20a.61.61 0 0 0-.607.607v6.787a.61.61 0 0 0 .607.607h6.787a.61.61 0 0 0 .607-.607v-6.787a.61.61 0 0 0-.607-.607zm22.786-5.333h-6.787a3.273 3.273 0 0 1-3.273-3.273V4.607a3.273 3.273 0 0 1 3.273-3.273h6.787a3.273 3.273 0 0 1 3.273 3.273v6.787a3.273 3.273 0 0 1-3.273 3.273zM20.607 4a.61.61 0 0 0-.607.607v6.787a.61.61 0 0 0 .607.607h6.787a.61.61 0 0 0 .607-.607V4.607A.61.61 0 0 0 27.394 4zM12 14.667H4A2.667 2.667 0 0 1 1.333 12V4A2.667 2.667 0 0 1 4 1.333h8A2.667 2.667 0 0 1 14.667 4v8A2.667 2.667 0 0 1 12 14.667zM4 4v8h8V4zm25.333 18.667h-4v-4a1.333 1.333 0 0 0-2.666 0v4h-4a1.333 1.333 0 0 0 0 2.666h4v4a1.333 1.333 0 0 0 2.666 0v-4h4a1.333 1.333 0 0 0 0-2.666z"></path> - </symbol> - <symbol id="column-one" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 32H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h28a2 2 0 0 1 2 2v28a2 2 0 0 1-2 2zM2.667 29.333h26.667V2.666H2.667zm22.666-4H6.666a1.333 1.333 0 0 1 0-2.666h18.667a1.333 1.333 0 0 1 0 2.666zm1.334-6.666c0-.736-.597-1.333-1.333-1.333H6.667a1.333 1.333 0 0 0 0 2.666h18.667c.736 0 1.333-.597 1.333-1.333zm0-5.334c0-.736-.597-1.333-1.333-1.333H6.667a1.333 1.333 0 0 0 0 2.666h18.667c.736 0 1.333-.597 1.333-1.333zm0-5.333c0-.736-.597-1.333-1.333-1.333H6.667a1.333 1.333 0 0 0 0 2.666h18.667c.736 0 1.333-.597 1.333-1.333z"></path> - </symbol> - <symbol id="column-two" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.667 0H2.334A2.333 2.333 0 0 0 .001 2.333v27.333a2.333 2.333 0 0 0 2.333 2.333h27.333A2.333 2.333 0 0 0 32 29.666V2.333A2.333 2.333 0 0 0 29.667 0zm-27 2.667h12v26.667h-12zm26.666 26.666h-12V2.666h12zm-2.666-4H20a1.333 1.333 0 0 1 0-2.666h6.667a1.333 1.333 0 0 1 0 2.666zM28 18.667c0-.736-.597-1.333-1.333-1.333H20A1.333 1.333 0 0 0 20 20h6.667c.736 0 1.333-.597 1.333-1.333zm0-5.334c0-.736-.597-1.333-1.333-1.333H20a1.333 1.333 0 0 0 0 2.666h6.667c.736 0 1.333-.597 1.333-1.333zM28 8c0-.736-.597-1.333-1.333-1.333H20a1.333 1.333 0 0 0 0 2.666h6.667C27.403 9.333 28 8.736 28 8z"></path> - </symbol> - <symbol id="comment" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26 11.667c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667v0c1.473 0 2.667 1.194 2.667 2.667v0zM16 9c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM8.667 9c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM32 3v17.293c0.001 0.026 0.001 0.056 0.001 0.087 0 1.631-1.322 2.953-2.953 2.953-0.017 0-0.034-0-0.050-0l0.003 0h-11.503l-9.623 8.333c-0.232 0.205-0.538 0.331-0.873 0.333h-0c-0 0-0 0-0.001 0-0.2 0-0.391-0.044-0.561-0.123l0.008 0.003c-0.464-0.215-0.78-0.677-0.78-1.213 0-0 0-0 0-0v0-7.333h-2.667c-0.025 0.001-0.054 0.001-0.083 0.001-1.613 0-2.92-1.307-2.92-2.92 0-0.043 0.001-0.085 0.003-0.127l-0 0.006v-17.293c-0-0.017-0.001-0.037-0.001-0.057 0-0.813 0.323-1.55 0.848-2.090l-0.001 0.001c0.55-0.528 1.299-0.854 2.123-0.854 0.020 0 0.040 0 0.060 0.001l-0.003-0h26.277c1.637 0 2.697 1.177 2.697 3zM29.333 3c0-0.008 0-0.018 0-0.027 0-0.109-0.012-0.214-0.035-0.316l0.002 0.009h-26.277c-0.014-0.002-0.030-0.002-0.047-0.002-0.090 0-0.175 0.027-0.245 0.074l0.002-0.001c-0.043 0.063-0.068 0.142-0.068 0.226 0 0.013 0.001 0.026 0.002 0.039l-0-0.002v17.293c0 0.373 0.12 0.373 0.333 0.373h4c0.736 0 1.333 0.597 1.333 1.333v0 5.747l7.793-6.747c0.232-0.205 0.538-0.331 0.873-0.333h12c0.001 0 0.002 0 0.002 0 0.184 0 0.333-0.149 0.333-0.333 0-0.014-0.001-0.028-0.003-0.042l0 0.002z"></path> - </symbol> - <symbol id="components" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M21.447 32h-7.113c-0.001 0-0.003 0-0.005 0-0.736 0-1.333-0.597-1.333-1.333 0-0.443 0.216-0.835 0.548-1.077l0.004-0.003c0.942-0.696 1.547-1.803 1.547-3.051 0-0.001 0-0.001 0-0.002v0c0.001-0.033 0.002-0.072 0.002-0.111 0-2.042-1.655-3.697-3.697-3.697s-3.697 1.655-3.697 3.697c0 0.039 0.001 0.078 0.002 0.117l-0-0.006c0 0.001 0 0.003 0 0.004 0 1.248 0.603 2.354 1.533 3.045l0.010 0.007c0.333 0.245 0.547 0.636 0.547 1.077 0 0.736-0.597 1.333-1.333 1.333-0.001 0-0.002 0-0.004 0h-7.123c-0.736 0-1.333-0.597-1.333-1.333v0-20.113c0-0.736 0.597-1.333 1.333-1.333v0h4.333c-0.401-0.819-0.636-1.782-0.637-2.8v-0c-0.001-0.043-0.002-0.094-0.002-0.146 0-3.514 2.849-6.363 6.363-6.363s6.363 2.849 6.363 6.363c0 0.051-0.001 0.102-0.002 0.153l0-0.008c-0.002 1.018-0.238 1.981-0.657 2.838l0.017-0.038h4.333c0.736 0 1.333 0.597 1.333 1.333v0 3.493c0.819-0.401 1.782-0.636 2.8-0.637h0c3.509 0.007 6.351 2.853 6.351 6.363 0 3.514-2.849 6.363-6.363 6.363-1.015 0-1.974-0.238-2.825-0.66l0.037 0.017v5.173c0 0.736-0.597 1.333-1.333 1.333v0zM2.667 29.333h3c-0.402-0.819-0.637-1.782-0.637-2.8v-0c-0.001-0.043-0.002-0.094-0.002-0.146 0-3.514 2.849-6.363 6.363-6.363s6.363 2.849 6.363 6.363c0 0.051-0.001 0.102-0.002 0.153l0-0.008c-0.002 1.018-0.238 1.981-0.657 2.838l0.017-0.038h3v-6.627c0-0.001 0-0.003 0-0.005 0-0.736 0.597-1.333 1.333-1.333 0.443 0 0.835 0.216 1.077 0.548l0.003 0.004c0.696 0.942 1.802 1.547 3.050 1.547 0.001 0 0.003 0 0.004 0h-0c0.033 0.001 0.072 0.002 0.111 0.002 2.042 0 3.697-1.655 3.697-3.697s-1.655-3.697-3.697-3.697c-0.039 0-0.078 0.001-0.117 0.002l0.006-0c-0.001 0-0.002 0-0.004 0-1.248 0-2.354 0.604-3.042 1.536l-0.007 0.010c-0.245 0.336-0.637 0.551-1.080 0.551-0.736 0-1.333-0.597-1.333-1.333 0-0.002 0-0.003 0-0.005v0-4.95h-5.78c-0.004 0-0.009 0-0.014 0-0.669 0-1.224-0.493-1.319-1.136l-0.001-0.007c-0.006-0.046-0.010-0.098-0.010-0.152s0.004-0.106 0.011-0.158l-0.001 0.006c0.011-0.129 0.039-0.248 0.080-0.36l-0.003 0.010c0.096-0.252 0.257-0.462 0.463-0.614l0.004-0.003c0.942-0.697 1.547-1.804 1.547-3.053 0-0 0-0 0-0v0c0.001-0.033 0.002-0.072 0.002-0.111 0-2.042-1.655-3.697-3.697-3.697s-3.697 1.655-3.697 3.697c0 0.039 0.001 0.078 0.002 0.117l-0-0.006c0 0 0 0.001 0 0.002 0 1.243 0.599 2.347 1.523 3.038l0.010 0.007 0.073 0.053c0.137 0.117 0.249 0.259 0.33 0.419l0.004 0.008c0.079 0.154 0.13 0.335 0.14 0.527l0 0.003c0.002 0.024 0.002 0.052 0.002 0.080 0 0.144-0.023 0.283-0.065 0.413l0.003-0.009c-0.056 0.168-0.138 0.313-0.242 0.439l0.002-0.002c-0.116 0.135-0.256 0.247-0.412 0.33l-0.008 0.004c-0.155 0.081-0.337 0.132-0.53 0.143l-0.003 0h-5.86z"></path> - </symbol> - <symbol id="contentlist" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22 25.333h-8c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h8c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM23.333 18.667c0-0.736-0.597-1.333-1.333-1.333v0h-8c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h8c0.736 0 1.333-0.597 1.333-1.333v0zM10 22.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM10 17.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM28 10.617v20.050c0 0.736-0.597 1.333-1.333 1.333v0h-21.333c-0.736 0-1.333-0.597-1.333-1.333v0-29.333c0-0.736 0.597-1.333 1.333-1.333v0h13.127c0 0 0.001 0 0.001 0 0.397 0 0.753 0.173 0.998 0.449l0.001 0.001 8.207 9.283c0.207 0.234 0.333 0.543 0.333 0.882 0 0.001 0 0.001 0 0.002v-0zM18.667 3.583v4.397c0 1.933 0.75 2.687 2.667 2.687h3.597zM25.333 29.333v-16h-4c-3.39 0-5.333-1.95-5.333-5.353v-5.313h-9.333v26.667z"></path> - </symbol> - <symbol id="content-draft" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.667 0h-9.333c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333v0-29.333c0-0.736-0.597-1.333-1.333-1.333v0zM19.667 29.333v-26.667h6.667v1.333h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v2.667h-0.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h0.667v2.667h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v2.667h-0.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h0.667v2.667h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v1.333zM8.667 32c-0.455-0-0.857-0.228-1.097-0.576l-0.003-0.004-4.333-6.333c-0.146-0.211-0.233-0.472-0.233-0.753v0-14.333c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 13.92l3 4.387 3-4.387v-13.92c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 14.333c-0 0.282-0.087 0.543-0.236 0.758l0.003-0.004-4.333 6.333c-0.243 0.352-0.645 0.58-1.1 0.58v0zM10 22v-13.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 13.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM13.633 0.783c-0.49-0.485-1.165-0.784-1.909-0.784-0.020 0-0.040 0-0.061 0.001l0.003-0h-6c-0.028-0.001-0.061-0.002-0.094-0.002-1.397 0-2.534 1.114-2.572 2.502l-0 0.003v2.163c0 0.736 0.597 1.333 1.333 1.333v0h8.667c0.736 0 1.333-0.597 1.333-1.333v0-2.103c0.001-0.020 0.001-0.044 0.001-0.068 0-0.667-0.268-1.271-0.701-1.712l0 0z"></path> - </symbol> - <symbol id="content-tree" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18 20h12c1.105 0 2-0.895 2-2v0-4c0-1.105-0.895-2-2-2v0h-12c-1.105 0-2 0.895-2 2v0 0.667h-5.333v-5.333h6c1.105 0 2-0.895 2-2v0-5.333c0-1.105-0.895-2-2-2v0h-14.667c-1.105 0-2 0.895-2 2v0 5.333c0 1.105 0.895 2 2 2v0h6v18.667c0 0.736 0.597 1.333 1.333 1.333v0h6.667v0.667c0 1.105 0.895 2 2 2v0h12c1.105 0 2-0.895 2-2v0-4c0-1.105-0.895-2-2-2v0h-12c-1.105 0-2 0.895-2 2v0 0.667h-5.333v-9.333h5.333v0.667c0 1.105 0.895 2 2 2v0zM18.667 14.667h10.667v2.667h-10.667zM18.667 26.667h10.667v2.667h-10.667zM2.667 6.667v-4h13.333v4z"></path> - </symbol> - <symbol id="content-type" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16.25 13.3c-0.238 0.226-0.561 0.365-0.917 0.365-0.38 0-0.723-0.159-0.966-0.415l-0.001-0.001-3.87-4.073c-0.227-0.239-0.366-0.562-0.366-0.918 0-0.191 0.040-0.373 0.113-0.537l-0.003 0.009c0.667-1.5 0.423-3.12-0.577-4.12-0.48-0.475-1.108-0.799-1.81-0.898l-0.017-0.002 1.333 1.333c0.242 0.241 0.392 0.575 0.392 0.944 0 0.165-0.030 0.323-0.085 0.469l0.003-0.009-0.877 2.37c-0.137 0.367-0.421 0.651-0.777 0.787l-0.009 0.003-2.383 0.877c-0.138 0.053-0.298 0.083-0.464 0.083-0.368 0-0.701-0.149-0.942-0.39v0l-1.317-1.333c0.103 0.717 0.426 1.345 0.897 1.827l-0.001-0.001c1 1 2.613 1.227 4.11 0.577 0.156-0.069 0.338-0.109 0.529-0.109 0.354 0 0.676 0.138 0.915 0.363l-0.001-0.001 4.087 3.863c0.281 0.246 0.458 0.605 0.458 1.006 0 0.736-0.597 1.333-1.333 1.333-0.372 0-0.709-0.153-0.951-0.399l-0-0-3.493-3.303c-0.568 0.188-1.221 0.297-1.9 0.297-1.672 0-3.19-0.659-4.309-1.733l0.002 0.002c-1.763-1.767-2.213-4.527-1.15-7.030 0.208-0.482 0.679-0.813 1.228-0.813 0.368 0 0.701 0.149 0.942 0.39v0l2.57 2.573 1-0.37 0.357-0.983-2.563-2.593c-0.24-0.241-0.389-0.574-0.389-0.941 0-0.547 0.329-1.017 0.8-1.222l0.009-0.003c2.503-1.073 5.26-0.62 7.030 1.153 1.077 1.119 1.74 2.642 1.74 4.32 0 0.675-0.107 1.325-0.306 1.934l0.012-0.044 3.3 3.48c0.226 0.238 0.365 0.561 0.365 0.917 0 0.38-0.159 0.723-0.415 0.966l-0.001 0.001zM30.28 20.433c-1.116-1.071-2.634-1.731-4.306-1.731-0.679 0-1.333 0.109-1.944 0.31l0.044-0.012-3.493-3.303c-0.242-0.246-0.579-0.399-0.951-0.399-0.736 0-1.333 0.597-1.333 1.333 0 0.401 0.177 0.76 0.456 1.004l0.002 0.001 4.087 3.85c0.238 0.225 0.56 0.363 0.914 0.363 0.191 0 0.373-0.040 0.538-0.113l-0.009 0.003c1.497-0.65 3.11-0.407 4.11 0.593 0.471 0.482 0.794 1.11 0.895 1.809l0.002 0.018-1.333-1.333c-0.241-0.241-0.574-0.39-0.942-0.39-0.167 0-0.326 0.031-0.473 0.087l0.009-0.003-2.377 0.88c-0.366 0.139-0.649 0.423-0.784 0.781l-0.003 0.009-0.877 2.37c-0.052 0.137-0.082 0.294-0.082 0.459 0 0.369 0.15 0.703 0.392 0.944l1.333 1.333c-0.716-0.104-1.343-0.431-1.82-0.907l0 0c-1-1-1.227-2.62-0.577-4.12 0.069-0.156 0.109-0.338 0.109-0.529 0-0.356-0.139-0.679-0.367-0.918l0.001 0.001-3.867-4.073c-0.24-0.233-0.567-0.377-0.929-0.377-0.736 0-1.333 0.597-1.333 1.333 0 0.336 0.124 0.644 0.33 0.878l-0.001-0.002 3.3 3.48c-0.187 0.565-0.294 1.216-0.294 1.892 0 1.677 0.662 3.199 1.739 4.32l-0.002-0.002c1.083 1.068 2.57 1.727 4.212 1.727 0.026 0 0.052-0 0.078-0.001l-0.004 0c0.994-0.003 1.937-0.214 2.791-0.591l-0.045 0.018c0.48-0.209 0.809-0.679 0.809-1.226 0-0.367-0.148-0.7-0.389-0.941l0 0-2.563-2.593 0.367-1 1-0.37 2.57 2.573c0.241 0.241 0.574 0.39 0.942 0.39 0.549 0 1.020-0.331 1.224-0.805l0.003-0.009c1.053-2.487 0.603-5.247-1.16-7.013zM31.613 1.7l-1.313-1.303c-0.241-0.241-0.575-0.39-0.943-0.39-0.346 0-0.661 0.132-0.898 0.348l0.001-0.001-3.363 3.063c-0.266 0.245-0.433 0.594-0.433 0.983 0 0.297 0.097 0.572 0.262 0.794l-0.003-0.004-13.923 13.923-1.39-1.39c-0.241-0.241-0.574-0.389-0.942-0.389s-0.7 0.149-0.942 0.389l0-0-6.627 6.637c-0.682 0.683-1.104 1.625-1.104 2.667s0.422 1.984 1.104 2.667l1.213 1.213c0.679 0.682 1.618 1.103 2.655 1.103 0.004 0 0.008 0 0.012-0h-0.001c0.004 0 0.008 0 0.013 0 1.037 0 1.976-0.422 2.654-1.103l6.633-6.634c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0l-1.39-1.387 13.92-13.927c0.217 0.165 0.492 0.264 0.789 0.267h0.034c0.378-0.010 0.716-0.176 0.952-0.436l0.001-0.001 3.080-3.363c0.211-0.235 0.339-0.547 0.339-0.889 0-0.373-0.153-0.709-0.399-0.951l-0-0zM5.763 29.033c-0.198 0.204-0.474 0.332-0.78 0.333h-0c-0.306-0-0.583-0.128-0.78-0.333l-0-0-1.203-1.227c-0.204-0.198-0.332-0.474-0.333-0.78v-0c0.002-0.307 0.13-0.584 0.333-0.783l0-0 5.667-5.69 1.19 1.19c0.103 0.16 0.235 0.292 0.388 0.394l0.005 0.003 1.197 1.193z"></path> - </symbol> - <symbol id="content-type-group" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 17v-3a2 2 0 0 0-2-2H25V5.333h5a.667.667 0 0 0 .667-.667V3.333A.667.667 0 0 0 30 2.666c-1.333 0-2.603-1.667-2.977-2.333A.666.666 0 0 0 26.446 0h-7.113a.667.667 0 0 0-.667.667v4c0 .368.298.667.667.667H21v6.667h-7.287l1.537-5.77 1.697-.98.387-4.617-2.37-.633-1.963 4.187.98 1.697-1.647 6.117h-1.38l-1.78-4.913a4.208 4.208 0 0 0 .907-2.618c0-.57-.113-1.114-.317-1.61l.01.028c-.607-1.667-2-2.78-3.603-2.887L6.129.001a.668.668 0 0 0-.533.266l-.001.002a.663.663 0 0 0-.092.632L5.501.896l1 2.71-.817.73-1.077-.033-1-2.707a.668.668 0 0 0-1.077-.26c-1.16 1.113-1.507 2.847-.903 4.517a4.243 4.243 0 0 0 3.136 2.828l.028.005 1.21 3.317H3.334a2 2 0 0 0-2 2v3c-.736 0-1.333.597-1.333 1.333v2.667c0 .736.597 1.333 1.333 1.333v7.667a2 2 0 0 0 2 2h25.333a2 2 0 0 0 2-2v-7.667c.736 0 1.333-.597 1.333-1.333v-2.667c0-.736-.597-1.333-1.333-1.333zM20 4V1.333h6.077c.467.693 1.667 2.237 3.257 2.593v.073zm2.333 1.333h1.333V12h-1.333zM5.9 7.843a.671.671 0 0 0-.585-.437h-.002c-1.087-.067-2-.817-2.433-2a3.094 3.094 0 0 1-.215-1.144c0-.349.057-.685.162-.998l-.006.022.687 1.89a.668.668 0 0 0 .606.44h.001l1.793.057h.015a.677.677 0 0 0 .452-.171l-.001.001 1.333-1.197a.664.664 0 0 0 .182-.731l.002.005-.683-1.897c.61.388 1.071.964 1.307 1.644l.007.022c.433 1.193.22 2.353-.573 3.103a.665.665 0 0 0-.166.718l-.002-.005 1.753 4.833H7.427zM28 29.333H4V21h8v.667c0 .736.597 1.333 1.333 1.333h5.333c.736 0 1.333-.597 1.333-1.333V21h8zm-13.333-9V19h2.667v1.333zm13.333-2h-8v-.667c0-.736-.597-1.333-1.333-1.333h-5.333c-.736 0-1.333.597-1.333 1.333v.667h-8v-3.667h24z"></path> - </symbol> - <symbol id="copy" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22 13.383l-6.667-7.597c-0.245-0.278-0.602-0.453-1-0.453h-10.667c-0.736 0-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333v0h17.333c0.736 0 1.333-0.597 1.333-1.333v0-16.403c-0-0.338-0.127-0.647-0.335-0.881l0.001 0.001zM19.020 14.030h-2.433c-1.173 0-1.81-0.307-1.81-1.933v-2.903zM5 29.333v-21.333h7.107v4.1c0 2.92 1.63 4.597 4.477 4.597h3.083v12.637zM29.333 8.050l-6.667-7.597c-0.245-0.278-0.602-0.453-1-0.453h-10.667c-0.736 0-1.333 0.597-1.333 1.333v0 1.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0h7.107v4.1c0 2.92 1.63 4.597 4.477 4.597h3.083v12.637h-1.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h3c0.736 0 1.333-0.597 1.333-1.333v0-16.403c-0-0.338-0.127-0.647-0.335-0.881l0.001 0.001zM22.113 6.763v-2.903l4.243 4.837h-2.433c-1.173 0-1.81-0.307-1.81-1.933z"></path> - </symbol> - <symbol id="copy-subtree" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 12H18a2 2 0 0 0-2 2v.667h-5.333V9.334h6a2 2 0 0 0 2-2V2.001a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v5.333a2 2 0 0 0 2 2h6v6.667c0 .736.597 1.333 1.333 1.333H16v.667a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zM2.667 6.667v-4H16v4zM32 26v4a2 2 0 0 1-2 2H18a2 2 0 0 1-2-2v-.667h-.667a1.333 1.333 0 0 1 0-2.666H16V26a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2zm-20.667.667h-.667V26A1.333 1.333 0 0 0 8 26v2c0 .736.597 1.333 1.333 1.333h2a1.333 1.333 0 0 0 0-2.666zm-2-3.334c.736 0 1.333-.597 1.333-1.333v-2A1.333 1.333 0 0 0 8 20v2c0 .736.597 1.333 1.333 1.333z"></path> - </symbol> - <symbol id="core" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 21.333h-8c-0.736 0-1.333-0.597-1.333-1.333v0-8c0-0.736 0.597-1.333 1.333-1.333v0h8c0.736 0 1.333 0.597 1.333 1.333v0 8c0 0.736-0.597 1.333-1.333 1.333v0zM13.333 18.667h5.333v-5.333h-5.333zM30.667 14.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-2.667v-2.667h2.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-2.667c-0.001-1.354-1.012-2.472-2.32-2.642l-0.013-0.001v-2.69c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2.667h-3v-2.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2.667h-2.667v-2.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2.667h-2.667v-2.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2.667c-1.473 0-2.667 1.194-2.667 2.667v0h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v2.667h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v2.667h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v2.667h-2.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667c0 1.473 1.194 2.667 2.667 2.667v0 2.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.667h2.667v2.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.667h2.667v2.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.667h2.667v2.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.667c1.473 0 2.667-1.194 2.667-2.667v0h2.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-2.667v-2.667h2.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-2.667v-2.667zM6.667 25.333v-18.667h18.667v18.667z"></path> - </symbol> - <symbol id="create-content" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.35 8.91l-7.183-8.44c-0.243-0.284-0.6-0.465-0.999-0.47h-11.501c-0.736 0-1.333 0.597-1.333 1.333v0 26.667c0 0.736 0.597 1.333 1.333 1.333v0h8.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-7.333v-24h7.833v4.71c0 3.17 1.763 5 4.837 5h3.33v5.623c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-8.227c0-0 0-0 0-0.001 0-0.33-0.12-0.632-0.318-0.865l0.002 0.002zM18.507 7.377v-3.573l5 5.893h-2.84c-1.803 0-2.16-0.85-2.16-2.32zM29.333 26.667c0 0.736-0.597 1.333-1.333 1.333v0h-2.667v2.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-2.667h-2.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h2.667v-2.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 2.667h2.667c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="create" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.333 14.667h-10v-10c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 10h-10c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h10v10c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-10h10c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="custom_tags" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M4 25.333c-0 0-0.001 0-0.001 0-0.736 0-1.333-0.597-1.333-1.333 0-0.367 0.148-0.699 0.388-0.94l-0 0 7.060-7.097-7.053-7c-0.243-0.242-0.394-0.577-0.394-0.947 0-0.737 0.597-1.334 1.334-1.334 0.367 0 0.699 0.148 0.94 0.388l8 7.93c0.243 0.242 0.393 0.576 0.393 0.946 0 0.003 0 0.005-0 0.008v-0c-0 0.369-0.148 0.703-0.387 0.947l0-0-8 8.040c-0.242 0.243-0.576 0.393-0.946 0.393-0 0-0.001 0-0.001 0h0zM29.333 24c0-0.736-0.597-1.333-1.333-1.333v0h-13.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h13.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="customer" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.333 24.833c0 1.193-0.76 4.083-4 4.71v1.123c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-1.093c-3.95-0.6-4-3.83-4-3.867h2.667v-0.040c0 0.223 0.217 1.333 2.667 1.333s2.667-1.667 2.667-2.183-0.243-2.15-2.667-2.15c-4.377 0-5.333-3.49-5.333-4.833 0-1.193 0.76-4.083 4-4.71v-1.123c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 1.093c3.95 0.6 4 3.83 4 3.867h-2.667v0.040c0-0.223-0.217-1.333-2.667-1.333s-2.667 1.667-2.667 2.183 0.243 2.15 2.667 2.15c4.377 0 5.333 3.49 5.333 4.833zM20.333 8c0-4.418-3.582-8-8-8s-8 3.582-8 8c0 4.418 3.582 8 8 8v0c4.418 0 8-3.582 8-8v0zM17.667 8c0 2.946-2.388 5.333-5.333 5.333s-5.333-2.388-5.333-5.333c0-2.946 2.388-5.333 5.333-5.333v0c2.946 0 5.333 2.388 5.333 5.333v0zM15.667 32h-11.363c-0.001 0-0.001 0-0.002 0-0.837 0-1.581-0.397-2.054-1.014l-0.005-0.006c-0.361-0.469-0.578-1.065-0.578-1.712 0-0.213 0.024-0.421 0.068-0.62l-0.004 0.019c0.033-0.352 0.089-0.672 0.169-0.982l-0.009 0.042c0.1-0.4 1.69-6.667 2.973-8.437 1.917-2.623 5.107-2.623 5.52-2.623h3c0.516 0.005 1.018 0.044 1.509 0.117l-0.059-0.007c0.65 0.097 1.142 0.651 1.142 1.32 0 0.737-0.597 1.334-1.334 1.334-0.068 0-0.134-0.005-0.199-0.015l0.007 0.001c-0.309-0.049-0.669-0.079-1.035-0.083l-0.005-0h-3.103s-2.133-0.043-3.29 1.537c-0.6 0.82-1.837 4.643-2.54 7.517v0.040c-0.041 0.16-0.072 0.349-0.086 0.542l-0.001 0.011c-0.007 0.092-0.022 0.177-0.046 0.259l0.002-0.009c-0.010 0.021-0.016 0.046-0.016 0.073 0 0.011 0.001 0.021 0.003 0.032l-0-0.001h11.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0z"></path> - </symbol> - <symbol id="dashboard" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26 16.667c-0.736 0-1.333-0.597-1.333-1.333v0c0-4.41-3.887-8-8.667-8s-8.667 3.59-8.667 8c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0c0-5.883 5.083-10.667 11.333-10.667s11.333 4.783 11.333 10.667c0 0.736-0.597 1.333-1.333 1.333v0zM32 16c0-8.837-7.163-16-16-16s-16 7.163-16 16c0 8.837 7.163 16 16 16v0c8.833-0.009 15.991-7.167 16-15.999v-0.001zM29.333 16c0 7.364-5.97 13.333-13.333 13.333s-13.333-5.97-13.333-13.333c0-7.364 5.97-13.333 13.333-13.333v0c7.364 0 13.333 5.97 13.333 13.333v0zM20.943 11.057c-0.241-0.242-0.575-0.391-0.943-0.391s-0.702 0.149-0.943 0.391v0l-2.37 2.37c-0.206-0.059-0.442-0.093-0.686-0.093h-0c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c-0-0.245-0.035-0.481-0.098-0.705l0.004 0.018 2.37-2.37c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="date" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.667 4h-7.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h7.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 4.223v24.887c0 0.001 0 0.002 0 0.003 0 1.591-1.287 2.881-2.876 2.887h-26.247c-1.59-0.006-2.877-1.296-2.877-2.887 0-0.001 0-0.002 0-0.004v0-24.897c0 0 0 0 0-0 0-1.587 1.284-2.874 2.869-2.88h1.464c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-1.463c-0.113 0.004-0.203 0.096-0.203 0.21 0 0.001 0 0.002 0 0.004v-0 7.12h26.667v-7.11c0-0.001 0-0.002 0-0.003 0-0.119-0.095-0.216-0.213-0.22h-1.454c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.453c1.591 0.004 2.88 1.295 2.88 2.887 0 0.001 0 0.002 0 0.004v-0zM29.333 29.11v-15.11h-26.667v15.11c-0 0.001-0 0.002-0 0.004 0 0.118 0.093 0.214 0.21 0.22l0.001 0h26.247c0.117-0.004 0.21-0.099 0.21-0.217 0-0.002-0-0.005-0-0.007v0zM9.667 5.027s0-0.027 0-0.027v-3.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.667s0 0.017 0 0.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM25 5.027s0-0.027 0-0.027v-3.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.667s0 0.017 0 0.027c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007z"></path> - </symbol> - <symbol id="discard" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M17.887 16l10.39-10.39c0.241-0.241 0.391-0.575 0.391-0.943 0-0.737-0.597-1.334-1.334-1.334-0.368 0-0.702 0.149-0.943 0.391v0l-10.39 10.39-10.39-10.39c-0.241-0.241-0.575-0.391-0.943-0.391-0.737 0-1.334 0.597-1.334 1.334 0 0.368 0.149 0.702 0.391 0.943l10.39 10.39-10.39 10.39c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391l10.39-10.39 10.39 10.39c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="download" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.18 21.027l-4.573 3.747c-0.436 0.35-0.997 0.561-1.607 0.561s-1.17-0.212-1.612-0.565l0.005 0.004-4.57-3.747c-0.279-0.246-0.453-0.603-0.453-1.002 0-0.736 0.597-1.333 1.333-1.333 0.305 0 0.585 0.102 0.81 0.274l-0.003-0.002 3.157 2.583v-20.213c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 20.213l3.153-2.583c0.228-0.189 0.524-0.303 0.847-0.303 0.737 0 1.335 0.598 1.335 1.335 0 0.415-0.189 0.785-0.486 1.030l-0.002 0.002zM28 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-21.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="drag" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M13.833 10.333h-4c-0.368 0-0.667-0.298-0.667-0.667v0-4c0-0.368 0.298-0.667 0.667-0.667v0h4c0.368 0 0.667 0.298 0.667 0.667v0 4c0 0.368-0.298 0.667-0.667 0.667v0zM22.833 9.667v-4c0-0.368-0.298-0.667-0.667-0.667v0h-4c-0.368 0-0.667 0.298-0.667 0.667v0 4c0 0.368 0.298 0.667 0.667 0.667v0h4c0.368 0 0.667-0.298 0.667-0.667v0zM14.5 18v-4c0-0.368-0.298-0.667-0.667-0.667v0h-4c-0.368 0-0.667 0.298-0.667 0.667v0 4c0 0.368 0.298 0.667 0.667 0.667v0h4c0.368 0 0.667-0.298 0.667-0.667v0zM22.833 18v-4c0-0.368-0.298-0.667-0.667-0.667v0h-4c-0.368 0-0.667 0.298-0.667 0.667v0 4c0 0.368 0.298 0.667 0.667 0.667v0h4c0.368 0 0.667-0.298 0.667-0.667v0zM14.5 26.333v-4c0-0.368-0.298-0.667-0.667-0.667v0h-4c-0.368 0-0.667 0.298-0.667 0.667v0 4c0 0.368 0.298 0.667 0.667 0.667v0h4c0.368 0 0.667-0.298 0.667-0.667v0zM22.833 26.333v-4c0-0.368-0.298-0.667-0.667-0.667v0h-4c-0.368 0-0.667 0.298-0.667 0.667v0 4c0 0.368 0.298 0.667 0.667 0.667v0h4c0.368 0 0.667-0.298 0.667-0.667v0z"></path> - </symbol> - <symbol id="dropdown" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 27.667h-10.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h10.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM28 21.667c0-0.736-0.597-1.333-1.333-1.333v0h-17.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h17.333c0.736 0 1.333-0.597 1.333-1.333v0zM5.593 27.64c0.095-0.019 0.179-0.046 0.258-0.080l-0.008 0.003c0.089-0.037 0.165-0.078 0.236-0.124l-0.006 0.004c0.148-0.099 0.271-0.222 0.367-0.365l0.003-0.005c0.042-0.066 0.083-0.141 0.116-0.22l0.004-0.010c0.031-0.071 0.058-0.155 0.075-0.242l0.001-0.008c0.016-0.078 0.026-0.168 0.027-0.259v-0.001c-0.002-0.368-0.151-0.701-0.39-0.943l0 0c-0.061-0.059-0.128-0.114-0.198-0.163l-0.005-0.003c-0.066-0.042-0.141-0.083-0.22-0.116l-0.010-0.004c-0.071-0.031-0.155-0.058-0.242-0.075l-0.008-0.001c-0.078-0.016-0.168-0.025-0.26-0.025s-0.182 0.009-0.269 0.026l0.009-0.001c-0.095 0.019-0.179 0.046-0.258 0.080l0.008-0.003c-0.089 0.037-0.165 0.078-0.236 0.124l0.006-0.004c-0.076 0.053-0.142 0.108-0.204 0.167l0-0c-0.241 0.241-0.39 0.574-0.39 0.942 0 0 0 0.001 0 0.001v-0c0.001 0.092 0.010 0.182 0.028 0.269l-0.002-0.009c0.019 0.095 0.046 0.179 0.080 0.258l-0.003-0.008c0.037 0.089 0.078 0.165 0.124 0.236l-0.004-0.006c0.099 0.148 0.222 0.271 0.365 0.367l0.005 0.003c0.066 0.042 0.141 0.083 0.22 0.116l0.010 0.004c0.071 0.031 0.155 0.058 0.242 0.075l0.008 0.001c0.078 0.017 0.168 0.027 0.26 0.027s0.182-0.010 0.268-0.028l-0.008 0.002zM5.593 22.973c0.095-0.019 0.179-0.046 0.258-0.080l-0.008 0.003c0.089-0.037 0.165-0.078 0.236-0.124l-0.006 0.004c0.148-0.099 0.271-0.222 0.367-0.365l0.003-0.005c0.042-0.066 0.083-0.141 0.116-0.22l0.004-0.010c0.031-0.071 0.058-0.155 0.075-0.242l0.001-0.008c0.017-0.078 0.027-0.168 0.027-0.26s-0.010-0.182-0.028-0.268l0.002 0.008c-0.019-0.095-0.046-0.179-0.080-0.258l0.003 0.008c-0.037-0.089-0.078-0.165-0.124-0.236l0.004 0.006c-0.099-0.148-0.222-0.271-0.365-0.367l-0.005-0.003c-0.066-0.042-0.141-0.083-0.22-0.116l-0.010-0.004c-0.071-0.031-0.155-0.058-0.242-0.075l-0.008-0.001c-0.078-0.017-0.168-0.027-0.26-0.027s-0.182 0.010-0.268 0.029l0.008-0.002c-0.095 0.019-0.179 0.046-0.258 0.080l0.008-0.003c-0.089 0.037-0.165 0.078-0.236 0.124l0.006-0.004c-0.148 0.099-0.271 0.222-0.367 0.365l-0.003 0.005c-0.042 0.066-0.083 0.141-0.116 0.22l-0.004 0.010c-0.031 0.071-0.058 0.155-0.075 0.242l-0.001 0.008c-0.017 0.078-0.027 0.168-0.027 0.26s0.010 0.182 0.028 0.268l-0.002-0.008c0.019 0.095 0.046 0.179 0.080 0.258l-0.003-0.008c0.037 0.089 0.078 0.165 0.124 0.236l-0.004-0.006c0.099 0.148 0.222 0.271 0.365 0.367l0.005 0.003c0.066 0.042 0.141 0.083 0.22 0.116l0.010 0.004c0.071 0.031 0.155 0.058 0.242 0.075l0.008 0.001c0.078 0.017 0.168 0.027 0.26 0.027s0.182-0.010 0.268-0.028l-0.008 0.002zM27.777 12.403c0.139-0.207 0.222-0.462 0.222-0.736 0-0.461-0.234-0.868-0.59-1.107l-0.005-0.003-2-1.333c-0.208-0.14-0.464-0.224-0.74-0.224s-0.532 0.084-0.745 0.227l0.005-0.003-2 1.333c-0.347 0.244-0.571 0.643-0.571 1.094 0 0.736 0.597 1.333 1.333 1.333 0.269 0 0.52-0.080 0.729-0.217l-0.005 0.003 1.257-0.833 1.27 0.84c0.207 0.139 0.462 0.222 0.736 0.222 0.461 0 0.868-0.234 1.107-0.59l0.003-0.005zM30.667 4.333h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 10.667c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0-10.667c0-0.736-0.597-1.333-1.333-1.333v0zM2.667 7h14.667v8h-14.667zM29.333 15h-9.333v-8h9.333z"></path> - </symbol> - <symbol id="edit" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.253 7.857l-1.36 2.183c-0.239 0.38-0.657 0.629-1.132 0.629-0.241 0-0.466-0.064-0.661-0.175l0.007 0.003-9.087-5.117c-0.408-0.233-0.678-0.666-0.678-1.161 0-0.261 0.075-0.505 0.205-0.711l-0.003 0.006 1.363-2.18c0.533-0.808 1.436-1.334 2.463-1.334 0.532 0 1.031 0.141 1.462 0.388l-0.014-0.008 6.393 3.6c0.671 0.379 1.166 1.004 1.365 1.75l0.005 0.020c0.056 0.204 0.088 0.437 0.088 0.679 0 0.53-0.154 1.024-0.421 1.439l0.006-0.011zM6.52 31.91l8.197-3.18c0.296-0.117 0.535-0.329 0.683-0.596l0.003-0.007 7.42-13.58c0.13-0.202 0.207-0.449 0.207-0.714 0-0.736-0.597-1.333-1.333-1.333-0.531 0-0.989 0.31-1.204 0.759l-0.003 0.008-7.193 13.153-6 2.333-0.297-6.213 7.257-13.25c0.13-0.202 0.207-0.449 0.207-0.714 0-0.736-0.597-1.333-1.333-1.333-0.531 0-0.989 0.31-1.204 0.759l-0.003 0.008-7.427 13.577c-0.104 0.185-0.165 0.406-0.165 0.642 0 0.020 0 0.041 0.001 0.061l-0-0.003 0.37 8.44c0.032 0.711 0.616 1.275 1.332 1.275 0.174 0 0.341-0.033 0.494-0.094l-0.009 0.003zM11.82 23.73l7.333-12.667c0.096-0.179 0.152-0.392 0.152-0.618 0-0.736-0.597-1.333-1.333-1.333-0.471 0-0.885 0.244-1.122 0.613l-0.003 0.005-7.333 12.667c-0.112 0.191-0.178 0.421-0.178 0.666 0 0.49 0.265 0.919 0.659 1.15l0.006 0.003c0.189 0.113 0.417 0.18 0.66 0.18 0.002 0 0.005 0 0.007-0h-0c0.49-0.001 0.919-0.266 1.15-0.66l0.003-0.006z"></path> - </symbol> - <symbol id="embed" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29 9.733L20.79.45a1.33 1.33 0 0 0-.999-.45H6.667c-.736 0-1.333.597-1.333 1.333v13a1.333 1.333 0 0 0 2.666 0V2.666h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16H7.999v-1a1.333 1.333 0 0 0-2.666 0v2.333c0 .736.597 1.333 1.333 1.333h21.333c.736 0 1.333-.597 1.333-1.333v-20.05-.001c0-.339-.126-.648-.335-.883l.001.001zM20 7.98V3.583l6.263 7.083h-3.597c-1.91 0-2.667-.753-2.667-2.687zm-2.667 13.353a2.161 2.161 0 0 1-.666 1.559l-.001.001-3.727 3.72a1.334 1.334 0 0 1-1.88-1.894l2.07-2.053h-9.13a1.333 1.333 0 0 1 0-2.666h9.127l-2.067-2.053a1.333 1.333 0 0 1 .94-2.281c.367 0 .699.148.94.387l3.743 3.72c.401.397.65.948.65 1.557v.003z"></path> - </symbol> - <symbol id="erp" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28 8c2.209 0 4-1.791 4-4s-1.791-4-4-4c-2.209 0-4 1.791-4 4v0c0 0.003 0 0.006 0 0.009 0 0.279 0.029 0.551 0.085 0.813l-0.005-0.026-10 6c-1.476-1.718-3.651-2.799-6.079-2.799-4.418 0-8 3.582-8 8s3.582 8 8 8c2.49 0 4.714-1.137 6.181-2.921l0.011-0.014 6.080 3.937c-0.169 0.498-0.267 1.071-0.267 1.667 0 2.946 2.388 5.333 5.333 5.333s5.333-2.388 5.333-5.333c0-2.946-2.388-5.333-5.333-5.333-0.002 0-0.004 0-0.007 0h0c-0.001 0-0.001 0-0.002 0-1.396 0-2.665 0.54-3.611 1.423l0.003-0.003-6.21-4c0.309-0.816 0.487-1.76 0.487-2.746 0-1.052-0.203-2.055-0.573-2.975l0.019 0.054 10-6c0.683 0.568 1.57 0.913 2.537 0.913 0.006 0 0.012 0 0.017-0h-0.001zM8 21.333c-2.946 0-5.333-2.388-5.333-5.333s2.388-5.333 5.333-5.333c2.946 0 5.333 2.388 5.333 5.333v0c0 2.946-2.388 5.333-5.333 5.333v0zM25.333 24c1.473 0 2.667 1.194 2.667 2.667s-1.194 2.667-2.667 2.667c-1.473 0-2.667-1.194-2.667-2.667v0c0-1.473 1.194-2.667 2.667-2.667v0z"></path> - </symbol> - <symbol id="error" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.333 25.333c0 1.841-1.492 3.333-3.333 3.333s-3.333-1.492-3.333-3.333c0-1.841 1.492-3.333 3.333-3.333v0c1.841 0 3.333 1.492 3.333 3.333v0zM17.333 18.667v-14c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 14c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="fields" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M6.667 24h-5.333c-0.736 0-1.333-0.597-1.333-1.333v0-13.333c0-0.736 0.597-1.333 1.333-1.333v0h5.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-4v10.667h4c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 22.667v-13.333c0-0.736-0.597-1.333-1.333-1.333v0h-16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h14.667v10.667h-14.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333v0zM15.93 26.667h-3.93v-21.333h3.93c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-10.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.070v21.333h-4.070c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h10.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="file-text" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M17.31 27.333h-2.62v-9.883h-3.357v-2.117h9.333v2.117h-3.357zM27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16z"></path> - </symbol> - <symbol id="file-video" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16zM23.333 20.543v3.583c0 1.063-0.527 1.517-0.843 1.69-0.199 0.115-0.438 0.183-0.694 0.183-0.001 0-0.002 0-0.003 0h0c-0.283-0-0.546-0.081-0.769-0.22l0.006 0.004-0.753-0.48c-0.667-0.423-0.943-1.303-0.943-2v1.363c0 0.013 0 0.028 0 0.044 0 1.067-0.854 1.934-1.915 1.956l-0.002 0h-6.83c-1.065-0.022-1.92-0.891-1.92-1.96 0-0.014 0-0.028 0-0.043l-0 0.002v-4.667c-0-0.008-0-0.018-0-0.027 0-1.076 0.861-1.951 1.931-1.973l0.002-0h6.793c1.076 0.022 1.94 0.9 1.94 1.98 0 0.007-0 0.014-0 0.021v-0.001 1.333c0-0.69 0.287-1.573 0.923-1.983l0.037-0.027 0.77-0.457c0.215-0.135 0.476-0.215 0.756-0.215 0.249 0 0.484 0.064 0.688 0.175l-0.008-0.004c0.38 0.223 0.833 0.69 0.833 1.72z"></path> - </symbol> - <symbol id="file" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 32h-21.333c-0.736 0-1.333-0.597-1.333-1.333v0-29.333c0-0.736 0.597-1.333 1.333-1.333v0h13.127c0 0 0.001 0 0.001 0 0.397 0 0.753 0.173 0.998 0.449l0.001 0.001 8.207 9.283c0.207 0.234 0.333 0.543 0.333 0.882 0 0.001 0 0.001 0 0.002v-0 20.050c0 0.736-0.597 1.333-1.333 1.333v0zM6.667 29.333h18.667v-16h-4c-3.39 0-5.333-1.95-5.333-5.353v-5.313h-9.333zM18.667 3.583v4.397c0 1.933 0.75 2.687 2.667 2.687h3.597z"></path> - </symbol> - <symbol id="filters" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.333 21.333c-2.946 0-5.333-2.388-5.333-5.333s2.388-5.333 5.333-5.333c2.946 0 5.333 2.388 5.333 5.333v0c0 2.946-2.388 5.333-5.333 5.333v0zM5.333 13.333c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM6.667 8v-6.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 6.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM6.667 30.667v-6.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 6.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM16 16.703c-2.946 0-5.333-2.388-5.333-5.333s2.388-5.333 5.333-5.333c2.946 0 5.333 2.388 5.333 5.333v0c0 2.946-2.388 5.333-5.333 5.333v0zM16 8.703c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM17.333 3.333v-2c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM17.333 30.667v-11.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 11.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM26.667 25.963c-2.946 0-5.333-2.388-5.333-5.333s2.388-5.333 5.333-5.333c2.946 0 5.333 2.388 5.333 5.333v0c0 2.946-2.388 5.333-5.333 5.333v0zM26.667 17.963c-1.473 0-2.667 1.194-2.667 2.667s1.194 2.667 2.667 2.667c1.473 0 2.667-1.194 2.667-2.667v0c0-1.473-1.194-2.667-2.667-2.667v0zM28 12.667v-11.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 11.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM28 30.667v-2c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="filters-funnel" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18.667 32a1.328 1.328 0 0 1-.842-.302l.002.002-5.333-4.333a1.33 1.33 0 0 1-.493-1.033v-9.85L2.474 5.041a1.992 1.992 0 0 1-.473-1.283v-.13c0-2.843 0-2.843.39-3.237.241-.241.574-.39.942-.39h25.335c.368 0 .701.149.942.39.39.397.39.397.39 3.367a1.992 1.992 0 0 1-.489 1.302l.002-.002L20 16.485v14.183c0 .736-.597 1.333-1.333 1.333zm-4-6.3l2.667 2.167V16c0-.326.117-.624.312-.855l-.002.002 9.69-11.637v-.843H4.667v.843l9.69 11.637c.193.229.31.528.31.853zM27.48 3.333zm-22.973 0v.017z"></path> - </symbol> - <symbol id="focus" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.61 31.61c-0.061 0.059-0.128 0.114-0.198 0.163l-0.005 0.003-0.103 0.057c-0.043 0.020-0.083 0.047-0.127 0.063-0.038 0.016-0.083 0.031-0.13 0.042l-0.006 0.001c-0.029 0.011-0.067 0.022-0.106 0.032l-0.008 0.002c-0.078 0.017-0.168 0.026-0.26 0.027h-6.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h3.447l-8.39-8.39c-0.241-0.241-0.391-0.575-0.391-0.943 0-0.737 0.597-1.334 1.334-1.334 0.368 0 0.702 0.149 0.943 0.391l8.39 8.39v-3.447c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 6.667c-0.001 0.092-0.010 0.182-0.028 0.269l0.002-0.009c-0.009 0.042-0.020 0.079-0.035 0.114l0.001-0.004c-0.013 0.056-0.028 0.102-0.046 0.148l0.003-0.008c-0.020 0.048-0.041 0.088-0.065 0.127l0.002-0.004c-0.020 0.037-0.037 0.073-0.057 0.107-0.050 0.075-0.105 0.141-0.166 0.2l-0 0zM31.973 1.073c-0.009-0.042-0.020-0.079-0.035-0.114l0.001 0.004c-0.013-0.056-0.028-0.102-0.046-0.148l0.003 0.008c-0.020-0.048-0.041-0.088-0.065-0.127l0.002 0.004c-0.020-0.037-0.037-0.073-0.057-0.107-0.050-0.075-0.105-0.141-0.166-0.2l-0-0c-0.061-0.059-0.128-0.114-0.198-0.163l-0.005-0.004-0.103-0.057c-0.036-0.023-0.077-0.045-0.121-0.064l-0.006-0.002c-0.038-0.016-0.083-0.031-0.13-0.042l-0.006-0.001c-0.029-0.011-0.067-0.022-0.106-0.032l-0.008-0.002c-0.078-0.016-0.168-0.026-0.259-0.027h-6.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h3.447l-8.39 8.39c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391l8.39-8.39v3.447c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-6.667c-0.001-0.092-0.010-0.182-0.028-0.269l0.002 0.009zM12.943 19.073c-0.241-0.242-0.575-0.391-0.943-0.391s-0.702 0.149-0.943 0.391v0l-8.39 8.373v-3.447c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 6.667c0.001 0.092 0.010 0.182 0.028 0.269l-0.002-0.009c0.009 0.042 0.020 0.079 0.035 0.114l-0.001-0.004c0.013 0.056 0.028 0.102 0.046 0.148l-0.003-0.008c0.020 0.048 0.041 0.088 0.065 0.127l-0.002-0.004c0.020 0.037 0.037 0.073 0.057 0.107 0.050 0.075 0.105 0.141 0.166 0.2l0 0c0.061 0.059 0.128 0.114 0.198 0.163l0.005 0.003 0.103 0.057c0.043 0.020 0.083 0.047 0.127 0.063 0.038 0.016 0.083 0.031 0.13 0.042l0.006 0.001c0.029 0.011 0.067 0.022 0.106 0.032l0.008 0.002c0.077 0.018 0.167 0.028 0.259 0.030l0.001 0h6.667c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-3.447l8.39-8.39c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0zM12.943 11.073l-8.39-8.407h3.447c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-6.667c-0.092 0.001-0.182 0.010-0.269 0.028l0.009-0.002c-0.046 0.011-0.084 0.022-0.121 0.036l0.008-0.002c-0.053 0.013-0.099 0.027-0.143 0.046l0.006-0.002c-0.049 0.021-0.091 0.044-0.13 0.069l0.004-0.002-0.103 0.053c-0.076 0.053-0.142 0.108-0.204 0.167l0.001-0c-0.061 0.059-0.116 0.125-0.164 0.196l-0.003 0.004c-0.020 0.037-0.037 0.077-0.057 0.11-0.022 0.035-0.043 0.075-0.061 0.118l-0.002 0.006c-0.015 0.038-0.030 0.084-0.042 0.132l-0.002 0.008c-0.013 0.031-0.025 0.068-0.033 0.106l-0.001 0.004c-0.016 0.078-0.026 0.168-0.027 0.259v6.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-3.447l8.39 8.39c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="folder-empty" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 28h-21.333c-0.6-0-1.107-0.396-1.274-0.941l-0.003-0.009-4-13.333c-0.036-0.115-0.056-0.247-0.056-0.383 0-0.736 0.597-1.333 1.333-1.333h29.333c0.736 0 1.333 0.597 1.333 1.333 0 0.137-0.021 0.269-0.059 0.393l0.003-0.009-4 13.333c-0.17 0.554-0.677 0.95-1.277 0.95h-0zM6.333 25.333h19.333l3.2-10.667h-25.74zM27.333 9.333v-0.977c-0.017-1.305-1.079-2.357-2.386-2.357-0.008 0-0.017 0-0.025 0h-10.922l-0.667-0.98c-0.438-0.62-1.153-1.020-1.96-1.020-0.014 0-0.028 0-0.042 0l0.002-0h-4.277c-0.006-0-0.013-0-0.020-0-1.296 0-2.349 1.040-2.37 2.332l-0 0.002v3c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.667h3.877l1 1.427c0.242 0.345 0.636 0.569 1.083 0.573h11.374v0.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="folder" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.167 30.667h-26.333c-0.009 0-0.019 0-0.030 0-1.541 0-2.792-1.244-2.803-2.782v-15.884c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 15.867c0 0.002 0 0.004 0 0.005 0 0.77-0.315 1.466-0.823 1.968l-0 0c-0.512 0.511-1.219 0.827-1.999 0.827-0.004 0-0.008 0-0.012-0h0.001zM2.667 13.333v14.55c0.010 0.067 0.067 0.117 0.135 0.117 0.005 0 0.010-0 0.015-0.001l-0.001 0h26.333c0.003 0 0.006 0 0.009 0 0.049 0 0.094-0.019 0.128-0.050l-0 0c0.027-0.019 0.044-0.049 0.047-0.083l0-0v-14.533zM32 8.667v-1.467c-0.015-1.586-1.305-2.867-2.893-2.867-0.007 0-0.014 0-0.021 0h-16.349l-1.18-1.743c-0.524-0.763-1.392-1.257-2.375-1.257-0.003 0-0.006 0-0.008 0h-6.29c-0.006-0-0.013-0-0.020-0-1.573 0-2.85 1.269-2.863 2.839v4.495c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4.493c0.015-0.099 0.099-0.174 0.201-0.174 0.005 0 0.011 0 0.016 0.001l-0.001-0h6.29c0.002-0 0.004-0 0.006-0 0.068 0 0.129 0.033 0.167 0.083l0 0 1.577 2.333c0.243 0.354 0.646 0.583 1.102 0.583 0.002 0 0.003 0 0.005 0h17.056c0.005-0 0.010-0.001 0.016-0.001 0.117 0 0.214 0.087 0.231 0.199l0 0.001v1.467c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="form-data" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 2.667h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0-24c0-0.736-0.597-1.333-1.333-1.333v0zM12.333 21.333v-2.667h7.333v2.667zM19.667 24v2.667h-7.333v-2.667zM19.667 13.333v2.667h-7.333v-2.667zM29.333 13.333v2.667h-7v-2.667zM9.667 16h-7v-2.667h7zM2.667 18.667h7v2.667h-7zM22.333 18.667h7v2.667h-7zM29.333 5.333v5.333h-26.667v-5.333zM2.667 24h7v2.667h-7zM22.333 26.667v-2.667h7v2.667z"></path> - </symbol> - <symbol id="form" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M15.333 20c-0.364-0.001-0.693-0.147-0.934-0.384l-8.666-8.523c-0.245-0.242-0.397-0.578-0.397-0.95 0-0.737 0.597-1.334 1.334-1.334 0.365 0 0.696 0.147 0.937 0.384l7.69 7.577 14.407-15.027c0.243-0.254 0.585-0.411 0.963-0.411 0.737 0 1.334 0.597 1.334 1.334 0 0.359-0.141 0.684-0.371 0.924l0-0-15.333 16c-0.243 0.253-0.584 0.41-0.962 0.41-0.001 0-0.001 0-0.002 0h0zM29.333 27.763v-13.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 13.333c0 0.131-0.106 0.237-0.237 0.237v0h-23.527c-0.131 0-0.237-0.106-0.237-0.237v0-23.527c0-0.131 0.106-0.237 0.237-0.237v0h13.253c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-13.253c-1.603 0.002-2.901 1.301-2.903 2.903v23.527c0.002 1.603 1.301 2.901 2.903 2.903h23.527c1.603-0.002 2.901-1.301 2.903-2.903v-0z"></path> - </symbol> - <symbol id="future-publication" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.333 32c-4.786 0-8.667-3.88-8.667-8.667s3.88-8.667 8.667-8.667c4.786 0 8.667 3.88 8.667 8.667v0c0 4.786-3.88 8.667-8.667 8.667v0zM23.333 17.333c-3.314 0-6 2.686-6 6s2.686 6 6 6c3.314 0 6-2.686 6-6v0c0-3.314-2.686-6-6-6v0zM26.277 24.723l-1.61-1.607v-3.117c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.48c-0.008 0.056-0.013 0.122-0.013 0.188 0 0.368 0.149 0.701 0.39 0.942v0l2 2c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0zM19.333 2.547c0-0.736-0.597-1.333-1.333-1.333v0h-6.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h6.667c0.736 0 1.333-0.597 1.333-1.333v0zM26.597 1.213h-1.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.333c0.039 0.003 0.070 0.036 0.070 0.076 0 0.001-0 0.003-0 0.004v-0 6.333h-24v-6.333c-0-0.002-0-0.004-0-0.007 0-0.036 0.028-0.065 0.064-0.067h1.334c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.333c-1.507 0.004-2.728 1.226-2.73 2.733v22.63c0 0.001 0 0.002 0 0.003 0 1.513 1.224 2.741 2.736 2.747h10.264c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-10.263c-0.040-0.005-0.070-0.039-0.070-0.079 0-0.001 0-0.003 0-0.004v0-13.613h24v0.030c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.040c0 0 0 0 0-0 0-1.513-1.224-2.741-2.736-2.747h-0.001zM9.030 4.623v-3.29c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.29c-0.733 0.456-1.213 1.257-1.213 2.17 0 1.406 1.14 2.547 2.547 2.547s2.547-1.14 2.547-2.547c0-0.913-0.481-1.714-1.203-2.163l-0.011-0.006zM22.97 4.623v-3.29c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.29c-0.733 0.456-1.213 1.257-1.213 2.17 0 1.406 1.14 2.547 2.547 2.547s2.547-1.14 2.547-2.547c0-0.913-0.481-1.714-1.203-2.163l-0.011-0.006z"></path> - </symbol> - <symbol id="gallery" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 23c-0.736 0-1.333-0.597-1.333-1.333v0-12h-18.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h18.667c1.473 0 2.667 1.194 2.667 2.667v0 12c0 0.736-0.597 1.333-1.333 1.333v0zM32 17.333v-12c0-1.473-1.194-2.667-2.667-2.667v0h-18.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h18.667v12c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM21.333 28.667h-18.667c-1.473 0-2.667-1.194-2.667-2.667v0-12c0-1.473 1.194-2.667 2.667-2.667v0h18.667c1.473 0 2.667 1.194 2.667 2.667v0 12c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 14v12h18.667v-12zM14.667 17c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM19.53 21.667l-3.14-2.667c-0.232-0.202-0.538-0.324-0.872-0.324s-0.639 0.123-0.873 0.326l0.002-0.001-1.647 1.44-2.667-2.713c-0.242-0.243-0.576-0.393-0.946-0.393-0.004 0-0.008 0-0.012 0h0.001c-0.369 0.001-0.702 0.151-0.943 0.393l-4.047 4.073c-0.196 0.231-0.315 0.532-0.315 0.86 0 0.736 0.597 1.333 1.333 1.333 0.335 0 0.642-0.124 0.876-0.328l-0.002 0.001 3.093-3.117 3.667 3.713c0.242 0.243 0.577 0.394 0.947 0.394 0.737 0 1.334-0.597 1.334-1.334 0-0.367-0.148-0.699-0.387-0.94l0 0-0.043-0.050 0.667-0.573 2.26 1.92c0.204 0.134 0.455 0.213 0.724 0.213 0.736 0 1.333-0.597 1.333-1.333 0-0.344-0.13-0.658-0.345-0.895l0.001 0.001z"></path> - </symbol> - <symbol id="go-to-root" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 32H18a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2zm-11.333-2.667h10.667v-2.667H18.667zM30 20H18a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2zm-11.333-2.667h10.667v-2.667H18.667zM13 8.88a1.332 1.332 0 1 0 1.999-1.76L9.562.91A2.5 2.5 0 0 0 7.702 0H7.66c-.746 0-1.414.332-1.865.857L5.792.86.332 7.123a1.333 1.333 0 1 0 1.997 1.756l.002-.003 4-4.58v20.37c0 2.75 1.92 4.667 4.667 4.667h2.333a1.333 1.333 0 0 0 0-2.666h-2.333c-1.27 0-2-.727-2-2v-7.333h4.333a1.333 1.333 0 0 0 0-2.666H8.998V4.315z"></path> - </symbol> - <symbol id="go-up" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 22.667h-19.62c-1.3 0-2.047-.73-2.047-2V10.98l4 4.567a1.332 1.332 0 1 0 1.999-1.76l-5.437-6.21a2.449 2.449 0 0 0-3.767-.053l-.003.003-5.46 6.263a1.333 1.333 0 0 0 2 1.752l4-4.58v9.703c0 2.747 1.94 4.667 4.713 4.667h19.62a1.333 1.333 0 0 0 0-2.666z"></path> - </symbol> - <symbol id="h1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.333 29.333c-0.736 0-1.333-0.597-1.333-1.333v0-20.493l-3 3.38c-0.246 0.277-0.603 0.45-1 0.45-0.738 0-1.336-0.598-1.336-1.336 0-0.341 0.128-0.652 0.338-0.888l-0.001 0.001 5.333-6c0.245-0.277 0.602-0.451 1-0.451 0.736 0 1.333 0.597 1.333 1.333 0 0.002 0 0.003 0 0.005v-0 24c0 0.736-0.597 1.333-1.333 1.333v0zM7.333 13.167c-2.1 0-3.597 0.48-4.667 1.16v-10.327c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.060c0-0.517 0.303-3.107 4.667-3.107s4.667 2.667 4.667 3.167v9c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9c0-0.057-0.083-5.833-7.333-5.833z"></path> - </symbol> - <symbol id="h2" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 29.333h-12c-0 0-0.001 0-0.001 0-0.736 0-1.333-0.597-1.333-1.333 0-0.276 0.084-0.532 0.227-0.745l-0.003 0.005 10.743-16.117c0.416-0.587 0.666-1.318 0.666-2.107 0-1.069-0.457-2.030-1.186-2.701l-0.003-0.002c-0.76-0.667-1.803-1-3.11-1-4.537 0-4.667 4.19-4.667 4.667 0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0c0-2.537 1.533-7.333 7.333-7.333 1.973 0 3.61 0.563 4.87 1.667 1.289 1.168 2.095 2.849 2.095 4.718 0 1.332-0.409 2.568-1.109 3.59l0.014-0.022-9.38 14.047h9.51c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM7.333 13.167c-2.1 0-3.597 0.48-4.667 1.16v-10.327c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.063c0.017-0.527 0.313-3.103 4.667-3.103s4.667 2.667 4.667 3.167v9c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9c0-0.057-0.083-5.833-7.333-5.833z"></path> - </symbol> - <symbol id="h3" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.283 29.333c-4 0-5.907-2.053-6.8-3.777-0.078-0.164-0.123-0.357-0.123-0.56 0-0.736 0.597-1.333 1.333-1.333 0.492 0 0.922 0.267 1.153 0.664l0.003 0.006c0.817 1.573 2.267 2.333 4.433 2.333 0.51 0 5.050-0.17 5.050-4.667 0-4.613-4.843-4.667-5.050-4.667-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0c4.907 0 5.050-4.19 5.050-4.667s-0.193-4.667-5.050-4.667c-2.12 0-3.553 0.73-4.38 2.237-0.238 0.388-0.66 0.644-1.141 0.644-0.736 0-1.333-0.597-1.333-1.333 0-0.218 0.052-0.423 0.145-0.605l-0.003 0.008c0.903-1.65 2.807-3.617 6.713-3.617 6.103 0 7.717 4.797 7.717 7.333 0 1.707-0.73 4.433-3.063 6.037 1.983 1.297 3.063 3.367 3.063 5.963 0 2.963-1.407 5.257-3.96 6.457-1.101 0.515-2.387 0.834-3.742 0.876l-0.015 0zM7.333 13.167c-2.1 0-3.597 0.48-4.667 1.16v-10.327c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.063c0.017-0.527 0.313-3.103 4.667-3.103s4.667 2.667 4.667 3.167v9c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9c0-0.057-0.083-5.833-7.333-5.833z"></path> - </symbol> - <symbol id="h4" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 4v24c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-10.667h-10.667c-0.001 0-0.002 0-0.002 0-0.736 0-1.333-0.597-1.333-1.333 0-0.118 0.015-0.233 0.044-0.343l-0.002 0.009 3-12c0.153-0.581 0.674-1.002 1.293-1.002 0.738 0 1.336 0.598 1.336 1.336 0 0.118-0.015 0.233-0.044 0.343l0.002-0.009-2.587 10.333h8.96v-10.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0zM7.333 13.167c-2.1 0-3.597 0.48-4.667 1.16v-10.327c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.063c0.017-0.527 0.313-3.103 4.667-3.103s4.667 2.667 4.667 3.167v9c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9c0-0.057-0.083-5.833-7.333-5.833z"></path> - </symbol> - <symbol id="h5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.387 29.333c-2.914-0.019-5.456-1.593-6.84-3.933l-0.020-0.037c-0.123-0.198-0.195-0.438-0.195-0.695 0-0.737 0.598-1.335 1.335-1.335 0.48 0 0.901 0.254 1.137 0.634l0.003 0.006c0.93 1.621 2.65 2.695 4.621 2.695 0.042 0 0.083-0 0.125-0.001l-0.006 0h0.037c0.48-0.043 4.75-0.543 4.75-6.427 0-5.81-4.197-6.040-4.667-6.050-3.573 0-4.79 2.203-4.8 2.223-0.221 0.449-0.675 0.753-1.2 0.753-0.736 0-1.333-0.597-1.333-1.333 0-0.008 0-0.016 0-0.024l-0 0.001v-11.81c0-0.736 0.597-1.333 1.333-1.333v0h9c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-7.667v7.377c1.284-0.748 2.826-1.189 4.471-1.189 0.069 0 0.138 0.001 0.206 0.002l-0.010-0c0.073 0 7.333 0.1 7.333 8.717 0 8.163-6.57 9-7.203 9.077-0.030 0.017-0.177 0.017-0.41 0.017zM7.333 13.167c-2.1 0-3.597 0.48-4.667 1.16v-10.327c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 24c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.063c0.017-0.527 0.313-3.103 4.667-3.103s4.667 2.667 4.667 3.167v9c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9c0-0.057-0.083-5.833-7.333-5.833z"></path> - </symbol> - <symbol id="h6" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.693 15.12c-1.97-1.787-4.513-1.757-5.053-1.73-0.009-0-0.019-0-0.030-0-1.737 0-3.341 0.575-4.63 1.544l0.020-0.014v-1.88c0-2.913 0.75-5.030 2.227-6.293 2.13-1.82 5.197-1.363 5.227-1.357 0.053 0.008 0.115 0.012 0.177 0.012 0.736 0 1.333-0.597 1.333-1.333 0-0.646-0.459-1.184-1.069-1.307l-0.009-0.001c-0.173-0.030-4.283-0.667-7.367 1.937-2.113 1.793-3.187 4.6-3.187 8.333v8.97c0.003 0.138 0.025 0.269 0.066 0.393l-0.003-0.009c0.223 2.21 1.14 4.040 2.667 5.303 1.24 1.019 2.843 1.637 4.59 1.637s3.35-0.618 4.603-1.647l-0.013 0.010c1.263-1.020 2.757-2.963 2.757-6.4 0-2.7-0.777-4.773-2.307-6.167zM27.563 25.637c-0.786 0.633-1.797 1.015-2.897 1.015s-2.11-0.383-2.906-1.022l0.009 0.007c-1.053-0.863-1.64-2.13-1.75-3.763 0-0.19-0.020-0.387-0.020-0.587 0-1.803 0.463-3.163 1.377-4.037 1.187-1.14 2.857-1.193 3.167-1.193h0.050c0.028 0.002 0.062 0.003 0.095 0.003s0.067-0.001 0.1-0.003l-0.005 0c0.017 0 1.85-0.133 3.133 1.053 0.94 0.867 1.417 2.27 1.417 4.177s-0.597 3.38-1.77 4.35zM14.667 19v9c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-9c0-0.517-0.303-3.167-4.667-3.167s-4.667 2.583-4.667 3.103v9.063c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-24c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 10.333c1.070-0.667 2.567-1.16 4.667-1.16 7.25-0.010 7.333 5.763 7.333 5.827z"></path> - </symbol> - <symbol id="history" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M21.493 20.58c-0.241 0.242-0.575 0.391-0.943 0.391s-0.702-0.149-0.943-0.391v0l-2-2c-0.181 0.051-0.389 0.082-0.604 0.087l-0.003 0c-1.471-0.003-2.662-1.196-2.662-2.667 0-0.98 0.529-1.836 1.316-2.3l0.012-0.007v-6.283c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 6.283c0.802 0.469 1.332 1.326 1.333 2.307v0c-0.001 0.27-0.041 0.53-0.115 0.776l0.005-0.019 1.937 1.937c0.242 0.241 0.391 0.575 0.391 0.943s-0.149 0.702-0.391 0.943v0zM16.897 1c-5.874 0.016-10.963 3.362-13.48 8.248l-0.040 0.085-0.78-2.333c-0.152-0.581-0.673-1.002-1.292-1.002-0.736 0-1.333 0.597-1.333 1.333 0 0.181 0.036 0.354 0.102 0.511l-0.003-0.009 1.827 5.467c0.182 0.533 0.679 0.91 1.264 0.91 0.001 0 0.002 0 0.002 0h-0c0.141-0.001 0.276-0.023 0.403-0.063l-0.010 0.003 5.89-1.817c0.552-0.172 0.945-0.679 0.945-1.277 0-0.737-0.598-1.335-1.335-1.335-0.139 0-0.273 0.021-0.399 0.061l0.009-0.003-3 0.927c2.065-4.192 6.307-7.026 11.211-7.026 6.831 0 12.379 5.499 12.456 12.312l0 0.007c-0.073 6.822-5.62 12.324-12.453 12.324-5.63 0-10.387-3.735-11.928-8.863l-0.023-0.088c-0.156-0.575-0.674-0.991-1.289-0.991-0.736 0-1.333 0.597-1.333 1.333 0 0.14 0.021 0.274 0.061 0.401l-0.003-0.009c1.887 6.35 7.67 10.901 14.517 10.901 8.311 0 15.055-6.706 15.116-15.002l0-0.006c-0.040-8.293-6.771-15-15.070-15-0.012 0-0.024 0-0.035 0h0.002z"></path> - </symbol> - <symbol id="home-page" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.72 32h-5c-0.736 0-1.333-0.597-1.333-1.333v0-5h-2.72v5c0 0.736-0.597 1.333-1.333 1.333v0h-5c-2.070 0-3.63-1.857-3.63-4.333l-0.037-9c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0l0.017 9c0 0.69 0.3 1.667 0.963 1.667h3.687v-5c0-0.736 0.597-1.333 1.333-1.333v0h5.373c0.736 0 1.333 0.597 1.333 1.333v0 5h3.667c0.71 0 0.96-1 0.96-1.667v-9c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0 9c0 2.517-1.517 4.333-3.613 4.333zM29.963 16.52l0.050-0.057 1.403-1.64 0.083-0.11c0.316-0.442 0.506-0.993 0.506-1.589 0-0.854-0.39-1.617-1.001-2.121l-0.005-0.004-2.847-2.367c-0.386-0.368-0.672-0.838-0.815-1.366l-0.005-0.021v-4.523c0-1.603-0.96-2.723-2.333-2.723h-2c-1.373 0-2.333 1.12-2.333 2.723v0.457l-2.777-2.093-0.843-0.647c-0.269-0.267-0.638-0.434-1.046-0.44h-0.034c-0.316 0.007-0.604 0.123-0.829 0.312l0.002-0.002-0.333 0.257-13.733 10.397c-0.654 0.514-1.070 1.304-1.070 2.192 0 0.583 0.179 1.124 0.486 1.571l-0.006-0.009 0.090 0.117 1.427 1.637 0.047 0.050c0.48 0.502 1.155 0.813 1.903 0.813 0 0 0 0 0 0v0c0.002 0 0.004 0 0.006 0 0.741 0 1.418-0.278 1.93-0.736l-0.003 0.003 10.117-8.010 10.44 8c0.445 0.449 1.059 0.731 1.738 0.743l0.002 0c0.709-0.009 1.343-0.321 1.781-0.811l0.002-0.002zM2.683 13.090l13.317-10.090 0.277 0.213 4.92 3.707c0.221 0.169 0.501 0.27 0.805 0.27 0.22 0 0.427-0.053 0.609-0.147l-0.007 0.003c0.73-0.38 0.73-0.87 0.73-2.070v-2.31h1.333v4.58c0 1.217 0.96 2.797 1.833 3.477l2.803 2.333h0.023c0.006 0.016 0.010 0.035 0.010 0.055s-0.004 0.039-0.010 0.056l0-0.001-1.173 1.37-11.333-8.697c-0.224-0.176-0.509-0.282-0.82-0.282s-0.596 0.106-0.823 0.284l0.003-0.002-11 8.7c-0.034 0.026-0.065 0.053-0.094 0.080l0-0c-0.040 0.020-0.103 0.037-0.117 0.043l-1.303-1.487c-0.002-0.008-0.003-0.017-0.003-0.026 0-0.023 0.007-0.044 0.020-0.061l-0 0z"></path> - </symbol> - <symbol id="image-center" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 15.667h-9.333v-2.667h5.333c0.368 0 0.667-0.298 0.667-0.667v0-8c0-0.368-0.298-0.667-0.667-0.667v0h-5.333v-2.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 2.333h-5.333c-0.368 0-0.667 0.298-0.667 0.667v0 8c0 0.368 0.298 0.667 0.667 0.667v0h5.333v2.667h-9.333c-1.105 0-2 0.895-2 2v0 8.667c0 1.105 0.895 2 2 2v0h9.333v2.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-2.333h9.333c1.105 0 2-0.895 2-2v0-8.667c0-1.105-0.895-2-2-2v0zM26 25.667h-20v-7.333h20z"></path> - </symbol> - <symbol id="image-left" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.017 28.333h-21.31c-1.105 0-2-0.895-2-2v0-8.667c0-1.105 0.895-2 2-2v0h21.31c1.105 0 2 0.895 2 2v0 8.667c0 1.105-0.895 2-2 2v0zM7.373 25.667h19.977v-7.333h-19.977zM19.333 12.333v-8c0-0.368-0.298-0.667-0.667-0.667v0h-13.333c-0.368 0-0.667 0.298-0.667 0.667v0 8c0 0.368 0.298 0.667 0.667 0.667v0h13.333c0.368 0 0.667-0.298 0.667-0.667v0zM2.683 30.667v-29.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="image-right" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.303 28.333h-21.333c-1.105 0-2-0.895-2-2v0-8.667c0-1.105 0.895-2 2-2v0h21.333c1.105 0 2 0.895 2 2v0 8.667c0 1.105-0.895 2-2 2v0zM4.637 25.667h20v-7.333h-20zM13.333 13h13.333c0.368 0 0.667-0.298 0.667-0.667v0-8c0-0.368-0.298-0.667-0.667-0.667v0h-13.333c-0.368 0-0.667 0.298-0.667 0.667v0 8c0 0.368 0.298 0.667 0.667 0.667v0zM32 30.667v-29.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="image" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.333 28.333h-26.667c-1.473 0-2.667-1.194-2.667-2.667v0-19.333c0-1.473 1.194-2.667 2.667-2.667v0h26.667c1.473 0 2.667 1.194 2.667 2.667v0 19.333c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 6.333v19.333h26.667v-19.333zM27.547 21.843c-0.236 0.218-0.553 0.351-0.901 0.351-0.386 0-0.734-0.164-0.978-0.427l-0.001-0.001-3.31-3.587-1.227 1.38 1.153 1.17c0.237 0.241 0.384 0.572 0.384 0.937 0 0.737-0.597 1.334-1.334 1.334-0.372 0-0.708-0.152-0.95-0.397l-0-0-7.59-7.703-6.51 6.597c-0.242 0.245-0.578 0.397-0.95 0.397-0.737 0-1.334-0.597-1.334-1.334 0-0.365 0.147-0.696 0.384-0.937l-0 0 7.463-7.56c0.241-0.244 0.576-0.396 0.947-0.397h0c0 0 0.001 0 0.001 0 0.371 0 0.707 0.152 0.949 0.397l0 0 5.51 5.603 2.093-2.333c0.239-0.282 0.591-0.463 0.985-0.473l0.002-0c0.393 0.003 0.747 0.167 1 0.43l0 0 4.303 4.667c0.216 0.236 0.348 0.552 0.348 0.898 0 0.391-0.169 0.743-0.437 0.987l-0.001 0.001zM19.333 9.333c-1.105 0-2 0.895-2 2s0.895 2 2 2c1.105 0 2-0.895 2-2v0c0-1.105-0.895-2-2-2v0z"></path> - </symbol> - <symbol id="information" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24 29.333h-3.333v-17.333c0-0.736-0.597-1.333-1.333-1.333v0h-10.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2.667v16h-3.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM14 13.333h4v16h-4zM16 9.333c-2.577 0-4.667-2.089-4.667-4.667s2.089-4.667 4.667-4.667c2.577 0 4.667 2.089 4.667 4.667v0c0 2.577-2.089 4.667-4.667 4.667v0zM16 2.667c-1.105 0-2 0.895-2 2s0.895 2 2 2c1.105 0 2-0.895 2-2v0c0-1.105-0.895-2-2-2v0z"></path> - </symbol> - <symbol id="input-hidden" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.88 21.223c-0.030 0.059-0.057 0.107-0.087 0.153l0.004-0.006c0 0.023-0.027 0.053-0.047 0.077-3.14 4.36-6.51 6.553-10.047 6.553-0.71-0.002-1.4-0.085-2.061-0.242l0.061 0.012c-0.57-0.16-0.981-0.675-0.981-1.286 0-0.736 0.597-1.333 1.333-1.333 0.086 0 0.171 0.008 0.252 0.024l-0.008-0.001c2.94 0.667 5.883-0.867 8.757-4.573-0.263-0.403-0.527-0.787-0.79-1.14-0.154-0.215-0.246-0.482-0.246-0.772 0-0.736 0.597-1.333 1.333-1.333 0.427 0 0.806 0.2 1.050 0.512l0.002 0.003c0.473 0.633 0.943 1.333 1.403 2.1v0c0 0.017 0 0.033 0.023 0.047 0.030 0.052 0.059 0.114 0.084 0.178l0.003 0.009c0 0.043 0.023 0.087 0.037 0.13s0.023 0.080 0.030 0.12c0.002 0.020 0.002 0.043 0.002 0.067s-0.001 0.047-0.003 0.070l0-0.003c0 0.043 0 0.083 0 0.127s0 0.077 0 0.113c-0.002 0.052-0.009 0.101-0.021 0.148l0.001-0.005c0 0.037-0.023 0.070-0.033 0.107s-0.033 0.1-0.053 0.147zM22.88 13.457c-7-1.17-11.813 6.177-12 6.49 0 0 0 0.033-0.023 0.047s-0.047 0.083-0.067 0.127-0.033 0.077-0.047 0.117-0.023 0.083-0.033 0.123-0.020 0.083-0.027 0.127 0 0.087 0 0.13 0 0.083 0 0.123 0 0.090 0 0.137 0 0.077 0.023 0.117c0.013 0.051 0.028 0.095 0.046 0.137l-0.002-0.007c0.016 0.048 0.032 0.087 0.050 0.124l-0.003-0.007c0 0.020 0 0.040 0.023 0.057 0.475 0.835 0.955 1.548 1.483 2.223l-0.030-0.039c0.246 0.318 0.628 0.52 1.057 0.52 0.001 0 0.002 0 0.004 0h-0c0 0 0 0 0 0 0.736 0 1.333-0.597 1.333-1.333 0-0.308-0.104-0.591-0.279-0.816l0.002 0.003c-0.333-0.427-0.59-0.81-0.79-1.12 1.213-1.567 4.633-5.35 8.853-4.647 0.074 0.015 0.16 0.023 0.248 0.023 0.736 0 1.333-0.597 1.333-1.333 0-0.67-0.494-1.224-1.137-1.319l-0.007-0.001zM29.66 13.457c-0.252-0.277-0.613-0.45-1.015-0.45-0.34 0-0.651 0.124-0.89 0.328l0.002-0.002-5.27 4.56c-0.341-0.144-0.738-0.227-1.154-0.227-1.666 0-3.019 1.338-3.043 2.998l-0 0.002c0.002 0.275 0.040 0.54 0.108 0.792l-0.005-0.022-5.27 4.563c-0.279 0.246-0.453 0.603-0.453 1.002 0 0.337 0.125 0.645 0.331 0.88l-0.001-0.002c0.252 0.279 0.615 0.453 1.018 0.453 0.341 0 0.654-0.125 0.894-0.332l-0.002 0.002 5.267-4.563c0.341 0.143 0.738 0.227 1.154 0.227 1.666 0 3.020-1.338 3.046-2.998l0-0.002c-0.001-0.275-0.040-0.54-0.112-0.791l0.005 0.021 5.27-4.563c0.28-0.246 0.455-0.604 0.455-1.003 0-0.336-0.125-0.644-0.33-0.878l0.001 0.002zM9.333 20.667c0-0.736-0.597-1.333-1.333-1.333v0h-5.333v-13.333h26.667v4.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-5.667c0-0.736-0.597-1.333-1.333-1.333v0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 16c0 0.736 0.597 1.333 1.333 1.333v0h6.667c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="input-line-multiple" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.667 19.667h-18.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h18.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM18 13.667c0-0.736-0.597-1.333-1.333-1.333v0h-10.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h10.667c0.736 0 1.333-0.597 1.333-1.333v0zM32 24v-16c0-0.736-0.597-1.333-1.333-1.333v0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 16c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 9.333h26.667v13.333h-26.667z"></path> - </symbol> - <symbol id="input-line" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.333 17.333h-13.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h13.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 24v-16c0-0.736-0.597-1.333-1.333-1.333v0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 16c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 9.333h26.667v13.333h-26.667z"></path> - </symbol> - <symbol id="input-number" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24 20.667c-0.277-0-0.535-0.085-0.748-0.23l0.005 0.003-2-1.333c-0.347-0.244-0.571-0.643-0.571-1.094 0-0.736 0.597-1.333 1.333-1.333 0.269 0 0.52 0.080 0.729 0.217l-0.005-0.003 1.257 0.837 1.27-0.84c0.217-0.159 0.488-0.254 0.782-0.254 0.736 0 1.333 0.597 1.333 1.333 0 0.481-0.255 0.902-0.637 1.137l-0.006 0.003-2 1.333c-0.208 0.14-0.463 0.223-0.739 0.223-0.002 0-0.003 0-0.005 0h0zM27.12 14.737c0.139-0.207 0.222-0.462 0.222-0.736 0-0.461-0.234-0.868-0.59-1.107l-0.005-0.003-2-1.333c-0.208-0.14-0.464-0.224-0.74-0.224s-0.532 0.084-0.745 0.227l0.005-0.003-2 1.333c-0.347 0.244-0.571 0.643-0.571 1.094 0 0.736 0.597 1.333 1.333 1.333 0.269 0 0.52-0.080 0.729-0.217l-0.005 0.003 1.247-0.833 1.27 0.84c0.207 0.139 0.462 0.222 0.736 0.222 0.461 0 0.868-0.234 1.107-0.59l0.003-0.005zM32 24v-16c0-0.736-0.597-1.333-1.333-1.333v0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 16c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 9.333h26.667v13.333h-26.667z"></path> - </symbol> - <symbol id="italic" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M12 32c-0.001 0-0.002 0-0.003 0-0.104 0-0.205-0.012-0.302-0.035l0.009 0.002c-0.597-0.141-1.034-0.669-1.034-1.299 0-0.105 0.012-0.208 0.035-0.306l-0.002 0.009 4.833-21.21c0.14-0.598 0.669-1.037 1.3-1.037 0.736 0 1.333 0.597 1.333 1.333 0 0.105-0.012 0.208-0.035 0.306l0.002-0.009-4.837 21.21c-0.14 0.598-0.669 1.037-1.3 1.037-0 0-0 0-0 0v0zM20.927 1.060c-0.466-0.646-1.217-1.062-2.065-1.062-0.031 0-0.062 0.001-0.092 0.002l0.004-0c-1.626 0.053-3.007 1.047-3.62 2.454l-0.010 0.026c-0.134 0.3-0.211 0.649-0.211 1.017 0 0.51 0.15 0.985 0.407 1.383l-0.006-0.010c0.467 0.646 1.219 1.061 2.067 1.061 0.030 0 0.061-0.001 0.091-0.002l-0.004 0c1.626-0.053 3.007-1.047 3.62-2.454l0.010-0.026c0.134-0.3 0.213-0.649 0.213-1.018 0-0.51-0.15-0.985-0.409-1.382l0.006 0.010z"></path> - </symbol> - <symbol id="keyword" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 9.333h-6.027l1.667-6.333c0.036-0.114 0.056-0.246 0.056-0.382 0-0.736-0.597-1.333-1.333-1.333-0.636 0-1.167 0.445-1.301 1.040l-0.002 0.009-1.84 7h-7.963l1.647-6.333c0.011-0.064 0.017-0.138 0.017-0.213 0-0.736-0.597-1.333-1.333-1.333-0.574 0-1.063 0.362-1.251 0.87l-0.003 0.009-1.833 7h-6.747c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h6.050l-2.083 8h-7.053c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h6.36l-1.65 6.333c-0.027 0.1-0.042 0.214-0.042 0.333 0 0.617 0.42 1.137 0.99 1.289l0.009 0.002c0.1 0.027 0.215 0.043 0.333 0.043h0c0.618-0 1.137-0.421 1.288-0.991l0.002-0.009 1.823-7h7.963l-1.647 6.333c-0.027 0.101-0.043 0.217-0.043 0.337 0 0.617 0.419 1.136 0.987 1.288l0.009 0.002c0.1 0.027 0.215 0.043 0.333 0.043h0c0.616-0.004 1.132-0.425 1.281-0.994l0.002-0.009 1.833-7h6.413c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5.717l2.083-8h6.72c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM19.107 20h-7.963l2.083-8h7.963z"></path> - </symbol> - <symbol id="landing_page" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 32h-28c-1.105 0-2-0.895-2-2v0-28c0-1.105 0.895-2 2-2v0h28c1.105 0 2 0.895 2 2v0 28c0 1.105-0.895 2-2 2v0zM2.667 29.333h26.667v-18.667h-26.667zM2.667 8h26.667v-5.333h-26.667zM6.667 5.333c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM9.333 4c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM13.333 4c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="landingpage-add" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 26.667c0 0.736-0.597 1.333-1.333 1.333v0h-2.667v2.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-2.667h-2.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h2.667v-2.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 2.667h2.667c0.736 0 1.333 0.597 1.333 1.333v0zM4.667 3.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM9.333 4.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM12.667 4.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM29.333 2v16c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-8.667h-24v17.333h15c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-15.667c-1.105 0-2-0.895-2-2v0-25.333c0-1.105 0.895-2 2-2v0h25.333c1.105 0 2 0.895 2 2v0zM26.667 6.667v-4h-24v4z"></path> - </symbol> - <symbol id="landingpage-preview" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.667 4.667a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm2-1a1 1 0 1 0 1 1 1 1 0 0 0-1-1zm3 0a1 1 0 1 0 1 1 1 1 0 0 0-1-1zM28 2v11.333a1.333 1.333 0 0 1-2.666 0v-4H2.667v16h4.62a1.333 1.333 0 0 1 0 2.666H2a2 2 0 0 1-2-2v-24a2 2 0 0 1 2-2h24a2 2 0 0 1 2 2zm-2.667 4.667v-4H2.666v4zM32 24.333a1.299 1.299 0 0 1-.24.811l.003-.004c-3.23 4.553-6.72 6.86-10.387 6.86h-.297c-6.047-.207-10.05-6.667-10.217-6.933a1.283 1.283 0 0 1-.19-.666.384.384 0 0 1 .001-.102v.002a1.29 1.29 0 0 1 .193-.672l-.003.006c.167-.3 4.17-6.757 10.217-6.967 3.77-.13 7.367 2.18 10.667 6.857.159.214.254.483.254.774l-.001.038v-.002zm-2.953 0c-2.587-3.407-5.233-5.087-7.87-5-3.65.127-6.51 3.533-7.587 5 1.077 1.463 3.957 4.88 7.59 5 2.64.083 5.277-1.6 7.86-5zm-7.714-3.036a3 3 0 1 0 3 3 3 3 0 0 0-3-3z"></path> - </symbol> - <symbol id="languages-add" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.967 4.943l-0.030-0.047-0.073-0.073c-2.691-2.967-6.56-4.823-10.863-4.823s-8.173 1.856-10.852 4.81l-0.011 0.012-0.073 0.073-0.030 0.047c-2.288 2.575-3.686 5.986-3.686 9.723 0 8.095 6.559 14.659 14.652 14.667h0.001c1.139-0 2.248-0.129 3.313-0.372l-0.1 0.019c0.601-0.138 1.042-0.668 1.042-1.302 0-0.737-0.597-1.334-1.334-1.334-0.103 0-0.204 0.012-0.301 0.034l0.009-0.002c-0.351 0.084-0.792 0.16-1.241 0.211l-0.056 0.005v-4.49c1.572 0.055 3.066 0.288 4.493 0.679l-0.137-0.032h0.037c0.037 0.010 0.081 0.018 0.126 0.023l0.004 0c0.023 0.002 0.049 0.004 0.075 0.004s0.052-0.001 0.078-0.004l-0.003 0h0.22c0.084-0.011 0.16-0.029 0.232-0.053l-0.009 0.003 0.060-0.020c0.065-0.026 0.118-0.051 0.169-0.078l-0.009 0.004 0.053-0.027c0.072-0.043 0.134-0.087 0.192-0.135l-0.002 0.002c0.053-0.046 0.101-0.095 0.145-0.148l0.002-0.002c0.015-0.016 0.029-0.033 0.042-0.052l0.001-0.002c0.033-0.044 0.065-0.093 0.094-0.144l0.003-0.006c0.010-0.016 0.020-0.034 0.029-0.054l0.001-0.003c0.001-0.003 0.001-0.008 0.001-0.012s-0-0.008-0.001-0.012l0 0c0.847-1.76 1.43-3.807 1.634-5.964l0.006-0.073h3.050c-0.073 0.678-0.19 1.29-0.353 1.882l0.019-0.082c-0.029 0.104-0.046 0.223-0.046 0.346 0 0.613 0.413 1.129 0.976 1.285l0.009 0.002c0.1 0.029 0.214 0.046 0.333 0.047h0c0.616-0.002 1.134-0.422 1.285-0.991l0.002-0.009c0.324-1.142 0.513-2.454 0.52-3.809l0-0.004c0-0.003 0-0.006 0-0.010 0-3.736-1.404-7.145-3.712-9.727l0.012 0.014zM9.967 3.78c-0.588 0.661-1.128 1.392-1.597 2.171l-0.037 0.066c-0.443-0.166-0.805-0.329-1.155-0.511l0.058 0.027c0.788-0.678 1.685-1.264 2.654-1.723l0.072-0.031zM5.357 7.533c0.511 0.316 1.106 0.617 1.726 0.868l0.081 0.029c-0.559 1.434-0.957 3.098-1.118 4.829l-0.006 0.074h-2.963c0.251-2.206 1.069-4.183 2.302-5.831l-0.022 0.031zM3.077 16h2.963c0.161 1.73 0.529 3.322 1.082 4.825l-0.042-0.131c-0.711 0.286-1.318 0.6-1.889 0.964l0.046-0.027c-1.145-1.578-1.918-3.495-2.155-5.575l-0.005-0.055zM7.057 23.667c0.315-0.176 0.694-0.354 1.085-0.508l0.065-0.023c0.534 0.919 1.111 1.715 1.76 2.445l-0.013-0.015c-1.108-0.523-2.062-1.158-2.909-1.911l0.012 0.011zM13.667 25.353c-1.090-0.821-2.008-1.801-2.736-2.916l-0.027-0.044c0.801-0.145 1.753-0.249 2.72-0.289l0.043-0.001zM13.667 19.433c-1.449 0.054-2.824 0.227-4.157 0.511l0.157-0.028c-0.449-1.14-0.782-2.465-0.94-3.843l-0.007-0.073h4.947zM13.667 13.333h-4.947c0.161-1.533 0.514-2.937 1.038-4.254l-0.038 0.107c1.159 0.252 2.515 0.425 3.901 0.479l0.045 0.001zM13.667 7c-0.967-0.043-1.872-0.14-2.758-0.288l0.132 0.018c0.726-1.075 1.597-1.988 2.597-2.736l0.029-0.021zM16.333 4.050c0.993 0.758 1.836 1.648 2.521 2.654l0.025 0.039c-0.731 0.124-1.608 0.215-2.499 0.255l-0.048 0.002zM16.333 9.667c1.399-0.053 2.725-0.215 4.013-0.48l-0.157 0.027c0.486 1.205 0.839 2.603 0.994 4.061l0.006 0.069h-4.857zM20.267 19.903c-1.156-0.249-2.508-0.417-3.89-0.469l-0.044-0.001v-3.433h4.86c-0.16 1.446-0.487 2.767-0.964 4.013l0.037-0.11zM21.6 6.043c-0.524-0.881-1.088-1.645-1.719-2.347l0.012 0.014c1.1 0.5 2.047 1.109 2.893 1.834l-0.016-0.014c-0.317 0.169-0.703 0.342-1.101 0.491l-0.069 0.023zM22.76 8.46c0.728-0.283 1.348-0.595 1.934-0.956l-0.051 0.029c1.211 1.617 2.029 3.594 2.275 5.745l0.005 0.055h-3.053c-0.165-1.795-0.558-3.448-1.153-5.001l0.043 0.128zM31.667 26.667c0 0.736-0.597 1.333-1.333 1.333v0h-2.667v2.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-2.667h-2.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h2.667v-2.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 2.667h2.667c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="languages" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.96 5.387l-0.033-0.050c-0.025-0.029-0.051-0.055-0.079-0.079l-0.001-0.001c-2.935-3.234-7.154-5.257-11.847-5.257s-8.912 2.023-11.835 5.244l-0.012 0.013c-0.029 0.025-0.055 0.051-0.079 0.079l-0.001 0.001s-0.020 0.033-0.033 0.050c-2.507 2.806-4.040 6.53-4.040 10.611 0 3.736 1.284 7.173 3.435 9.892l-0.026-0.033c0.038 0.243 0.136 0.458 0.279 0.636l-0.002-0.003c0.166 0.2 0.386 0.35 0.637 0.427l0.009 0.003c2.905 2.955 6.945 4.786 11.412 4.786 8.837 0 16-7.163 16-16 0-3.943-1.426-7.553-3.791-10.342l0.019 0.023zM26.603 24.070c-0.617-0.42-1.323-0.806-2.066-1.12l-0.081-0.030c0.645-1.636 1.1-3.532 1.274-5.508l0.006-0.078h3.53c-0.264 2.568-1.224 4.871-2.686 6.768l0.023-0.031zM2.733 17.333h3.53c0.169 1.961 0.588 3.772 1.226 5.475l-0.046-0.141c-0.883 0.319-1.64 0.683-2.352 1.113l0.059-0.033c-1.3-1.796-2.169-3.984-2.412-6.357l-0.005-0.056zM5.353 8c0.627 0.394 1.351 0.757 2.109 1.050l0.088 0.030c-0.65 1.636-1.106 3.532-1.281 5.509l-0.006 0.078h-3.53c0.265-2.539 1.208-4.816 2.644-6.699l-0.024 0.032zM14.667 7.69c-1.183-0.042-2.304-0.161-3.399-0.355l0.146 0.021c0.878-1.338 1.96-2.461 3.215-3.358l0.038-0.026zM14.667 10.357v4.31h-5.723c0.177-1.788 0.593-3.43 1.219-4.964l-0.043 0.118c1.342 0.293 2.905 0.487 4.505 0.539l0.042 0.001zM14.667 17.333v4.12c-1.662 0.033-3.261 0.211-4.813 0.522l0.172-0.029c-0.531-1.348-0.914-2.911-1.077-4.539l-0.006-0.074zM14.667 24.123v3.903c-1.359-0.97-2.488-2.165-3.356-3.542l-0.030-0.052c0.997-0.168 2.166-0.28 3.355-0.309l0.031-0.001zM17.333 24.157c1.214 0.062 2.349 0.206 3.456 0.428l-0.149-0.025c-0.887 1.373-1.988 2.524-3.269 3.441l-0.038 0.026zM17.333 21.49v-4.157h5.72c-0.173 1.759-0.577 3.376-1.185 4.889l0.042-0.119c-1.346-0.326-2.915-0.548-4.524-0.618l-0.052-0.002zM17.333 14.667v-4.307c1.64-0.053 3.202-0.247 4.716-0.571l-0.173 0.031c0.585 1.416 1.002 3.058 1.174 4.773l0.006 0.074zM17.333 7.69v-3.717c1.292 0.921 2.373 2.044 3.221 3.333l0.029 0.047c-0.948 0.173-2.068 0.294-3.207 0.335l-0.043 0.001zM21.023 3.667c1.444 0.601 2.686 1.386 3.771 2.346l-0.014-0.012c-0.417 0.232-0.911 0.457-1.423 0.643l-0.074 0.024c-0.668-1.137-1.416-2.12-2.265-3.005l0.005 0.005zM8.717 6.667c-0.588-0.212-1.082-0.437-1.554-0.695l0.058 0.029c1.070-0.947 2.311-1.732 3.665-2.301l0.088-0.033c-0.842 0.874-1.589 1.851-2.216 2.907l-0.040 0.073zM8.57 25.117c0.697 1.223 1.494 2.279 2.407 3.221l-0.004-0.004c-1.552-0.644-2.883-1.508-4.028-2.571l0.008 0.008c0.448-0.236 0.983-0.463 1.536-0.65l0.080-0.023zM23.293 25.333c0.567 0.227 1.047 0.467 1.503 0.742l-0.046-0.026c-1.064 0.925-2.296 1.693-3.638 2.251l-0.089 0.033c0.848-0.88 1.599-1.863 2.23-2.927l0.040-0.073zM25.737 14.667c-0.179-2.060-0.635-3.962-1.335-5.742l0.048 0.139c0.847-0.318 1.571-0.676 2.253-1.095l-0.056 0.032c1.412 1.85 2.355 4.128 2.615 6.609l0.005 0.057z"></path> - </symbol> - <symbol id="layout-switch" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.333 26.667c0 0.736-0.597 1.333-1.333 1.333v0h-7.070l1.753 1.713c0.261 0.244 0.423 0.59 0.423 0.975 0 0.736-0.597 1.333-1.333 1.333-0.374 0-0.711-0.154-0.953-0.401l-0-0-3.51-3.43c-0.397-0.385-0.643-0.923-0.643-1.518 0-0.002 0-0.004 0-0.005v0c0-0.002 0-0.004 0-0.006 0-0.596 0.248-1.134 0.646-1.517l0.001-0.001 3.507-3.43c0.238-0.222 0.558-0.359 0.91-0.359 0.736 0 1.333 0.597 1.333 1.333 0 0.363-0.145 0.692-0.38 0.932l0-0-1.75 1.713h7.067c0.736 0 1.333 0.597 1.333 1.333v0zM32 22.667c0-0.001 0-0.002 0-0.003 0-0.582-0.237-1.109-0.62-1.49l-3.45-3.45c-0.243-0.255-0.586-0.414-0.966-0.414-0.736 0-1.333 0.597-1.333 1.333 0 0.381 0.159 0.724 0.415 0.967l0.001 0.001 1.73 1.723h-7.11c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h7.033l-1.773 1.703c-0.253 0.243-0.411 0.585-0.411 0.963 0 0.737 0.597 1.334 1.334 1.334 0.359 0 0.684-0.141 0.924-0.371l-0 0 3.56-3.43c0.401-0.38 0.654-0.913 0.667-1.504l0-0.002v-0.027zM26 0h-24c-1.105 0-2 0.895-2 2v0 24c0 1.105 0.895 2 2 2v0h5.287c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-4.62v-16h22.667v4.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-12c0-1.105-0.895-2-2-2v0zM2.667 6.667v-4h22.667v4zM4.667 3.667c-0.552 0-1 0.448-1 1s0.448 1 1 1c0.552 0 1-0.448 1-1v0c0-0.552-0.448-1-1-1v0zM7.667 3.667c-0.552 0-1 0.448-1 1s0.448 1 1 1c0.552 0 1-0.448 1-1v0c0-0.552-0.448-1-1-1v0zM10.667 3.667c-0.552 0-1 0.448-1 1s0.448 1 1 1c0.552 0 1-0.448 1-1v0c0-0.552-0.448-1-1-1v0z"></path> - </symbol> - <symbol id="link-anchor" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.467 17.6l-2.667-2c-0.22-0.166-0.498-0.267-0.8-0.267s-0.58 0.1-0.803 0.269l0.003-0.002-2.667 2c-0.325 0.246-0.533 0.632-0.533 1.067 0 0.736 0.597 1.333 1.333 1.333 0.302 0 0.58-0.1 0.803-0.269l-0.003 0.002 0.533-0.4c-0 4.316-3.155 7.895-7.284 8.557l-0.050 0.007v-14.563h2c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-2v-1.53c1.944-0.593 3.333-2.37 3.333-4.472 0-2.577-2.089-4.667-4.667-4.667s-4.667 2.089-4.667 4.667c0 2.102 1.39 3.879 3.3 4.463l0.033 0.009v1.53h-2c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2v14.563c-4.179-0.669-7.333-4.248-7.333-8.563v0l0.533 0.4c0.22 0.166 0.498 0.267 0.8 0.267 0.736 0 1.333-0.597 1.333-1.333 0-0.435-0.208-0.821-0.53-1.064l-0.003-0.002-2.667-2c-0.22-0.166-0.498-0.267-0.8-0.267s-0.58 0.1-0.803 0.269l0.003-0.002-2.667 2c-0.325 0.246-0.533 0.632-0.533 1.067 0 0.736 0.597 1.333 1.333 1.333 0.302 0 0.58-0.1 0.803-0.269l-0.003 0.002 0.533-0.4c0.008 5.786 4.345 10.556 9.945 11.248l0.055 0.006v0.080c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-0.080c5.655-0.697 9.992-5.467 10-11.252v-0.001l0.533 0.4c0.22 0.166 0.498 0.267 0.8 0.267 0.736 0 1.333-0.597 1.333-1.333 0-0.435-0.208-0.821-0.53-1.064l-0.003-0.002zM14 4.667c0-1.105 0.895-2 2-2s2 0.895 2 2c0 1.105-0.895 2-2 2v0c-1.105 0-2-0.895-2-2v0z"></path> - </symbol> - <symbol id="link-content" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.307 15.56l-1.2-1.2c-.636-.635-1.514-1.028-2.483-1.028s-1.848.393-2.483 1.028l-3.177 3.18a3.489 3.489 0 0 0-.96 3.148L19 20.666a3.482 3.482 0 0 0-3.13.951L12.69 24.8c-.633.636-1.025 1.513-1.025 2.482s.392 1.846 1.025 2.482l1.2 1.2c.636.634 1.514 1.027 2.483 1.027s1.847-.392 2.483-1.027l3.177-3.18a3.48 3.48 0 0 0 .963-3.138l.004.022c.2.04.431.063.666.063h.004c.964 0 1.837-.39 2.47-1.02l3.18-3.183a3.508 3.508 0 0 0 1.02-2.477c0-.973-.395-1.853-1.033-2.49zm-9.154 10.333l-3.18 3.18a.861.861 0 0 1-1.194-.001l-1.203-1.2a.844.844 0 0 1 0-1.194l3.18-3.18a.829.829 0 0 1 .895-.165l-.005-.002c.062.023.136.042.212.056l.008.001-2.2 2.207a1 1 0 0 0 .707 1.707.997.997 0 0 0 .696-.303l2.207-2.203c.014.078.034.146.059.211l-.003-.008a.833.833 0 0 1-.18.907zm7.267-7.266l-3.18 3.18a.825.825 0 0 1-.892.181l.006.002a1.306 1.306 0 0 0-.212-.056l-.008-.001 2.2-2.197a1 1 0 0 0-1.405-1.402l.002-.002-2.207 2.203a1.137 1.137 0 0 0-.059-.211l.003.008a.831.831 0 0 1 .18-.9l3.18-3.18a.865.865 0 0 1 1.194 0l1.203 1.2a.844.844 0 0 1-.003 1.187zM9 29.333H3A1.333 1.333 0 0 1 1.667 28V1.333C1.667.597 2.264 0 3 0h18.667C22.403 0 23 .597 23 1.333v9.333a1.333 1.333 0 0 1-2.666 0v-8h-16v24h4.667a1.333 1.333 0 0 1 0 2.666z"></path> - </symbol> - <symbol id="link-remove" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M7.523 32c-0.003 0-0.007 0-0.011 0-1.467 0-2.795-0.595-3.756-1.557l-2.2-2.2c-0.961-0.965-1.556-2.295-1.556-3.765s0.594-2.8 1.556-3.765l5.83-5.83c0.243-0.256 0.587-0.416 0.967-0.416 0.736 0 1.333 0.597 1.333 1.333 0 0.38-0.159 0.722-0.414 0.965l-0.001 0-5.83 5.83c-0.479 0.482-0.775 1.146-0.775 1.88s0.296 1.398 0.776 1.88l2.2 2.2c0.482 0.48 1.147 0.777 1.882 0.777s1.399-0.297 1.882-0.777l-0 0 5.823-5.83c0.241-0.241 0.575-0.391 0.943-0.391 0.737 0 1.334 0.597 1.334 1.334 0 0.368-0.149 0.702-0.391 0.943l-5.827 5.83c-0.965 0.962-2.296 1.557-3.766 1.557-0 0-0.001 0-0.001 0h0zM24.613 17.117l5.83-5.83c0.961-0.965 1.556-2.295 1.556-3.765s-0.594-2.8-1.556-3.765l-2.2-2.2c-0.965-0.962-2.296-1.558-3.767-1.558s-2.802 0.595-3.767 1.558l-5.827 5.83c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0l5.827-5.83c0.482-0.48 1.147-0.777 1.882-0.777s1.399 0.297 1.882 0.777l2.2 2.2c0.479 0.482 0.775 1.146 0.775 1.88s-0.296 1.398-0.776 1.88l-5.833 5.83c-0.242 0.241-0.391 0.575-0.391 0.943 0 0.736 0.596 1.332 1.331 1.333h0c0 0 0.001 0 0.001 0 0.37 0 0.704-0.15 0.946-0.393l0-0zM8 11.667c0-0.736-0.597-1.333-1.333-1.333v0h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h5.333c0.736 0 1.333-0.597 1.333-1.333v0zM13 6.667v-5.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM32 20.333c0-0.736-0.597-1.333-1.333-1.333v0h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h5.333c0.736 0 1.333-0.597 1.333-1.333v0zM21.667 30.667v-5.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="link" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M7.523 32c-0.003 0-0.007 0-0.011 0-1.467 0-2.795-0.595-3.756-1.557l-2.2-2.2c-0.961-0.965-1.556-2.295-1.556-3.765s0.594-2.8 1.556-3.765l5.83-5.83c0.96-0.957 2.285-1.549 3.748-1.549 0.68 0 1.33 0.128 1.928 0.361l-0.036-0.012c0.537 0.18 0.917 0.679 0.917 1.267 0 0.736-0.597 1.333-1.333 1.333-0.192 0-0.374-0.040-0.539-0.113l0.009 0.003c-0.28-0.11-0.605-0.174-0.944-0.174-0.727 0-1.385 0.293-1.863 0.768l-5.83 5.83c-0.479 0.482-0.775 1.146-0.775 1.88s0.296 1.398 0.776 1.88l2.2 2.2c0.482 0.48 1.147 0.777 1.882 0.777s1.399-0.297 1.882-0.777l-0 0 5.823-5.83c0.476-0.477 0.771-1.135 0.771-1.862 0-0.355-0.070-0.693-0.197-1.002l0.006 0.017c-0.061-0.148-0.097-0.32-0.097-0.5 0-0.738 0.598-1.335 1.335-1.335 0.557 0 1.035 0.341 1.235 0.827l0.003 0.009c0.242 0.585 0.382 1.263 0.382 1.975 0 1.463-0.593 2.787-1.552 3.745l-0 0-5.827 5.843c-0.965 0.962-2.296 1.557-3.766 1.557-0 0-0.001 0-0.001 0h0zM9.333 24c-0 0-0.001 0-0.001 0-0.736 0-1.333-0.597-1.333-1.333 0-0.368 0.149-0.702 0.391-0.943v0l13.333-13.333c0.241-0.241 0.575-0.391 0.943-0.391 0.737 0 1.334 0.597 1.334 1.334 0 0.368-0.149 0.702-0.391 0.943v0l-13.333 13.333c-0.241 0.241-0.574 0.39-0.942 0.39-0 0-0.001 0-0.001 0h0zM20.867 18.667c-0.014 0-0.031 0-0.048 0-0.663 0-1.297-0.122-1.882-0.346l0.036 0.012c-0.537-0.18-0.917-0.679-0.917-1.267 0-0.736 0.597-1.333 1.333-1.333 0.192 0 0.374 0.040 0.539 0.113l-0.009-0.003c0.28 0.11 0.605 0.174 0.944 0.174 0.727 0 1.385-0.293 1.863-0.768l-0 0 5.83-5.847c0.479-0.482 0.775-1.146 0.775-1.88s-0.296-1.398-0.776-1.88l-2.2-2.2c-0.482-0.48-1.147-0.777-1.882-0.777s-1.399 0.297-1.882 0.777l0-0-5.823 5.83c-0.476 0.477-0.771 1.135-0.771 1.862 0 0.355 0.070 0.693 0.197 1.002l-0.006-0.018c0.061 0.148 0.097 0.32 0.097 0.5 0 0.738-0.598 1.335-1.335 1.335-0.557 0-1.035-0.341-1.235-0.827l-0.003-0.009c-0.242-0.585-0.382-1.263-0.382-1.975 0-1.463 0.593-2.787 1.552-3.745l0-0 5.827-5.843c0.965-0.962 2.296-1.558 3.767-1.558s2.802 0.595 3.767 1.558l2.2 2.2c0.961 0.965 1.556 2.295 1.556 3.765s-0.594 2.8-1.556 3.765l-5.83 5.83c-0.957 0.958-2.28 1.55-3.741 1.55-0.002 0-0.004 0-0.006 0h0z"></path> - </symbol> - <symbol id="list-numbered" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.5 28h-16c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h16c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM28.833 16c0-0.736-0.597-1.333-1.333-1.333v0h-16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333v0zM28.833 5.333c0-0.736-0.597-1.333-1.333-1.333v0h-16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333v0zM8.083 23.733c-0.13-0.235-0.307-0.429-0.518-0.576l-0.005-0.004c-0.234-0.159-0.506-0.283-0.799-0.356l-0.018-0.004c-0.305-0.081-0.655-0.127-1.017-0.127-0.014 0-0.028 0-0.042 0l0.002-0c-0.006-0-0.013-0-0.020-0-0.263 0-0.522 0.022-0.773 0.064l0.027-0.004c-0.256 0.041-0.481 0.097-0.698 0.169l0.031-0.009c-0.411 0.137-0.768 0.322-1.088 0.555l0.012-0.008 0.84 1.097c0.087-0.060 0.18-0.123 0.287-0.183 0.102-0.060 0.222-0.117 0.347-0.164l0.017-0.006c0.114-0.047 0.252-0.092 0.394-0.126l0.019-0.004c0.136-0.030 0.292-0.047 0.453-0.047 0.006 0 0.012 0 0.018 0h-0.001c0.029-0.003 0.064-0.004 0.098-0.004 0.273 0 0.525 0.089 0.728 0.24l-0.003-0.002c0.162 0.152 0.262 0.367 0.262 0.605 0 0.022-0.001 0.043-0.002 0.064l0-0.003c0 0.002 0 0.004 0 0.007 0 0.153-0.029 0.3-0.083 0.434l0.003-0.008c-0.065 0.135-0.159 0.247-0.274 0.332l-0.002 0.002c-0.149 0.107-0.324 0.19-0.513 0.238l-0.011 0.002c-0.228 0.055-0.489 0.087-0.758 0.087-0.023 0-0.046-0-0.069-0.001l0.003 0h-0.547v1.157h0.557c0.024-0 0.052-0.001 0.080-0.001 0.285 0 0.564 0.027 0.835 0.078l-0.028-0.004c0.219 0.040 0.415 0.113 0.592 0.215l-0.009-0.005c0.142 0.078 0.255 0.192 0.331 0.329l0.002 0.004c0.063 0.122 0.1 0.267 0.1 0.421 0 0.005-0 0.009-0 0.014v-0.001c-0.003 0.168-0.034 0.328-0.090 0.476l0.003-0.010c-0.054 0.149-0.145 0.274-0.262 0.369l-0.002 0.001c-0.136 0.11-0.3 0.193-0.478 0.238l-0.009 0.002c-0.214 0.055-0.459 0.087-0.712 0.087-0.020 0-0.040-0-0.061-0.001l0.003 0c-0.174-0-0.345-0.012-0.513-0.036l0.020 0.002c-0.203-0.025-0.382-0.058-0.556-0.103l0.030 0.006c-0.18-0.047-0.36-0.1-0.54-0.163-0.201-0.072-0.37-0.147-0.531-0.234l0.021 0.010v1.4c0.299 0.138 0.648 0.253 1.012 0.328l0.031 0.005c0.346 0.066 0.745 0.103 1.152 0.103 0.014 0 0.029-0 0.043-0h-0.002c0.024 0 0.052 0.001 0.080 0.001 0.46 0 0.906-0.065 1.327-0.186l-0.034 0.008c0.371-0.108 0.694-0.28 0.972-0.504l-0.005 0.004c0.245-0.2 0.438-0.452 0.565-0.741l0.005-0.013c0.118-0.272 0.187-0.589 0.187-0.922 0-0.007-0-0.015-0-0.022v0.001c0.001-0.025 0.002-0.054 0.002-0.084 0-0.457-0.193-0.869-0.501-1.159l-0.001-0.001c-0.391-0.325-0.888-0.535-1.432-0.573l-0.008-0v-0.030c0.253-0.060 0.475-0.146 0.681-0.257l-0.015 0.007c0.207-0.109 0.384-0.243 0.536-0.402l0.001-0.001c0.15-0.158 0.272-0.345 0.356-0.552l0.004-0.012c0.084-0.209 0.133-0.452 0.133-0.706 0-0.007-0-0.015-0-0.022v0.001c0.001-0.016 0.001-0.034 0.001-0.053 0-0.268-0.064-0.52-0.178-0.743l0.004 0.009zM8.5 18.597h-3.227v-0.073l0.953-0.957c0.28-0.277 0.547-0.55 0.793-0.817 0.234-0.245 0.453-0.509 0.652-0.788l0.015-0.022c0.171-0.239 0.322-0.513 0.438-0.804l0.009-0.026c0.101-0.261 0.16-0.562 0.16-0.878 0-0.003 0-0.006-0-0.010v0.001c0-0.011 0-0.025 0-0.038 0-0.319-0.063-0.623-0.176-0.901l0.006 0.016c-0.114-0.276-0.281-0.51-0.488-0.699l-0.002-0.001c-0.216-0.191-0.473-0.342-0.754-0.439l-0.016-0.005c-0.293-0.1-0.631-0.157-0.982-0.157-0.019 0-0.038 0-0.057 0.001l0.003-0c-0.014-0-0.030-0-0.047-0-0.29 0-0.571 0.034-0.841 0.099l0.025-0.005c-0.268 0.065-0.502 0.152-0.723 0.262l0.019-0.009c-0.227 0.108-0.421 0.227-0.602 0.363l0.009-0.006c-0.177 0.137-0.333 0.277-0.51 0.423l0.91 1.073c0.242-0.223 0.513-0.421 0.805-0.586l0.021-0.011c0.238-0.132 0.521-0.209 0.823-0.21h0c0.015-0.001 0.032-0.001 0.049-0.001 0.229 0 0.438 0.084 0.598 0.222l-0.001-0.001c0.151 0.144 0.245 0.347 0.245 0.572 0 0.017-0.001 0.034-0.002 0.051l0-0.002c0 0.001 0 0.002 0 0.003 0 0.238-0.042 0.466-0.118 0.678l0.004-0.014c-0.093 0.236-0.205 0.439-0.339 0.625l0.006-0.009c-0.162 0.232-0.324 0.434-0.498 0.625l0.005-0.005c-0.19 0.213-0.403 0.443-0.64 0.697l-1.863 2v1.167h5.337zM7.5 1.333h-1.4l-2.6 2.053 0.833 1.017 0.92-0.737c0.057-0.043 0.113-0.093 0.173-0.147l0.17-0.16 0.15-0.147 0.093-0.097v0.397c0 0.143 0 0.293-0.017 0.437s0 0.283 0 0.417v4.967h1.677z"></path> - </symbol> - <symbol id="list" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M28.333 6.667h-16c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h16c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM29.667 16c0-0.736-0.597-1.333-1.333-1.333v0h-16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333v0zM29.667 26.667c0-0.736-0.597-1.333-1.333-1.333v0h-16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h16c0.736 0 1.333-0.597 1.333-1.333v0zM5.667 23.333c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0zM5.667 12.667c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0zM5.667 2c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0z"></path> - </symbol> - <symbol id="location-add-new" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 24H18a2 2 0 0 0-2 2v.667h-5.333v-9.333h4v-2.667h-4V9.334h6a2 2 0 0 0 2-2V2.001a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v5.333a2 2 0 0 0 2 2h6v18.667c0 .736.597 1.333 1.333 1.333H16v.667a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zM2.667 2.667H16v4H2.667zm26.666 26.666H18.666v-2.667h10.667zM26.667 16c0 .736-.597 1.333-1.333 1.333h-2.667V20a1.333 1.333 0 0 1-2.666 0v-2.667h-2.667a1.333 1.333 0 0 1 0-2.666h2.667V12a1.333 1.333 0 0 1 2.666 0v2.667h2.667c.736 0 1.333.597 1.333 1.333z"></path> - </symbol> - <symbol id="lock-unlock" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 26.667c-2.209 0-4-1.791-4-4s1.791-4 4-4c2.209 0 4 1.791 4 4v0c0 2.209-1.791 4-4 4v0zM16 21.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM26 13.333h-16.667v-7.19c0.002-1.919 1.557-3.475 3.476-3.477h6.247c1.735 0.002 3.183 1.225 3.532 2.856l0.004 0.024c0.131 0.611 0.666 1.062 1.307 1.062 0.737 0 1.335-0.598 1.335-1.335 0-0.097-0.010-0.191-0.030-0.282l0.002 0.009c-0.62-2.872-3.136-4.994-6.149-5h-6.247c-3.391 0.004-6.14 2.752-6.143 6.143v7.19h-0.667c-1.473 0-2.667 1.194-2.667 2.667v0 13.333c0 1.473 1.194 2.667 2.667 2.667v0h20c1.473 0 2.667-1.194 2.667-2.667v0-13.333c0-1.473-1.194-2.667-2.667-2.667v0zM6 29.333v-13.333h20v13.333z"></path> - </symbol> - <symbol id="lock" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 22.667c0 2.209-1.791 4-4 4s-4-1.791-4-4c0-2.209 1.791-4 4-4v0c2.209 0 4 1.791 4 4v0zM28.667 16v13.333c0 1.473-1.194 2.667-2.667 2.667v0h-20c-1.473 0-2.667-1.194-2.667-2.667v0-13.333c0-1.473 1.194-2.667 2.667-2.667v0h0.667v-7.19c0.004-3.391 2.752-6.14 6.143-6.143h6.247c3.464 0.006 6.271 2.812 6.277 6.276v7.057h0.667c1.473 0 2.667 1.194 2.667 2.667v0zM9.333 13.333h13.333v-7.057c-0.002-1.993-1.617-3.608-3.61-3.61h-6.247c-1.919 0.002-3.475 1.557-3.477 3.477v0zM26 29.333v-13.333h-20v13.333z"></path> - </symbol> - <symbol id="logout" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.087 0-14.667-6.48-14.667-14.443 0.029-5.912 3.608-10.981 8.713-13.187l0.094-0.036c0.155-0.069 0.336-0.108 0.527-0.108 0.736 0 1.333 0.597 1.333 1.333 0 0.546-0.328 1.015-0.798 1.222l-0.009 0.003c-4.24 1.825-7.162 5.954-7.193 10.769l-0 0.004c0 6.493 5.383 11.777 12 11.777s12-5.283 12-11.777c-0.025-4.826-2.948-8.963-7.117-10.761l-0.076-0.029c-0.407-0.234-0.677-0.665-0.677-1.16 0-0.736 0.597-1.333 1.333-1.333 0.142 0 0.278 0.022 0.406 0.063l-0.009-0.003c5.199 2.242 8.778 7.311 8.807 13.22v0.004c0 7.963-6.58 14.443-14.667 14.443zM17.333 14.667v-13.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 13.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="maform" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 32h-29.333c-0.736 0-1.333-0.597-1.333-1.333v0-29.333c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 28h28c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM12 26v-17.333c0-1.105-0.895-2-2-2v0h-4c-1.105 0-2 0.895-2 2v0 17.333c0 1.105 0.895 2 2 2v0h4c1.105 0 2-0.895 2-2v0zM6.667 9.333h2.667v16h-2.667zM21.333 26v-12c0-1.105-0.895-2-2-2v0h-4c-1.105 0-2 0.895-2 2v0 12c0 1.105 0.895 2 2 2v0h4c1.105 0 2-0.895 2-2v0zM16 14.667h2.667v10.667h-2.667zM30.703 26v-22.667c0-1.105-0.895-2-2-2v0h-4c-1.105 0-2 0.895-2 2v0 22.667c0 1.105 0.895 2 2 2v0h4c1.105 0 2-0.895 2-2v0zM25.37 4h2.667v21.333h-2.667z"></path> - </symbol> - <symbol id="mail" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 6c0-0.736-0.597-1.333-1.333-1.333v0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 20.020c-0.002 0.023-0.003 0.050-0.003 0.077s0.001 0.054 0.003 0.080l-0-0.004v0.11c0.013 0.051 0.028 0.095 0.046 0.137l-0.002-0.007c0.010 0.045 0.022 0.084 0.038 0.121l-0.002-0.004c0 0.037 0.040 0.073 0.060 0.11 0.022 0.044 0.045 0.081 0.069 0.117l-0.002-0.003c0.022 0.032 0.045 0.061 0.070 0.087l-0-0c0.033 0.040 0.063 0.080 0.1 0.117s0.057 0.043 0.083 0.067c0.031 0.027 0.064 0.053 0.099 0.077l0.004 0.003 0.123 0.067c0.028 0.016 0.062 0.033 0.097 0.048l0.007 0.002c0.040 0.017 0.089 0.033 0.139 0.045l0.007 0.002 0.097 0.027c0.089 0.024 0.193 0.038 0.299 0.040l0.001 0h29.333c0.091-0 0.18-0.010 0.265-0.028l-0.009 0.002 0.097-0.027c0.058-0.014 0.107-0.030 0.154-0.049l-0.007 0.003c0.042-0.017 0.076-0.034 0.108-0.053l-0.005 0.003 0.123-0.067c0.039-0.027 0.073-0.053 0.105-0.081l-0.002 0.001c0.027-0.023 0.057-0.040 0.083-0.067s0.067-0.077 0.1-0.117c0.025-0.026 0.048-0.054 0.069-0.084l0.001-0.002c0.023-0.033 0.046-0.072 0.065-0.112l0.002-0.005c0.020-0.037 0.043-0.070 0.060-0.107 0.014-0.032 0.026-0.071 0.036-0.111l0.001-0.005c0.015-0.035 0.030-0.079 0.042-0.123l0.002-0.007v-0.11c0.002-0.023 0.003-0.050 0.003-0.077s-0.001-0.054-0.003-0.080l0 0.004v-0.020zM2.667 8.3l7.537 6.333-7.537 8.003zM16.053 15.967c-0.050 0.040-0.057 0.040-0.107 0l-10.28-8.633h20.667zM12.25 16.333l1.98 1.667c0.473 0.406 1.093 0.654 1.77 0.654s1.297-0.247 1.774-0.657l-0.004 0.003 1.98-1.667 7.83 8.333h-23.16zM21.797 14.617l7.537-6.317v14.333z"></path> - </symbol> - <symbol id="markup" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.667 9.733L19.457.45a1.33 1.33 0 0 0-.999-.45H5.334c-.736 0-1.333.597-1.333 1.333v29.333c0 .736.597 1.333 1.333 1.333h21.333c.736 0 1.333-.597 1.333-1.333v-20.05-.001c0-.339-.126-.648-.335-.883l.001.001zm-2.737.934h-3.597c-1.92 0-2.667-.753-2.667-2.687V3.583zM6.667 29.333V2.666H16v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16zm12-3.333h-.002a1.333 1.333 0 0 1-.856-2.355l.002-.002 2.773-2.333-2.767-2.293a1.332 1.332 0 0 1 .823-2.382c.337 0 .644.125.879.33l-.002-.001 4 3.317a1.333 1.333 0 0 1 .002 2.045l-.002.002-4 3.353a1.326 1.326 0 0 1-.849.32h-.001zm-4.31-.477a1.33 1.33 0 0 0-.164-1.878l-.002-.002-2.773-2.333 2.767-2.293a1.332 1.332 0 0 0-.877-2.337 1.33 1.33 0 0 0-.826.286l.003-.002-4 3.317a1.333 1.333 0 0 0-.002 2.045l.002.002 4 3.353a1.33 1.33 0 0 0 1.878-.164l.002-.002z"></path> - </symbol> - <symbol id="media" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.023 9.333c-0.305-0.212-0.683-0.338-1.090-0.338-0.252 0-0.492 0.048-0.713 0.136l0.013-0.005-3.177 1.13c-1.173 0.433-2.057 1.877-2.057 3.357v-2.763c-0.032-1.581-1.321-2.85-2.906-2.85-0.017 0-0.033 0-0.050 0l0.003-0h-18.117c-0.014-0-0.031-0-0.047-0-1.574 0-2.853 1.261-2.883 2.828l-0 0.003v10.37c0.030 1.553 1.296 2.8 2.853 2.8 0.015 0 0.031-0 0.046-0l-0.002 0h18.217c0.013 0 0.028 0 0.044 0 1.552 0 2.813-1.243 2.843-2.788l0-0.003v-2.833c0 1.44 0.94 2.913 2.13 3.353l3.057 1.147c0.209 0.078 0.451 0.123 0.703 0.123h0c0.416-0 0.801-0.133 1.116-0.357l-0.006 0.004c0.64-0.457 1-1.27 1-2.293v-8.687c0-1.363-0.533-2.020-0.977-2.333zM21.333 21.21c0 0.037-0.073 0.123-0.22 0.123h-18.217c-0.143 0-0.23-0.087-0.23-0.133v-10.37c0-0.067 0.103-0.163 0.263-0.163h18.117c0.177 0 0.287 0.11 0.287 0.183zM29.333 20.083l-2.247-0.843c-0.245-0.212-0.403-0.518-0.42-0.861l-0-0.003v-4.763c0.006-0.331 0.131-0.632 0.335-0.861l-0.001 0.001 2.333-0.827z"></path> - </symbol> - <symbol id="merge" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.43 14.4l-3.74-4.577c-0.246-0.321-0.63-0.527-1.062-0.527-0.736 0-1.333 0.597-1.333 1.333 0 0.338 0.126 0.646 0.333 0.881l-0.001-0.001 2.58 3.157h-11.567l-5.333-7c-0.434-0.598-1.125-0.986-1.908-1l-0.002-0-8.063 0.037c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0l7.903-0.037 5.097 6.693-5.077 6.64-7.923-0.037c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0l8.097 0.037c0.768-0.011 1.447-0.386 1.872-0.96l0.005-0.006 5.36-7.033h11.54l-2.58 3.157c-0.206 0.233-0.332 0.542-0.332 0.88 0 0.736 0.597 1.333 1.333 1.333 0.432 0 0.816-0.205 1.059-0.523l0.002-0.003 3.74-4.573c0.349-0.435 0.56-0.994 0.56-1.602s-0.211-1.167-0.564-1.607l0.004 0.005z"></path> - </symbol> - <symbol id="move" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.167 29.333h-6.167c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h6.157c0.053-0.003 0.101-0.021 0.141-0.050l-0.001 0c0.023-0.020 0.037-0.049 0.037-0.082 0-0 0-0.001 0-0.001v0-14.533h-26.667v14.55c0.010 0.067 0.067 0.117 0.135 0.117 0.005 0 0.010-0 0.015-0.001l-0.001 0h6.183c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-6.183c-0.004 0-0.009 0-0.013 0-1.541 0-2.792-1.244-2.803-2.782v-15.884c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 15.867c0 0.002 0 0.004 0 0.005 0 0.77-0.315 1.466-0.823 1.968l-0 0c-0.512 0.511-1.219 0.827-1.999 0.827-0.004 0-0.008 0-0.012-0h0.001zM32 7.333v-1.467c-0.015-1.586-1.305-2.867-2.893-2.867-0.007 0-0.014 0-0.021 0h-16.349l-1.18-1.743c-0.524-0.763-1.392-1.257-2.375-1.257-0.003 0-0.006 0-0.008 0h-6.29c-0.006-0-0.013-0-0.020-0-1.573 0-2.85 1.269-2.863 2.839v4.495c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4.493c0.015-0.099 0.099-0.174 0.201-0.174 0.005 0 0.011 0 0.016 0.001l-0.001-0h6.29c0.002-0 0.004-0 0.006-0 0.068 0 0.129 0.033 0.167 0.083l0 0.001 1.577 2.333c0.243 0.354 0.646 0.583 1.102 0.583 0.002 0 0.003 0 0.005 0h17.056c0.005-0 0.010-0.001 0.016-0.001 0.117 0 0.214 0.087 0.231 0.199l0 0.001v1.467c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM21.28 21.727l-3.717-3.727c-0.399-0.402-0.951-0.65-1.562-0.65s-1.163 0.249-1.562 0.65l-0 0-3.72 3.727c-0.239 0.241-0.387 0.573-0.387 0.94 0 0.737 0.597 1.334 1.334 1.334 0.37 0 0.705-0.151 0.947-0.394l0-0 2.053-2.067v9.127c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.13l2.053 2.070c0.242 0.243 0.577 0.394 0.947 0.394 0.737 0 1.334-0.597 1.334-1.334 0-0.367-0.148-0.699-0.387-0.94l0 0z"></path> - </symbol> - <symbol id="newsletter" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 8h-8c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h8c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM21.333 11.333c0-0.736-0.597-1.333-1.333-1.333v0h-8c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h8c0.736 0 1.333-0.597 1.333-1.333v0zM32 13.277v16.67c-0.015 1.137-0.94 2.053-2.080 2.053-0.009 0-0.019-0-0.028-0h-27.789c-0.007 0-0.015 0-0.024 0-1.138 0-2.063-0.914-2.080-2.049l-0-0.002v-16.853c0-0.87 0-0.94 5.933-4.85 0.019-0.013 0.041-0.025 0.064-0.036l0.003-0.001v-6.877c0-0.736 0.597-1.333 1.333-1.333v0h17.333c0.736 0 1.333 0.597 1.333 1.333v0 7.020c0.057 0.027 0.104 0.052 0.15 0.080l-0.006-0.004c5.857 3.903 5.857 3.97 5.857 4.833zM26 11.543v3.407l2.623-1.617c-0.547-0.38-1.373-0.95-2.623-1.79zM8.667 2.667v13.733l7.333 4.667 7.333-4.483v-13.917zM3.487 13.107l2.513 1.6v-3.307c-1.17 0.78-1.97 1.327-2.513 1.707zM29.333 16.033l-12.613 7.73c-0.048 0.032-0.104 0.064-0.162 0.090l-0.008 0.003c-0.16 0.074-0.348 0.117-0.545 0.117-0.002 0-0.003 0-0.005 0h0c-0.198-0.001-0.386-0.045-0.555-0.123l0.008 0.003c-0.069-0.032-0.128-0.067-0.184-0.106l0.004 0.003-12.607-8.003v13.587h26.667z"></path> - </symbol> - <symbol id="news" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.667 24.667h-12c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h12c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM26 18.667c0-0.736-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333v0zM26 13.333v-6.667c0-0.736-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333v0 6.667c0 0.736 0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333v0zM14 8h9.333v4h-9.333zM30.667 28.667v-26c0-1.105-0.895-2-2-2v0h-20c-1.105 0-2 0.895-2 2v0 11.333h-3.060c-0.174 0.001-0.34 0.034-0.493 0.093l0.009-0.003c-1.026 0.252-1.778 1.158-1.79 2.242v10.335c0 2.577 2.089 4.667 4.667 4.667v0h22c1.473 0 2.667-1.194 2.667-2.667v0zM6.667 16.667v8c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-21.333h18.667v25.333h-22c-1.105 0-2-0.895-2-2v0-10z"></path> - </symbol> - <symbol id="notice" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM17.333 16v-8.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 8.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM16 19.333c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0z"></path> - </symbol> - <symbol id="open-newtab" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16.533 32h-14.4c-1.177-0.002-2.131-0.956-2.133-2.133v-14.4c0.002-1.177 0.956-2.131 2.133-2.133h8.867c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-8.333v13.333h13.333v-8.333c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 8.867c-0.002 1.177-0.956 2.131-2.133 2.133h-0zM30.667 0h-8c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.787l-13.73 13.723c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391l13.723-13.72v4.777c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-8c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="open-sametab" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.333 32h-26.667c-1.473 0-2.667-1.194-2.667-2.667v0-26.667c0-1.473 1.194-2.667 2.667-2.667v0h26.667c1.473 0 2.667 1.194 2.667 2.667v0 26.667c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 2.667v26.667h26.667v-26.667zM24 6.667h-7.96c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.74l-13.723 13.723c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0l13.723-13.723v4.74c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-7.96c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="options" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.333 26.667c0 1.841-1.492 3.333-3.333 3.333s-3.333-1.492-3.333-3.333c0-1.841 1.492-3.333 3.333-3.333v0c1.841 0 3.333 1.492 3.333 3.333v0zM16 12.667c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0zM16 2c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0z"></path> - </symbol> - <symbol id="order-management" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.313 7.54c0.011-0.064 0.017-0.138 0.017-0.214 0-0.215-0.051-0.417-0.141-0.597l0.003 0.008-3-6c-0.224-0.44-0.674-0.737-1.192-0.737-0 0-0.001 0-0.001 0h-20.667c-0.517 0-0.966 0.295-1.187 0.726l-0.003 0.007-3 5.933c-0.089 0.174-0.142 0.38-0.142 0.598 0 0.084 0.008 0.167 0.023 0.247l-0.001-0.008c-0.014 0.067-0.022 0.144-0.023 0.222v15.608c0 0 0 0 0 0 0 1.105 0.895 2 2 2 0.006 0 0.012-0 0.018-0h8.649c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-8v-14.040c0.697 0 1.633 0 2.737 0.017 2.437 0 5.667 0.020 8.93 0.023h12.337v3.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4.273c-0.003-0.068-0.011-0.132-0.025-0.194l0.001 0.008zM13.667 6c-4.333 0-8.023 0-10.18-0.030l1.667-3.3h8.513zM20.313 6h-3.98v-3.333h7.843l1.667 3.35zM21.773 26.333c-0 0-0.001 0-0.002 0-0.356 0-0.68-0.14-0.919-0.367l0.001 0.001-2.103-2c-0.281-0.246-0.457-0.605-0.457-1.005 0-0.736 0.597-1.333 1.333-1.333 0.375 0 0.714 0.155 0.956 0.404l0 0 1.083 1.020 3-3.573c0.246-0.316 0.627-0.518 1.055-0.518 0.736 0 1.333 0.597 1.333 1.333 0 0.343-0.13 0.656-0.342 0.892l0.001-0.001-3.893 4.667c-0.235 0.281-0.58 0.463-0.967 0.48l-0.003 0zM32 22.667c0-5.155-4.179-9.333-9.333-9.333s-9.333 4.179-9.333 9.333c0 5.155 4.179 9.333 9.333 9.333v0c5.155 0 9.333-4.179 9.333-9.333v0zM29.333 22.667c0 3.682-2.985 6.667-6.667 6.667s-6.667-2.985-6.667-6.667c0-3.682 2.985-6.667 6.667-6.667v0c3.682 0 6.667 2.985 6.667 6.667v0z"></path> - </symbol> - <symbol id="panels" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.333 0h-22.333v32h32v-32zM9.667 29.333h-7v-26.667h7zM12.667 29.333v-26.667h6.667v26.667zM29.333 29.333h-7v-26.667h7z"></path> - </symbol> - <symbol id="paragraph-add" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.333 0h-16.733c-0.073 0-7.267 0.147-7.267 8.29s7.117 8.27 7.2 8.27h4.8v14.107c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-28h2.667v15.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-15.333h4c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM14.333 13.893h-4.783c-0.463-0.010-4.55-0.25-4.55-5.603s4.153-5.623 4.61-5.623h4.723zM29.667 26.667c0 0.736-0.597 1.333-1.333 1.333v0h-2.667v2.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-2.667h-2.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h2.667v-2.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 2.667h2.667c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="paragraph" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.333 0h-16.733c-0.073 0-7.267 0.147-7.267 8.29s7.117 8.27 7.2 8.27h4.8v14.107c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-28h2.667v28c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-28h4c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM15.333 13.893h-4.783c-0.463-0.010-4.55-0.25-4.55-5.603s4.153-5.623 4.61-5.623h4.723z"></path> - </symbol> - <symbol id="pdf-file" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M12.667 20.080c0.003 0.041 0.005 0.090 0.005 0.138 0 0.598-0.258 1.135-0.67 1.507l-0.002 0.002c-0.484 0.36-1.093 0.576-1.752 0.576-0.066 0-0.132-0.002-0.197-0.006l0.009 0h-0.627v2.37h-1.433v-6.667h2.157c0.061-0.005 0.131-0.007 0.202-0.007 0.625 0 1.204 0.199 1.676 0.536l-0.009-0.006c0.396 0.348 0.645 0.856 0.645 1.422 0 0.047-0.002 0.094-0.005 0.141l0-0.006zM9.433 21.137h0.47c0.037 0.003 0.079 0.005 0.123 0.005 0.326 0 0.63-0.098 0.883-0.265l-0.006 0.004c0.201-0.195 0.326-0.468 0.326-0.769 0-0.275-0.104-0.525-0.274-0.715l0.001 0.001c-0.21-0.153-0.473-0.244-0.758-0.244-0.037 0-0.074 0.002-0.111 0.005l0.005-0h-0.66zM19 21.27q0 1.643-0.903 2.52t-2.61 0.877h-1.82v-6.667h2q1.577 0 2.447 0.863c0.552 0.579 0.892 1.364 0.892 2.229 0 0.062-0.002 0.124-0.005 0.186l0-0.009zM17.583 21.307q0-2.15-1.827-2.15h-0.727v4.333h0.583q1.97 0.010 1.97-2.183zM21.457 24.667h-1.457v-6.667h4v1.157h-2.543v1.72h2.367v1.153h-2.367zM27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16z"></path> - </symbol> - <symbol id="personalize-block" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.763 32h-2.86c-0.564-0.005-1.043-0.36-1.234-0.858l-0.003-0.009-0.497-1.333c-0.36-0.172-0.663-0.345-0.951-0.537l0.028 0.017-1.58 0.283c-0.068 0.012-0.146 0.019-0.225 0.019-0.486 0-0.912-0.261-1.145-0.65l-0.003-0.006-1.427-2.41c-0.118-0.195-0.188-0.43-0.188-0.682 0-0.332 0.122-0.636 0.323-0.87l-0.001 0.002 1-1.187v-0.887l-1-1.19c-0.201-0.232-0.323-0.537-0.323-0.87 0-0.251 0.069-0.486 0.19-0.686l-0.003 0.006 1.427-2.41c0.236-0.396 0.662-0.656 1.149-0.656 0.081 0 0.159 0.007 0.236 0.021l-0.008-0.001 1.58 0.273c0.262-0.176 0.565-0.35 0.881-0.503l0.046-0.020 0.497-1.333c0.196-0.502 0.673-0.851 1.233-0.857h2.861c0.564 0.005 1.043 0.36 1.234 0.858l0.003 0.009 0.497 1.333c0.361 0.173 0.665 0.347 0.955 0.541l-0.028-0.018 1.577-0.283c0.068-0.012 0.145-0.019 0.225-0.019 0.486 0 0.912 0.261 1.145 0.65l0.003 0.006 1.427 2.41c0.118 0.195 0.188 0.43 0.188 0.682 0 0.332-0.122 0.636-0.323 0.87l0.001-0.002-1 1.19v0.89l1 1.187c0.201 0.232 0.323 0.537 0.323 0.87 0 0.251-0.069 0.486-0.19 0.686l0.003-0.006-1.417 2.413c-0.236 0.395-0.662 0.655-1.148 0.655-0.084 0-0.167-0.008-0.247-0.023l0.008 0.001-1.59-0.273c-0.259 0.174-0.559 0.346-0.872 0.497l-0.045 0.020-0.493 1.323c-0.193 0.507-0.673 0.861-1.236 0.867h-0.001zM20.83 29.333h1l0.37-1c0.142-0.365 0.43-0.646 0.791-0.777l0.009-0.003c0.488-0.184 0.911-0.426 1.286-0.724l-0.010 0.007c0.225-0.178 0.513-0.286 0.826-0.286 0.081 0 0.159 0.007 0.236 0.021l-0.008-0.001 1.233 0.213 0.467-0.783-0.773-0.903c-0.204-0.233-0.328-0.54-0.328-0.876 0-0.078 0.007-0.154 0.019-0.228l-0.001 0.008c0.034-0.2 0.053-0.431 0.053-0.666 0-0 0-0.001 0-0.001v0c-0-0.235-0.018-0.466-0.053-0.692l0.003 0.025c-0.010-0.061-0.016-0.132-0.016-0.204 0-0.333 0.122-0.637 0.324-0.871l-0.001 0.002 0.773-0.9-0.47-0.797-1.227 0.207c-0.067 0.012-0.145 0.019-0.224 0.019-0.315 0-0.604-0.109-0.832-0.291l0.003 0.002c-0.367-0.291-0.79-0.534-1.247-0.709l-0.033-0.011c-0.367-0.135-0.652-0.416-0.79-0.771l-0.003-0.009-0.37-1h-1l-0.37 1c-0.142 0.365-0.43 0.646-0.791 0.777l-0.009 0.003c-0.491 0.186-0.915 0.429-1.294 0.728l0.010-0.008c-0.225 0.18-0.514 0.289-0.829 0.289-0.079 0-0.157-0.007-0.232-0.020l0.008 0.001-1.227-0.21-0.47 0.797 0.773 0.9c0.202 0.233 0.326 0.538 0.326 0.873 0 0.072-0.006 0.142-0.017 0.211l0.001-0.008c-0.032 0.2-0.050 0.431-0.050 0.667v0c0 0.235 0.019 0.466 0.057 0.691l-0.003-0.024c0.010 0.060 0.015 0.13 0.015 0.201 0 0.334-0.123 0.64-0.327 0.874l0.001-0.002-0.77 0.897 0.47 0.8 1.223-0.203c0.069-0.012 0.147-0.020 0.228-0.020 0.313 0 0.601 0.108 0.828 0.288l-0.003-0.002c0.367 0.292 0.79 0.535 1.248 0.709l0.032 0.011c0.367 0.135 0.652 0.416 0.79 0.771l0.003 0.009zM21.333 27c-2.025 0-3.667-1.642-3.667-3.667s1.642-3.667 3.667-3.667c2.025 0 3.667 1.642 3.667 3.667v0c0 2.025-1.642 3.667-3.667 3.667v0zM21.333 22.333c-0.552 0-1 0.448-1 1s0.448 1 1 1c0.552 0 1-0.448 1-1v0c0-0.552-0.448-1-1-1v0zM23 8.91l-7.167-8.44c-0.243-0.284-0.6-0.465-0.999-0.47h-11.501c-0.736 0-1.333 0.597-1.333 1.333v0 26.667c0 0.736 0.597 1.333 1.333 1.333v0h7c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5.667v-24h7.833v4.71c0 3.17 1.763 5 4.837 5h3.387c0.171 0.554 0.677 0.949 1.277 0.949 0.734 0 1.329-0.592 1.333-1.325v-2.227c0-0 0-0 0-0.001 0-0.33-0.12-0.632-0.318-0.865l0.002 0.002zM15.157 7.377v-3.573l5 5.893h-2.823c-1.803 0-2.16-0.85-2.16-2.32z"></path> - </symbol> - <symbol id="personalize-content" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M21.457 32h-0.15c-0.651-0.052-1.218-0.378-1.589-0.861l-0.004-0.005-6.43-8.207c-0.177-0.224-0.283-0.51-0.283-0.822 0-0.002 0-0.003 0-0.005v0l0.023-6.497c-0-0.007-0-0.016-0-0.024 0-0.695 0.312-1.316 0.803-1.733l0.003-0.003c0.367-0.317 0.848-0.51 1.375-0.51 0.131 0 0.26 0.012 0.385 0.035l-0.013-0.002 5.69 1.043c0.331 0.062 0.612 0.239 0.808 0.487l0.002 0.003 6.423 8.197c0.315 0.41 0.504 0.93 0.504 1.494 0 0.68-0.275 1.296-0.721 1.742l0-0-5.333 5.090c-0.38 0.357-0.893 0.577-1.458 0.577-0.013 0-0.025-0-0.037-0l0.002 0zM15.667 21.647l5.833 7.44 4.76-4.527-5.97-7.617-4.6-0.843zM19.24 18.84c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0zM24.017 8.91l-7.183-8.44c-0.243-0.284-0.6-0.465-0.999-0.47h-11.501c-0.736 0-1.333 0.597-1.333 1.333v0 26.667c0 0.736 0.597 1.333 1.333 1.333v0h7c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-5.667v-24h7.833v4.71c0 3.17 1.763 5 4.837 5h3.387c0.171 0.554 0.677 0.949 1.277 0.949 0.734 0 1.329-0.592 1.333-1.325v-2.227c0-0 0-0 0-0.001 0-0.33-0.12-0.632-0.318-0.865l0.002 0.002zM16.173 7.377v-3.573l5 5.893h-2.84c-1.803 0-2.16-0.85-2.16-2.32z"></path> - </symbol> - <symbol id="personalize" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.763 32h-2.86c-0.564-0.005-1.043-0.36-1.234-0.858l-0.003-0.009-0.497-1.333c-0.36-0.172-0.663-0.345-0.951-0.537l0.028 0.017-1.58 0.283c-0.068 0.012-0.145 0.019-0.225 0.019-0.486 0-0.912-0.261-1.145-0.65l-0.003-0.006-1.427-2.41c-0.118-0.195-0.188-0.43-0.188-0.682 0-0.332 0.122-0.636 0.323-0.87l-0.001 0.002 1-1.187v-0.887l-1-1.19c-0.201-0.232-0.323-0.537-0.323-0.87 0-0.251 0.069-0.486 0.19-0.686l-0.003 0.006 1.427-2.41c0.236-0.396 0.662-0.656 1.149-0.656 0.081 0 0.159 0.007 0.236 0.021l-0.008-0.001 1.58 0.273c0.262-0.176 0.565-0.35 0.881-0.503l0.046-0.020 0.497-1.333c0.196-0.502 0.673-0.851 1.233-0.857h2.861c0.564 0.005 1.043 0.36 1.234 0.858l0.003 0.009 0.497 1.333c0.361 0.173 0.665 0.347 0.955 0.541l-0.028-0.018 1.577-0.283c0.068-0.012 0.145-0.019 0.225-0.019 0.486 0 0.912 0.261 1.145 0.65l0.003 0.006 1.427 2.41c0.118 0.195 0.188 0.43 0.188 0.682 0 0.332-0.122 0.636-0.323 0.87l0.001-0.002-1 1.19v0.89l1 1.187c0.201 0.232 0.323 0.537 0.323 0.87 0 0.251-0.069 0.486-0.19 0.686l0.003-0.006-1.417 2.413c-0.236 0.395-0.662 0.655-1.148 0.655-0.084 0-0.167-0.008-0.247-0.023l0.008 0.001-1.59-0.273c-0.259 0.174-0.559 0.346-0.872 0.497l-0.045 0.020-0.493 1.323c-0.193 0.507-0.673 0.861-1.236 0.867h-0.001zM22.83 29.333h1l0.37-1c0.142-0.365 0.43-0.646 0.791-0.777l0.009-0.003c0.488-0.184 0.911-0.426 1.286-0.724l-0.010 0.007c0.225-0.178 0.513-0.286 0.826-0.286 0.081 0 0.159 0.007 0.236 0.021l-0.008-0.001 1.233 0.213 0.467-0.783-0.773-0.903c-0.204-0.233-0.328-0.54-0.328-0.876 0-0.078 0.007-0.154 0.019-0.228l-0.001 0.008c0.034-0.2 0.053-0.431 0.053-0.666 0-0 0-0.001 0-0.001v0c-0-0.235-0.018-0.466-0.053-0.692l0.003 0.025c-0.010-0.061-0.016-0.132-0.016-0.204 0-0.333 0.122-0.637 0.324-0.871l-0.001 0.002 0.773-0.9-0.47-0.797-1.227 0.207c-0.067 0.012-0.145 0.019-0.224 0.019-0.315 0-0.604-0.109-0.832-0.291l0.003 0.002c-0.367-0.291-0.79-0.534-1.247-0.709l-0.033-0.011c-0.367-0.135-0.652-0.416-0.79-0.771l-0.003-0.009-0.37-1h-1l-0.37 1c-0.142 0.365-0.43 0.646-0.791 0.777l-0.009 0.003c-0.491 0.186-0.915 0.429-1.294 0.728l0.010-0.008c-0.225 0.18-0.514 0.289-0.829 0.289-0.079 0-0.157-0.007-0.232-0.020l0.008 0.001-1.227-0.21-0.47 0.797 0.773 0.9c0.202 0.233 0.326 0.538 0.326 0.873 0 0.072-0.006 0.142-0.017 0.211l0.001-0.008c-0.032 0.2-0.050 0.431-0.050 0.667v0c0 0.235 0.019 0.466 0.057 0.691l-0.003-0.024c0.010 0.060 0.015 0.13 0.015 0.201 0 0.334-0.123 0.64-0.327 0.874l0.001-0.002-0.77 0.897 0.47 0.8 1.223-0.203c0.069-0.012 0.147-0.020 0.228-0.020 0.313 0 0.601 0.108 0.828 0.288l-0.003-0.002c0.367 0.292 0.79 0.535 1.248 0.709l0.032 0.011c0.367 0.135 0.652 0.416 0.79 0.771l0.003 0.009zM23.333 27c-2.025 0-3.667-1.642-3.667-3.667s1.642-3.667 3.667-3.667c2.025 0 3.667 1.642 3.667 3.667v0c0 2.025-1.642 3.667-3.667 3.667v0zM23.333 22.333c-0.552 0-1 0.448-1 1s0.448 1 1 1c0.552 0 1-0.448 1-1v0c0-0.552-0.448-1-1-1v0zM4.667 3.607c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM8 3.607c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM11.333 3.607c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM24.667 0h-22.667c-1.105 0-2 0.895-2 2v0 22.667c0 1.105 0.895 2 2 2v0h10c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-9.333v-14.333h21.333v2.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-10c0-1.105-0.895-2-2-2v0zM2.667 7v-4.333h21.333v4.333z"></path> - </symbol> - <symbol id="pin-unpin" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M27.963 20.47c-0.196 0.509-0.68 0.863-1.248 0.863-0.001 0-0.002 0-0.002 0h-9.38v9.333c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-9.333h-2.113l-6.943 6.943c-0.241 0.241-0.575 0.391-0.943 0.391-0.737 0-1.334-0.597-1.334-1.334 0-0.368 0.149-0.702 0.391-0.943v0l22.667-22.667c0.241-0.241 0.575-0.391 0.943-0.391 0.737 0 1.334 0.597 1.334 1.334 0 0.368-0.149 0.702-0.391 0.943v0l-13.057 13.057h10.447c0.067-1.21-0.72-2.353-2.333-3.41-0.394-0.236-0.654-0.662-0.654-1.148 0-0.736 0.597-1.333 1.333-1.333 0.286 0 0.551 0.090 0.768 0.244l-0.004-0.003c3.053 1.983 4.223 4.707 3.187 7.453zM5.757 21.247c0.508-0.196 0.862-0.68 0.862-1.247 0-0.169-0.031-0.33-0.088-0.479l0.003 0.009c-1.113-2.947 3.063-4.79 3.24-4.867 0.374-0.163 0.654-0.481 0.761-0.871l0.002-0.009 1.667-6.167c0.068-0.269 0.106-0.578 0.106-0.896 0-0.907-0.315-1.739-0.842-2.395l0.006 0.007c-0.513-0.633-0.587-1-0.547-1.137 0.073-0.197 0.45-0.417 0.74-0.53h8.667c0.333 0.123 0.757 0.38 0.76 0.613 0.010 0.729 0.603 1.315 1.333 1.315 0.736 0 1.333-0.597 1.333-1.333 0-0.006-0-0.013-0-0.019v0.001c-0.037-1.937-1.833-2.91-2.89-3.2-0.099-0.028-0.213-0.043-0.33-0.043-0.001 0-0.002 0-0.004 0h-9.070c-0.001 0-0.002 0-0.004 0-0.117 0-0.231 0.016-0.339 0.045l0.009-0.002c-0.223 0.060-2.177 0.617-2.737 2.333-0.373 1.14-0.030 2.367 1 3.647 0.147 0.193 0.235 0.437 0.235 0.702 0 0.066-0.006 0.131-0.016 0.194l0.001-0.007-1.493 5.58c-2.333 1.21-5.567 4.060-4.087 7.977 0.196 0.509 0.68 0.863 1.248 0.863 0.001 0 0.002 0 0.002 0h-0c0.001 0 0.002 0 0.003 0 0.168 0 0.328-0.032 0.476-0.090l-0.009 0.003z"></path> - </symbol> - <symbol id="pin" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.873 12.493l-1.497-5.547c-0.013-0.064-0.020-0.138-0.020-0.214 0-0.265 0.089-0.51 0.238-0.706l-0.002 0.003c1.043-1.28 1.383-2.507 1-3.647-0.55-1.717-2.503-2.28-2.723-2.34-0.099-0.028-0.213-0.043-0.331-0.043-0.001 0-0.002 0-0.002 0h-4.203c-0.119 0.002-0.234 0.019-0.343 0.049l0.010-0.002c-0.099-0.028-0.214-0.045-0.332-0.047h-4.198c-0.001 0-0.001 0-0.002 0-0.118 0-0.232 0.016-0.34 0.045l0.009-0.002c-0.22 0.060-2.173 0.617-2.737 2.333-0.37 1.14-0.030 2.367 1 3.647 0.148 0.195 0.237 0.441 0.237 0.709 0 0.064-0.005 0.127-0.015 0.188l0.001-0.007-1.497 5.58c-2.333 1.21-5.563 4.060-4.087 7.977 0.195 0.508 0.68 0.863 1.247 0.863h9.38v9.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-9.333h9.38c0.567-0 1.051-0.355 1.244-0.854l0.003-0.009c1.477-3.917-1.747-6.767-4.087-7.977zM25.667 18.667h-9.333c-0.026-0.002-0.057-0.002-0.088-0.002s-0.062 0.001-0.093 0.003l0.004-0c-0.023-0.001-0.051-0.002-0.078-0.002s-0.055 0.001-0.082 0.002l0.004-0c-0.024-0.002-0.052-0.002-0.080-0.002s-0.056 0.001-0.084 0.003l0.004-0c-0.026-0.002-0.056-0.003-0.087-0.003s-0.061 0.001-0.091 0.003l0.004-0h-9.317c-0.12-2.44 3.267-3.937 3.423-4 0.375-0.162 0.656-0.48 0.764-0.871l0.002-0.009 1.667-6.167c0.068-0.27 0.107-0.579 0.107-0.898 0-0.907-0.317-1.741-0.846-2.396l0.006 0.007c-0.513-0.633-0.583-1-0.543-1.137 0.070-0.197 0.447-0.417 0.737-0.53h4c0.119-0.002 0.234-0.019 0.343-0.049l-0.010 0.002c0.099 0.028 0.214 0.045 0.332 0.047h4.001c0.307 0.117 0.69 0.333 0.75 0.543 0.040 0.127-0.030 0.503-0.543 1.137-0.52 0.646-0.835 1.477-0.835 2.381 0 0.315 0.038 0.622 0.11 0.915l-0.005-0.026 1.667 6.167c0.105 0.398 0.379 0.716 0.738 0.88l0.008 0.003c0.16 0.063 3.547 1.56 3.443 4z"></path> - </symbol> - <symbol id="places" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.667 24.333c-0.002 0-0.003 0-0.005 0-0.491 0-0.92-0.265-1.151-0.66l-0.003-0.006s-0.173-0.307-0.463-0.837c-0.108-0.189-0.172-0.415-0.172-0.655 0-0.505 0.281-0.945 0.695-1.171l0.007-0.003c0.179-0.095 0.391-0.151 0.616-0.151 0.051 0 0.1 0.003 0.15 0.008l-0.006-0.001c4.073-7.447 5-11.333 5-12.253 0-3.090-2.093-5.603-4.667-5.603-1.337 0.025-2.521 0.658-3.29 1.634l-0.007 0.009c-0.242 0.243-0.576 0.394-0.946 0.394-0.736 0-1.333-0.597-1.333-1.333 0-0.287 0.091-0.554 0.246-0.771l-0.003 0.004c1.27-1.565 3.181-2.567 5.327-2.603l0.006-0c4.043 0 7.333 3.71 7.333 8.27 0 4.063-5.543 13.943-6.173 15.063-0.235 0.401-0.664 0.667-1.155 0.667-0.002 0-0.004 0-0.006 0h0zM8.493 23.667s0.173-0.307 0.463-0.837c0.108-0.189 0.172-0.415 0.172-0.655 0-0.505-0.281-0.945-0.695-1.171l-0.007-0.003c-0.179-0.095-0.391-0.151-0.617-0.151-0.051 0-0.1 0.003-0.15 0.008l0.006-0.001c-4.077-7.447-5-11.343-5-12.253 0-3.090 2.093-5.603 4.667-5.603 1.337 0.025 2.521 0.658 3.29 1.634l0.007 0.009c0.242 0.243 0.576 0.394 0.946 0.394 0.736 0 1.333-0.597 1.333-1.333 0-0.287-0.091-0.554-0.246-0.771l0.003 0.004c-1.27-1.565-3.181-2.567-5.327-2.603l-0.006-0c-4.043 0-7.333 3.71-7.333 8.27 0 4.063 5.543 13.943 6.173 15.063 0.235 0.401 0.664 0.667 1.155 0.667 0.002 0 0.004 0 0.006 0h-0c0.002 0 0.003 0 0.005 0 0.491 0 0.92-0.265 1.151-0.66l0.003-0.006zM19.307 13.913c0-1.841-1.492-3.333-3.333-3.333s-3.333 1.492-3.333 3.333c0 1.841 1.492 3.333 3.333 3.333v0c1.841 0 3.333-1.492 3.333-3.333v0zM16.64 13.913c0 0.368-0.298 0.667-0.667 0.667s-0.667-0.298-0.667-0.667c0-0.368 0.298-0.667 0.667-0.667v0c0.368 0 0.667 0.298 0.667 0.667v0zM27.973 8.913c0 1.841-1.492 3.333-3.333 3.333v0h-0.133c0.085 0.46 0.133 0.989 0.133 1.53v0c0 4.333-6.747 15.933-7.517 17.24-0.236 0.395-0.662 0.656-1.148 0.656s-0.912-0.261-1.145-0.65l-0.003-0.006c-0.77-1.307-7.52-12.893-7.52-17.24 0.002-0.542 0.051-1.071 0.145-1.585l-0.008 0.055h-0.083c-0.002 0-0.004 0-0.006 0-1.841 0-3.333-1.492-3.333-3.333s1.492-3.333 3.333-3.333c1.215 0 2.278 0.65 2.861 1.622l0.008 0.015c1.514-1.375 3.534-2.217 5.75-2.217 2.232 0 4.265 0.854 5.789 2.252l-0.006-0.006c0.588-1 1.658-1.661 2.883-1.661 1.839 0 3.33 1.489 3.333 3.327v0zM7.36 9.58c0.368 0 0.667-0.298 0.667-0.667s-0.298-0.667-0.667-0.667c-0.368 0-0.667 0.298-0.667 0.667v0c0 0.368 0.298 0.667 0.667 0.667v0zM21.973 13.77c0-3.314-2.686-6-6-6s-6 2.686-6 6v0c0 2.373 3.317 9.087 6 13.883 2.693-4.797 6-11.51 6-13.883zM25.307 8.913c0-0.368-0.298-0.667-0.667-0.667s-0.667 0.298-0.667 0.667c0 0.368 0.298 0.667 0.667 0.667v0c0.368 0 0.667-0.298 0.667-0.667v0z"></path> - </symbol> - <symbol id="place" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-0.479-0-0.898-0.252-1.133-0.631l-0.003-0.006c-0.977-1.59-9.53-15.697-9.53-20.94 0-5.757 4.783-10.423 10.667-10.423s10.667 4.667 10.667 10.423c0 5.243-8.553 19.35-9.53 20.94-0.238 0.384-0.658 0.637-1.137 0.637v0zM16 2.667c-4.41 0-8 3.48-8 7.757 0 3.333 5.033 12.593 8 17.647 2.967-5.053 8-14.333 8-17.647 0-4.277-3.59-7.757-8-7.757zM16 14.667c-2.209 0-4-1.791-4-4s1.791-4 4-4c2.209 0 4 1.791 4 4v0c0 2.209-1.791 4-4 4v0zM16 9.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="portfolio" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.547 6.667h-6.88v-1.153c0-2.307-1.617-4.18-3.607-4.18h-6.12c-1.99 0-3.607 1.873-3.607 4.18v1.153h-2.57c-0.014-0.001-0.031-0.001-0.048-0.001s-0.034 0-0.051 0.001l0.002-0c-0.014-0.001-0.031-0.001-0.048-0.001s-0.034 0-0.051 0.001l0.002-0h-4.117c-1.357 0.008-2.453 1.109-2.453 2.467 0 0.002 0 0.005 0 0.007v-0 19.053c0 0.002 0 0.004 0 0.007 0 1.358 1.097 2.459 2.453 2.467h27.094c1.357-0.008 2.453-1.109 2.453-2.467 0-0.002 0-0.005 0-0.007v0-19.053c0-0.002 0-0.004 0-0.007 0-1.358-1.097-2.459-2.453-2.467h-0.001zM24 9.333v18.667h-16v-18.667zM12 5.513c0-0.847 0.513-1.513 0.94-1.513h6.12c0.427 0 0.94 0.667 0.94 1.513v1.153h-8zM2.667 9.333h2.667v18.667h-2.667zM29.333 28h-2.667v-18.667h2.667z"></path> - </symbol> - <symbol id="previewed" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0zM20.94 19.057l-3.607-3.607v-8.783c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 9.303c-0 0.010-0 0.022-0 0.034 0 0.366 0.148 0.698 0.387 0.939l4 4c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="price" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.333 22.56a1.331 1.331 0 0 1-.943-.393l-3.557-3.553a1.333 1.333 0 0 1 1.883-1.886l-.001-.001 3.557 3.557a1.333 1.333 0 0 1-.94 2.276zm-2.166 2.72c.242-.241.391-.575.391-.943s-.149-.702-.391-.943l-3.56-3.557a1.334 1.334 0 1 0-1.886 1.886l3.557 3.557c.241.242.575.391.943.391s.702-.149.943-.391zM31.333 2.497c-1.863 1.047-3.703 2.667-5.28 4.333l4.223 4.227c.242.241.391.575.391.943s-.149.702-.391.943L11.609 31.61c-.241.242-.575.391-.943.391s-.702-.149-.943-.391L.39 22.277c-.242-.241-.391-.575-.391-.943s.149-.702.391-.943L19.057 1.724c.241-.242.575-.391.943-.391s.702.149.943.391l3.207 3.21C25.873 3.151 27.907 1.357 30 .171a1.338 1.338 0 0 1 1.313 2.33l-.007.003zM24.25 8.8c-.493.58-.94 1.127-1.333 1.61a3.29 3.29 0 0 1 .403 1.589c0 .921-.373 1.754-.977 2.357a3.333 3.333 0 1 1-2.357-5.689h.015H20c.292.002.574.041.843.112l-.023-.005c.437-.557.957-1.193 1.537-1.867L20 4.554 3.22 21.334l7.447 7.447 16.78-16.78z"></path> - </symbol> - <symbol id="product_list" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22 17.333h-7.977c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h7.977c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM23.31 20.667c0-0.736-0.597-1.333-1.333-1.333v0h-7.977c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h7.977c0.736 0 1.333-0.597 1.333-1.333v0zM23.31 25.333c0-0.736-0.597-1.333-1.333-1.333v0h-7.977c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h7.977c0.736 0 1.333-0.597 1.333-1.333v0zM28.667 2.667v26.667c0 1.473-1.194 2.667-2.667 2.667v0h-20c-1.473 0-2.667-1.194-2.667-2.667v0-26.667c0-1.473 1.194-2.667 2.667-2.667v0h20c1.473 0 2.667 1.194 2.667 2.667v0zM11.12 2.667l0.75 2.667h8.277l0.77-2.667zM26 29.333v-26.667h-2.307l-1.263 4.37c-0.166 0.561-0.676 0.963-1.281 0.963-0.001 0-0.002 0-0.003 0h-10.287c-0.608-0-1.121-0.407-1.281-0.964l-0.002-0.009-1.227-4.36h-2.35v26.667zM10 24c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM11.333 20.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM11.333 16c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="product" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.947 10c0.032-0.109 0.051-0.235 0.051-0.365 0-0.157-0.027-0.308-0.077-0.448l0.003 0.009-2.157-6.14c-0.302-0.818-1.075-1.39-1.981-1.39-0.007 0-0.013 0-0.020 0h-23.549c-0.006-0-0.012-0-0.019-0-0.906 0-1.679 0.572-1.976 1.375l-0.005 0.015-2.14 6.093c-0.049 0.132-0.077 0.283-0.077 0.442 0 0.136 0.021 0.267 0.059 0.391l-0.003-0.009c-0.034 0.107-0.055 0.231-0.057 0.359v17.891c0 0 0 0 0 0 0 1.163 0.941 2.106 2.103 2.11h27.79c1.164-0.002 2.107-0.946 2.107-2.11 0 0 0 0 0 0v0-17.89c-0.003-0.12-0.022-0.234-0.056-0.342l0.002 0.009zM28.793 8.333h-7.733l-0.8-4h7.127zM18.667 14.437l-2.12-0.957c-0.16-0.074-0.348-0.116-0.545-0.116s-0.385 0.043-0.553 0.12l0.008-0.003-2.123 0.943v-3.423h5.333zM17.54 4.333l0.793 4h-4.667l0.8-4zM4.607 4.333h7.133l-0.797 4c-3.187 0-6 0-7.737-0.030zM2.667 27.667v-16.713c0.747 0 1.79 0 3.27 0.020l4.73 0.027v5.477c0 0.001 0 0.002 0 0.002 0 0.736 0.597 1.333 1.333 1.333 0.197 0 0.384-0.043 0.552-0.119l-0.008 0.003 3.457-1.54 3.453 1.56c0.161 0.073 0.349 0.115 0.546 0.117h0c0.736 0 1.333-0.597 1.333-1.333v0-5.5h8v16.667z"></path> - </symbol> - <symbol id="portfolio" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.443 7.087c-0.503-0.656-1.285-1.078-2.165-1.087h-6.611v-1.533c0-1.88-1.667-3.467-3.607-3.467h-6.12c-0.019-0-0.042-0.001-0.064-0.001-1.93 0-3.5 1.543-3.542 3.463l-0 0.004v1.533h-6.483c-0.018-0-0.040-0.001-0.062-0.001-0.922 0-1.746 0.423-2.288 1.085l-0.004 0.005c-0.309 0.397-0.495 0.903-0.495 1.452 0 0.151 0.014 0.299 0.041 0.442l-0.002-0.015v7.697c0 0 0 0 0 0.001 0 0.722 0.574 1.31 1.291 1.333l0.002 0v11.667c0 0.736 0.597 1.333 1.333 1.333v0h26.667c0.736 0 1.333-0.597 1.333-1.333v0-11.667c0.712-0.031 1.277-0.616 1.277-1.332 0-0 0-0.001 0-0.001v0l0.020-7.57c0.022-0.127 0.035-0.273 0.035-0.422 0-0.603-0.209-1.157-0.559-1.593l0.004 0.005zM12 4.467c0.058-0.454 0.442-0.801 0.906-0.801 0.012 0 0.024 0 0.035 0.001l-0.002-0h6.12c0.010-0 0.022-0.001 0.034-0.001 0.465 0 0.848 0.347 0.906 0.796l0 0.005v1.533h-8zM2.85 8.667h26.423c0.020 0.007 0.036 0.019 0.050 0.033l0 0c-0.016 0.076-0.026 0.163-0.027 0.253v6.381h-9.297v-2c0-0.736-0.597-1.333-1.333-1.333v0h-5.333c-0.736 0-1.333 0.597-1.333 1.333v0 2h-9.293v-6.503c0.002-0.021 0.003-0.046 0.003-0.072s-0.001-0.050-0.003-0.075l0 0.003c0.042-0.013 0.090-0.020 0.14-0.020 0.001 0 0.002 0 0.003 0h-0zM17.333 16.837v1.83h-2.667v-4h2.667v1.83c-0.010 0.050-0.016 0.108-0.016 0.167s0.006 0.117 0.017 0.172l-0.001-0.006zM4 28.333v-10.333h8v2c0 0.736 0.597 1.333 1.333 1.333v0h5.333c0.736 0 1.333-0.597 1.333-1.333v0-2h8v10.333z"></path> - </symbol> - <symbol id="profile" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 18.667c-5.155 0-9.333-4.179-9.333-9.333s4.179-9.333 9.333-9.333c5.155 0 9.333 4.179 9.333 9.333v0c0 5.155-4.179 9.333-9.333 9.333v0zM16 2.667c-3.682 0-6.667 2.985-6.667 6.667s2.985 6.667 6.667 6.667c3.682 0 6.667-2.985 6.667-6.667v0c0-3.682-2.985-6.667-6.667-6.667v0zM27.427 31.93c0.532-0.183 0.907-0.68 0.907-1.263 0-0.153-0.026-0.299-0.073-0.436l0.003 0.009c-0.7-2.080-2.037-5.723-3.213-7.103-0.917-1.377-2.507-2.323-4.717-2.803-0.881-0.204-1.896-0.325-2.938-0.333l-0.006-0h-3.86c-0.43 0-4.357-0.023-6.73 2.91-1.087 1.333-2.39 5.16-3.070 7.313-0.051 0.136-0.081 0.294-0.081 0.458 0 0.736 0.597 1.333 1.333 1.333 0.614 0 1.131-0.415 1.286-0.979l0.002-0.009c1.153-3.667 2.14-5.867 2.603-6.44 1.607-1.987 4.56-1.92 4.587-1.92h3.95c1.15-0.020 4.38 0.31 5.45 2 0.046 0.071 0.093 0.134 0.145 0.192l-0.001-0.002c0.493 0.537 1.53 2.667 2.733 6.243 0.185 0.528 0.68 0.9 1.261 0.9 0.001 0 0.002 0 0.003 0h-0c0.153-0.001 0.299-0.026 0.436-0.073l-0.010 0.003z"></path> - </symbol> - <symbol id="publish-later-cancel" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.12 1.333h-1.453c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h1.453c0.119 0.004 0.213 0.101 0.213 0.22 0 0.001 0 0.002-0 0.004v-0 7.11h-26.667v-7.12c-0-0.001-0-0.002-0-0.003 0-0.114 0.090-0.206 0.203-0.21h1.464c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.463c-1.586 0.006-2.87 1.293-2.87 2.88 0 0 0 0 0 0v0 24.897c0 0.001 0 0.002 0 0.003 0 1.592 1.289 2.883 2.88 2.887h26.244c1.59-0.006 2.877-1.296 2.877-2.887 0-0.001 0-0.002 0-0.004v0-24.887c0-0.001 0-0.002 0-0.003 0-1.592-1.289-2.883-2.88-2.887h-0zM29.12 29.333h-26.24c-0.119-0.004-0.213-0.101-0.213-0.22 0-0.001 0-0.002 0-0.004v0-15.11h26.667v15.11c0 0.002 0 0.004 0 0.007 0 0.117-0.093 0.213-0.21 0.217h-0zM19.667 4h-7.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h7.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM20.273 25.943c-0.241 0.241-0.574 0.389-0.942 0.389s-0.701-0.149-0.942-0.389l0 0-2.39-2.4-2.39 2.397c-0.236 0.215-0.551 0.347-0.897 0.347-0.736 0-1.333-0.597-1.333-1.333 0-0.344 0.13-0.658 0.345-0.895l-0.001 0.001 2.393-2.393-2.37-2.38c-0.226-0.238-0.364-0.561-0.364-0.916 0-0.736 0.597-1.333 1.333-1.333 0.357 0 0.682 0.141 0.922 0.37l-0.001-0 2.363 2.357 2.363-2.37c0.239-0.229 0.564-0.369 0.921-0.369 0.736 0 1.333 0.597 1.333 1.333 0 0.355-0.139 0.678-0.365 0.917l0.001-0.001-2.37 2.393 2.393 2.407c0.236 0.24 0.381 0.57 0.381 0.933 0 0.365-0.147 0.696-0.385 0.937l0-0zM11 7.333c0 0.001 0 0.002 0 0.003 0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-0.982 0.531-1.84 1.321-2.303l0.013-0.007v-3.693c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 3.693c0.802 0.469 1.332 1.326 1.333 2.307v0zM26.333 7.333c0 0.001 0 0.002 0 0.003 0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-0.982 0.531-1.84 1.321-2.303l0.013-0.007v-3.693c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 3.693c0.802 0.469 1.332 1.326 1.333 2.307v0z"></path> - </symbol> - <symbol id="publish-later" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.667 4h-7.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h7.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 4.223v24.887c0 0.001 0 0.002 0 0.003 0 1.591-1.287 2.881-2.876 2.887h-26.244c-1.591-0.004-2.88-1.295-2.88-2.887 0-0.001 0-0.002 0-0.004v0-24.897c0 0 0 0 0-0 0-1.587 1.284-2.874 2.869-2.88h1.464c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-1.463c-0.113 0.004-0.203 0.096-0.203 0.21 0 0.001 0 0.002 0 0.004v-0 7.12h26.667v-7.11c0-0.001 0-0.002 0-0.003 0-0.119-0.095-0.216-0.213-0.22h-1.454c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.453c1.591 0.004 2.88 1.295 2.88 2.887 0 0.001 0 0.002 0 0.004v-0zM29.333 29.11v-15.11h-26.667v15.11c-0 0.001-0 0.002-0 0.003 0 0.119 0.095 0.216 0.213 0.22h26.244c0.117-0.004 0.21-0.099 0.21-0.217 0-0.002-0-0.005-0-0.007v0zM19.333 19.5c0-0.009 0-0.019 0-0.029 0-1.918-1.555-3.473-3.473-3.473-0.046 0-0.091 0.001-0.137 0.003l0.007-0c-0.023-0.001-0.051-0.001-0.079-0.001-1.082 0-2.047 0.495-2.683 1.271l-0.005 0.006c-0.211 0.235-0.34 0.547-0.34 0.89 0 0.736 0.597 1.333 1.333 1.333 0.445 0 0.839-0.218 1.081-0.552l0.003-0.004c0.173-0.174 0.413-0.281 0.677-0.281 0.486 0 0.888 0.363 0.949 0.833l0 0.005c0 0.547-0.52 0.833-1.033 0.833h-0.363c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h0.287c0.51 0 1.060 0.26 1.060 0.833-0.002 0.464-0.379 0.84-0.843 0.84-0.038 0-0.075-0.002-0.111-0.007l0.004 0c-0.021 0.002-0.045 0.003-0.070 0.003-0.234 0-0.446-0.097-0.596-0.253l-0-0c-0.245-0.342-0.641-0.563-1.088-0.563-0.736 0-1.333 0.597-1.333 1.333 0 0.383 0.162 0.729 0.421 0.972l0.001 0.001c0.641 0.721 1.571 1.174 2.606 1.174 0.021 0 0.043-0 0.064-0.001l-0.003 0c2.057 0 3.61-1.503 3.61-3.5 0-0.005 0-0.010 0-0.015 0-0.805-0.286-1.544-0.761-2.12l0.005 0.006c0.506-0.588 0.813-1.358 0.813-2.2 0-0.001 0-0.002 0-0.003v0zM9.667 5.027v-3.693c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.693c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM25 5.027v-3.693c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.693c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007z"></path> - </symbol> - <symbol id="publish" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 1.223c-0.057-0.67-0.601-1.196-1.274-1.223l-0.003-0c-0.153 0-3.84-0.15-9.783 2.703-3.96 1.903-8.333 6.367-11.647 10.227l-5.030 0.287c-0.525 0.031-0.966 0.361-1.157 0.821l-0.003 0.009-3 7.41c-0.061 0.147-0.096 0.318-0.096 0.497 0 0.736 0.597 1.333 1.333 1.333h0c0.002 0 0.004 0 0.006 0 0.167 0 0.327-0.032 0.473-0.090l-0.009 0.003 3.143-1.187 5 4.953-1.213 3.227c-0.055 0.141-0.087 0.303-0.087 0.473 0 0.736 0.597 1.333 1.333 1.333 0.005 0 0.009-0 0.014-0h-0.001c0.18-0 0.352-0.036 0.509-0.1l-0.009 0.003 7.453-3.020c0.472-0.195 0.803-0.641 0.83-1.167l0-0.003 0.257-5.143c3.837-3.367 8.293-7.79 10.227-11.643 2.993-5.96 2.733-9.553 2.733-9.703zM5.26 15.833l1.74-0.097c-1.493 1.88-2.523 3.31-2.82 3.733l-0.44 0.167zM16.167 26.73l-3.833 1.56 0.15-0.4c0.177-0.13 1.697-1.25 3.773-2.95zM26.883 9.73c-2.83 5.633-12.12 13.050-15.070 15.333l-4.887-4.857c2.19-2.967 9.397-12.333 15.17-15.093 3.253-1.563 5.72-2.13 7.14-2.333-0.193 1.373-0.75 3.76-2.353 6.95zM8.853 28.293c-0.187 0.35-0.707 1.040-2.277 1.837-1.414 0.73-3.062 1.359-4.784 1.801l-0.166 0.036c-0.087 0.021-0.187 0.033-0.29 0.033-0.001 0-0.002 0-0.004 0h0c-0.735-0.002-1.33-0.598-1.33-1.333 0-0.192 0.040-0.374 0.113-0.539l-0.003 0.009c0.51-1.73 1.151-3.231 1.944-4.636l-0.057 0.11c0.857-1.547 1.63-2.023 1.92-2.157l1.107 2.427c0.041-0.014 0.076-0.033 0.108-0.057l-0.001 0.001c-0.33 0.297-0.599 0.655-0.791 1.056l-0.009 0.020c-0.333 0.59-0.597 1.157-0.82 1.667 0.577-0.223 1.213-0.497 1.88-0.827 0.448-0.19 0.83-0.449 1.154-0.767l-0 0c-0.030 0.033-0.054 0.073-0.069 0.118l-0.001 0.002zM20.213 15.543c-0.479 0.487-1.144 0.789-1.88 0.79h-0c-0.004 0-0.008 0-0.013 0-0.728 0-1.387-0.296-1.864-0.773l-0-0c-0.487-0.483-0.789-1.153-0.789-1.893 0-1.465 1.182-2.655 2.644-2.667h0.001c1.471 0.003 2.662 1.195 2.662 2.667 0 0.732-0.295 1.395-0.772 1.877l0-0z"></path> - </symbol> - <symbol id="quote" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14 9.333v5.333c0 0.013 0 0.029 0 0.045 0 2.208-0.585 4.28-1.608 6.068l0.032-0.060c-1.247 2.113-3.053 3.28-5.090 3.28-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0c1.707 0 3.52-2.093 3.917-5.333h-3.917c-0.736 0-1.333-0.597-1.333-1.333v0-5.333c0-0.736 0.597-1.333 1.333-1.333v0h5.333c0.736 0 1.333 0.597 1.333 1.333v0zM24.667 8h-5.333c-0.736 0-1.333 0.597-1.333 1.333v0 5.333c0 0.736 0.597 1.333 1.333 1.333v0h3.917c-0.397 3.24-2.21 5.333-3.917 5.333-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0c2.037 0 3.843-1.167 5.090-3.28 0.992-1.729 1.577-3.8 1.577-6.008 0-0.016-0-0.032-0-0.047v0.002-5.333c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="radio-button-multiple" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 25h-14.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h14.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM12 23.333c0-3.314-2.686-6-6-6s-6 2.686-6 6c0 3.314 2.686 6 6 6v0c3.314 0 6-2.686 6-6v0zM9.333 23.333c0 1.841-1.492 3.333-3.333 3.333s-3.333-1.492-3.333-3.333c0-1.841 1.492-3.333 3.333-3.333v0c1.841 0 3.333 1.492 3.333 3.333v0zM30.667 10h-14.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h14.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM12 8.667c0-3.314-2.686-6-6-6s-6 2.686-6 6c0 3.314 2.686 6 6 6v0c3.314 0 6-2.686 6-6v0zM9.333 8.667c0 1.841-1.492 3.333-3.333 3.333s-3.333-1.492-3.333-3.333c0-1.841 1.492-3.333 3.333-3.333v0c1.841 0 3.333 1.492 3.333 3.333v0zM6 7.333v0c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="radio-button" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 16c0 2.209-1.791 4-4 4s-4-1.791-4-4c0-2.209 1.791-4 4-4v0c2.209 0 4 1.791 4 4v0zM26.667 16c0-5.891-4.776-10.667-10.667-10.667s-10.667 4.776-10.667 10.667c0 5.891 4.776 10.667 10.667 10.667v0c5.891 0 10.667-4.776 10.667-10.667v0zM24 16c0 4.418-3.582 8-8 8s-8-3.582-8-8c0-4.418 3.582-8 8-8v0c4.418 0 8 3.582 8 8v0z"></path> - </symbol> - <symbol id="rate-review" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.47 11.783l-2.803 2.783 0.607 4c0.005 0.030 0.008 0.065 0.008 0.1 0 0.368-0.298 0.667-0.667 0.667-0.003 0-0.005 0-0.008-0h0c-0.122-0-0.237-0.032-0.337-0.088l0.003 0.002-3.347-1.913-3.41 1.823c-0.091 0.050-0.199 0.079-0.314 0.079-0.146 0-0.281-0.047-0.391-0.127l0.002 0.001c-0.169-0.122-0.278-0.319-0.278-0.542 0-0.044 0.004-0.087 0.012-0.129l-0.001 0.004 0.703-4-2.733-2.857c-0.114-0.119-0.184-0.281-0.184-0.459 0-0.337 0.249-0.615 0.573-0.66l0.004-0 3.8-0.527 1.747-3.593c0.108-0.208 0.321-0.348 0.567-0.348 0.014 0 0.028 0 0.041 0.001l-0.002-0c0.267 0.001 0.496 0.159 0.602 0.386l0.002 0.004 1.667 3.637 3.777 0.64c0.248 0.043 0.446 0.218 0.522 0.449l0.001 0.005c0.018 0.058 0.029 0.125 0.029 0.195 0 0.183-0.073 0.348-0.192 0.469l0-0zM7.407 31.783l9.663-6.45h11.93c0.029 0.001 0.064 0.002 0.098 0.002 1.603 0 2.903-1.3 2.903-2.903 0-0.035-0.001-0.069-0.002-0.103l0 0.005v-19.333c0.001-0.023 0.001-0.050 0.001-0.077 0-1.615-1.309-2.923-2.923-2.923-0.018 0-0.036 0-0.054 0l0.003-0h-26.027c-0.029-0.001-0.064-0.002-0.098-0.002-1.603 0-2.903 1.3-2.903 2.903 0 0.035 0.001 0.069 0.002 0.103l-0-0.005v19.333c-0.001 0.029-0.002 0.064-0.002 0.098 0 1.603 1.3 2.903 2.903 2.903 0.035 0 0.069-0.001 0.103-0.002l-0.005 0h2.333v5.333c0 0 0 0.001 0 0.002 0 0.506 0.282 0.946 0.696 1.172l0.007 0.003c0.182 0.099 0.399 0.158 0.629 0.158 0.276 0 0.533-0.084 0.745-0.228l-0.005 0.003zM29.027 2.667c0.167 0 0.307 0.027 0.307 0.333v19.333c0 0.253-0.080 0.333-0.333 0.333h-12.333c-0 0-0.001 0-0.001 0-0.275 0-0.531 0.083-0.743 0.226l0.005-0.003-7.927 5.287v-4.177c0-0.736-0.597-1.333-1.333-1.333v0h-3.667c-0.263 0-0.333-0.070-0.333-0.333v-19.333c0-0.263 0.070-0.333 0.333-0.333z"></path> - </symbol> - <symbol id="rate" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.47 14.967l-3.137 3.12 0.667 4.48c0.007 0.035 0.010 0.075 0.010 0.116 0 0.228-0.114 0.428-0.288 0.549l-0.002 0.001c-0.105 0.073-0.236 0.117-0.377 0.117v0c-0.122-0-0.237-0.032-0.337-0.088l0.003 0.002-3.757-2.157-3.823 2.040c-0.091 0.049-0.199 0.078-0.313 0.078-0.148 0-0.284-0.048-0.395-0.13l0.002 0.001c-0.168-0.123-0.276-0.319-0.276-0.54 0-0.045 0.004-0.089 0.013-0.131l-0.001 0.004 0.783-4.45-3.060-3.2c-0.111-0.119-0.178-0.278-0.178-0.454 0-0.334 0.245-0.61 0.565-0.659l0.004-0 4.263-0.6 1.957-4.023c0.11-0.225 0.338-0.377 0.6-0.377 0.003 0 0.007 0 0.010 0h-0c0.264 0.004 0.49 0.161 0.595 0.386l0.002 0.004 1.86 4.077 4.25 0.703c0.249 0.042 0.449 0.217 0.525 0.449l0.001 0.005c0.020 0.060 0.031 0.13 0.031 0.202 0 0.186-0.076 0.353-0.198 0.474l-0 0zM29.067 23.217c0.176-0.122 0.29-0.323 0.29-0.55 0-0.041-0.004-0.081-0.011-0.121l0.001 0.004-0.68-4.463 3.133-3.12c0.121-0.121 0.196-0.288 0.196-0.472 0-0.069-0.011-0.136-0.030-0.2l0.001 0.005c-0.078-0.236-0.278-0.411-0.523-0.453l-0.004-0.001-4.247-0.713-1.86-4.077c-0.109-0.228-0.338-0.383-0.603-0.383s-0.494 0.155-0.602 0.379l-0.002 0.004-1.957 4.010-4.263 0.6c-0.327 0.046-0.577 0.324-0.577 0.661 0 0.18 0.071 0.343 0.187 0.463l-0-0 3.060 3.2-0.783 4.453c-0.008 0.038-0.012 0.082-0.012 0.127 0 0.221 0.108 0.417 0.274 0.539l0.002 0.001c0.109 0.080 0.245 0.128 0.393 0.128 0.115 0 0.223-0.029 0.317-0.080l-0.004 0.002 3.823-2.040 3.773 2.14c0.096 0.054 0.211 0.087 0.333 0.087h0c0.141-0.005 0.27-0.053 0.375-0.131l-0.002 0.001zM24.28 19.757l-2.947 1.577 0.607-3.447c0.006-0.034 0.010-0.074 0.010-0.114 0-0.18-0.071-0.343-0.187-0.463l0 0-2.38-2.51 3.333-0.463c0.224-0.032 0.409-0.171 0.505-0.363l0.002-0.004 1.493-3.073 1.413 3.103c0.093 0.199 0.275 0.342 0.493 0.379l0.004 0.001 3.313 0.55-2.45 2.44c-0.122 0.121-0.198 0.288-0.198 0.474 0 0.035 0.003 0.070 0.008 0.103l-0-0.004 0.523 3.457-2.9-1.647c-0.093-0.052-0.204-0.083-0.322-0.083s-0.229 0.031-0.325 0.084l0.003-0.002z"></path> - </symbol> - <symbol id="refresh" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.113 6.077c-0.133-0.049-0.287-0.077-0.447-0.077-0.576 0-1.067 0.366-1.253 0.878l-0.003 0.009-0.71 2c-2.684-4.74-7.693-7.887-13.437-7.887v0c-8.417 0-15.263 6.73-15.263 15s6.847 15 15.263 15c6.79 0 12.83-4.48 14.683-10.89 0.040-0.121 0.063-0.261 0.063-0.405 0-0.736-0.597-1.333-1.333-1.333-0.616 0-1.135 0.418-1.288 0.986l-0.002 0.009c-1.527 5.28-6.513 8.967-12.123 8.967-6.93 0-12.597-5.533-12.597-12.333s5.667-12.333 12.597-12.333c0.002 0 0.004 0 0.006 0 4.935 0 9.213 2.816 11.314 6.928l0.033 0.072-3.577-1.057c-0.112-0.034-0.24-0.053-0.373-0.053-0.736 0-1.333 0.597-1.333 1.333 0 0.603 0.401 1.113 0.951 1.278l0.009 0.002 6.107 1.777c0.111 0.034 0.238 0.053 0.37 0.053 0.001 0 0.002 0 0.003 0h-0c0.575-0.001 1.064-0.366 1.25-0.877l0.003-0.009 1.897-5.333c0.049-0.133 0.077-0.287 0.077-0.447 0-0.576-0.366-1.067-0.878-1.253l-0.009-0.003z"></path> - </symbol> - <symbol id="rejected" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.333 11v4.667a1.333 1.333 0 0 1-2.666 0v-4.64c-.02-.25-.25-1.36-2.667-1.36-2.637 0-2.667 1.333-2.667 1.333v9.527c.553-.57 1.147-1.19 1.697-1.777a1.335 1.335 0 1 1 1.94 1.834l.001-.001C14.798 25 14.668 25 14.001 25s-.797 0-4.97-4.417a1.335 1.335 0 0 1 1.939-1.835l.001.001c.55.583 1.143 1.207 1.697 1.777v-9.527c0-.04.06-4 5.333-4s5.333 3.96 5.333 4zM32 16c0-8.837-7.163-16-16-16S0 7.163 0 16s7.163 16 16 16c8.833-.009 15.991-7.167 16-15.999V16zm-2.667 0c0 7.364-5.97 13.333-13.333 13.333S2.667 23.363 2.667 16C2.667 8.636 8.637 2.667 16 2.667c7.364 0 13.333 5.97 13.333 13.333z"></path> - </symbol> - <symbol id="relations" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.333 8c0 0.368-0.298 0.667-0.667 0.667v0h-5.333c-0.368 0-0.667-0.298-0.667-0.667v0-5.333c0-0.368 0.298-0.667 0.667-0.667v0h5.333c0.368 0 0.667 0.298 0.667 0.667v0zM8.667 24.667c0-0.368-0.298-0.667-0.667-0.667v0h-4.667c-0.368 0-0.667 0.298-0.667 0.667v0 4.667c0 0.368 0.298 0.667 0.667 0.667v0h4.667c0.368 0 0.667-0.298 0.667-0.667v0zM19 24.667c0-0.368-0.298-0.667-0.667-0.667v0h-4.667c-0.368 0-0.667 0.298-0.667 0.667v0 4.667c0 0.368 0.298 0.667 0.667 0.667v0h4.667c0.368 0 0.667-0.298 0.667-0.667v0zM29.333 24.667c0-0.368-0.298-0.667-0.667-0.667v0h-4.667c-0.368 0-0.667 0.298-0.667 0.667v0 4.667c0 0.368 0.298 0.667 0.667 0.667v0h4.667c0.368 0 0.667-0.298 0.667-0.667v0zM26.667 15h-9.333v-3c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3h-9.333c-0.736 0-1.333 0.597-1.333 1.333v0 4.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-3h8v3c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-3h8v3c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4.333c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="restore-parent" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18 20h12c1.105 0 2-0.895 2-2v0-4c0-1.105-0.895-2-2-2v0h-12c-1.105 0-2 0.895-2 2v0 0.667h-5.333v-5.333h6c1.105 0 2-0.895 2-2v0-5.333c0-1.105-0.895-2-2-2v0h-14.667c-1.105 0-2 0.895-2 2v0 5.333c0 1.105 0.895 2 2 2v0h6v18.667c0 0.736 0.597 1.333 1.333 1.333v0h6.667v0.667c0 1.105 0.895 2 2 2v0h12c1.105 0 2-0.895 2-2v0-4c0-1.105-0.895-2-2-2v0h-12c-1.105 0-2 0.895-2 2v0 0.667h-5.333v-9.333h5.333v0.667c0 1.105 0.895 2 2 2v0zM18.667 26.667h10.667v2.667h-10.667zM2.667 6.667v-4h13.333v4z"></path> - </symbol> - <symbol id="restore" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16.737 1c-5.744 0-10.753 3.147-13.397 7.81l-0.040 0.076-0.71-2c-0.189-0.521-0.68-0.887-1.257-0.887-0.737 0-1.334 0.597-1.334 1.334 0 0.16 0.028 0.314 0.080 0.456l-0.003-0.009 1.897 5.333c0.189 0.52 0.678 0.885 1.253 0.887h0c0.001 0 0.002 0 0.003 0 0.132 0 0.26-0.020 0.38-0.056l-0.009 0.002 6.107-1.777c0.559-0.167 0.96-0.677 0.96-1.28 0-0.736-0.597-1.333-1.333-1.333-0.133 0-0.262 0.020-0.383 0.056l0.009-0.002-3.577 1.057c2.134-4.184 6.412-7 11.347-7 0.002 0 0.004 0 0.007 0h-0c6.947 0 12.597 5.533 12.597 12.333s-5.667 12.333-12.597 12.333c-5.61 0-10.597-3.687-12.123-8.967-0.155-0.577-0.674-0.995-1.29-0.995-0.736 0-1.333 0.597-1.333 1.333 0 0.145 0.023 0.284 0.066 0.415l-0.003-0.009c1.853 6.41 7.893 10.89 14.683 10.89 8.417 0 15.263-6.73 15.263-15s-6.847-15-15.263-15z"></path> - </symbol> - <symbol id="review" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.957 8.080l-1.643 6.253c-0.153 0.578-0.671 0.998-1.286 1h-0c-0.002 0-0.003 0-0.005 0-0.175 0-0.341-0.034-0.494-0.097l0.009 0.003-5.29-2.083c-0.501-0.201-0.848-0.682-0.848-1.245 0-0.739 0.599-1.338 1.338-1.338 0.176 0 0.345 0.034 0.499 0.096l-0.009-0.003 2.56 1c-1.787-4.543-6.023-7.667-10.75-7.667-5.297 0-9.933 3.843-11.28 9.35-0.155 0.577-0.674 0.995-1.29 0.995-0.736 0-1.333-0.597-1.333-1.333 0-0.105 0.012-0.207 0.035-0.305l-0.002 0.009c1.637-6.717 7.34-11.383 13.87-11.383 5.447 0 10.35 3.333 12.757 8.293l0.583-2.223c0.135-0.604 0.667-1.049 1.303-1.049 0.736 0 1.333 0.597 1.333 1.333 0 0.136-0.020 0.268-0.058 0.392l0.003-0.009zM28.847 17.72c-0.097-0.025-0.208-0.040-0.322-0.040-0.623 0-1.146 0.427-1.292 1.004l-0.002 0.009c-1.357 5.48-5.99 9.307-11.267 9.307-4.727 0-8.963-3.123-10.75-7.667l2.56 1c0.145 0.059 0.314 0.093 0.49 0.093 0.737 0 1.335-0.598 1.335-1.335 0-0.561-0.346-1.041-0.836-1.238l-0.009-0.003-5.29-2.090c-0.144-0.058-0.311-0.092-0.486-0.092-0.617 0-1.137 0.42-1.288 0.989l-0.002 0.009-1.643 6.253c-0.028 0.102-0.044 0.218-0.044 0.339 0 0.618 0.421 1.138 0.991 1.289l0.009 0.002c0.1 0.027 0.215 0.043 0.333 0.043h0c0.618-0 1.137-0.421 1.288-0.991l0.002-0.009 0.583-2.223c2.407 4.963 7.31 8.297 12.757 8.297 6.507 0 12.203-4.667 13.857-11.333 0.025-0.096 0.039-0.206 0.039-0.319 0-0.622-0.426-1.145-1.003-1.292l-0.009-0.002z"></path> - </symbol> - <symbol id="roles" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M10.667 16c-4.418 0-8-3.582-8-8s3.582-8 8-8c4.418 0 8 3.582 8 8v0c0 4.418-3.582 8-8 8v0zM10.667 2.667c-2.946 0-5.333 2.388-5.333 5.333s2.388 5.333 5.333 5.333c2.946 0 5.333-2.388 5.333-5.333v0c0-2.946-2.388-5.333-5.333-5.333v0zM15.333 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-11.333c-0.005-0.015-0.008-0.033-0.008-0.052s0.003-0.036 0.008-0.053l-0 0.001c0.021-0.073 0.037-0.158 0.043-0.246l0-0.004c0.015-0.205 0.046-0.394 0.092-0.577l-0.005 0.023v-0.040c0.703-2.873 1.94-6.697 2.54-7.513 1.167-1.583 3.293-1.54 3.303-1.54h3.103c0.374 0.006 0.735 0.037 1.089 0.089l-0.045-0.006c0.058 0.009 0.124 0.014 0.192 0.014 0.737 0 1.334-0.597 1.334-1.334 0-0.669-0.493-1.223-1.135-1.319l-0.007-0.001c-0.434-0.068-0.936-0.108-1.447-0.11h-3.003c-0.41 0-3.603 0-5.52 2.63-1.287 1.757-2.867 8.037-2.977 8.437-0.071 0.266-0.127 0.584-0.158 0.909l-0.002 0.025c-0.041 0.179-0.064 0.385-0.064 0.596 0 0.646 0.217 1.241 0.582 1.717l-0.005-0.007c0.477 0.626 1.221 1.026 2.060 1.027h11.364c0.736 0 1.333-0.597 1.333-1.333v0zM23.333 32c-4.786 0-8.667-3.88-8.667-8.667s3.88-8.667 8.667-8.667c4.786 0 8.667 3.88 8.667 8.667v0c0 4.786-3.88 8.667-8.667 8.667v0zM23.333 17.333c-3.314 0-6 2.686-6 6s2.686 6 6 6c3.314 0 6-2.686 6-6v0c0-3.314-2.686-6-6-6v0zM22.763 26.667h0.1c0.54-0.042 1.011-0.303 1.331-0.693l0.003-0.003 0.040-0.050 2.823-3.783c0.181-0.226 0.29-0.515 0.29-0.83 0-0.736-0.597-1.333-1.333-1.333-0.451 0-0.849 0.224-1.091 0.566l-0.003 0.004-2.333 3.103-1.060-0.947c-0.235-0.209-0.546-0.336-0.887-0.336-0.738 0-1.336 0.598-1.336 1.336 0 0.397 0.173 0.754 0.448 0.999l0.001 0.001 1.7 1.51c0.33 0.284 0.762 0.457 1.235 0.457 0.008 0 0.016-0 0.023-0h-0.001z"></path> - </symbol> - <symbol id="rss" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M8.023 27.333c0 1.841-1.492 3.333-3.333 3.333s-3.333-1.492-3.333-3.333c0-1.841 1.492-3.333 3.333-3.333v0c1.841 0 3.333 1.492 3.333 3.333v0zM30.667 29.333c0-8.257-2.46-14.903-7.333-19.75-8.183-8.18-20.153-8.25-20.667-8.25-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0c4.697 0.183 9.080 1.348 13.007 3.294l-0.187-0.084c8.303 4.18 12.513 11.623 12.513 22.123 0 0.736 0.597 1.333 1.333 1.333v0c0.736 0 1.333-0.597 1.333-1.333v0zM25.363 29.333c0-11.7-6.203-17.26-11.407-19.86-3.285-1.643-7.139-2.664-11.216-2.805l-0.047-0.001c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0c3.722 0.155 7.192 1.086 10.301 2.633l-0.148-0.067c6.533 3.303 9.85 9.167 9.85 17.433 0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM20.030 29.307c0-8.947-4.753-13.2-8.74-15.193-2.508-1.254-5.449-2.032-8.561-2.139l-0.036-0.001c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0c2.714 0.108 5.246 0.782 7.515 1.905l-0.109-0.049c4.817 2.41 7.263 6.72 7.263 12.81 0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM14.697 29.333c0-3.88-1.143-6.91-3.4-9-3.54-3.31-8.487-3-8.697-3-0.698 0.049-1.245 0.627-1.245 1.333 0 0.738 0.598 1.336 1.336 1.336 0.032 0 0.064-0.001 0.096-0.003l-0.004 0c0.043 0 4.037-0.23 6.713 2.28 1.667 1.577 2.533 3.947 2.533 7.047 0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="save" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.37 5.497l-4.96-4.89c-0.38-0.375-0.902-0.607-1.478-0.607-0.001 0-0.002 0-0.002 0h-22.047c-1.592 0.002-2.881 1.292-2.883 2.883v26.234c0.002 1.592 1.292 2.881 2.883 2.883h26.233c1.592-0.002 2.881-1.292 2.883-2.883v-22.117c-0.001-0.588-0.242-1.12-0.63-1.503l-0-0zM10.667 2.667h10.667v6.333c0 0.017 0 0.047-0.060 0.103-0.14 0.119-0.323 0.191-0.522 0.191-0.012 0-0.024-0-0.036-0.001l0.002 0h-0.050l-9.333 0.040c-0.38 0-0.667-0.217-0.667-0.333zM24.667 29.333h-17.333v-10.52c0.019-0.085 0.093-0.148 0.183-0.148 0.007 0 0.015 0 0.022 0.001l-0.001-0h16.907c0.006-0.001 0.013-0.001 0.020-0.001 0.1 0 0.184 0.072 0.203 0.166l0 0.001zM29.333 29.117c0 0.12-0.097 0.217-0.217 0.217v0h-1.783v-10.5c-0.017-1.568-1.292-2.833-2.863-2.833-0.009 0-0.019 0-0.028 0h-16.905c-0.007-0-0.015-0-0.024-0-1.561 0-2.828 1.256-2.846 2.812l-0 0.002v10.52h-1.783c-0.12 0-0.217-0.097-0.217-0.217v0-26.233c0-0.12 0.097-0.217 0.217-0.217v0h5.117v6.333c0 1.65 1.497 3 3.333 3l9.333-0.033c0.038 0.002 0.082 0.002 0.126 0.002 0.911 0 1.738-0.358 2.349-0.94l-0.001 0.001c0.53-0.51 0.86-1.225 0.86-2.018 0-0.004 0-0.009-0-0.013v0.001-6.333h0.7l4.633 4.567zM20 8.683h-2.667v-5.333h2.667z"></path> - </symbol> - <symbol id="schedule" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M19.667 4h-7.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h7.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 4.223v24.887c0 0.001 0 0.002 0 0.003 0 1.591-1.287 2.881-2.876 2.887h-26.244c-1.591-0.004-2.88-1.295-2.88-2.887 0-0.001 0-0.002 0-0.004v0-24.897c0 0 0 0 0-0 0-1.587 1.284-2.874 2.869-2.88h1.464c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-1.463c-0.113 0.004-0.203 0.096-0.203 0.21 0 0.001 0 0.002 0 0.004v-0 7.12h26.667v-7.11c0-0.001 0-0.002 0-0.003 0-0.119-0.095-0.216-0.213-0.22h-1.454c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.453c1.591 0.004 2.88 1.295 2.88 2.887 0 0.001 0 0.002 0 0.004v-0zM29.333 29.11v-15.11h-26.667v15.11c-0 0.001-0 0.002-0 0.003 0 0.119 0.095 0.216 0.213 0.22h26.244c0.117-0.004 0.21-0.099 0.21-0.217 0-0.002-0-0.005-0-0.007v0zM11 7.333c0 0.001 0 0.002 0 0.003 0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-0.982 0.531-1.84 1.321-2.303l0.013-0.007v-3.693c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 3.693c0.802 0.469 1.332 1.326 1.333 2.307v0zM25 5.027v-3.693c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.693c-0.803 0.47-1.333 1.328-1.333 2.309 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.982-0.531-1.84-1.321-2.303l-0.013-0.007zM8 17.333c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM11.333 16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM16 16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM20.667 16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM25.333 16c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM6.667 20.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM11.333 20.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM16 20.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM20.667 20.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM25.333 20.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM6.667 24.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0zM11.333 24.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0c0-0.736-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="search" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.61 29.723l-9.093-9.093c1.754-2.156 2.816-4.936 2.816-7.964 0-6.996-5.671-12.667-12.667-12.667s-12.667 5.671-12.667 12.667c0 6.996 5.671 12.667 12.667 12.667 3.028 0 5.808-1.062 7.987-2.835l-0.023 0.018 9.093 9.093c0.241 0.241 0.575 0.391 0.943 0.391 0.737 0 1.334-0.597 1.334-1.334 0-0.368-0.149-0.702-0.391-0.943v0zM2.667 12.667c0-0.001 0-0.002 0-0.004 0-5.523 4.477-10 10-10s10 4.477 10 10c0 2.707-1.075 5.162-2.822 6.963l0.002-0.003c-0.040 0.033-0.083 0.063-0.123 0.1-0.034 0.037-0.067 0.077-0.097 0.12l-0.003 0.004c-1.798 1.742-4.252 2.816-6.957 2.816-5.522 0-9.998-4.475-10-9.996v-0z"></path> - </symbol> - <symbol id="sections" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.953 9.747l0.047-5.553v-0.177c0-0.002 0-0.005 0-0.008 0-0.32-0.115-0.613-0.305-0.84l0.002 0.002c-0.617-0.903-2.223-1.503-5.117-1.963-2.995-0.447-6.451-0.703-9.967-0.703-0.215 0-0.431 0.001-0.646 0.003l0.033-0c-0.182-0.002-0.397-0.003-0.613-0.003-3.516 0-6.972 0.255-10.351 0.749l0.384-0.046c-3.85 0.613-5.42 1.46-5.42 2.967v5.527c0 0.56 0.345 1.039 0.834 1.237l0.009 0.003c0.073 0.030 0.353 0.137 0.82 0.293-1.157 0.5-1.667 1.14-1.667 2h0.003v5.723c0 0.567 0.355 1.051 0.854 1.244l0.009 0.003c0.073 0.030 0.377 0.14 0.88 0.3-1.217 0.497-1.743 1.14-1.743 2.003v5.513c0 0.001 0 0.001 0 0.002 0 0.571 0.359 1.059 0.864 1.248l0.009 0.003c0.367 0.133 6.26 2.233 14.97 2.233 0.006 0 0.012 0 0.019 0 5.395 0 10.605-0.794 15.519-2.272l-0.381 0.098c0.555-0.167 0.952-0.672 0.957-1.27v-0l0.043-5.397v-0.17c0-0.867-0.537-1.51-1.77-2 0.257-0.073 0.517-0.147 0.777-0.227 0.55-0.171 0.944-0.674 0.947-1.27v-0l0.047-5.733v-0.113c-0.030-0.813-0.547-1.433-1.667-1.92l0.69-0.233c0.538-0.175 0.922-0.668 0.93-1.252v-0.001zM2.667 8.747v-2.203c0.755 0.228 1.685 0.435 2.636 0.579l0.117 0.015c2.986 0.447 6.432 0.703 9.938 0.703 0.226 0 0.451-0.001 0.677-0.003l-0.034 0c0.191 0.002 0.417 0.003 0.642 0.003 3.506 0 6.951-0.255 10.32-0.749l-0.383 0.046c1.061-0.158 1.984-0.364 2.883-0.628l-0.149 0.038-0.020 2.2c-12.823 3.767-23.603 0.96-26.627 0zM16 3.173c5.77 0 9.783 0.507 11.897 1-2.113 0.493-6.127 1-11.897 1s-9.783-0.507-11.897-1c2.113-0.507 6.127-1 11.897-1zM2.667 18v-2.423c0.752 0.219 1.684 0.419 2.636 0.559l0.121 0.015c2.958 0.427 6.374 0.67 9.847 0.67 0.256 0 0.512-0.001 0.768-0.004l-0.039 0c0.217 0.002 0.473 0.004 0.729 0.004 3.473 0 6.889-0.244 10.232-0.715l-0.385 0.044c1.066-0.153 1.991-0.351 2.893-0.604l-0.157 0.038-0.020 2.417c-12.81 3.593-23.577 0.917-26.627 0zM2.667 27.047v-2.217c0.75 0.212 1.685 0.408 2.639 0.545l0.125 0.015c2.997 0.426 6.458 0.669 9.977 0.669 0.209 0 0.417-0.001 0.625-0.003l-0.032 0c0.176 0.002 0.385 0.002 0.593 0.002 3.518 0 6.98-0.243 10.368-0.714l-0.392 0.045c1.070-0.15 1.999-0.344 2.905-0.591l-0.162 0.038v2.207c-12.82 3.487-23.58 0.903-26.647 0.003zM25.417 22.153c0.89 0.117 1.6 0.237 2.16 0.353-2.193 0.447-6.097 0.88-11.577 0.88s-9.383-0.433-11.577-0.88c0.523-0.11 1.18-0.223 2-0.333 0.303-0.042 0.568-0.182 0.766-0.386l0-0c2.583 0.46 5.556 0.723 8.59 0.723 0.027 0 0.054-0 0.081-0h-0.004c3.106-0.001 6.148-0.271 9.104-0.789l-0.314 0.046c0.2 0.205 0.466 0.345 0.763 0.386l0.007 0.001zM25.417 12.82c1 0.133 1.75 0.273 2.333 0.403-2.16 0.473-6.12 0.943-11.75 0.943s-9.59-0.47-11.743-0.94c0.55-0.123 1.257-0.253 2.163-0.38 0.245-0.035 0.461-0.134 0.639-0.279l-0.002 0.002c2.629 0.496 5.653 0.78 8.744 0.78 0.015 0 0.030 0 0.045-0h-0.002c3.157-0.001 6.245-0.292 9.241-0.848l-0.311 0.048c0.175 0.142 0.392 0.239 0.63 0.273l0.007 0.001z"></path> - </symbol> - <symbol id="send-review" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.963 27.39a1.333 1.333 0 0 1-1.333-1.333v-6.033l-1.963-.023A2.73 2.73 0 0 1 0 17.31V2.695A2.754 2.754 0 0 1 2.694.002H24.52C25.947.002 27 1.135 27 2.695v14.62c0 1.537-1.113 2.687-2.57 2.687h-9.513l-8.073 7.06c-.233.205-.541.33-.878.33h-.002zM2.667 2.723v14.59c.01.016.027.026.047.027l3.263.03c.736 0 1.333.597 1.333 1.333v4.42l6.23-5.457c.233-.206.54-.332.876-.333h9.917V2.666H2.766a.17.17 0 0 0-.1.056zm21.853-.056zM26.037 32h-.018c-.339 0-.648-.126-.883-.335l.001.001-7.667-7.02-2.787.02a1.333 1.333 0 0 1 0-2.666l3.3-.043.047-.001c.336 0 .642.127.874.335l-.001-.001 5.833 5.333V23.29c0-.736.597-1.333 1.333-1.333l3.287-.033s.023-.02.023-.027V7.694a1.337 1.337 0 1 1 .18-2.666h-.004.217c1.467 0 2.273.803 2.273 2.26v14.637a2.72 2.72 0 0 1-2.632 2.69h-.004l-2 .02v6.033c0 .736-.597 1.333-1.333 1.333zm3.846-24.287zm-10.55.754L15.74 5.034a1.334 1.334 0 0 0-1.84 1.932h.001l1.787 1.7H8.335a1.333 1.333 0 0 0 0 2.666h7.267l-1.837 1.667a1.347 1.347 0 1 0 1.808 1.999l-.001.001 3.737-3.433a2.094 2.094 0 0 0 .028-3.099l-.001-.001z"/> - </symbol> - <symbol id="server" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M9.333 19c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM6.667 17.667c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0zM6.667 5.333c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0zM10.667 5.333c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333c0.736 0 1.333-0.597 1.333-1.333v0zM17.333 10.667v1.667h13.333c0.736 0 1.333 0.597 1.333 1.333v0 8c0 0.736-0.597 1.333-1.333 1.333v0h-13.333v1.233c1.139 0.412 2.021 1.294 2.425 2.405l0.009 0.028h12.113v2.667h-12.113c-0.569 1.56-2.040 2.654-3.767 2.654s-3.197-1.094-3.758-2.626l-0.009-0.028h-12.173v-2.667h12.173c0.412-1.139 1.294-2.021 2.405-2.425l0.028-0.009v-1.233h-13.333c-0.736 0-1.333-0.597-1.333-1.333v0-8c0-0.736 0.597-1.333 1.333-1.333v0h13.333v-1.667h-13.333c-0.736 0-1.333-0.597-1.333-1.333v0-8c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 8c0 0.736-0.597 1.333-1.333 1.333v0zM17.333 28c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333c0 0.736 0.597 1.333 1.333 1.333v0c0.736 0 1.333-0.597 1.333-1.333v0zM2.667 15v5.333h26.667v-5.333zM2.667 8h26.667v-5.333h-26.667z"></path> - </symbol> - <symbol id="settings-block" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18.857 32h-5.713c-0 0-0.001 0-0.001 0-0.569 0-1.054-0.356-1.246-0.858l-0.003-0.009-1.183-3.173c-0.99-0.422-1.842-0.909-2.628-1.48l0.038 0.026-3.697 0.637c-0.068 0.012-0.147 0.019-0.227 0.019-0.485 0-0.91-0.259-1.143-0.647l-0.003-0.006-2.857-4.823c-0.116-0.194-0.185-0.428-0.185-0.678 0-0.334 0.123-0.64 0.326-0.874l-0.001 0.002 2.403-2.803c-0.044-0.4-0.070-0.863-0.070-1.333v-0c0-0.002 0-0.005 0-0.008 0-0.467 0.025-0.928 0.075-1.382l-0.005 0.056-2.403-2.797c-0.202-0.232-0.324-0.538-0.324-0.872 0-0.245 0.066-0.474 0.181-0.671l-0.003 0.006 2.857-4.837c0.237-0.394 0.661-0.653 1.147-0.653 0.080 0 0.159 0.007 0.235 0.021l-0.008-0.001 3.693 0.637c0.751-0.548 1.606-1.037 2.514-1.427l0.086-0.033 1.183-3.173c0.195-0.511 0.68-0.867 1.249-0.867 0 0 0.001 0 0.001 0h5.713c0 0 0.001 0 0.001 0 0.569 0 1.054 0.356 1.246 0.858l0.003 0.009 1.183 3.173c0.994 0.423 1.849 0.912 2.638 1.486l-0.038-0.026 3.693-0.637c0.068-0.012 0.147-0.019 0.227-0.019 0.485 0 0.91 0.259 1.143 0.647l0.003 0.006 2.857 4.837c0.112 0.191 0.178 0.42 0.178 0.665 0 0.334-0.123 0.639-0.326 0.873l0.001-0.002-2.403 2.797c0.045 0.398 0.070 0.859 0.070 1.326 0 0.003 0 0.005 0 0.008v-0c-0 0.47-0.026 0.934-0.075 1.39l0.005-0.057 2.403 2.813c0.201 0.232 0.323 0.537 0.323 0.87 0 0.251-0.069 0.486-0.19 0.686l0.003-0.006-2.847 4.823c-0.237 0.394-0.661 0.653-1.147 0.653-0.080 0-0.159-0.007-0.235-0.021l0.008 0.001-3.71-0.64c-0.746 0.542-1.596 1.026-2.497 1.414l-0.086 0.033-1.183 3.173c-0.195 0.511-0.68 0.867-1.249 0.867-0 0-0.001 0-0.001 0h0zM14.070 29.333h3.86l1.070-2.86c0.139-0.364 0.422-0.646 0.777-0.78l0.009-0.003c1.117-0.418 2.083-0.969 2.942-1.65l-0.022 0.017c0.225-0.178 0.512-0.286 0.825-0.286 0.080 0 0.157 0.007 0.233 0.020l-0.008-0.001 3.373 0.58 1.9-3.21-2.177-2.527c-0.2-0.232-0.322-0.536-0.322-0.869 0-0.072 0.006-0.143 0.017-0.212l-0.001 0.008c0.076-0.468 0.12-1.008 0.12-1.558 0-0.001 0-0.001 0-0.002v0c-0-0.545-0.044-1.079-0.128-1.601l0.008 0.057c-0.010-0.060-0.015-0.13-0.015-0.201 0-0.334 0.123-0.64 0.327-0.874l-0.001 0.002 2.173-2.527-1.9-3.21-3.353 0.577c-0.069 0.012-0.147 0.020-0.228 0.020-0.313 0-0.601-0.108-0.828-0.288l0.003 0.002c-0.841-0.669-1.813-1.226-2.863-1.622l-0.074-0.024c-0.365-0.137-0.648-0.419-0.784-0.774l-0.003-0.009-1.070-2.86h-3.86l-1.070 2.86c-0.139 0.364-0.422 0.646-0.777 0.78l-0.009 0.003c-1.124 0.421-2.095 0.977-2.959 1.664l0.022-0.017c-0.225 0.178-0.513 0.286-0.825 0.286-0.081 0-0.159-0.007-0.236-0.021l0.008 0.001-3.353-0.577-1.9 3.21 2.173 2.527c0.202 0.232 0.325 0.538 0.325 0.873 0 0.071-0.006 0.14-0.016 0.208l0.001-0.008c-0.076 0.464-0.12 0.998-0.12 1.543v0c0 0 0 0.001 0 0.002 0 0.549 0.044 1.088 0.128 1.613l-0.008-0.058c0.010 0.061 0.016 0.132 0.016 0.204 0 0.333-0.122 0.637-0.324 0.871l0.001-0.002-2.173 2.52 1.903 3.21 3.357-0.577c0.068-0.012 0.146-0.019 0.225-0.019 0.313 0 0.6 0.108 0.828 0.288l-0.003-0.002c0.84 0.665 1.809 1.219 2.856 1.615l0.074 0.025c0.365 0.137 0.648 0.419 0.784 0.774l0.003 0.009zM16 22.667c-3.682 0-6.667-2.985-6.667-6.667s2.985-6.667 6.667-6.667c3.682 0 6.667 2.985 6.667 6.667v0c0 3.682-2.985 6.667-6.667 6.667v0zM16 12c-2.209 0-4 1.791-4 4s1.791 4 4 4c2.209 0 4-1.791 4-4v0c0-2.209-1.791-4-4-4v0z"></path> - </symbol> - <symbol id="settings-config" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 8c-4.418 0-8 3.582-8 8s3.582 8 8 8c4.418 0 8-3.582 8-8v0c0-4.418-3.582-8-8-8v0zM16 10.667c1.594 0.008 3.022 0.713 3.995 1.827l0.005 0.006-4 2-4-2c0.978-1.12 2.406-1.826 3.999-1.833h0.001zM10.667 16c0.001-0.396 0.044-0.781 0.127-1.152l-0.007 0.036 3.88 1.94v4.333c-2.316-0.613-3.997-2.688-4-5.156v-0zM17.333 21.157v-4.333l3.88-1.94c0.076 0.337 0.12 0.725 0.12 1.122 0 2.468-1.682 4.544-3.963 5.143l-0.037 0.008zM18.857 32h-5.713c-0 0-0.001 0-0.001 0-0.569 0-1.054-0.356-1.246-0.858l-0.003-0.009-1.183-3.173c-0.99-0.422-1.842-0.909-2.628-1.48l0.038 0.026-3.697 0.637c-0.068 0.012-0.147 0.019-0.227 0.019-0.485 0-0.91-0.259-1.143-0.647l-0.003-0.006-2.857-4.823c-0.116-0.194-0.185-0.428-0.185-0.678 0-0.334 0.123-0.64 0.326-0.874l-0.001 0.002 2.403-2.803c-0.044-0.4-0.070-0.863-0.070-1.333v-0c0-0.002 0-0.005 0-0.008 0-0.467 0.025-0.928 0.075-1.382l-0.005 0.056-2.403-2.797c-0.202-0.232-0.324-0.538-0.324-0.872 0-0.245 0.066-0.474 0.181-0.671l-0.003 0.006 2.857-4.837c0.237-0.394 0.661-0.653 1.147-0.653 0.080 0 0.159 0.007 0.235 0.021l-0.008-0.001 3.693 0.637c0.751-0.549 1.607-1.038 2.515-1.427l0.085-0.033 1.183-3.173c0.195-0.511 0.68-0.867 1.249-0.867 0 0 0.001 0 0.001 0h5.713c0 0 0.001 0 0.001 0 0.569 0 1.054 0.356 1.246 0.858l0.003 0.009 1.183 3.173c0.993 0.422 1.849 0.911 2.637 1.486l-0.037-0.026 3.693-0.637c0.068-0.012 0.147-0.019 0.227-0.019 0.485 0 0.91 0.259 1.143 0.647l0.003 0.006 2.857 4.837c0.112 0.191 0.178 0.42 0.178 0.665 0 0.334-0.123 0.639-0.326 0.873l0.001-0.002-2.403 2.797c0.045 0.398 0.070 0.859 0.070 1.326 0 0.003 0 0.005 0 0.008v-0c-0 0.47-0.026 0.934-0.075 1.39l0.005-0.057 2.403 2.813c0.201 0.232 0.323 0.537 0.323 0.87 0 0.251-0.069 0.486-0.19 0.686l0.003-0.006-2.847 4.823c-0.237 0.394-0.661 0.653-1.147 0.653-0.080 0-0.159-0.007-0.235-0.021l0.008 0.001-3.71-0.64c-0.746 0.54-1.596 1.024-2.496 1.413l-0.087 0.034-1.183 3.173c-0.195 0.511-0.68 0.867-1.249 0.867-0 0-0.001 0-0.001 0h0zM14.070 29.333h3.86l1.070-2.86c0.139-0.364 0.422-0.646 0.777-0.78l0.009-0.003c1.117-0.418 2.083-0.969 2.942-1.65l-0.022 0.017c0.225-0.178 0.512-0.286 0.825-0.286 0.080 0 0.157 0.007 0.233 0.020l-0.008-0.001 3.373 0.58 1.9-3.21-2.177-2.527c-0.201-0.232-0.323-0.537-0.323-0.87 0-0.073 0.006-0.145 0.017-0.215l-0.001 0.008c0.076-0.467 0.12-1.005 0.12-1.553 0-0.001 0-0.003 0-0.004v0c-0-0.545-0.044-1.079-0.128-1.601l0.008 0.057c-0.010-0.060-0.015-0.13-0.015-0.201 0-0.334 0.123-0.64 0.327-0.874l-0.001 0.002 2.173-2.527-1.9-3.21-3.353 0.577c-0.069 0.012-0.147 0.020-0.228 0.020-0.313 0-0.601-0.108-0.828-0.288l0.003 0.002c-0.841-0.669-1.813-1.226-2.863-1.622l-0.074-0.024c-0.365-0.137-0.648-0.419-0.784-0.774l-0.003-0.009-1.070-2.86h-3.86l-1.070 2.86c-0.139 0.364-0.422 0.646-0.777 0.78l-0.009 0.003c-1.124 0.421-2.095 0.977-2.959 1.664l0.022-0.017c-0.225 0.178-0.513 0.286-0.825 0.286-0.081 0-0.159-0.007-0.236-0.021l0.008 0.001-3.353-0.577-1.9 3.21 2.173 2.527c0.202 0.232 0.325 0.538 0.325 0.873 0 0.071-0.006 0.14-0.016 0.208l0.001-0.008c-0.076 0.464-0.12 0.998-0.12 1.543v0c0 0 0 0.001 0 0.002 0 0.549 0.044 1.088 0.128 1.613l-0.008-0.058c0.010 0.061 0.016 0.132 0.016 0.204 0 0.333-0.122 0.637-0.324 0.871l0.001-0.002-2.173 2.52 1.903 3.21 3.357-0.577c0.068-0.012 0.146-0.019 0.225-0.019 0.313 0 0.6 0.108 0.828 0.288l-0.003-0.002c0.84 0.665 1.809 1.219 2.856 1.615l0.074 0.025c0.365 0.137 0.648 0.419 0.784 0.774l0.003 0.009z"></path> - </symbol> - <symbol id="sites-all" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.073 8H1.926A1.927 1.927 0 0 0-.001 9.927v20.147c0 1.064.863 1.927 1.927 1.927h20.163a1.914 1.914 0 0 0 1.91-1.91V9.927A1.927 1.927 0 0 0 22.072 8zm-.74 2.667V14.1H2.666v-3.433zM2.667 29.333V16.766h18.667v12.567zm8-17a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm-2.667 0a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm-2.667 0a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm21.334 14.334a1.333 1.333 0 0 1-1.333-1.333V6.667H6.667a1.333 1.333 0 0 1 0-2.666h18.667a2.667 2.667 0 0 1 2.667 2.667v18.667c0 .736-.597 1.333-1.333 1.333zm4-5.334A1.333 1.333 0 0 1 29.334 20V2.643l-17.333.023a1.333 1.333 0 0 1 0-2.666h17.333a2.668 2.668 0 0 1 2.667 2.642V20c0 .736-.597 1.333-1.333 1.333z"></path> - </symbol> - <symbol id="slider-lastviewed" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M4 21.333c-0.461-0-0.868-0.235-1.107-0.592l-0.003-0.005-2.667-4.020c-0.14-0.208-0.224-0.464-0.224-0.74s0.084-0.532 0.227-0.745l-0.003 0.005 2.667-3.98c0.244-0.347 0.643-0.571 1.094-0.571 0.736 0 1.333 0.597 1.333 1.333 0 0.269-0.080 0.52-0.217 0.729l0.003-0.005-2.167 3.257 2.173 3.28c0.132 0.204 0.211 0.453 0.211 0.72 0 0.732-0.59 1.326-1.321 1.333h-0.001zM21.333 19.963c-1.657 0-3 1.343-3 3s1.343 3 3 3c1.657 0 3-1.343 3-3v0c0-1.657-1.343-3-3-3v0zM32 23c0.001 0.016 0.001 0.036 0.001 0.055 0 0.282-0.089 0.542-0.241 0.756l0.003-0.004c-3.23 4.553-6.72 6.86-10.387 6.86h-0.297c-6.047-0.207-10.050-6.667-10.217-6.933-0.118-0.19-0.19-0.42-0.193-0.666v-0.068c-0.001-0.008-0.001-0.017-0.001-0.027s0-0.019 0.001-0.028l-0 0.001c0.001-0.247 0.072-0.477 0.193-0.672l-0.003 0.005c0.17-0.28 4.173-6.737 10.22-6.947 3.777-0.123 7.367 2.18 10.667 6.857 0.159 0.214 0.254 0.483 0.254 0.774 0 0.013-0 0.025-0.001 0.038l0-0.002zM29.047 23c-2.59-3.403-5.227-5.090-7.867-5-3.63 0.12-6.513 3.533-7.587 5 1.073 1.467 3.957 4.88 7.587 5 2.633 0.090 5.277-1.597 7.86-5zM10.667 18.667v-14.667h10.667v8c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-8c0-1.473-1.194-2.667-2.667-2.667v0h-10.667c-1.473 0-2.667 1.194-2.667 2.667v0 14.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="slider" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M21.333 30.667h-10.667c-1.473 0-2.667-1.194-2.667-2.667v0-24c0-1.473 1.194-2.667 2.667-2.667v0h10.667c1.473 0 2.667 1.194 2.667 2.667v0 24c0 1.473-1.194 2.667-2.667 2.667v0zM21.333 4h-10.667v24h10.667zM4.737 21.11c0.361-0.242 0.595-0.649 0.595-1.11 0-0.274-0.083-0.529-0.225-0.741l0.003 0.005-2.173-3.263 2.17-3.24c0.134-0.205 0.214-0.455 0.214-0.724 0-0.736-0.597-1.333-1.333-1.333-0.451 0-0.85 0.224-1.091 0.567l-0.003 0.004-2.667 3.98c-0.14 0.208-0.224 0.464-0.224 0.74s0.084 0.532 0.227 0.745l-0.003-0.005 2.667 4.020c0.242 0.361 0.649 0.595 1.11 0.595 0.274 0 0.529-0.083 0.741-0.225l-0.005 0.003zM29.11 20.737l2.667-4.020c0.14-0.208 0.224-0.464 0.224-0.74s-0.084-0.532-0.227-0.745l0.003 0.005-2.667-3.98c-0.244-0.347-0.643-0.571-1.094-0.571-0.736 0-1.333 0.597-1.333 1.333 0 0.269 0.080 0.52 0.217 0.729l-0.003-0.005 2.167 3.257-2.173 3.28c-0.159 0.217-0.254 0.488-0.254 0.782 0 0.736 0.597 1.333 1.333 1.333 0.481 0 0.902-0.255 1.137-0.637l0.003-0.006z"></path> - </symbol> - <symbol id="spinner" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M10.667 6.667c0 2.209-1.791 4-4 4s-4-1.791-4-4c0-2.209 1.791-4 4-4v0c2.209 0 4 1.791 4 4v0zM4 12c-2.209 0-4 1.791-4 4s1.791 4 4 4c2.209 0 4-1.791 4-4v0c0-2.209-1.791-4-4-4v0zM6.667 21.333c-2.209 0-4 1.791-4 4s1.791 4 4 4c2.209 0 4-1.791 4-4v0c0-2.209-1.791-4-4-4v0zM16 24c-2.209 0-4 1.791-4 4s1.791 4 4 4c2.209 0 4-1.791 4-4v0c0-2.209-1.791-4-4-4v0zM25.333 21.333c-2.209 0-4 1.791-4 4s1.791 4 4 4c2.209 0 4-1.791 4-4v0c0-2.209-1.791-4-4-4v0zM32 16c0-2.209-1.791-4-4-4s-4 1.791-4 4c0 2.209 1.791 4 4 4v0c2.209 0 4-1.791 4-4v0zM29.333 16c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM29.333 6.667c0-2.209-1.791-4-4-4s-4 1.791-4 4c0 2.209 1.791 4 4 4v0c2.209 0 4-1.791 4-4v0zM26.667 6.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0zM20 4c0-2.209-1.791-4-4-4s-4 1.791-4 4c0 2.209 1.791 4 4 4v0c2.209 0 4-1.791 4-4v0zM17.333 4c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="square" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 32h-29.333c-0.736 0-1.333-0.597-1.333-1.333v0-29.333c0-0.736 0.597-1.333 1.333-1.333v0h29.333c0.736 0 1.333 0.597 1.333 1.333v0 29.333c0 0.736-0.597 1.333-1.333 1.333v0zM2.667 29.333h26.667v-26.667h-26.667z"></path> - </symbol> - <symbol id="stats" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 32h-29.333c-0.736 0-1.333-0.597-1.333-1.333v0-29.333c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 28h28c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM30.573 3.303c-0.332-1.122-1.353-1.926-2.562-1.926-1.473 0-2.667 1.194-2.667 2.667 0 0.179 0.018 0.354 0.051 0.523l-0.003-0.017-3.237 2.917c-0.247-0.084-0.532-0.132-0.828-0.132-0.981 0-1.839 0.53-2.302 1.319l-0.007 0.013c-0.132 0.228-0.235 0.492-0.294 0.773l-0.003 0.017-3.623 1.21c-0.467-0.41-1.083-0.661-1.757-0.661-1.473 0-2.667 1.194-2.667 2.667 0 0.523 0.15 1.010 0.41 1.422l-0.006-0.011-4.513 8.583c-1.42 0.066-2.546 1.234-2.546 2.664 0 1.473 1.194 2.667 2.667 2.667s2.667-1.194 2.667-2.667c0-0.534-0.157-1.031-0.427-1.448l0.006 0.010 4.507-8.56c0.213-0.008 0.416-0.039 0.609-0.091l-0.019 0.004c0.969-0.269 1.705-1.046 1.91-2.019l0.003-0.017 3.63-1.21c0.467 0.413 1.084 0.666 1.76 0.667h0c0 0 0.001 0 0.001 0 0.247 0 0.487-0.034 0.714-0.098l-0.019 0.004c1.143-0.318 1.967-1.349 1.967-2.573 0-0.195-0.021-0.385-0.061-0.568l0.003 0.018 3.24-2.917c0.244 0.082 0.526 0.131 0.819 0.133h0.001c0.001 0 0.003 0 0.004 0 0.248 0 0.487-0.034 0.714-0.098l-0.019 0.004c1.142-0.318 1.966-1.349 1.966-2.573 0-0.248-0.034-0.488-0.097-0.716l0.004 0.019z"></path> - </symbol> - <symbol id="strikethrough" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 14.667h-9.687c-7.917-2.643-6.55-7.503-6.487-7.707 0.573-1.793 1.58-3 3.077-3.667 4.097-1.83 10.377 0.92 10.43 0.947 0.171 0.086 0.373 0.136 0.587 0.136 0.736 0 1.333-0.597 1.333-1.333 0-0.556-0.341-1.033-0.825-1.233l-0.009-0.003c-0.287-0.14-7.397-3.267-12.597-0.943-2.203 0.98-3.73 2.76-4.537 5.277-0.159 0.579-0.25 1.244-0.25 1.93 0 1.283 0.32 2.492 0.884 3.551l-0.020-0.041c0.659 1.231 1.539 2.261 2.597 3.070l0.023 0.017h-5.853c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h11.21c3.783 1.207 6.233 2.823 7.093 4.667 0.503 1.087 0.48 2.303-0.063 3.62-0.24 0.513-3.74 6.643-16.447 1.97-0.137-0.052-0.295-0.082-0.46-0.082-0.736 0-1.334 0.597-1.334 1.334 0 0.571 0.359 1.059 0.864 1.249l0.009 0.003c3.793 1.387 6.883 1.907 9.42 1.907 7.79 0 10.237-4.963 10.373-5.243l0.020-0.047c0.853-2.023 0.867-4.030 0.043-5.81-0.617-1.333-1.693-2.527-3.213-3.567h3.817c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="subscriber" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.213 6.073c-3.233-2.31-6.723-4.74-8.073-5.693-0.335-0.234-0.75-0.374-1.198-0.374s-0.864 0.14-1.205 0.379l0.007-0.005c-14.743 10.187-14.743 10.38-14.743 11.313v18.197c0 0 0 0 0 0 0 1.163 0.941 2.106 2.103 2.11h27.79c1.164-0.002 2.107-0.946 2.107-2.11 0 0 0 0 0 0v0-18c0-0.873 0-0.963-6.787-5.817zM2.667 13.64l8.15 8.257-8.15 5.87zM15.873 21.54c0.035-0.025 0.078-0.040 0.125-0.040s0.090 0.015 0.126 0.040l-0.001-0 10.817 7.793h-21.883zM21.183 21.9l8.15-8.233v14.103zM15.937 2.79c5.373 3.75 9.683 6.83 11.963 8.543l-8.9 9-1.333-0.957c-0.466-0.339-1.050-0.543-1.682-0.543s-1.216 0.203-1.69 0.548l0.008-0.006-1.303 0.957-9-9.107c2.263-1.677 6.567-4.72 11.937-8.437zM14.887 15.667c-0 0-0.001 0-0.001 0-0.365 0-0.695-0.146-0.936-0.383l-2.886-2.84c-0.245-0.242-0.397-0.578-0.397-0.95 0-0.737 0.597-1.334 1.334-1.334 0.365 0 0.696 0.147 0.937 0.384l1.923 1.893 4.177-4.36c0.243-0.253 0.585-0.411 0.963-0.411 0.737 0 1.334 0.597 1.334 1.334 0 0.359-0.141 0.684-0.371 0.924l0-0-5.117 5.333c-0.239 0.249-0.572 0.405-0.942 0.41h-0.001z"></path> - </symbol> - <symbol id="subscript" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.273 30.277c-0.241 0.241-0.574 0.389-0.942 0.389s-0.701-0.149-0.942-0.389l0 0-1.723-1.73-1.723 1.727c-0.236 0.215-0.551 0.347-0.897 0.347-0.736 0-1.333-0.597-1.333-1.333 0-0.344 0.131-0.658 0.345-0.895l-0.001 0.001 1.727-1.727-1.707-1.717c-0.213-0.235-0.344-0.549-0.344-0.894 0-0.736 0.597-1.333 1.333-1.333 0.346 0 0.661 0.132 0.898 0.348l-0.001-0.001 1.703 1.697 1.703-1.707c0.245-0.271 0.598-0.44 0.99-0.44 0.736 0 1.333 0.597 1.333 1.333 0 0.39-0.168 0.742-0.435 0.986l-0.001 0.001-1.707 1.727 1.727 1.737c0.237 0.24 0.383 0.571 0.383 0.935 0 0.366-0.147 0.698-0.386 0.938l0-0zM20 4h-17.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h7.333v22.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-22.667h7.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="superscript" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M21.333 5.333c0 0.736-0.597 1.333-1.333 1.333v0h-7.333v22.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-22.667h-7.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h17.333c0.736 0 1.333 0.597 1.333 1.333v0zM30.277 7.060l-1.727-1.727 1.707-1.717c0.213-0.235 0.344-0.549 0.344-0.894 0-0.736-0.597-1.333-1.333-1.333-0.346 0-0.661 0.132-0.898 0.348l0.001-0.001-1.703 1.697-1.703-1.707c-0.245-0.271-0.598-0.44-0.99-0.44-0.736 0-1.333 0.597-1.333 1.333 0 0.39 0.168 0.742 0.435 0.986l0.001 0.001 1.707 1.727-1.727 1.737c-0.268 0.245-0.436 0.596-0.436 0.986 0 0.736 0.597 1.333 1.333 1.333 0.392 0 0.745-0.169 0.989-0.439l0.001-0.001 1.723-1.737 1.723 1.727c0.245 0.271 0.598 0.44 0.99 0.44 0.736 0 1.333-0.597 1.333-1.333 0-0.39-0.168-0.742-0.435-0.986l-0.001-0.001z"></path> - </symbol> - <symbol id="swap" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 17.333v5.78c0 2-1.31 3.553-3 3.553h-25.213l2.58 3.157c0.187 0.228 0.3 0.522 0.3 0.843 0 0.737-0.597 1.334-1.334 1.334-0.416 0-0.787-0.19-1.031-0.488l-0.002-0.002-3.74-4.577c-0.349-0.429-0.56-0.983-0.56-1.585 0-0.005 0-0.010 0-0.016v0.001c-0-0.004-0-0.009-0-0.014 0-0.603 0.211-1.157 0.564-1.591l-0.004 0.005 3.74-4.577c0.246-0.3 0.618-0.49 1.033-0.49 0.737 0 1.334 0.597 1.334 1.334 0 0.321-0.113 0.616-0.302 0.846l0.002-0.002-2.583 3.157h25.233c0.208-0.207 0.336-0.493 0.336-0.81 0-0.027-0.001-0.054-0.003-0.081l0 0.004v-5.78c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0zM32 6.667c0-0.001 0-0.003 0-0.005 0-0.603-0.211-1.156-0.564-1.59l0.004 0.005-3.74-4.587c-0.246-0.3-0.618-0.49-1.033-0.49-0.737 0-1.334 0.597-1.334 1.334 0 0.321 0.113 0.616 0.302 0.846l-0.002-0.002 2.58 3.157h-25.213c-1.69 0-3 1.56-3 3.553v5.78c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-5.78c-0.002-0.023-0.003-0.050-0.003-0.077 0-0.316 0.128-0.603 0.336-0.809l0-0h25.233l-2.6 3.157c-0.187 0.228-0.3 0.522-0.3 0.843 0 0.737 0.597 1.334 1.334 1.334 0.416 0 0.787-0.19 1.031-0.488l0.002-0.002 3.74-4.573c0.349-0.43 0.56-0.983 0.56-1.586 0-0.006-0-0.012-0-0.018v0.001z"></path> - </symbol> - <symbol id="system-information" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20.667 23.333h-1.333v-9.57c-0.001-0.076-0.010-0.15-0.025-0.221l0.001 0.007c0.014-0.065 0.022-0.139 0.023-0.216v-0.001c0-0.736-0.597-1.333-1.333-1.333v0h-6c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h0.667v8.667h-1.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM15.333 14.667h1.333v8.667h-1.333zM16 11.333c-1.841 0-3.333-1.492-3.333-3.333s1.492-3.333 3.333-3.333c1.841 0 3.333 1.492 3.333 3.333v0c0 1.841-1.492 3.333-3.333 3.333v0zM16 7.333c-0.368 0-0.667 0.298-0.667 0.667s0.298 0.667 0.667 0.667c0.368 0 0.667-0.298 0.667-0.667v0c0-0.368-0.298-0.667-0.667-0.667v0zM16 32c-8.837 0-16-7.163-16-16s7.163-16 16-16c8.837 0 16 7.163 16 16v0c-0.009 8.833-7.167 15.991-15.999 16h-0.001zM16 2.667c-7.364 0-13.333 5.97-13.333 13.333s5.97 13.333 13.333 13.333c7.364 0 13.333-5.97 13.333-13.333v0c0-7.364-5.97-13.333-13.333-13.333v0z"></path> - </symbol> - <symbol id="table-add" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h14.28c0.070-0.002 0.136-0.009 0.201-0.021l-0.008 0.001c0.057 0.011 0.124 0.018 0.192 0.020l0.002 0c0.736 0 1.333-0.597 1.333-1.333v0-13.333h13.333c0.736 0 1.333-0.597 1.333-1.333v0-14.667c0-0.736-0.597-1.333-1.333-1.333v0zM14.667 29.333h-4.667v-4.667h4.667zM7.333 29.333h-4.667v-4.667h4.667zM2.667 2.667h4.667v4.667h-4.667zM2.667 22v-4.667h4.667v4.667zM2.667 14.667v-4.667h4.667v4.667zM10 10h4.667v4.667h-4.667zM10 7.333v-4.667h4.667v4.667zM17.333 2.667h4.667v4.667h-4.667zM24.667 2.667h4.667v4.667h-4.667zM29.333 10v4.667h-4.667v-4.667zM14.667 22h-4.667v-4.667h4.667zM17.333 10h4.667v4.667h-4.667zM30 24.667c0 0.736-0.597 1.333-1.333 1.333v0h-2.667v2.667c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-2.667h-2.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h2.667v-2.667c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 2.667h2.667c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="table-cell" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 0h-29.333c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h29.333c0.736 0 1.333-0.597 1.333-1.333v0-29.333c0-0.736-0.597-1.333-1.333-1.333v0zM22 2.667v4.667h-4.667v-4.667zM14.667 2.667v4.667h-4.667v-4.667zM2.667 2.667h4.667v4.667h-4.667zM2.667 10h4.667v4.667h-4.667zM2.667 17.333h4.667v4.667h-4.667zM7.333 29.333h-4.667v-4.667h4.667zM10 29.333v-4.667h4.697v4.667zM17.363 29.333v-4.667h4.637v4.667zM29.333 29.333h-4.667v-4.667h4.667zM29.333 22h-4.667v-4.667h4.667zM29.333 14.667h-4.667v-4.667h4.667zM29.333 7.333h-4.667v-4.667h4.667z"></path> - </symbol> - <symbol id="table-column" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 6.667c-0.066 0.002-0.129 0.009-0.19 0.021l0.007-0.001c-0.054-0.011-0.117-0.018-0.182-0.020l-0.001-0h-6.667c-0.736 0-1.333 0.597-1.333 1.333v0 22.667c0 0.736 0.597 1.333 1.333 1.333v0h6.667c0.066-0.002 0.129-0.009 0.19-0.021l-0.007 0.001c0.054 0.011 0.117 0.018 0.182 0.020l0.001 0c0.736 0 1.333-0.597 1.333-1.333v0-22.667c0-0.736-0.597-1.333-1.333-1.333v0zM29.333 22h-4.367v-5.333h4.367zM29.333 14h-4.367v-4.667h4.367zM24.967 24.667h4.367v4.667h-4.367zM19.667 0.667v22.667c0 0.368-0.298 0.667-0.667 0.667v0h-6c-0.368 0-0.667-0.298-0.667-0.667v0-22.667c0-0.368 0.298-0.667 0.667-0.667v0h6c0.368 0 0.667 0.298 0.667 0.667v0zM8.367 6.667c-0.066 0.002-0.129 0.009-0.19 0.021l0.007-0.001c-0.054-0.011-0.117-0.018-0.182-0.020l-0.001-0h-6.667c-0.736 0-1.333 0.597-1.333 1.333v0 22.667c0 0.736 0.597 1.333 1.333 1.333v0h6.667c0.066-0.002 0.129-0.009 0.19-0.021l-0.007 0.001c0.054 0.011 0.117 0.018 0.182 0.020l0.001 0c0.736 0 1.333-0.597 1.333-1.333v0-22.667c0-0.736-0.597-1.333-1.333-1.333v0zM7.033 22h-4.367v-5.333h4.367zM7.033 14h-4.367v-4.667h4.367zM2.667 24.667h4.367v4.667h-4.367z"></path> - </symbol> - <symbol id="table-row" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.333 23.633c0-0.736-0.597-1.333-1.333-1.333v0h-22.667c-0.736 0-1.333 0.597-1.333 1.333v0 6.667c0.002 0.066 0.009 0.129 0.021 0.19l-0.001-0.007c-0.011 0.054-0.018 0.117-0.020 0.182l-0 0.001c0 0.736 0.597 1.333 1.333 1.333v0h22.667c0.736 0 1.333-0.597 1.333-1.333v0c-0.002-0.066-0.009-0.129-0.021-0.19l0.001 0.007c0.011-0.054 0.018-0.117 0.020-0.182l0-0.001zM10 29.203v-4.237h5.333v4.367h-5.333v-0.13zM7.333 29.333h-4.667v-4.367h4.667v4.367zM18 29.213v-4.247h4.667v4.367h-4.667v-0.12zM32 13v6c0 0.368-0.298 0.667-0.667 0.667v0h-22.667c-0.368 0-0.667-0.298-0.667-0.667v0-6c0-0.368 0.298-0.667 0.667-0.667v0h22.667c0.368 0 0.667 0.298 0.667 0.667v0zM25.333 1.333c0-0.736-0.597-1.333-1.333-1.333v0h-22.667c-0.736 0-1.333 0.597-1.333 1.333v0 6.667c0.002 0.066 0.009 0.129 0.021 0.19l-0.001-0.007c-0.011 0.054-0.018 0.117-0.020 0.182l-0 0.001c0 0.736 0.597 1.333 1.333 1.333v0h22.667c0.736 0 1.333-0.597 1.333-1.333v0c-0.002-0.066-0.009-0.129-0.021-0.19l0.001 0.007c0.011-0.054 0.018-0.117 0.020-0.182l0-0.001zM10 6.903v-4.237h5.333v4.367h-5.333v-0.13zM7.333 7.033h-4.667v-4.367h4.667v4.367zM18 6.917v-4.25h4.667v4.367h-4.667v-0.117z"></path> - </symbol> - <symbol id="tag" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M9.333 24c-0.001 0-0.001 0-0.002 0-0.326 0-0.625-0.117-0.857-0.312l0.002 0.002-8-6.69c-0.292-0.246-0.477-0.613-0.477-1.022 0-0.006 0-0.013 0-0.019v0.001c0.002-0.41 0.189-0.775 0.481-1.018l0.002-0.002 8-6.633c0.233-0.204 0.54-0.329 0.877-0.329 0.736 0 1.333 0.597 1.333 1.333 0 0.425-0.198 0.803-0.508 1.047l-0.003 0.002-6.767 5.61 6.773 5.667c0.298 0.246 0.487 0.616 0.487 1.030 0 0.736-0.597 1.333-1.333 1.333-0.004 0-0.007 0-0.011-0h0.001zM23.523 23.69l8-6.69c0.292-0.246 0.477-0.613 0.477-1.022 0-0.006-0-0.013-0-0.019v0.001c-0.002-0.41-0.189-0.775-0.481-1.018l-0.002-0.002-8-6.633c-0.224-0.177-0.511-0.284-0.823-0.284-0.736 0-1.333 0.597-1.333 1.333 0 0.4 0.176 0.759 0.455 1.003l0.002 0.001 6.767 5.61-6.773 5.667c-0.293 0.246-0.478 0.613-0.478 1.023 0 0.737 0.598 1.335 1.335 1.335 0.327 0 0.627-0.118 0.859-0.313l-0.002 0.002zM13.95 28.357l6.667-24c0.022-0.090 0.034-0.193 0.034-0.299 0-0.736-0.597-1.333-1.333-1.333-0.588 0-1.087 0.381-1.264 0.909l-0.003 0.009-6.667 24c-0.042 0.124-0.066 0.266-0.066 0.415 0 0.736 0.597 1.333 1.333 1.333 0.63 0 1.159-0.438 1.298-1.026l0.002-0.009z"></path> - </symbol> - <symbol id="tags" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.22 30.24h-0.217c-0.827-0.065-1.551-0.456-2.053-1.043l-0.003-0.004-11.613-13.457c-0.206-0.233-0.332-0.54-0.333-0.876v-0l0.043-10.667c-0-0.005-0-0.010-0-0.016 0-0.863 0.387-1.635 0.996-2.154l0.004-0.003c0.496-0.438 1.151-0.705 1.869-0.705 0.164 0 0.325 0.014 0.481 0.041l-0.017-0.002 10.29 1.73c0.318 0.054 0.591 0.214 0.789 0.442l0.001 0.002 11.597 13.443c0.441 0.527 0.708 1.212 0.708 1.96 0 0.899-0.387 1.707-1.002 2.268l-0.002 0.002-9.667 8.333c-0.495 0.439-1.15 0.707-1.867 0.707-0.001 0-0.002 0-0.003 0h0zM2.667 14.37l11.3 13.080c0.054 0.066 0.133 0.11 0.222 0.12l0.001 0c0.008 0.001 0.018 0.002 0.028 0.002 0.043 0 0.084-0.015 0.116-0.039l-0 0 9.667-8.333c0.059-0.069 0.094-0.159 0.094-0.257 0-0.085-0.027-0.163-0.072-0.228l0.001 0.001-11.287-13.083-9.813-1.633c-0.003-0-0.007-0-0.010-0-0.051 0-0.096 0.020-0.13 0.054v0c-0.042 0.030-0.070 0.078-0.073 0.133l-0 0zM21.14 30.333l9.783-8.537c0.662-0.597 1.077-1.457 1.077-2.415 0-0.801-0.29-1.534-0.77-2.1l0.004 0.005-10.35-12.353c-0.246-0.293-0.613-0.478-1.023-0.478-0.737 0-1.335 0.598-1.335 1.335 0 0.327 0.118 0.627 0.313 0.859l-0.002-0.002 10.353 12.353c0.090 0.103 0.145 0.238 0.145 0.386 0 0.158-0.063 0.302-0.165 0.408l0-0-9.787 8.54c-0.244 0.242-0.395 0.577-0.395 0.948 0 0.736 0.597 1.333 1.333 1.333 0.31 0 0.595-0.106 0.822-0.283l-0.003 0.002zM7.783 12.707c1.472-0.001 2.665-1.195 2.665-2.667s-1.194-2.667-2.667-2.667-2.667 1.194-2.667 2.667c0 0.737 0.299 1.404 0.782 1.887l0 0c0.479 0.482 1.143 0.78 1.876 0.78 0.004 0 0.007 0 0.011-0h-0.001z"></path> - </symbol> - <symbol id="tastes" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M12.333 32h-4A1.333 1.333 0 0 1 7 30.667v-15.86c-1.939-.591-3.326-2.361-3.333-4.456v-.148l1-9a1.341 1.341 0 1 1 2.665.299l.001-.006-1 8.933a2 2 0 0 0 1.998 1.903h.002c.736 0 1.333.597 1.333 1.333v15.667h1.333V13.665c0-.736.597-1.333 1.333-1.333h.002a2 2 0 0 0 1.998-1.916v-.004l-1-8.933a1.341 1.341 0 0 1 2.666-.299l.001.006 1 9.017v.147a4.67 4.67 0 0 1-3.3 4.448l-.033.009v15.86c0 .736-.597 1.333-1.333 1.333zm0-23.333v-8a.667.667 0 1 0-1.334 0v8a.667.667 0 1 0 1.334 0zm-2.666 0v-8a.667.667 0 1 0-1.334 0v8a.667.667 0 1 0 1.334 0zM23.76 32h-4.093a1.333 1.333 0 0 1-1.333-1.333V3.307c0-.244.066-.473.18-.67l-.003.006c.157-.273 1.62-2.71 3.793-2.643 1.333.04 2.453.953 3.333 2.717 2.457 4.93 2.697 15.767 2.697 16.223l.001.042c0 .618-.421 1.138-.991 1.289l-.009.002-2.237.567v9.827c0 .736-.597 1.333-1.333 1.333h-.003zM21 29.333h1.427v-9.545c0-.618.42-1.138.991-1.289l.009-.002 2.2-.557c-.12-2.803-.603-10.463-2.38-14.033-.48-.97-.88-1.24-1.023-1.24-.283-.02-.85.51-1.223 1.033z"></path> - </symbol> - <symbol id="timeline" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M9 24.667c-0.512-0-0.957-0.289-1.18-0.713l-0.003-0.007-3.333-6.427c-0.095-0.177-0.15-0.387-0.15-0.61 0-0.001 0-0.003 0-0.004v0-14.907c0-0.736 0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333v0 14.907c0 0.001 0 0.002 0 0.004 0 0.223-0.056 0.433-0.153 0.617l0.003-0.007-3.333 6.427c-0.227 0.431-0.671 0.72-1.183 0.72h-0zM7 16.58l2 3.857 2-3.857v-13.247h-4zM23 24.667c-0.512-0-0.957-0.289-1.18-0.713l-0.003-0.007-3.333-6.427c-0.095-0.177-0.15-0.387-0.15-0.61 0-0.001 0-0.003 0-0.004v0-14.907c0-0.736 0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333v0 14.907c0 0.001 0 0.002 0 0.004 0 0.223-0.055 0.433-0.153 0.617l0.003-0.007-3.333 6.427c-0.227 0.431-0.671 0.72-1.183 0.72h-0zM21 16.58l2 3.857 2-3.857v-13.247h-4zM30.667 27h-5.037c-0.503-0.991-1.515-1.658-2.683-1.658s-2.18 0.667-2.675 1.641l-0.008 0.017h-8.527c-0.504-0.991-1.515-1.658-2.683-1.658s-2.18 0.667-2.675 1.641l-0.008 0.017h-5.037c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h5.037c0.503 0.991 1.516 1.658 2.683 1.658s2.18-0.667 2.675-1.641l0.008-0.017h8.527c0.503 0.991 1.515 1.658 2.683 1.658s2.18-0.667 2.675-1.641l0.008-0.017h5.037c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> - </symbol> - <symbol id="trash-empty" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29 5.333h-5.333v-2.64c0-1.537-0.917-2.693-2.133-2.693h-11.753c-1.207 0-2.113 1.143-2.113 2.667v2.667h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2v21.15c0.013 1.577 1.295 2.85 2.873 2.85 0.006 0 0.012-0 0.018-0h16.949c0.004 0 0.009 0 0.013 0 1.546 0 2.8-1.246 2.813-2.789v-21.211h2c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM10.333 2.667h10.667v2.667h-10.667zM24.333 29.21c-0.011 0.071-0.072 0.124-0.145 0.124-0.005 0-0.011-0-0.016-0.001l0.001 0h-16.95c-0.004 0-0.010 0.001-0.015 0.001-0.107 0-0.195-0.080-0.208-0.183l-0-0.001v-21.15h17.333zM20.273 22.943c-0.241 0.241-0.574 0.389-0.942 0.389s-0.701-0.149-0.942-0.389l0 0-2.39-2.4-2.39 2.397c-0.236 0.215-0.551 0.347-0.897 0.347-0.736 0-1.333-0.597-1.333-1.333 0-0.344 0.13-0.658 0.345-0.895l-0.001 0.001 2.393-2.393-2.37-2.38c-0.226-0.238-0.364-0.561-0.364-0.916 0-0.736 0.597-1.333 1.333-1.333 0.357 0 0.682 0.141 0.922 0.37l-0.001-0 2.363 2.357 2.363-2.37c0.239-0.229 0.564-0.369 0.921-0.369 0.736 0 1.333 0.597 1.333 1.333 0 0.355-0.139 0.678-0.365 0.917l0.001-0.001-2.37 2.393 2.393 2.407c0.236 0.24 0.381 0.57 0.381 0.933 0 0.365-0.147 0.696-0.385 0.937l0-0z"></path> - </symbol> - <symbol id="trash-notrashed" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M23.37 32h-14.687c-1.482-0.042-2.667-1.254-2.667-2.742 0-0.019 0-0.038 0.001-0.057l-0 0.003v-15.87c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 15.87c-0 0.002-0 0.004-0 0.007 0 0.046 0.015 0.089 0.040 0.124l-0-0.001h14.61c0.002-0.011 0.004-0.023 0.004-0.035s-0.001-0.024-0.004-0.036l0 0.001v-15.93c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 15.93c0 0.015 0 0.033 0 0.051 0 1.464-1.171 2.655-2.628 2.686l-0.003 0zM24.86 5.333l-23.193 6.207c-0.1 0.027-0.214 0.042-0.333 0.043h-0.001c-0.729-0.009-1.317-0.602-1.317-1.333 0-0.612 0.412-1.128 0.975-1.285l0.009-0.002 4.817-1.297-0.56-2.1c-0.37-1.373 0.183-2.63 1.28-2.927l9.667-2.583c1.107-0.297 2.223 0.527 2.597 1.913l0.533 2.077 4.837-1.297c0.103-0.029 0.222-0.045 0.345-0.045 0.738 0 1.337 0.599 1.337 1.337 0 0.616-0.416 1.134-0.983 1.289l-0.009 0.002zM8.393 7l8.37-2.243-0.517-1.933-8.37 2.223z"></path> - </symbol> - <symbol id="trash-send" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24.507 32h-16.95c-0.005 0-0.011 0-0.017 0-1.579 0-2.86-1.273-2.873-2.849v-16.485c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 16.483c0.013 0.104 0.102 0.184 0.208 0.184 0.005 0 0.011-0 0.016-0.001l-0.001 0h16.95c0.005 0 0.010 0.001 0.015 0.001 0.073 0 0.134-0.053 0.145-0.123l0-0.001v-16.543c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 16.543c-0.013 1.544-1.268 2.79-2.813 2.79-0.005 0-0.009 0-0.014-0h0.001zM30.667 6.667c0 0.736-0.597 1.333-1.333 1.333v0h-26.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h5.333v-2.667c0-1.513 0.907-2.667 2.113-2.667h11.753c1.217 0 2.133 1.157 2.133 2.693v2.64h5.333c0.736 0 1.333 0.597 1.333 1.333v0zM10.667 5.333h10.667v-2.667h-10.667zM21.28 19.81c-0.243-0.256-0.586-0.415-0.967-0.415-0.357 0-0.681 0.14-0.921 0.369l0.001-0-2.060 1.963v-7.27c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 7.267l-2.063-1.96c-0.239-0.227-0.562-0.367-0.918-0.367-0.736 0-1.333 0.597-1.333 1.333 0 0.38 0.159 0.723 0.414 0.966l0.001 0.001 3.7 3.517c0.395 0.378 0.932 0.61 1.523 0.61 0.004 0 0.007 0 0.011-0h-0.001c0.001 0 0.003 0 0.005 0 0.591 0 1.128-0.231 1.526-0.608l-0.001 0.001 3.703-3.52c0.256-0.243 0.415-0.586 0.415-0.967 0-0.357-0.14-0.681-0.369-0.921l0 0.001z"></path> - </symbol> - <symbol id="trash" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.333 5.333h-5.333v-2.64c0-1.537-0.917-2.693-2.133-2.693h-11.753c-1.207 0-2.113 1.143-2.113 2.667v2.667h-5.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h2v21.15c0.013 1.577 1.295 2.85 2.873 2.85 0.006 0 0.012-0 0.018-0h16.949c0.004 0 0.009 0 0.013 0 1.546 0 2.8-1.246 2.813-2.789v-21.211h2c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM10.667 2.667h10.667v2.667h-10.667zM24.667 29.21c-0.011 0.071-0.072 0.124-0.145 0.124-0.005 0-0.011-0-0.016-0.001l0.001 0h-16.95c-0.004 0-0.010 0.001-0.015 0.001-0.107 0-0.195-0.080-0.208-0.183l-0-0.001v-21.15h17.333zM16 26.667c-0.736 0-1.333-0.597-1.333-1.333v0-13.333c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 13.333c0 0.736-0.597 1.333-1.333 1.333v0zM13 22.667v-8c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 8c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM21.667 22.667v-8c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 8c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="twitter" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M12.73 30c-5.923 0-10.73-3.117-10.983-3.283-0.357-0.243-0.587-0.647-0.587-1.105 0-0.549 0.332-1.020 0.806-1.225l0.009-0.003 4.203-1.75c-1.977-1.433-4.643-3.94-5.71-7.557-1.040-3.517-0.333-7.28 2.053-11.183 0.238-0.385 0.658-0.637 1.137-0.637 0.415 0 0.787 0.19 1.031 0.488l0.002 0.002c0.128 0.071 0.238 0.155 0.333 0.253l0 0c4.853 4.957 8.26 5.9 9.367 5.957 0.443-2.020 1.943-6.147 5.277-7.51 2.353-0.963 5-0.367 7.867 1.773h3.133c0 0 0.001 0 0.001 0 0.736 0 1.333 0.597 1.333 1.333 0 0.171-0.032 0.335-0.091 0.486l0.003-0.009-1.817 4.733 0.1 1.23c0.019 0.202 0.029 0.437 0.029 0.675 0 0.944-0.168 1.848-0.477 2.685l0.017-0.054c-2.767 7.497-6.853 12.187-12.187 13.94-1.43 0.482-3.077 0.76-4.789 0.76-0.021 0-0.043-0-0.064-0h0.003zM5.643 25.74c2.58 1.113 6.873 2.367 11.123 0.963 4.51-1.493 8.040-5.637 10.497-12.333 0.188-0.51 0.297-1.1 0.297-1.715 0-0.159-0.007-0.316-0.022-0.472l0.001 0.020-0.127-1.537c-0.003-0.034-0.005-0.074-0.005-0.114 0-0.17 0.032-0.333 0.091-0.482l-0.003 0.009 1.233-3.207h-1.64c-0 0-0 0-0.001 0-0.313 0-0.601-0.108-0.829-0.289l0.003 0.002c-2.2-1.733-4.080-2.3-5.583-1.687-2.39 0.977-3.577 4.743-3.74 6-0.113 0.743-0.645 1.337-1.343 1.54l-0.013 0.003c-2.057 0.71-6.493-0.827-11.583-5.72-1.333 2.693-1.667 5.237-0.98 7.583 1.4 4.783 6.597 7.333 6.667 7.36 0.45 0.221 0.754 0.675 0.754 1.201 0 0.553-0.336 1.027-0.816 1.229l-0.009 0.003z"></path> - </symbol> - <symbol id="unarchive" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.333 3.27c-0.363-0.372-0.87-0.603-1.43-0.603h-0l-27.8 0.063c-1.171 0.044-2.104 1.004-2.104 2.182 0 0.023 0 0.045 0.001 0.068l-0-0.003v6.123c0 0.003-0 0.006-0 0.010 0 0.633 0.255 1.206 0.667 1.624l-0-0c0.344 0.346 0.81 0.569 1.328 0.6l0.006 0v13.923c0.002 1.146 0.931 2.075 2.076 2.077h23.847c1.146-0.002 2.075-0.931 2.077-2.076v-14.020c1.124-0.094 2.001-1.029 2.001-2.17 0-0.024-0-0.047-0.001-0.070l0 0.003v-6.1c0-0.003 0-0.007 0-0.011 0-0.632-0.255-1.204-0.667-1.619l0 0zM2.667 5.397l26.667-0.063v5.243l-26.667 0.090zM4.667 26.667v-13.333l22.667-0.073v13.407zM20.273 24.277c-0.241 0.241-0.574 0.389-0.942 0.389s-0.701-0.149-0.942-0.389l0 0-2.39-2.4-2.39 2.397c-0.236 0.215-0.551 0.347-0.897 0.347-0.736 0-1.333-0.597-1.333-1.333 0-0.344 0.13-0.658 0.345-0.895l-0.001 0.001 2.393-2.393-2.37-2.38c-0.226-0.238-0.364-0.561-0.364-0.916 0-0.736 0.597-1.333 1.333-1.333 0.357 0 0.682 0.141 0.922 0.37l-0.001-0 2.363 2.357 2.363-2.37c0.239-0.229 0.564-0.369 0.921-0.369 0.736 0 1.333 0.597 1.333 1.333 0 0.355-0.139 0.678-0.365 0.917l0.001-0.001-2.37 2.393 2.393 2.407c0.236 0.24 0.381 0.57 0.381 0.933 0 0.365-0.147 0.696-0.385 0.937l0-0z"></path> - </symbol> - <symbol id="underscore" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 32h-21.333c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h21.333c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM16 28c9.88-0.053 10-8.633 10-8.72v-17.907c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333v0 17.91c0 0.247-0.083 6-7.333 6.053h-0.070c-6.987-0.003-7.247-5.443-7.263-6.057v-17.947c0-0.736-0.597-1.333-1.333-1.333v0c-0.736 0-1.333 0.597-1.333 1.333v0 17.947c0 0.143 0.030 3.553 2.577 6.090 1.757 1.747 4.23 2.63 7.333 2.63z"></path> - </symbol> - <symbol id="unpublish-hide" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18.333 2.667h-4.667c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h4.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM2.667 7.667v-4.75c0-0.153 0.097-0.25 0.167-0.25h4.5c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-4.5c-1.57 0.026-2.834 1.305-2.834 2.88 0 0.013 0 0.026 0 0.039l-0-0.002v4.75c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM2.667 18.333v-4.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 4.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM8.667 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-4.5c-0.092 0-0.167-0.075-0.167-0.167v0-4.5c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 4.5c0.002 1.564 1.269 2.831 2.833 2.833h4.5c0.736 0 1.333-0.597 1.333-1.333v0zM19.667 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-4.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.667c0.736 0 1.333-0.597 1.333-1.333v0zM32 29.167v-4.5c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 4.5c0 0.092-0.075 0.167-0.167 0.167v0h-4.5c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.5c1.564-0.002 2.831-1.269 2.833-2.833v-0zM32 18.333v-4.667c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 4.667c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM32 7.333v-4.5c-0.002-1.564-1.269-2.831-2.833-2.833h-4.5c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h4.5c0.092 0 0.167 0.075 0.167 0.167v0 4.5c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0zM27 16c0 0.012 0.001 0.026 0.001 0.041 0 0.284-0.089 0.547-0.24 0.764l0.003-0.004c-3.333 4.787-6.92 7.2-10.687 7.2-0.003 0-0.006 0-0.009 0-0.586 0-1.159-0.056-1.715-0.162l0.057 0.009c-0.625-0.119-1.091-0.661-1.091-1.312 0-0.737 0.597-1.334 1.334-1.334 0.086 0 0.17 0.008 0.252 0.024l-0.008-0.001c3.077 0.573 6.153-1.19 9.153-5.223-0.433-0.583-0.87-1.12-1.303-1.607-0.21-0.235-0.339-0.548-0.339-0.89 0-0.739 0.599-1.339 1.339-1.339 0.397 0 0.754 0.173 0.999 0.447l0.001 0.001c0.687 0.773 1.352 1.616 1.967 2.497l0.059 0.089c0.143 0.209 0.228 0.467 0.228 0.745 0 0.019-0 0.038-0.001 0.057l0-0.003zM17.47 8.117c-7.183-1.183-12.073 6.817-12.28 7.16-0.115 0.19-0.183 0.419-0.183 0.664 0 0.001 0 0.002 0 0.003v-0c-0.003 0.017-0.006 0.036-0.007 0.056l-0 0.001c-0.001 0.008-0.001 0.018-0.001 0.028s0 0.020 0.001 0.030l-0-0.001c0 0.002 0 0.004 0 0.006 0 0.244 0.068 0.472 0.187 0.667l-0.003-0.006c0.682 1.071 1.407 2.003 2.21 2.861l-0.010-0.011c0.24 0.232 0.566 0.374 0.926 0.374 0.736 0 1.333-0.597 1.333-1.333 0-0.326-0.117-0.625-0.311-0.856l0.002 0.002c-0.487-0.53-0.953-1.104-1.383-1.706l-0.037-0.054c1.2-1.667 4.753-5.97 9.127-5.25 0.065 0.011 0.139 0.017 0.215 0.017 0.737 0 1.334-0.597 1.334-1.334 0-0.661-0.481-1.21-1.111-1.316l-0.008-0.001zM24.277 7.723c-0.241-0.242-0.575-0.391-0.943-0.391s-0.702 0.149-0.943 0.391l-5.433 5.437c-0.285-0.101-0.614-0.16-0.957-0.16h-0c-1.657 0-3 1.343-3 3v0c0 0.343 0.059 0.672 0.166 0.977l-0.006-0.021-5.437 5.433c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0l5.433-5.437c0.285 0.101 0.614 0.16 0.957 0.16h0c1.657 0 3-1.343 3-3v0c-0-0.343-0.059-0.672-0.166-0.977l0.006 0.021 5.437-5.433c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="unpublish-reveal" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.76 15.2c-3.427-4.907-7.12-7.33-11.013-7.2-6.247 0.217-10.383 7-10.557 7.277-0.115 0.19-0.183 0.419-0.183 0.664 0 0.001 0 0.002 0 0.003v-0c-0.003 0.017-0.006 0.036-0.007 0.056l-0 0.001c-0.001 0.009-0.001 0.018-0.001 0.028s0 0.020 0.001 0.030l-0-0.001c0 0.002 0 0.004 0 0.006 0 0.244 0.068 0.472 0.187 0.667l-0.003-0.006c0.18 0.277 4.317 7.060 10.563 7.277h0.3c3.783 0 7.39-2.42 10.713-7.2 0.148-0.226 0.237-0.503 0.237-0.8s-0.088-0.574-0.24-0.806l0.003 0.006zM15.847 21.333c-3.82-0.127-6.847-3.81-7.933-5.333 1.087-1.523 4.113-5.207 7.933-5.333 2.747-0.090 5.51 1.703 8.203 5.333-2.693 3.627-5.453 5.423-8.203 5.333zM19 16c0 1.657-1.343 3-3 3s-3-1.343-3-3c0-1.657 1.343-3 3-3v0c1.657 0 3 1.343 3 3v0z"></path> - <path d="M29.333 32h-26.667c-1.473 0-2.667-1.194-2.667-2.667v0-26.667c0-1.473 1.194-2.667 2.667-2.667v0h26.667c1.473 0 2.667 1.194 2.667 2.667v0 26.667c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 2.667v26.667h26.667v-26.667z"></path> - </symbol> - <symbol id="upload" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M22.363 6.183c-0.246 0.296-0.615 0.483-1.027 0.483-0.001 0-0.002 0-0.003 0h0c-0.323 0-0.618-0.115-0.849-0.305l0.002 0.002-3.153-2.58v20.217c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333v0-20.213l-3.157 2.583c-0.221 0.17-0.502 0.272-0.807 0.272-0.736 0-1.333-0.597-1.333-1.333 0-0.399 0.175-0.756 0.452-1l0.001-0.001 4.577-3.747c0.436-0.349 0.995-0.56 1.603-0.56s1.168 0.211 1.608 0.564l-0.005-0.004 4.573 3.747c0.298 0.246 0.487 0.616 0.487 1.030 0 0.323-0.115 0.618-0.305 0.849l0.002-0.002zM28 30.667c0-0.736-0.597-1.333-1.333-1.333v0h-21.333c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0z"></path> - </symbol> - <symbol id="user" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 16c-4.418 0-8-3.582-8-8s3.582-8 8-8c4.418 0 8 3.582 8 8v0c0 4.418-3.582 8-8 8v0zM16 2.667c-2.946 0-5.333 2.388-5.333 5.333s2.388 5.333 5.333 5.333c2.946 0 5.333-2.388 5.333-5.333v0c0-2.946-2.388-5.333-5.333-5.333v0zM24.047 32h-16.077c-0.001 0-0.001 0-0.002 0-0.837 0-1.581-0.397-2.054-1.014l-0.005-0.006c-0.361-0.469-0.578-1.065-0.578-1.712 0-0.213 0.024-0.421 0.068-0.62l-0.004 0.019c0.033-0.352 0.089-0.672 0.169-0.982l-0.009 0.042c0.1-0.403 1.69-6.667 2.973-8.437 1.917-2.623 5.107-2.623 5.52-2.623h3c0.163 0 4.503-0.053 6.25 2.833 1.333 1.76 2.94 7.533 3.207 8.62 0 0.043 0 0.083 0.023 0.123l0.053 0.377c0.049 0.196 0.077 0.42 0.077 0.651 0 0.634-0.21 1.219-0.565 1.689l0.005-0.007c-0.469 0.635-1.213 1.043-2.053 1.047h-0.001zM8 29.333h16c0.004-0.013 0.007-0.028 0.007-0.043s-0.002-0.030-0.007-0.044l0 0.001c-0.015-0.048-0.028-0.106-0.036-0.165l-0.001-0.005-0.057-0.387c-0.597-2.427-2.020-6.73-2.73-7.58-0.045-0.056-0.088-0.118-0.126-0.183l-0.004-0.007c-0.767-1.333-3.133-1.607-3.967-1.587h-3.107s-2.137-0.043-3.29 1.537c-0.6 0.82-1.837 4.643-2.54 7.517v0.040c-0.041 0.16-0.071 0.349-0.086 0.542l-0.001 0.011c-0.007 0.092-0.022 0.177-0.046 0.259l0.002-0.009c-0.010 0.021-0.016 0.046-0.016 0.072 0 0.011 0.001 0.022 0.003 0.032l-0-0.001z"></path> - </symbol> - <symbol id="user_group" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.623 27.5h-2.217c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.893c-0.001-0.004-0.001-0.009-0.001-0.013s0-0.009 0.001-0.014l-0 0c-0.012-0.152-0.033-0.291-0.064-0.427l0.004 0.020v-0.040c-0.573-2.273-1.553-5.41-2.010-6.027-0.84-1.17-2.353-1.15-2.417-1.147h-1.11c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h1.047c0.383 0 3.043 0.023 4.647 2.253 1.050 1.463 2.333 6.6 2.417 6.957 0.060 0.226 0.107 0.495 0.132 0.77l0.001 0.020c0.034 0.156 0.053 0.335 0.053 0.519 0 0.588-0.198 1.13-0.531 1.563l0.004-0.006c-0.431 0.552-1.096 0.903-1.844 0.903-0.002 0-0.004 0-0.007 0h0zM25.833 13.89c2.419-1.082 4.074-3.468 4.074-6.241 0-3.73-2.996-6.76-6.712-6.816l-0.005-0c-0.382 0.001-0.756 0.032-1.12 0.092l0.040-0.005c-0.639 0.107-1.119 0.656-1.119 1.317 0 0.737 0.597 1.334 1.334 1.334 0.076 0 0.15-0.006 0.223-0.019l-0.008 0.001c0.192-0.034 0.413-0.053 0.639-0.053 0.004 0 0.008 0 0.011 0h-0.001c2.25 0.054 4.053 1.891 4.053 4.149 0 1.684-1.003 3.133-2.443 3.784l-0.026 0.011c-0.486 0.206-0.82 0.679-0.82 1.231 0 0.736 0.597 1.333 1.333 1.333 0.198 0 0.386-0.043 0.555-0.121l-0.008 0.003zM6.697 14c-0.192-0-0.374-0.041-0.539-0.113l0.009 0.003c-2.419-1.082-4.074-3.468-4.074-6.241 0-3.73 2.996-6.76 6.712-6.816l0.005-0c0.382 0.001 0.756 0.032 1.12 0.092l-0.040-0.005c0.639 0.107 1.119 0.656 1.119 1.317 0 0.737-0.597 1.334-1.334 1.334-0.076 0-0.15-0.006-0.223-0.019l0.008 0.001c-0.192-0.034-0.413-0.053-0.639-0.053-0.004 0-0.008 0-0.011 0h0.001c-2.25 0.054-4.053 1.891-4.053 4.149 0 1.684 1.003 3.133 2.443 3.784l0.026 0.011c0.476 0.21 0.803 0.679 0.803 1.223 0 0.736-0.597 1.333-1.333 1.333h-0zM5.927 26.167c0-0.736-0.597-1.333-1.333-1.333v0h-1.893c0.001-0.004 0.001-0.009 0.001-0.013s-0-0.009-0.001-0.014l0 0c0.012-0.152 0.033-0.291 0.064-0.427l-0.004 0.020v-0.040c0.55-2.273 1.54-5.41 2-6.043 0.84-1.17 2.333-1.147 2.403-1.147h1.123c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-1.037c-0.39 0-3.043 0.023-4.643 2.253-1.053 1.467-2.333 6.603-2.42 6.96-0.060 0.226-0.107 0.495-0.132 0.77l-0.001 0.020c-0.034 0.156-0.053 0.335-0.053 0.519 0 0.588 0.198 1.13 0.531 1.563l-0.004-0.006c0.43 0.559 1.098 0.915 1.85 0.917h2.217c0.736 0 1.333-0.597 1.333-1.333v0zM23.183 31.167h-14.353c-0.002 0-0.003 0-0.005 0-0.791 0-1.495-0.377-1.941-0.961l-0.004-0.006c-0.342-0.444-0.548-1.007-0.548-1.619 0-0.195 0.021-0.386 0.061-0.569l-0.003 0.018c0.031-0.322 0.083-0.614 0.155-0.897l-0.009 0.040c0.097-0.377 1.517-6 2.667-7.593 1.75-2.397 4.667-2.42 5.047-2.413h2.667c0.4 0 4.123 0 5.697 2.6 1.207 1.59 2.597 6.58 2.883 7.757 0 0.040 0.017 0.083 0.023 0.123l0.047 0.333c0.046 0.186 0.073 0.399 0.073 0.619 0 0.601-0.199 1.156-0.534 1.602l0.005-0.007c-0.445 0.588-1.141 0.966-1.925 0.973h-0.001zM9.020 28.5h13.957v-0.070l-0.047-0.333c-0.597-2.39-1.86-6.053-2.42-6.727-0.046-0.056-0.090-0.118-0.13-0.183l-0.004-0.007c-0.643-1.127-2.73-1.357-3.413-1.333h-2.78s-1.83-0.033-2.813 1.333c-0.517 0.71-1.633 4.17-2.247 6.667v0.040c-0.034 0.134-0.060 0.293-0.073 0.456l-0.001 0.011c-0.009 0.058-0.019 0.108-0.032 0.156l0.002-0.009zM16 16.833c-4.050 0-7.333-3.283-7.333-7.333s3.283-7.333 7.333-7.333c4.050 0 7.333 3.283 7.333 7.333v0c0 4.050-3.283 7.333-7.333 7.333v0zM16 4.833c-2.577 0-4.667 2.089-4.667 4.667s2.089 4.667 4.667 4.667c2.577 0 4.667-2.089 4.667-4.667v0c0-2.577-2.089-4.667-4.667-4.667v0z"></path> - </symbol> - <symbol id="users-personalization" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 32h-8a1.333 1.333 0 0 1 0-2.666h8A1.333 1.333 0 0 1 20 32zm2.667-5.333c0-.736-.597-1.333-1.333-1.333H10.667a1.333 1.333 0 0 0 0 2.666h10.667c.736 0 1.333-.597 1.333-1.333zM28 12c0 6.627-5.373 12-12 12S4 18.627 4 12 9.373 0 16 0s12 5.373 12 12zm-12 9.333h.007c1.37 0 2.671-.297 3.841-.83l-.058.024a7.84 7.84 0 0 0-.868-1.694l.018.028a1.454 1.454 0 0 1-.15-.173l-.003-.004c-.233-.333-1.27-.567-2.06-.567h-2c-.333 0-1.24.107-1.693.603a8.844 8.844 0 0 0-.821 1.726l-.019.064a9.07 9.07 0 0 0 3.807.823zm0-8.303a3 3 0 1 0-3-3 3 3 0 0 0 3 3zM25.333 12v-.008a9.333 9.333 0 1 0-15.448 7.051l.012.01a7.74 7.74 0 0 1 1.176-2.147l-.013.017c1.067-1.167 2.6-1.41 3.353-1.457-2.379-.708-4.084-2.876-4.084-5.441a5.667 5.667 0 1 1 7.195 5.458l-.04.009c.98.097 2.533.43 3.403 1.547a7.78 7.78 0 0 1 1.168 1.987l.019.053a9.319 9.319 0 0 0 3.26-7.08z"></path> - </symbol> - <symbol id="users-select" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.957 12h-.002c-.186 0-.363-.039-.523-.11l.008.003c-2.254-.959-3.805-3.154-3.805-5.712A6.19 6.19 0 0 1 8.908.085L8.871.08a1.334 1.334 0 1 1-.429 2.6l.009.003a3.477 3.477 0 0 0-.559-.043h-.015.001-.031a3.543 3.543 0 0 0-1.395 6.801l.023.009A1.334 1.334 0 0 1 5.96 12h-.001zM5.45 23c0-.736-.597-1.333-1.333-1.333h-1.45c.009-.098.024-.188.046-.275l-.003.012-.001-.018.001-.019v.001c.527-2.28 1.357-4.923 1.71-5.44a2.393 2.393 0 0 1 1.891-.927h1.022a1.333 1.333 0 0 0 0-2.666h-.958a5.036 5.036 0 0 0-4.102 2.109l-.01.015c-.917 1.333-2.023 6-2.1 6.333a4.34 4.34 0 0 0-.115.692l-.001.018a2.466 2.466 0 0 0 .468 2.006l-.004-.006c.4.509 1.016.833 1.708.833h1.912a1.334 1.334 0 0 0 1.32-1.333zm17.803-11.123c2.257-.952 3.812-3.146 3.812-5.704A6.173 6.173 0 0 0 20.892 0l-.097.001h.005a6.7 6.7 0 0 0-1.038.082L19.8.078a1.333 1.333 0 0 0 .383 2.61h.031-.002c.17-.03.366-.047.566-.047h.022-.001.037a3.591 3.591 0 0 1 3.59 3.541v.002a3.54 3.54 0 0 1-2.17 3.251l-.023.009a1.333 1.333 0 1 0 1.025 2.429l-.006.003zm3.83 7.383a1.336 1.336 0 0 0 .851-1.676l.003.009c-.147-.46-.293-.887-.43-1.263a7.634 7.634 0 0 0-.931-1.958l.018.028c-1.48-2.023-3.927-2.067-4.3-2.067h-.96a1.333 1.333 0 0 0 0 2.666h1.023s1.373-.017 2.1.98c.219.368.411.792.554 1.239l.012.044c.123.333.257.733.393 1.157.18.53.67.906 1.249.913h.001c.15-.002.293-.029.426-.076l-.009.003zM21 8.067a6.668 6.668 0 1 0-6.667 6.737A6.708 6.708 0 0 0 21 8.097v-.032.002zm-2.64 0a4.02 4.02 0 1 1-4.027-4.093 4.064 4.064 0 0 1 4.02 4.063v.032-.002zm.613 18.6c0-.736-.597-1.333-1.333-1.333H8.35c.01-.136.03-.261.06-.382l-.003.016v-.04c.613-2.437 1.583-5.267 2-5.823.86-1.103 2.46-1.087 2.46-1.087h.087c1.26-.027 2.787-.04 3.153 0 .187.047.77.213 1.12.333.083.083.22.243.333.367s.303.353.483.547a1.333 1.333 0 1 0 1.945-1.784l-.002-.002c-.157-.17-.297-.333-.423-.48a3.627 3.627 0 0 0-1.09-.991l-.017-.009a2.104 2.104 0 0 0-.23-.101l-.016-.005a12.525 12.525 0 0 0-1.645-.479l-.092-.017c-.54-.067-2.637-.03-3.543 0-.423 0-3.037.053-4.62 2.177-1.06 1.42-2.333 6.387-2.437 6.74a4.54 4.54 0 0 0-.135.754l-.002.02a2.483 2.483 0 0 0 .454 2.006l-.005-.006c.431.559 1.1.916 1.853.917h9.627a1.334 1.334 0 0 0 1.307-1.333zm11.694-1.334H28v-2.667a1.333 1.333 0 0 0-2.666 0v2.667h-2.667a1.333 1.333 0 0 0 0 2.666h2.667v2.667a1.333 1.333 0 0 0 2.666 0v-2.667h2.667a1.333 1.333 0 0 0 0-2.666z"></path> - </symbol> - <symbol id="version-compare-action" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 22.333c0 .736-.597 1.333-1.333 1.333h-12.8l4.44 4.757a1.333 1.333 0 1 1-1.945 1.821l-.001-.002-5.607-6a2.812 2.812 0 0 1-.753-1.881V22.332v-.024c0-.712.27-1.362.712-1.852l-.002.002 5.4-6a1.34 1.34 0 1 1 1.999 1.788l.001-.001-4.313 4.753h12.87c.736 0 1.333.597 1.333 1.333zM19.333 9.667v-.023c0-.71-.267-1.359-.706-1.85l.002.003-5.407-6a1.34 1.34 0 1 0-1.999 1.788l-.001-.001 4.313 4.75H2.665a1.333 1.333 0 0 0 0 2.666h12.8l-4.44 4.757a1.333 1.333 0 1 0 1.945 1.821l.001-.002 5.607-6a2.815 2.815 0 0 0 .753-1.885v-.025z"></path> - </symbol> - <symbol id="version-compare" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M32 4.667v22a2 2 0 0 1-2 2H20a1.333 1.333 0 0 1 0-2.666h9.333v-8.667h-6.41l1.743 1.713a1.333 1.333 0 1 1-1.863 1.907l-3.503-3.43a2.103 2.103 0 0 1-.633-1.507V16v-.004c0-.596.246-1.134.643-1.518l.001-.001 3.5-3.43a1.333 1.333 0 1 1 1.858 1.905l-.002.001-1.743 1.713h6.41V5.333h-9.333a1.333 1.333 0 0 1 0-2.666h10a2 2 0 0 1 2 2zM14.667 0c-.736 0-1.333.597-1.333 1.333v1.333H2.001a2 2 0 0 0-2 2v10h9.11l-1.727-1.723a1.333 1.333 0 1 1 1.882-1.886l.001.001 3.443 3.433c.385.38.624.907.624 1.491l-.001.048v-.002a2.128 2.128 0 0 1-.666 1.506l-.001.001-3.563 3.427a1.333 1.333 0 1 1-1.851-1.919l.001-.001 1.777-1.707H0v9.333a2 2 0 0 0 2 2h11.333v2a1.333 1.333 0 0 0 2.666 0V1.335c0-.736-.597-1.333-1.333-1.333z"></path> - </symbol> - <symbol id="versions" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 18h-8c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h8c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM32 4.9v6.1c0 0.017 0.001 0.037 0.001 0.057 0 1.14-0.877 2.076-1.993 2.169l-0.008 0.001v14.030c-0.002 1.146-0.931 2.075-2.076 2.077h-23.847c-1.146-0.002-2.075-0.931-2.077-2.076v-13.924c-0.524-0.031-0.99-0.254-1.333-0.6l-0-0c-0.412-0.417-0.667-0.991-0.667-1.623 0-0.003 0-0.007 0-0.010v0.001-6.123c-0.001-0.019-0.001-0.042-0.001-0.065 0-1.178 0.933-2.138 2.1-2.182l0.004-0 27.797-0.063c0.561 0.006 1.066 0.235 1.433 0.603l0 0c0.412 0.415 0.667 0.988 0.667 1.619 0 0.004 0 0.007-0 0.011v-0.001zM2.667 10.667l26.667-0.087v-5.247l-26.667 0.063zM27.333 26.667v-13.417l-22.667 0.083v13.333z"></path> - </symbol> - <symbol id="video" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M26.667 28.333h-21.333c-2.946 0-5.333-2.388-5.333-5.333v0-13.333c0-2.946 2.388-5.333 5.333-5.333v0h21.333c2.946 0 5.333 2.388 5.333 5.333v0 13.333c0 2.946-2.388 5.333-5.333 5.333v0zM5.333 7c-1.473 0-2.667 1.194-2.667 2.667v0 13.333c0 1.473 1.194 2.667 2.667 2.667v0h21.333c1.473 0 2.667-1.194 2.667-2.667v0-13.333c0-1.473-1.194-2.667-2.667-2.667v0zM20.503 15.713l-7.333-4.333c-0.049-0.029-0.107-0.047-0.17-0.047-0.184 0-0.333 0.149-0.333 0.333v0 8.667c0 0.184 0.149 0.333 0.333 0.333v0c0.063-0 0.121-0.017 0.171-0.047l-0.002 0.001 7.333-4.333c0.098-0.059 0.163-0.165 0.163-0.287s-0.065-0.228-0.162-0.286l-0.002-0.001z"></path> - </symbol> - <symbol id="view-custom" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 5.333h-20c-0.736 0-1.333-0.597-1.333-1.333s0.597-1.333 1.333-1.333v0h20c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0zM20.667 8c0-0.736-0.597-1.333-1.333-1.333v0h-8.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h8.667c0.736 0 1.333-0.597 1.333-1.333v0zM32 14c0-0.736-0.597-1.333-1.333-1.333v0h-20c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h20c0.736 0 1.333-0.597 1.333-1.333v0zM20.667 18c0-0.736-0.597-1.333-1.333-1.333v0h-8.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h8.667c0.736 0 1.333-0.597 1.333-1.333v0zM32 24c0-0.736-0.597-1.333-1.333-1.333v0h-20c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h20c0.736 0 1.333-0.597 1.333-1.333v0zM20.667 28c0-0.736-0.597-1.333-1.333-1.333v0h-8.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h8.667c0.736 0 1.333-0.597 1.333-1.333v0zM8 7.333v-2.667c0-1.473-1.194-2.667-2.667-2.667v0h-2.667c-1.473 0-2.667 1.194-2.667 2.667v0 2.667c0 1.473 1.194 2.667 2.667 2.667v0h2.667c1.473 0 2.667-1.194 2.667-2.667v0zM5.333 4.667v2.667h-2.667v-2.667zM8 17.333v-2.667c0-1.473-1.194-2.667-2.667-2.667v0h-2.667c-1.473 0-2.667 1.194-2.667 2.667v0 2.667c0 1.473 1.194 2.667 2.667 2.667v0h2.667c1.473 0 2.667-1.194 2.667-2.667v0zM5.333 14.667v2.667h-2.667v-2.667zM8 27.333v-2.667c0-1.473-1.194-2.667-2.667-2.667v0h-2.667c-1.473 0-2.667 1.194-2.667 2.667v0 2.667c0 1.473 1.194 2.667 2.667 2.667v0h2.667c1.473 0 2.667-1.194 2.667-2.667v0zM5.333 24.667v2.667h-2.667v-2.667z"></path> - </symbol> - <symbol id="view-desktop" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30 2.667h-28c-1.105 0-2 0.895-2 2v0 17.333c0 1.105 0.895 2 2 2v0h10v2.667h-0.667c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h9.333c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-0.667v-2.667h10c1.105 0 2-0.895 2-2v0-17.333c0-1.105-0.895-2-2-2v0zM17.333 26.667h-2.667v-2.667h2.667zM29.333 21.333h-26.667v-16h26.667z"></path> - </symbol> - <symbol id="view-grid" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M29.333 32h-9.333c-1.473 0-2.667-1.194-2.667-2.667v0-9.333c0-1.473 1.194-2.667 2.667-2.667v0h9.333c1.473 0 2.667 1.194 2.667 2.667v0 9.333c0 1.473-1.194 2.667-2.667 2.667v0zM20 20v9.333h9.333v-9.333zM12 32h-9.333c-1.473 0-2.667-1.194-2.667-2.667v0-9.333c0-1.473 1.194-2.667 2.667-2.667v0h9.333c1.473 0 2.667 1.194 2.667 2.667v0 9.333c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 20v9.333h9.333v-9.333zM29.333 14.667h-9.333c-1.473 0-2.667-1.194-2.667-2.667v0-9.333c0-1.473 1.194-2.667 2.667-2.667v0h9.333c1.473 0 2.667 1.194 2.667 2.667v0 9.333c0 1.473-1.194 2.667-2.667 2.667v0zM20 2.667v9.333h9.333v-9.333zM12 14.667h-9.333c-1.473 0-2.667-1.194-2.667-2.667v0-9.333c0-1.473 1.194-2.667 2.667-2.667v0h9.333c1.473 0 2.667 1.194 2.667 2.667v0 9.333c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 2.667v9.333h9.333v-9.333z"></path> - </symbol> - <symbol id="view-hide" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M31.783 16.733c-3.577 5.443-8.987 11.267-15.753 11.267-1.622-0.010-3.168-0.323-4.59-0.883l0.086 0.030c-0.441-0.224-0.737-0.674-0.737-1.193 0-0.736 0.597-1.333 1.333-1.333 0.124 0 0.243 0.017 0.357 0.048l-0.009-0.002c5.667 2.14 11.273-0.82 16.683-8.787-1.089-1.447-2.209-2.729-3.414-3.924l-0.002-0.002c-0.261-0.244-0.423-0.59-0.423-0.975 0-0.736 0.597-1.333 1.333-1.333 0.374 0 0.711 0.154 0.953 0.401l0 0c1.477 1.464 2.837 3.041 4.066 4.72l0.077 0.11c0.167 0.22 0.268 0.499 0.268 0.802 0 0.061-0.004 0.12-0.012 0.179l0.001-0.007c0.005 0.043 0.008 0.093 0.008 0.143 0 0.276-0.084 0.532-0.227 0.745l0.003-0.005zM20.45 4.757c-4.89-1.757-9.98-0.423-14.713 3.81-2.142 1.952-3.986 4.178-5.491 6.634l-0.076 0.133c-0.114 0.19-0.181 0.42-0.181 0.665 0 0.061 0.004 0.12 0.012 0.179l-0.001-0.007c-0.008 0.053-0.012 0.115-0.012 0.178 0 0.263 0.078 0.509 0.212 0.714l-0.003-0.005c1.271 1.843 2.659 3.452 4.201 4.898l0.016 0.015c0.239 0.226 0.561 0.365 0.917 0.365 0.737 0 1.335-0.598 1.335-1.335 0-0.382-0.16-0.726-0.417-0.969l-0.001-0.001c-1.243-1.178-2.37-2.452-3.378-3.818l-0.055-0.079c1.537-2.383 8.297-11.89 16.737-8.867 0.13 0.046 0.279 0.073 0.435 0.073 0.736 0 1.333-0.597 1.333-1.333 0-0.569-0.357-1.055-0.859-1.247l-0.009-0.003zM11.333 16.667c-0.026 0.002-0.057 0.003-0.088 0.003s-0.062-0.001-0.093-0.003l0.004 0c-0.662-0.084-1.169-0.645-1.169-1.323 0-0.065 0.005-0.128 0.014-0.191l-0.001 0.007c0.374-2.681 2.465-4.778 5.111-5.156l0.032-0.004c0.057-0.008 0.122-0.013 0.188-0.013 0.744 0 1.347 0.603 1.347 1.347 0 0.677-0.5 1.238-1.151 1.332l-0.007 0.001c-1.477 0.215-2.631 1.365-2.851 2.821l-0.002 0.019c-0.089 0.658-0.647 1.16-1.322 1.16-0.004 0-0.008-0-0.012-0h0.001zM16.837 22c2.591-0.328 4.642-2.288 5.104-4.804l0.006-0.036c0.020-0.12 0.033-0.243 0.047-0.363 0.004-0.039 0.006-0.084 0.006-0.13 0-0.74-0.6-1.34-1.34-1.34-0.694 0-1.265 0.528-1.333 1.204l-0 0.006c0 0.067-0.017 0.13-0.027 0.193-0.274 1.381-1.396 2.435-2.791 2.602l-0.015 0.002c-0.7 0.047-1.249 0.626-1.249 1.333 0 0.738 0.598 1.336 1.336 1.336 0.030 0 0.061-0.001 0.091-0.003l-0.004 0c0.025 0.002 0.055 0.002 0.085 0.002s0.060-0.001 0.089-0.003l-0.004 0zM28.277 3.733c-0.241-0.242-0.575-0.391-0.943-0.391s-0.702 0.149-0.943 0.391v0l-10.283 10.267h-0.107c-1.105 0-2 0.895-2 2v0 0.11l-10.28 10.28c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0l10.283-10.277h0.11c1.105 0 2-0.895 2-2v0-0.11l10.277-10.28c0.242-0.241 0.391-0.575 0.391-0.943s-0.149-0.702-0.391-0.943v0z"></path> - </symbol> - <symbol id="view-list" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M5.333 30h-2.667c-1.473 0-2.667-1.194-2.667-2.667v0-2.667c0-1.473 1.194-2.667 2.667-2.667v0h2.667c1.473 0 2.667 1.194 2.667 2.667v0 2.667c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 24.667v2.667h2.667v-2.667zM5.333 20h-2.667c-1.473 0-2.667-1.194-2.667-2.667v0-2.667c0-1.473 1.194-2.667 2.667-2.667v0h2.667c1.473 0 2.667 1.194 2.667 2.667v0 2.667c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 14.667v2.667h2.667v-2.667zM5.333 10h-2.667c-1.473 0-2.667-1.194-2.667-2.667v0-2.667c0-1.473 1.194-2.667 2.667-2.667v0h2.667c1.473 0 2.667 1.194 2.667 2.667v0 2.667c0 1.473-1.194 2.667-2.667 2.667v0zM2.667 4.667v2.667h2.667v-2.667zM30 30h-18.667c-1.105 0-2-0.895-2-2v0-4c0-1.105 0.895-2 2-2v0h18.667c1.105 0 2 0.895 2 2v0 4c0 1.105-0.895 2-2 2v0zM12 27.333h17.333v-2.667h-17.333zM30 20h-18.667c-1.105 0-2-0.895-2-2v0-4c0-1.105 0.895-2 2-2v0h18.667c1.105 0 2 0.895 2 2v0 4c0 1.105-0.895 2-2 2v0zM12 17.333h17.333v-2.667h-17.333zM30 10h-18.667c-1.105 0-2-0.895-2-2v0-4c0-1.105 0.895-2 2-2v0h18.667c1.105 0 2 0.895 2 2v0 4c0 1.105-0.895 2-2 2v0zM12 7.333h17.333v-2.667h-17.333z"></path> - </symbol> - <symbol id="view-mobile" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M20 4.333h-8c-1.473 0-2.667 1.194-2.667 2.667v0 17.333c0 1.473 1.194 2.667 2.667 2.667v0h8c1.473 0 2.667-1.194 2.667-2.667v0-17.333c0-1.473-1.194-2.667-2.667-2.667v0zM12 11h8v6.667h-8zM20 7v1.333h-8v-1.333zM12 24.333v-4h8v4zM17.333 22.333c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="view-tablet" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M24 3.333h-16c-1.473 0-2.667 1.194-2.667 2.667v0 20c0 1.473 1.194 2.667 2.667 2.667v0h16c1.473 0 2.667-1.194 2.667-2.667v0-20c0-1.473-1.194-2.667-2.667-2.667v0zM24 6v1.333h-16v-1.333zM24 10v9.333h-16v-9.333zM8 26v-4h16v4zM17.333 24c0 0.736-0.597 1.333-1.333 1.333s-1.333-0.597-1.333-1.333c0-0.736 0.597-1.333 1.333-1.333v0c0.736 0 1.333 0.597 1.333 1.333v0z"></path> - </symbol> - <symbol id="view" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M18 15.94c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2v0c1.105 0 2 0.895 2 2v0zM22 15.94c0 3.314-2.686 6-6 6s-6-2.686-6-6c0-3.314 2.686-6 6-6v0c3.314 0 6 2.686 6 6v0zM19.333 15.94c0-1.841-1.492-3.333-3.333-3.333s-3.333 1.492-3.333 3.333c0 1.841 1.492 3.333 3.333 3.333v0c1.841 0 3.333-1.492 3.333-3.333v0zM31.78 15.203c-5.027-7.617-10.447-11.38-16.113-11.203-9.143 0.307-15.237 10.83-15.493 11.28-0.11 0.192-0.174 0.422-0.174 0.667 0 0.019 0 0.037 0.001 0.056l-0-0.003c-0.001 0.018-0.001 0.039-0.001 0.060 0 0.243 0.065 0.47 0.178 0.666l-0.003-0.006c1.252 2.066 2.642 3.856 4.21 5.471l-0.007-0.007c3.557 3.677 7.463 5.683 11.29 5.817h0.4c5.52 0 10.807-3.767 15.713-11.203 0.138-0.228 0.22-0.503 0.22-0.797s-0.082-0.569-0.224-0.804l0.004 0.007zM15.763 25.333c-6.523-0.21-11.533-7.213-12.9-9.333 1.367-2.117 6.377-9.12 12.9-9.333 4.507-0.15 9 3 13.333 9.333-4.333 6.333-8.82 9.477-13.333 9.333z"></path> - </symbol> - <symbol id="wand" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M25.833 7.333c-0.217-0.001-0.423-0.053-0.605-0.144l0.008 0.004c-0.441-0.224-0.738-0.674-0.738-1.193 0-0.218 0.052-0.423 0.145-0.605l-0.003 0.008 2-4c0.224-0.441 0.674-0.738 1.193-0.738 0.737 0 1.334 0.597 1.334 1.334 0 0.217-0.052 0.423-0.144 0.604l0.003-0.008-2 4c-0.224 0.44-0.674 0.737-1.192 0.737-0 0-0.001 0-0.001 0h0zM31.43 14.423c0.044-0.126 0.069-0.271 0.069-0.423 0-0.584-0.376-1.081-0.899-1.261l-0.009-0.003-4-1.333c-0.112-0.034-0.242-0.054-0.375-0.054-0.736 0-1.333 0.597-1.333 1.333 0 0.567 0.354 1.051 0.853 1.244l0.009 0.003 4 1.333c0.126 0.044 0.271 0.069 0.423 0.069 0.584 0 1.081-0.376 1.261-0.899l0.003-0.009zM18.293 10c0.027-0.101 0.043-0.216 0.043-0.335 0-0.619-0.422-1.139-0.993-1.29l-0.009-0.002-5.333-1.333c-0.1-0.027-0.215-0.042-0.333-0.042-0.738 0-1.336 0.598-1.336 1.336 0 0.619 0.421 1.14 0.993 1.291l0.009 0.002 5.333 1.333c0.098 0.025 0.211 0.040 0.328 0.040 0.002 0 0.004 0 0.006 0h-0c0.001 0 0.002 0 0.002 0 0.618 0 1.138-0.42 1.289-0.991l0.002-0.009zM23.823 22.96c0.586-0.149 1.012-0.672 1.012-1.294 0-0.118-0.015-0.233-0.044-0.342l0.002 0.009-1.333-5.333c-0.153-0.581-0.674-1.002-1.293-1.002-0.738 0-1.336 0.598-1.336 1.336 0 0.118 0.015 0.233 0.044 0.343l-0.002-0.009 1.333 5.333c0.153 0.58 0.673 1 1.291 1 0.001 0 0.002 0 0.002 0h-0c0.115-0 0.226-0.015 0.333-0.042l-0.009 0.002zM20.49 7.96c0.586-0.149 1.012-0.672 1.012-1.294 0-0.118-0.015-0.233-0.044-0.342l0.002 0.009-1.333-5.333c-0.153-0.581-0.674-1.002-1.293-1.002-0.738 0-1.336 0.598-1.336 1.336 0 0.118 0.015 0.233 0.044 0.343l-0.002-0.009 1.333 5.333c0.153 0.58 0.673 1 1.291 1 0.001 0 0.002 0 0.002 0h-0c0.115-0 0.226-0.015 0.333-0.042l-0.009 0.002zM18.11 16.293l5.333-5.333c0.241-0.241 0.391-0.575 0.391-0.943 0-0.737-0.597-1.334-1.334-1.334-0.368 0-0.702 0.149-0.943 0.391v0l-5.333 5.333c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0zM2.777 31.627l12.667-12.667c0.241-0.241 0.391-0.575 0.391-0.943 0-0.737-0.597-1.334-1.334-1.334-0.368 0-0.702 0.149-0.943 0.391l-12.667 12.667c-0.241 0.241-0.391 0.575-0.391 0.943 0 0.737 0.597 1.334 1.334 1.334 0.368 0 0.702-0.149 0.943-0.391v0z"></path> - </symbol> - <symbol id="warning" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M16 17c-0.736 0-1.333-0.597-1.333-1.333v0-8c0-0.736 0.597-1.333 1.333-1.333s1.333 0.597 1.333 1.333v0 8c0 0.736-0.597 1.333-1.333 1.333v0zM16 19c-1.841 0-3.333 1.492-3.333 3.333s1.492 3.333 3.333 3.333c1.841 0 3.333-1.492 3.333-3.333v0c0-1.841-1.492-3.333-3.333-3.333v0zM31.197 28.607c0.5-0.832 0.796-1.835 0.796-2.908 0-0.857-0.189-1.671-0.527-2.4l0.015 0.035c-0.011-0.017-0.022-0.036-0.032-0.057l-0.001-0.003-10.78-18.813c-0.905-1.676-2.648-2.796-4.653-2.796s-3.749 1.12-4.64 2.768l-0.014 0.028-10.803 18.797c0 0.020-0.023 0.037-0.033 0.060-0.33 0.698-0.523 1.515-0.523 2.378 0 1.074 0.299 2.078 0.817 2.934l-0.014-0.025c0.667 1.1 1.69 1.73 2.79 1.73h24.813c1.097 0 2.113-0.63 2.79-1.727zM13.667 5.79c0.437-0.863 1.317-1.445 2.333-1.445s1.896 0.581 2.326 1.43l0.007 0.015 10.78 18.76c0.14 0.337 0.221 0.728 0.221 1.139 0 0.563-0.153 1.090-0.419 1.542l0.008-0.014c-0.17 0.277-0.373 0.457-0.517 0.457h-24.813c-0.143 0-0.333-0.18-0.517-0.457-0.258-0.437-0.41-0.962-0.41-1.524 0-0.412 0.082-0.805 0.231-1.163l-0.007 0.020z"></path> - </symbol> - <symbol id="wiki-file" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.080 24h-1.187l-0.667-2.45c-0.027-0.090-0.067-0.27-0.127-0.543s-0.1-0.457-0.1-0.55c0 0.117-0.047 0.3-0.1 0.553s-0.097 0.437-0.127 0.543l-0.667 2.447h-1.183l-1.257-4.667h1.027l0.64 2.547c0.11 0.47 0.19 0.877 0.24 1.223 0-0.12 0.043-0.31 0.090-0.563s0.097-0.453 0.137-0.593l0.71-2.613h0.983l0.72 2.613c0.030 0.117 0.070 0.297 0.117 0.537s0.083 0.447 0.107 0.62q0.037-0.25 0.11-0.62c0.047-0.25 0.093-0.45 0.133-0.603l0.627-2.547h1.027zM16 19.177c0-0.333 0.223-0.51 0.667-0.51s0.667 0.17 0.667 0.51c0 0.007 0.001 0.015 0.001 0.024 0 0.143-0.065 0.271-0.167 0.356l-0.001 0.001c-0.123 0.085-0.275 0.136-0.44 0.136-0.021 0-0.042-0.001-0.063-0.003l0.003 0c-0.443 0-0.667-0.17-0.667-0.513zM17.277 24h-1.223v-3.833h1.223zM18.88 21.917l0.4-0.583 0.94-1.167h1.033l-1.333 1.667 1.413 2.167h-1.057l-0.967-1.553-0.393 0.36v1.193h-0.917v-5.333h0.917v2.38l-0.050 0.87zM22 19.177c0-0.333 0.223-0.51 0.667-0.51s0.667 0.17 0.667 0.51c0 0.007 0.001 0.015 0.001 0.024 0 0.143-0.065 0.271-0.167 0.356l-0.001 0.001c-0.123 0.085-0.275 0.136-0.44 0.136-0.021 0-0.042-0.001-0.063-0.003l0.003 0c-0.443 0-0.667-0.17-0.667-0.513zM23.277 24h-1.223v-3.833h1.223zM27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16z"></path> - </symbol> - <symbol id="wiki" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M14.080 16.667h-1.187l-0.667-2.45c-0.027-0.090-0.067-0.27-0.127-0.543s-0.1-0.457-0.1-0.55c0 0.117-0.047 0.3-0.1 0.553s-0.097 0.437-0.127 0.543l-0.667 2.447h-1.183l-1.257-4.667h1.027l0.64 2.547c0.11 0.47 0.19 0.877 0.24 1.223 0-0.12 0.043-0.31 0.090-0.563s0.097-0.453 0.137-0.593l0.71-2.613h0.983l0.72 2.613c0.030 0.117 0.070 0.297 0.117 0.537s0.083 0.447 0.107 0.62q0.037-0.25 0.11-0.62c0.047-0.25 0.093-0.45 0.133-0.603l0.627-2.547h1.027zM16 11.843c0-0.333 0.223-0.51 0.667-0.51s0.667 0.17 0.667 0.51c0 0.007 0.001 0.015 0.001 0.024 0 0.143-0.065 0.271-0.167 0.356l-0.001 0.001c-0.123 0.085-0.275 0.136-0.44 0.136-0.021 0-0.042-0.001-0.063-0.003l0.003 0c-0.443 0-0.667-0.17-0.667-0.513zM17.277 16.667h-1.223v-3.833h1.223zM18.88 14.583l0.4-0.583 0.94-1.167h1.033l-1.333 1.667 1.413 2.167h-1.057l-0.967-1.553-0.393 0.36v1.193h-0.917v-5.333h0.917v2.38l-0.050 0.87zM22 11.843c0-0.333 0.223-0.51 0.667-0.51s0.667 0.17 0.667 0.51c0 0.007 0.001 0.015 0.001 0.024 0 0.143-0.065 0.271-0.167 0.356l-0.001 0.001c-0.123 0.085-0.275 0.136-0.44 0.136-0.021 0-0.042-0.001-0.063-0.003l0.003 0c-0.443 0-0.667-0.17-0.667-0.513zM23.277 16.667h-1.223v-3.833h1.223zM30.667 21.333h-1.333v-14.747c0-0.009 0-0.020 0-0.030 0-1.4-1.126-2.538-2.522-2.556l-0.002-0h-21.393c-1.524 0.024-2.75 1.265-2.75 2.793 0 0.013 0 0.026 0 0.039l-0-0.002v14.503h-1.333c-0.736 0-1.333 0.597-1.333 1.333v0 1.030c0.002 2.376 1.927 4.301 4.303 4.303h23.697c2.209 0 4-1.791 4-4v0-1.333c0-0.736-0.597-1.333-1.333-1.333v0zM5.333 6.83c-0.001-0.005-0.001-0.011-0.001-0.017 0-0.062 0.033-0.117 0.083-0.146l0.001-0h21.25v14.667h-21.333zM29.333 24c0 0.736-0.597 1.333-1.333 1.333v0h-23.697c-0.796-0.002-1.459-0.57-1.608-1.323l-0.002-0.010z"></path> - </symbol> - <symbol id="workflow" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M8.667 15.667v1c0 .736-.597 1.333-1.333 1.333h-3a1.335 1.335 0 0 1-.143-2.666h.003c-1.527-1.317-2.193-3.107-2.193-5.667 0-3.203 1.643-5.69 4.75-7.2a1.334 1.334 0 0 1 1.174 2.397l-.008.003c-2.187 1.06-3.25 2.63-3.25 4.8 0 1.817.35 2.913 1.36 3.747a1.333 1.333 0 0 1 2.64.253v2.001zM27.807 27C29.334 25.683 30 23.893 30 21.333c0-3.203-1.643-5.69-4.75-7.2a1.334 1.334 0 0 0-1.174 2.397l.008.003c2.187 1.06 3.25 2.63 3.25 4.8 0 1.817-.333 2.913-1.36 3.747a1.333 1.333 0 0 0-2.64.253v3.001c0 .736.597 1.333 1.333 1.333h3l.07.002a1.335 1.335 0 0 0 .073-2.668h-.003zM16 22.667a4.667 4.667 0 1 0 4.667 4.667A4.667 4.667 0 0 0 16 22.667zm4.667-4v-5.333a2 2 0 0 0-2-2h-5.333a2 2 0 0 0-2 2v5.333a2 2 0 0 0 2 2h5.333a2 2 0 0 0 2-2zM14 14h4v4h-4zm6.667-9.333A4.667 4.667 0 1 0 16 9.334a4.667 4.667 0 0 0 4.667-4.667zm-2.667 0a2 2 0 1 1-2-2 2 2 0 0 1 2 2z"></path> - </symbol> - <symbol id="object-state" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> - <path d="M30.667 32h-6.667c-0 0-0.001 0-0.001 0-0.736 0-1.333-0.597-1.333-1.333 0-0.218 0.052-0.423 0.144-0.604l-0.003 0.008 3.333-6.667c0.224-0.441 0.674-0.739 1.193-0.739s0.97 0.297 1.19 0.731l0.003 0.008 3.333 6.667c0.089 0.174 0.141 0.379 0.141 0.597 0 0.736-0.597 1.333-1.333 1.333-0 0-0.001 0-0.001 0h0zM26.157 29.333h2.353l-1.177-2.353zM4.667 32c-2.577 0-4.667-2.089-4.667-4.667s2.089-4.667 4.667-4.667c2.577 0 4.667 2.089 4.667 4.667v0c0 2.577-2.089 4.667-4.667 4.667v0zM4.667 25.333c-1.105 0-2 0.895-2 2s0.895 2 2 2c1.105 0 2-0.895 2-2v0c0-1.105-0.895-2-2-2v0zM18.667 32h-5.333c-1.105 0-2-0.895-2-2v0-5.333c0-1.105 0.895-2 2-2v0h5.333c1.105 0 2 0.895 2 2v0 5.333c0 1.105-0.895 2-2 2v0zM14 29.333h4v-4h-4zM27.333 13.333h-10v-3.333c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 3.333h-10c-0.736 0-1.333 0.597-1.333 1.333v0 5.333c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4h8.667v4c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-4h8.667v4c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-5.333c0-0.736-0.597-1.333-1.333-1.333v0zM21.333 7.333h-10.667c-0.736 0-1.333-0.597-1.333-1.333v0-4.667c0-0.736 0.597-1.333 1.333-1.333v0h10.667c0.736 0 1.333 0.597 1.333 1.333v0 4.667c0 0.736-0.597 1.333-1.333 1.333v0zM12 4.667h8v-2h-8z"></path> - </symbol> -</svg> diff --git a/docs/guidelines/img/UIG_Component_Modal_correct.png b/docs/guidelines/img/UIG_Component_Modal_correct.png deleted file mode 100644 index 53404a0b56..0000000000 Binary files a/docs/guidelines/img/UIG_Component_Modal_correct.png and /dev/null differ diff --git a/docs/guidelines/img/UIG_Component_Modal_wrong.png b/docs/guidelines/img/UIG_Component_Modal_wrong.png deleted file mode 100644 index 8883363d3b..0000000000 Binary files a/docs/guidelines/img/UIG_Component_Modal_wrong.png and /dev/null differ diff --git a/docs/guidelines/resources/colors.md b/docs/guidelines/resources/colors.md deleted file mode 100644 index 2937598146..0000000000 --- a/docs/guidelines/resources/colors.md +++ /dev/null @@ -1,190 +0,0 @@ -# Colors - -**Visual hierarchy and differentiation**. We prioritize Create and Edit content as main functions in our CMS with orange color. Because most of the application has soft colors, color stands out and catches user's attention. We also define secondary, neutral and negative colors. Despite of this, it is also important to remind to not always rely on color to provide visual differentiation. If too many colors are employed, colors might lose their meaning. - -##<div class="mgt-2 header-line">Primary colors</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="color-box"> - <div class="color-frame ez-color-primary"> - <p class="color-code"><code>#f15a10</code></p> - </div> - <p class="color-label">ez-color-primary</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-secondary"> - <p class="color-code"><code>#0f6d95</code></p> - </div> - <p class="color-label">ez-color-secondary</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-hyperlink"> - <p class="color-code"><code>#0645ad</code></p> - </div> - <p class="color-label">ez-color-hyperlink</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-positive"> - <p class="color-code"><code>#00825c</code></p> - </div> - <p class="color-label">ez-color-positive</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-warning"> - <p class="color-code"><code>#f7d000</code></p> - </div> - <p class="color-label">ez-color-warning</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-danger"> - <p class="color-code"><code>#d92d42</code></p> - </div> - <p class="color-label">ez-color-danger</p> - </div> - <div class="color-box"> - <div class="color-frame color-frame-light ez-color-white"> - <p class="color-code"><code>#fff</code></p> - </div> - <p class="color-label">ez-white</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-black"> - <p class="color-code"><code>#333</code></p> - </div> - <p class="color-label">ez-black</p> - </div> -</div> - -##<div class="mgt-2 header-line">Ground Base colors</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="color-box"> - <div class="color-frame color-frame-light ez-ground-base-pale"> - <p class="color-code"><code>#fafafa</code></p> - </div> - <p class="color-label">ez-ground-base-pale</p> - </div> - <div class="color-box"> - <div class="color-frame color-frame-light ez-ground-base-light"> - <p class="color-code"><code>#f5f5f5</code></p> - </div> - <p class="color-label">ez-ground-base-light</p> - </div> - <div class="color-box"> - <div class="color-frame color-frame-light ez-ground-base"> - <p class="color-code"><code>#f3f3f3</code></p> - </div> - <p class="color-label">ez-ground-base</p> - </div> - <div class="color-box"> - <div class="color-frame ez-ground-base-medium"> - <p class="color-code"><code>#ededed</code></p> - </div> - <p class="color-label">ez-ground-base-medium</p> - </div> - <div class="color-box"> - <div class="color-frame ez-ground-base-dark"> - <p class="color-code"><code>#e5e3e3</code></p> - </div> - <p class="color-label">ez-ground-base-dark</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-base-pale"> - <p class="color-code"><code>#dbdbdb</code></p> - </div> - <p class="color-label">ez-color-base-pale</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-base-light"> - <p class="color-code"><code>#878787</code></p> - </div> - <p class="color-label">ez-color-base-light</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-base-medium"> - <p class="color-code"><code>#646464</code></p> - </div> - <p class="color-label">ez-color-base-medium</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-base-dark"> - <p class="color-code"><code>#555</code></p> - </div> - <p class="color-label">ez-color-base-dark</p> - </div> -</div> - -##<div class="mgt-2 header-line">Ground Secondary colors</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="color-box"> - <div class="color-frame color-frame-light ez-secondary-gound-pale"> - <p class="color-code"><code>#e1f5ff</code></p> - </div> - <p class="color-label">ez-secondary-gound-pale</p> - </div> - <div class="color-box"> - <div class="color-frame ez-ground-primary"> - <p class="color-code"><code>#a8c8d5</code></p> - </div> - <p class="color-label">ez-ground-primary</p> - </div> - <div class="color-box"> - <div class="color-frame ez-secondary-ground"> - <p class="color-code"><code>#2b84b1</code></p> - </div> - <p class="color-label">ez-secondary-ground</p> - </div> - <div class="color-box"> - <div class="color-frame ez-secondary-ground-dark"> - <p class="color-code"><code>#0a5574</code></p> - </div> - <p class="color-label">ez-secondary-ground-dark</p> - </div> - <div class="color-box"> - <div class="color-frame ez-ground-primary-inverted"> - <p class="color-code"><code>#565e63</code></p> - </div> - <p class="color-label">ez-ground-primary-inverted</p> - </div> - <div class="color-box"> - <div class="color-frame ez-ground-primary-inverted-light"> - <p class="color-code"><code>#445a64</code></p> - </div> - <p class="color-label">ez-ground-primary-inverted-light</p> - </div> -</div> - -##<div class="mgt-2 header-line">Interaction colors</div> -<div class="wrapper-samples mgt-minus-3 mgb-5"> - <div class="color-box"> - <div class="color-frame color-frame-light ez-color-warning-pale"> - <p class="color-code"><code>#fceaec</code></p> - </div> - <p class="color-label">ez-color-warning-pale</p> - </div> - <div class="color-box"> - <div class="color-frame ez-color-warning-dark"> - <p class="color-code"><code>#aa0000</code></p> - </div> - <p class="color-label">ez-color-warning-dark</p> - </div> - <div class="color-box"> - <div class="color-frame ez-alloyeditor-color-hover"> - <p class="color-code"><code>#65b6f0</code></p> - </div> - <p class="color-label">ez-alloyeditor-color-hover</p> - </div> -</div> - -!!! note - - Hover states are based on SASS color functions. We are applying a 15% dark on the defined color variable. - ``` scss - $ez-color-hyperlink-hover: darken($ez-color-hyperlink, 15%); - ``` - Hover color variables using this approach are the ones related to interaction buttons or links: - ``` scss - $ez-color-primary-hover: darken($ez-color-primary, 15%); - $ez-color-secondary-hover: darken($ez-color-secondary, 15%); - $ez-color-hyperlink-hover: darken($ez-color-hyperlink, 15%); - $ez-color-danger-hover: darken($ez-color-danger, 15%); - $ez-color-base-medium-hover: darken($ez-color-base-medium, 15%); - ``` diff --git a/docs/guidelines/resources/icons.md b/docs/guidelines/resources/icons.md deleted file mode 100644 index d0f0673543..0000000000 --- a/docs/guidelines/resources/icons.md +++ /dev/null @@ -1,1519 +0,0 @@ -# Icons - -We provide visual context with icons, we want to emphasize the most important interactions. Our objective is to enhance usability. - -##<div class="mgt-2 header-line">Use</div> -<div class="mgt-minus-4 mgb-5">When using icons add this code below, specify the selected icon `svg identifier` and if needed customize your CSS accordingly:</div> - - -[[code_example {html} -<svg class="ez-icon"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Small icons</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--small` to modify an icon to its smallest size.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--small"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Small-Medium icons</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--small-medium` to modify an icon to its second smallest size.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--small-medium"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Medium icons</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--medium` to modify an icon to its medium size.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--medium"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Large icons</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--large` to modify an icon to its large size.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--large"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Extra Large icons</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--extra-large` to modify an icon to its largest size.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--extra-large"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -###<div class="mgt-2">Colored icons - dark</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--dark` to modify the color fill of an icon to the Sass variable defined for white, `$ez-black`.</div> -<div class="ez-guidelines-icons__colored"> -[[code_example {html} -<svg class="ez-icon ez-icon--medium ez-icon--dark ez-icon-content-draft"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#content-draft"></use> -</svg> -</button> -code_example]] -</div> - -###<div class="mgt-2">Colored icons - white</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--light` to modify the color fill of an icon to the Sass variable defined for white, `$ez-white`.</div> -<div class="ez-guidelines-icons__colored"> -[[code_example {html} -<button type="button" class="btn btn-primary"> - <svg class="ez-icon ez-icon--medium ez-icon--light ez-icon-create"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#create"></use> - </svg> - <span>Create</span> -</button> -code_example]] -</div> - -!!! note - We have used here a button as an example. If you want to dig more into buttons check [Buttons](../components/buttons.md) component. - -###<div class="mgt-2">Colored icons - secondary</div> -<div class="mgt-minus-3 mgb-5">Add class `ez-icon--secondary` to modify the color fill of an icon to the Sass variable defined for secondary color, `$ez-color-secondary`.</div> -[[code_example {html} -<svg class="ez-icon ez-icon--secondary"> - <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="../../ez-icons.svg#publish"></use> -</svg> -code_example]] - -##<div class="mgt-5">Icon set</div> -<div class="mgt-minus-4">Here you can find all SVG icons our CMS uses classified by categories:</div> - -###<div class="mgt-2 header-line">General</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="icon-box"> - <svg class="ez-icon ez-icon-about-info"> - <use xlink:href="../../ez-icons.svg#about-info"></use> - </svg> - <p class="icon-label">about-info</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-about"> - <use xlink:href="../../ez-icons.svg#about"></use> - </svg> - <p class="icon-label">about</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-airtime"> - <use xlink:href="../../ez-icons.svg#airtime"></use> - </svg> - <p class="icon-label">airtime</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-approved"> - <use xlink:href="../../ez-icons.svg#approved"></use> - </svg> - <p class="icon-label">approved</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-archive-restore"> - <use xlink:href="../../ez-icons.svg#archive-restore"></use> - </svg> - <p class="icon-label">archive-restore</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-assign-section"> - <use xlink:href="../../ez-icons.svg#assign-section"></use> - </svg> - <p class="icon-label">assign-section</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-assign-user"> - <use xlink:href="../../ez-icons.svg#assign-user"></use> - </svg> - <p class="icon-label">assign-user</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-author"> - <use xlink:href="../../ez-icons.svg#author"></use> - </svg> - <p class="icon-label">author</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-back"> - <use xlink:href="../../ez-icons.svg#back"></use> - </svg> - <p class="icon-label">back</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-back-current-date"> - <use xlink:href="../../ez-icons.svg#back-current-date"></use> - </svg> - <p class="icon-label">back-current-date</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-banner"> - <use xlink:href="../../ez-icons.svg#banner"></use> - </svg> - <p class="icon-label">banner</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-block-add"> - <use xlink:href="../../ez-icons.svg#block-add"></use> - </svg> - <p class="icon-label">block-add</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-block-invisible"> - <use xlink:href="../../ez-icons.svg#block-invisible"></use> - </svg> - <p class="icon-label">block-invisible</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-block-visible-recurring"> - <use xlink:href="../../ez-icons.svg#block-visible-recurring"></use> - </svg> - <p class="icon-label">block-visible-recurring</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-block-visible"> - <use xlink:href="../../ez-icons.svg#block-visible"></use> - </svg> - <p class="icon-label">block-visible</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bookmark-active"> - <use xlink:href="../../ez-icons.svg#bookmark-active"></use> - </svg> - <p class="icon-label">bookmark-active</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bookmark-manager"> - <use xlink:href="../../ez-icons.svg#bookmark-manager"></use> - </svg> - <p class="icon-label">bookmark-manager</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bookmark"> - <use xlink:href="../../ez-icons.svg#bookmark"></use> - </svg> - <p class="icon-label">bookmark</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-browse"> - <use xlink:href="../../ez-icons.svg#browse"></use> - </svg> - <p class="icon-label">browse</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bubbles"> - <use xlink:href="../../ez-icons.svg#bubbles"></use> - </svg> - <p class="icon-label">bubbles</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-caret-back"> - <use xlink:href="../../ez-icons.svg#caret-back"></use> - </svg> - <p class="icon-label">caret-back</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-caret-down"> - <use xlink:href="../../ez-icons.svg#caret-down"></use> - </svg> - <p class="icon-label">caret-down</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-caret-next"> - <use xlink:href="../../ez-icons.svg#caret-next"></use> - </svg> - <p class="icon-label">caret-next</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-caret-up"> - <use xlink:href="../../ez-icons.svg#caret-up"></use> - </svg> - <p class="icon-label">caret-up</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-caret-expanded"> - <use xlink:href="../../ez-icons.svg#caret-expanded"></use> - </svg> - <p class="icon-label">caret-expanded</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-category"> - <use xlink:href="../../ez-icons.svg#category"></use> - </svg> - <p class="icon-label">category</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-checkmark"> - <use xlink:href="../../ez-icons.svg#checkmark"></use> - </svg> - <p class="icon-label">checkmark</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-caret-down"> - <use xlink:href="../../ez-icons.svg#circle-caret-down"></use> - </svg> - <p class="icon-label">circle-caret-down</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-caret-left"> - <use xlink:href="../../ez-icons.svg#circle-caret-left"></use> - </svg> - <p class="icon-label">circle-caret-left</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-caret-right"> - <use xlink:href="../../ez-icons.svg#circle-caret-right"></use> - </svg> - <p class="icon-label">circle-caret-right</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-caret-up"> - <use xlink:href="../../ez-icons.svg#circle-caret-up"></use> - </svg> - <p class="icon-label">circle-caret-up</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-close"> - <use xlink:href="../../ez-icons.svg#circle-close"></use> - </svg> - <p class="icon-label">circle-close</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-circle-create"> - <use xlink:href="../../ez-icons.svg#circle-create"></use> - </svg> - <p class="icon-label">circle-create</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-clipboard"> - <use xlink:href="../../ez-icons.svg#clipboard"></use> - </svg> - <p class="icon-label">clipboard</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-column-one"> - <use xlink:href="../../ez-icons.svg#column-one"></use> - </svg> - <p class="icon-label">column-one</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-column-two"> - <use xlink:href="../../ez-icons.svg#column-two"></use> - </svg> - <p class="icon-label">column-two</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-comment"> - <use xlink:href="../../ez-icons.svg#comment"></use> - </svg> - <p class="icon-label">comment</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-content-draft"> - <use xlink:href="../../ez-icons.svg#content-draft"></use> - </svg> - <p class="icon-label">content-draft</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-content-tree"> - <use xlink:href="../../ez-icons.svg#content-tree"></use> - </svg> - <p class="icon-label">content-tree</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-content-type"> - <use xlink:href="../../ez-icons.svg#content-type"></use> - </svg> - <p class="icon-label">content-type</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-content-type-group"> - <use xlink:href="../../ez-icons.svg#content-type-group"></use> - </svg> - <p class="icon-label">content-type-group</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-copy"> - <use xlink:href="../../ez-icons.svg#copy"></use> - </svg> - <p class="icon-label">copy</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-copy-subtree"> - <use xlink:href="../../ez-icons.svg#copy-subtree"></use> - </svg> - <p class="icon-label">copy-subtree</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-create-content"> - <use xlink:href="../../ez-icons.svg#create-content"></use> - </svg> - <p class="icon-label">create-content</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-create"> - <use xlink:href="../../ez-icons.svg#create"></use> - </svg> - <p class="icon-label">create</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-dashboard"> - <use xlink:href="../../ez-icons.svg#dashboard"></use> - </svg> - <p class="icon-label">dashboard</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-discard"> - <use xlink:href="../../ez-icons.svg#discard"></use> - </svg> - <p class="icon-label">discard</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-download"> - <use xlink:href="../../ez-icons.svg#download"></use> - </svg> - <p class="icon-label">download</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-drag"> - <use xlink:href="../../ez-icons.svg#drag"></use> - </svg> - <p class="icon-label">drag</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-edit"> - <use xlink:href="../../ez-icons.svg#edit"></use> - </svg> - <p class="icon-label">edit</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-embed"> - <use xlink:href="../../ez-icons.svg#embed"></use> - </svg> - <p class="icon-label">embed</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-error"> - <use xlink:href="../../ez-icons.svg#error"></use> - </svg> - <p class="icon-label">error</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-fields"> - <use xlink:href="../../ez-icons.svg#fields"></use> - </svg> - <p class="icon-label">fields</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-file-text"> - <use xlink:href="../../ez-icons.svg#file-text"></use> - </svg> - <p class="icon-label">file-text</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-file-video"> - <use xlink:href="../../ez-icons.svg#file-video"></use> - </svg> - <p class="icon-label">file-video</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-file"> - <use xlink:href="../../ez-icons.svg#file"></use> - </svg> - <p class="icon-label">file</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-filters"> - <use xlink:href="../../ez-icons.svg#filters"></use> - </svg> - <p class="icon-label">filters</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-filters-funnel"> - <use xlink:href="../../ez-icons.svg#filters-funnel"></use> - </svg> - <p class="icon-label">filters-funnel</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-focus"> - <use xlink:href="../../ez-icons.svg#focus"></use> - </svg> - <p class="icon-label">focus</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-folder-empty"> - <use xlink:href="../../ez-icons.svg#folder-empty"></use> - </svg> - <p class="icon-label">folder-empty</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-folder"> - <use xlink:href="../../ez-icons.svg#folder"></use> - </svg> - <p class="icon-label">folder</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-form-data"> - <use xlink:href="../../ez-icons.svg#form-data"></use> - </svg> - <p class="icon-label">form-data</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-form"> - <use xlink:href="../../ez-icons.svg#form"></use> - </svg> - <p class="icon-label">form</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-future-publication"> - <use xlink:href="../../ez-icons.svg#future-publication"></use> - </svg> - <p class="icon-label">future-publication</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-go-to-root"> - <use xlink:href="../../ez-icons.svg#go-to-root"></use> - </svg> - <p class="icon-label">go-to-root</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-go-up"> - <use xlink:href="../../ez-icons.svg#go-up"></use> - </svg> - <p class="icon-label">go-up</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-history"> - <use xlink:href="../../ez-icons.svg#history"></use> - </svg> - <p class="icon-label">history</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-home-page"> - <use xlink:href="../../ez-icons.svg#home-page"></use> - </svg> - <p class="icon-label">home-page</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-information"> - <use xlink:href="../../ez-icons.svg#information"></use> - </svg> - <p class="icon-label">information</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-keyword"> - <use xlink:href="../../ez-icons.svg#keyword"></use> - </svg> - <p class="icon-label">keyword</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-landing_page"> - <use xlink:href="../../ez-icons.svg#landing_page"></use> - </svg> - <p class="icon-label">landing_page</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-landingpage-add"> - <use xlink:href="../../ez-icons.svg#landingpage-add"></use> - </svg> - <p class="icon-label">landingpage-add</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-landingpage-preview"> - <use xlink:href="../../ez-icons.svg#landingpage-preview"></use> - </svg> - <p class="icon-label">landingpage-preview</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-languages-add"> - <use xlink:href="../../ez-icons.svg#languages-add"></use> - </svg> - <p class="icon-label">languages-add</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-languages"> - <use xlink:href="../../ez-icons.svg#languages"></use> - </svg> - <p class="icon-label">languages</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-layout-switch"> - <use xlink:href="../../ez-icons.svg#layout-switch"></use> - </svg> - <p class="icon-label">layout-switch</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-location-add-new"> - <use xlink:href="../../ez-icons.svg#location-add-new"></use> - </svg> - <p class="icon-label">location-add-new</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-lock-unlock"> - <use xlink:href="../../ez-icons.svg#lock-unlock"></use> - </svg> - <p class="icon-label">lock-unlock</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-lock"> - <use xlink:href="../../ez-icons.svg#lock"></use> - </svg> - <p class="icon-label">lock</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-logout"> - <use xlink:href="../../ez-icons.svg#logout"></use> - </svg> - <p class="icon-label">logout</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-maform"> - <use xlink:href="../../ez-icons.svg#maform"></use> - </svg> - <p class="icon-label">maform</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-merge"> - <use xlink:href="../../ez-icons.svg#merge"></use> - </svg> - <p class="icon-label">merge</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-move"> - <use xlink:href="../../ez-icons.svg#move"></use> - </svg> - <p class="icon-label">move</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-notice"> - <use xlink:href="../../ez-icons.svg#notice"></use> - </svg> - <p class="icon-label">notice</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-object-state"> - <use xlink:href="../../ez-icons.svg#object-state"></use> - </svg> - <p class="icon-label">object-state</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-open-newtab"> - <use xlink:href="../../ez-icons.svg#open-newtab"></use> - </svg> - <p class="icon-label">open-newtab</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-open-sametab"> - <use xlink:href="../../ez-icons.svg#open-sametab"></use> - </svg> - <p class="icon-label">open-sametab</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-options"> - <use xlink:href="../../ez-icons.svg#options"></use> - </svg> - <p class="icon-label">options</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-panels"> - <use xlink:href="../../ez-icons.svg#panels"></use> - </svg> - <p class="icon-label">panels</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-pdf-file"> - <use xlink:href="../../ez-icons.svg#pdf-file"></use> - </svg> - <p class="icon-label">pdf-file</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-personalize-block"> - <use xlink:href="../../ez-icons.svg#personalize-block"></use> - </svg> - <p class="icon-label">personalize-block</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-personalize-content"> - <use xlink:href="../../ez-icons.svg#personalize-content"></use> - </svg> - <p class="icon-label">personalize-content</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-personalize"> - <use xlink:href="../../ez-icons.svg#personalize"></use> - </svg> - <p class="icon-label">personalize</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-pin-unpin"> - <use xlink:href="../../ez-icons.svg#pin-unpin"></use> - </svg> - <p class="icon-label">pin-unpin</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-pin"> - <use xlink:href="../../ez-icons.svg#pin"></use> - </svg> - <p class="icon-label">pin</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-portfolio"> - <use xlink:href="../../ez-icons.svg#portfolio"></use> - </svg> - <p class="icon-label">portfolio</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-previewed"> - <use xlink:href="../../ez-icons.svg#previewed"></use> - </svg> - <p class="icon-label">previewed</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-profile"> - <use xlink:href="../../ez-icons.svg#profile"></use> - </svg> - <p class="icon-label">profile</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-publish-later-cancel"> - <use xlink:href="../../ez-icons.svg#publish-later-cancel"></use> - </svg> - <p class="icon-label">publish-later-cancel</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-publish-later"> - <use xlink:href="../../ez-icons.svg#publish-later"></use> - </svg> - <p class="icon-label">publish-later</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-publish"> - <use xlink:href="../../ez-icons.svg#publish"></use> - </svg> - <p class="icon-label">publish</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-refresh"> - <use xlink:href="../../ez-icons.svg#refresh"></use> - </svg> - <p class="icon-label">refresh</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-rejected"> - <use xlink:href="../../ez-icons.svg#rejected"></use> - </svg> - <p class="icon-label">rejected</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-relations"> - <use xlink:href="../../ez-icons.svg#relations"></use> - </svg> - <p class="icon-label">relations</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-restore-parent"> - <use xlink:href="../../ez-icons.svg#restore-parent"></use> - </svg> - <p class="icon-label">restore-parent</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-restore"> - <use xlink:href="../../ez-icons.svg#restore"></use> - </svg> - <p class="icon-label">restore</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-review"> - <use xlink:href="../../ez-icons.svg#review"></use> - </svg> - <p class="icon-label">review</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-roles"> - <use xlink:href="../../ez-icons.svg#roles"></use> - </svg> - <p class="icon-label">roles</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-rss"> - <use xlink:href="../../ez-icons.svg#rss"></use> - </svg> - <p class="icon-label">rss</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-save"> - <use xlink:href="../../ez-icons.svg#save"></use> - </svg> - <p class="icon-label">save</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-schedule"> - <use xlink:href="../../ez-icons.svg#schedule"></use> - </svg> - <p class="icon-label">schedule</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-search"> - <use xlink:href="../../ez-icons.svg#search"></use> - </svg> - <p class="icon-label">search</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-sections"> - <use xlink:href="../../ez-icons.svg#sections"></use> - </svg> - <p class="icon-label">sections</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-send-review"> - <use xlink:href="../../ez-icons.svg#send-review"></use> - </svg> - <p class="icon-label">send-review</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-settings-block"> - <use xlink:href="../../ez-icons.svg#settings-block"></use> - </svg> - <p class="icon-label">settings-block</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-settings-config"> - <use xlink:href="../../ez-icons.svg#settings-config"></use> - </svg> - <p class="icon-label">settings-config</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-sites-all"> - <use xlink:href="../../ez-icons.svg#sites-all"></use> - </svg> - <p class="icon-label">sites-all</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-spinner"> - <use xlink:href="../../ez-icons.svg#spinner"></use> - </svg> - <p class="icon-label">spinner</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-stats"> - <use xlink:href="../../ez-icons.svg#stats"></use> - </svg> - <p class="icon-label">stats</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-swap"> - <use xlink:href="../../ez-icons.svg#swap"></use> - </svg> - <p class="icon-label">swap</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-system-information"> - <use xlink:href="../../ez-icons.svg#system-information"></use> - </svg> - <p class="icon-label">system-information</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-tags"> - <use xlink:href="../../ez-icons.svg#tags"></use> - </svg> - <p class="icon-label">tags</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-tastes"> - <use xlink:href="../../ez-icons.svg#tastes"></use> - </svg> - <p class="icon-label">tastes</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-timeline"> - <use xlink:href="../../ez-icons.svg#timeline"></use> - </svg> - <p class="icon-label">timeline</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-trash-empty"> - <use xlink:href="../../ez-icons.svg#trash-empty"></use> - </svg> - <p class="icon-label">trash-empty</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-trash-notrashed"> - <use xlink:href="../../ez-icons.svg#trash-notrashed"></use> - </svg> - <p class="icon-label">trash-notrashed</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-trash-send"> - <use xlink:href="../../ez-icons.svg#trash-send"></use> - </svg> - <p class="icon-label">trash-send</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-trash"> - <use xlink:href="../../ez-icons.svg#trash"></use> - </svg> - <p class="icon-label">trash</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-unarchive"> - <use xlink:href="../../ez-icons.svg#unarchive"></use> - </svg> - <p class="icon-label">unarchive</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-unpublish-hide"> - <use xlink:href="../../ez-icons.svg#unpublish-hide"></use> - </svg> - <p class="icon-label">unpublish-hide</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-unpublish-reveal"> - <use xlink:href="../../ez-icons.svg#unpublish-reveal"></use> - </svg> - <p class="icon-label">unpublish-reveal</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-upload"> - <use xlink:href="../../ez-icons.svg#upload"></use> - </svg> - <p class="icon-label">upload</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-user"> - <use xlink:href="../../ez-icons.svg#user"></use> - </svg> - <p class="icon-label">user</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-user_group"> - <use xlink:href="../../ez-icons.svg#user_group"></use> - </svg> - <p class="icon-label">user_group</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-users-personalization"> - <use xlink:href="../../ez-icons.svg#users-personalization"></use> - </svg> - <p class="icon-label">users-personalization</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-users-select"> - <use xlink:href="../../ez-icons.svg#users-select"></use> - </svg> - <p class="icon-label">users-select</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-version-compare-action"> - <use xlink:href="../../ez-icons.svg#version-compare-action"></use> - </svg> - <p class="icon-label">version-compare-action</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-version-compare"> - <use xlink:href="../../ez-icons.svg#version-compare"></use> - </svg> - <p class="icon-label">version-compare</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-versions"> - <use xlink:href="../../ez-icons.svg#versions"></use> - </svg> - <p class="icon-label">versions</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-custom"> - <use xlink:href="../../ez-icons.svg#view-custom"></use> - </svg> - <p class="icon-label">view-custom</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-desktop"> - <use xlink:href="../../ez-icons.svg#view-desktop"></use> - </svg> - <p class="icon-label">view-desktop</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-grid"> - <use xlink:href="../../ez-icons.svg#view-grid"></use> - </svg> - <p class="icon-label">view-grid</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-hide"> - <use xlink:href="../../ez-icons.svg#view-hide"></use> - </svg> - <p class="icon-label">view-hide</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-list"> - <use xlink:href="../../ez-icons.svg#view-list"></use> - </svg> - <p class="icon-label">view-list</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-mobile"> - <use xlink:href="../../ez-icons.svg#view-mobile"></use> - </svg> - <p class="icon-label">view-mobile</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view-tablet"> - <use xlink:href="../../ez-icons.svg#view-tablet"></use> - </svg> - <p class="icon-label">view-tablet</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-view"> - <use xlink:href="../../ez-icons.svg#view"></use> - </svg> - <p class="icon-label">view</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-wand"> - <use xlink:href="../../ez-icons.svg#wand"></use> - </svg> - <p class="icon-label">wand</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-warning"> - <use xlink:href="../../ez-icons.svg#warning"></use> - </svg> - <p class="icon-label">warning</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-workflow"> - <use xlink:href="../../ez-icons.svg#workflow"></use> - </svg> - <p class="icon-label">workflow</p> - </div> -</div> - -###<div class="mgt-3 header-line">Content types</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="icon-box"> - <svg class="ez-icon ez-icon-about"> - <use xlink:href="../../ez-icons.svg#about"></use> - </svg> - <p class="icon-label">about</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-article"> - <use xlink:href="../../ez-icons.svg#article"></use> - </svg> - <p class="icon-label">article</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-blog-post"> - <use xlink:href="../../ez-icons.svg#blog_post"></use> - </svg> - <p class="icon-label">blog_post</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-blog"> - <use xlink:href="../../ez-icons.svg#blog"></use> - </svg> - <p class="icon-label">blog</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-contentlist"> - <use xlink:href="../../ez-icons.svg#contentlist"></use> - </svg> - <p class="icon-label">contentlist</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-folder"> - <use xlink:href="../../ez-icons.svg#folder"></use> - </svg> - <p class="icon-label">folder</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-gallery"> - <use xlink:href="../../ez-icons.svg#gallery"></use> - </svg> - <p class="icon-label">gallery</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-image"> - <use xlink:href="../../ez-icons.svg#image"></use> - </svg> - <p class="icon-label">image</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-landing_page"> - <use xlink:href="../../ez-icons.svg#landing_page"></use> - </svg> - <p class="icon-label">landing_page</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-media"> - <use xlink:href="../../ez-icons.svg#media"></use> - </svg> - <p class="icon-label">media</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-news"> - <use xlink:href="../../ez-icons.svg#news"></use> - </svg> - <p class="icon-label">news</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-place"> - <use xlink:href="../../ez-icons.svg#place"></use> - </svg> - <p class="icon-label">place</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-places"> - <use xlink:href="../../ez-icons.svg#places"></use> - </svg> - <p class="icon-label">places</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-product_list"> - <use xlink:href="../../ez-icons.svg#product_list"></use> - </svg> - <p class="icon-label">product_list</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-product"> - <use xlink:href="../../ez-icons.svg#product"></use> - </svg> - <p class="icon-label">product</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-wiki-file"> - <use xlink:href="../../ez-icons.svg#wiki-file"></use> - </svg> - <p class="icon-label">wiki-file</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-wiki"> - <use xlink:href="../../ez-icons.svg#wiki"></use> - </svg> - <p class="icon-label">wiki</p> - </div> -</div> - -###<div class="mgt-3 header-line">Rich Text Editor</div> -<div class="wrapper-samples mgt-minus-3"> - <div class="icon-box"> - <svg class="ez-icon ez-icon-align-center"> - <use xlink:href="../../ez-icons.svg#align-center"></use> - </svg> - <p class="icon-label">align-center</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-align-justify"> - <use xlink:href="../../ez-icons.svg#align-justify"></use> - </svg> - <p class="icon-label">align-justify</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-align-left"> - <use xlink:href="../../ez-icons.svg#align-left"></use> - </svg> - <p class="icon-label">align-left</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-align-right"> - <use xlink:href="../../ez-icons.svg#align-right"></use> - </svg> - <p class="icon-label">align-right</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bold"> - <use xlink:href="../../ez-icons.svg#bold"></use> - </svg> - <p class="icon-label">bold</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-custom_tags"> - <use xlink:href="../../ez-icons.svg#custom_tags"></use> - </svg> - <p class="icon-label">custom_tags</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h1"> - <use xlink:href="../../ez-icons.svg#h1"></use> - </svg> - <p class="icon-label">h1</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h2"> - <use xlink:href="../../ez-icons.svg#h2"></use> - </svg> - <p class="icon-label">h2</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h3"> - <use xlink:href="../../ez-icons.svg#h3"></use> - </svg> - <p class="icon-label">h3</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h4"> - <use xlink:href="../../ez-icons.svg#h4"></use> - </svg> - <p class="icon-label">h4</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h5"> - <use xlink:href="../../ez-icons.svg#h5"></use> - </svg> - <p class="icon-label">h5</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-h6"> - <use xlink:href="../../ez-icons.svg#h6"></use> - </svg> - <p class="icon-label">h6</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-image-center"> - <use xlink:href="../../ez-icons.svg#image-center"></use> - </svg> - <p class="icon-label">image-center</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-image-left"> - <use xlink:href="../../ez-icons.svg#image-left"></use> - </svg> - <p class="icon-label">image-left</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-image-right"> - <use xlink:href="../../ez-icons.svg#image-right"></use> - </svg> - <p class="icon-label">image-right</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-italic"> - <use xlink:href="../../ez-icons.svg#italic"></use> - </svg> - <p class="icon-label">italic</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-link-anchor"> - <use xlink:href="../../ez-icons.svg#link-anchor"></use> - </svg> - <p class="icon-label">link-anchor</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-link-content"> - <use xlink:href="../../ez-icons.svg#link-content"></use> - </svg> - <p class="icon-label">link-content</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-link-remove"> - <use xlink:href="../../ez-icons.svg#link-remove"></use> - </svg> - <p class="icon-label">link-remove</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-link"> - <use xlink:href="../../ez-icons.svg#link"></use> - </svg> - <p class="icon-label">link</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-list-numbered"> - <use xlink:href="../../ez-icons.svg#list-numbered"></use> - </svg> - <p class="icon-label">list-numbered</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-list"> - <use xlink:href="../../ez-icons.svg#list"></use> - </svg> - <p class="icon-label">list</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-markup"> - <use xlink:href="../../ez-icons.svg#markup"></use> - </svg> - <p class="icon-label">markup</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-paragraph-add"> - <use xlink:href="../../ez-icons.svg#paragraph-add"></use> - </svg> - <p class="icon-label">paragraph-add</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-paragraph"> - <use xlink:href="../../ez-icons.svg#paragraph"></use> - </svg> - <p class="icon-label">paragraph</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-quote"> - <use xlink:href="../../ez-icons.svg#quote"></use> - </svg> - <p class="icon-label">quote</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-strikethrough"> - <use xlink:href="../../ez-icons.svg#strikethrough"></use> - </svg> - <p class="icon-label">strikethrough</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-subscript"> - <use xlink:href="../../ez-icons.svg#subscript"></use> - </svg> - <p class="icon-label">subscript</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-superscript"> - <use xlink:href="../../ez-icons.svg#superscript"></use> - </svg> - <p class="icon-label">superscript</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-table-add"> - <use xlink:href="../../ez-icons.svg#table-add"></use> - </svg> - <p class="icon-label">table-add</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-table-cell"> - <use xlink:href="../../ez-icons.svg#table-cell"></use> - </svg> - <p class="icon-label">table-cell</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-table-column"> - <use xlink:href="../../ez-icons.svg#table-column"></use> - </svg> - <p class="icon-label">table-column</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-table-row"> - <use xlink:href="../../ez-icons.svg#table-row"></use> - </svg> - <p class="icon-label">table-row</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-tag"> - <use xlink:href="../../ez-icons.svg#tag"></use> - </svg> - <p class="icon-label">tag</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-twitter"> - <use xlink:href="../../ez-icons.svg#twitter"></use> - </svg> - <p class="icon-label">twitter</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-underscore"> - <use xlink:href="../../ez-icons.svg#underscore"></use> - </svg> - <p class="icon-label">underscore</p> - </div> -</div> - -###<div class="mgt-3 header-line">Form Builder</div> -<div class="wrapper-samples mgt-minus-3 mgb-5"> - <div class="icon-box"> - <svg class="ez-icon ez-icon-button"> - <use xlink:href="../../ez-icons.svg#button"></use> - </svg> - <p class="icon-label">button</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-captcha"> - <use xlink:href="../../ez-icons.svg#captcha"></use> - </svg> - <p class="icon-label">captcha</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-checkbox-multiple"> - <use xlink:href="../../ez-icons.svg#checkbox-multiple"></use> - </svg> - <p class="icon-label">checkbox-multiple</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-checkbox"> - <use xlink:href="../../ez-icons.svg#checkbox"></use> - </svg> - <p class="icon-label">checkbox</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-date"> - <use xlink:href="../../ez-icons.svg#date"></use> - </svg> - <p class="icon-label">date</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-dropdown"> - <use xlink:href="../../ez-icons.svg#dropdown"></use> - </svg> - <p class="icon-label">dropdown</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-input-hidden"> - <use xlink:href="../../ez-icons.svg#input-hidden"></use> - </svg> - <p class="icon-label">input-hidden</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-input-line-multiple"> - <use xlink:href="../../ez-icons.svg#input-line-multiple"></use> - </svg> - <p class="icon-label">input-line-multiple</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-input-line"> - <use xlink:href="../../ez-icons.svg#input-line"></use> - </svg> - <p class="icon-label">input-line</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-input-number"> - <use xlink:href="../../ez-icons.svg#input-number"></use> - </svg> - <p class="icon-label">input-number</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-radio-button-multiple"> - <use xlink:href="../../ez-icons.svg#radio-button-multiple"></use> - </svg> - <p class="icon-label">radio-button-multiple</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-radio-button"> - <use xlink:href="../../ez-icons.svg#radio-button"></use> - </svg> - <p class="icon-label">radio-button</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-rate"> - <use xlink:href="../../ez-icons.svg#rate"></use> - </svg> - <p class="icon-label">rate</p> - </div> -</div> - -###<div class="mgt-3 header-line">eCommerce</div> -<div class="wrapper-samples mgt-minus-3 mgb-5"> - <div class="icon-box"> - <svg class="ez-icon ez-icon-b2b"> - <use xlink:href="../../ez-icons.svg#b2b"></use> - </svg> - <p class="icon-label">b2b</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-bestseller"> - <use xlink:href="../../ez-icons.svg#bestseller"></use> - </svg> - <p class="icon-label">bestseller</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-cart-upload"> - <use xlink:href="../../ez-icons.svg#cart-upload"></use> - </svg> - <p class="icon-label">cart-upload</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-cart-wishlist"> - <use xlink:href="../../ez-icons.svg#cart-wishlist"></use> - </svg> - <p class="icon-label">cart-wishlist</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-cart"> - <use xlink:href="../../ez-icons.svg#cart"></use> - </svg> - <p class="icon-label">cart</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-catalog"> - <use xlink:href="../../ez-icons.svg#catalog"></use> - </svg> - <p class="icon-label">catalog</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-components"> - <use xlink:href="../../ez-icons.svg#components"></use> - </svg> - <p class="icon-label">components</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-core"> - <use xlink:href="../../ez-icons.svg#core"></use> - </svg> - <p class="icon-label">core</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-customer"> - <use xlink:href="../../ez-icons.svg#customer"></use> - </svg> - <p class="icon-label">customer</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-erp"> - <use xlink:href="../../ez-icons.svg#erp"></use> - </svg> - <p class="icon-label">erp</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-mail"> - <use xlink:href="../../ez-icons.svg#mail"></use> - </svg> - <p class="icon-label">mail</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-newsletter"> - <use xlink:href="../../ez-icons.svg#newsletter"></use> - </svg> - <p class="icon-label">newsletter</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-order-management"> - <use xlink:href="../../ez-icons.svg#order-management"></use> - </svg> - <p class="icon-label">order-management</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-price"> - <use xlink:href="../../ez-icons.svg#price"></use> - </svg> - <p class="icon-label">price</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-rate-review"> - <use xlink:href="../../ez-icons.svg#rate-review"></use> - </svg> - <p class="icon-label">rate-review</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-server"> - <use xlink:href="../../ez-icons.svg#server"></use> - </svg> - <p class="icon-label">server</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-slider-lastviewed"> - <use xlink:href="../../ez-icons.svg#slider-lastviewed"></use> - </svg> - <p class="icon-label">slider-lastviewed</p> - </div> - <div class="icon-box"> - <svg class="ez-icon ez-icon-slider"> - <use xlink:href="../../ez-icons.svg#slider"></use> - </svg> - <p class="icon-label">slider</p> - </div> -</div> - -!!! note - - Following our design principles and philosophy, expandable and flexible, we provide a few more icons than the ones we are using so that you and your team have the option of customizing your eZ Platform CMS. diff --git a/docs/guidelines/resources/typography.md b/docs/guidelines/resources/typography.md deleted file mode 100644 index 4cc16ea22f..0000000000 --- a/docs/guidelines/resources/typography.md +++ /dev/null @@ -1,39 +0,0 @@ -# Typography - -**Typographic elements do not come with any other styling properties attached** to them in order to emphasize modularity and make them more flexible in where they can be used. If you need to add more styling properties don't forget to look for any other component before. - -We have selected `Noto Sans` due to the fact that it's intended to be visually harmonious across multiple languages, a priority for us when promoting a multilanguage platform. - -##<div class="mgt-4 header-line">Type weights and styles</div> -<div class="wrapper-samples mgt-minus-2"> - <div class="type-box"> - <div class="letter-sample ez-guidelines-font">A</div> - <div class="weight-text mgb-1">REGULAR (400)</div> - </div> - <div class="type-box"> - <div class="letter-sample letter-sample-italic ez-guidelines-font">A</div> - <div class="weight-text mgb-1">ITALIC (400)</div> - </div> - <div class="type-box"> - <div class="letter-sample letter-sample-bold ez-guidelines-font">A</div> - <div class="weight-text mgb-1">BOLD (700)</div> - </div> -</div> - -##<div class="mgt-4 header-line">Type sizes</div> -<div class="mgb-4 mgt-minus-2">We use standard type sizes and weights to define a layout composition.</div> - -<h1 class="ez-guidelines-font ez-guidelines-h1">eZ is an open source content management system</h1> -<div class="mgb-2"><code>h1</code><span class="mgl-2">1.75rem</span></div> - -<h2 class="ez-guidelines-font ez-guidelines-h2">eZ is an open source content management system</h2> -<div class="mgb-2"><code>h2</code><span class="mgl-2">1.5rem</span></div> - -<h3 class="ez-guidelines-font ez-guidelines-h3">eZ is an open source content management system</h3> -<div class="mgb-2"><code>h3</code><span class="mgl-2">1.25rem</span></div> - -<h4 class="ez-guidelines-font ez-guidelines-h4">eZ is an open source content management system</h4> -<div class="mgb-2"><code>h4</code><span class="mgl-2">1.125rem</span></div> - -<h5 class="ez-guidelines-font ez-guidelines-h5">eZ is an open source content management system</h5> -<div class="mgb-2"><code>h5</code><span class="mgl-2">1.0625rem</span></div> diff --git a/docs/images/404.png b/docs/images/404.png new file mode 100644 index 0000000000..759822d602 Binary files /dev/null and b/docs/images/404.png differ diff --git a/docs/images/Build site.svg b/docs/images/Build site.svg new file mode 100644 index 0000000000..15682956d1 --- /dev/null +++ b/docs/images/Build site.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:#ae1164;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px;}.cls-2{stroke:#ae1164;}</style></defs><path class="cls-2" d="M42.99,2c0-.55-.45-1-1-1h-14.96c-.55-.02-1.01,.42-1.03,.97,0,.29,.11,.57,.32,.76l14.97,14.95c.4,.39,1.03,.38,1.42-.01,.18-.19,.28-.43,.29-.69V2Z"/><path class="cls-2" d="M28.14,4.54l11.31,11.31h0l-12.73,12.73c-.78,.78-2.05,.78-2.83,0l-8.48-8.48c-.78-.78-.78-2.05,0-2.83L28.14,4.54Z"/><path class="cls-2" d="M17.74,23.41l-12.73,12.74"/><path class="cls-2" d="M21.05,25.77s-5.66,4.24,0,9.9"/><path class="cls-1" d="M26.36,47l2-4,4-2-4-2-2-4-2,4-4,2,4,2,2,4Z"/></svg> \ No newline at end of file diff --git a/docs/images/Idea.svg b/docs/images/Idea.svg new file mode 100644 index 0000000000..53b42249c0 --- /dev/null +++ b/docs/images/Idea.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:#db0032;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}.cls-2{stroke:#ae1164;}</style></defs><g><path class="cls-2" d="M30,34.66v4.34c0,3.31-2.69,6-6,6s-6-2.69-6-6v-4.34"/><line class="cls-2" x1="24" y1="45" x2="24" y2="47"/><path class="cls-1" d="M26,35.86v-11.86c0-1.66,1.34-3,3-3s3,1.34,3,3-1.34,3-3,3h-10c-1.66,0-3-1.34-3-3s1.34-3,3-3,3,1.34,3,3v11.86"/><line class="cls-2" x1="18.08" y1="40" x2="29.92" y2="40"/></g><circle class="cls-2" cx="24" cy="22" r="14"/><line class="cls-1" x1="24" y1="1" x2="24" y2="3"/><line class="cls-1" x1="9.16" y1="7.16" x2="10.56" y2="8.56"/><line class="cls-1" x1="3" y1="22" x2="5" y2="22"/><line class="cls-1" x1="9.16" y1="36.86" x2="10.56" y2="35.44"/><line class="cls-1" x1="38.84" y1="36.86" x2="37.44" y2="35.44"/><line class="cls-1" x1="45" y1="22" x2="43" y2="22"/><line class="cls-1" x1="38.84" y1="7.16" x2="37.44" y2="8.56"/></svg> \ No newline at end of file diff --git a/docs/images/Reward.svg b/docs/images/Reward.svg new file mode 100644 index 0000000000..f97cbe79bf --- /dev/null +++ b/docs/images/Reward.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:#ae1164;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px;}.cls-2{stroke:#ae1164;}</style></defs><path class="cls-2" d="M20,2.25l4.36,9.4h8.48c1.15-.05,2.11,.84,2.16,1.99,.03,.62-.23,1.22-.7,1.64l-7.35,7.24,4.08,9.37c.47,1.13-.07,2.43-1.2,2.9-.64,.27-1.37,.22-1.96-.13l-9.86-5.55-9.87,5.54c-1.06,.61-2.42,.25-3.03-.82-.34-.6-.39-1.32-.13-1.96l4.08-9.36L1.71,15.28c-.86-.76-.95-2.07-.19-2.93,.41-.47,1.02-.73,1.64-.71H11.64L16,2.25c.58-1.1,1.95-1.53,3.06-.94,.4,.21,.73,.54,.94,.94Z"/><line class="cls-1" x1="44" y1="27.96" x2="44" y2="33.96"/><line class="cls-1" x1="41" y1="30.96" x2="47" y2="30.96"/><line class="cls-1" x1="21" y1="40.96" x2="21" y2="46.96"/><line class="cls-1" x1="18" y1="43.96" x2="24" y2="43.96"/><line class="cls-1" x1="41" y1="2.96" x2="41" y2="8.96"/><line class="cls-1" x1="38" y1="5.96" x2="44" y2="5.96"/></svg> \ No newline at end of file diff --git a/docs/images/Site Factory.svg b/docs/images/Site Factory.svg new file mode 100644 index 0000000000..0542ecf2a5 --- /dev/null +++ b/docs/images/Site Factory.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:#ffffff;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px;}.cls-2{stroke:#ffffff;}</style></defs><path class="cls-2" d="M43,11h1c.79,0,1.55,.32,2.12,.88,.56,.56,.88,1.32,.88,2.12v26c0,.79-.32,1.55-.88,2.12-.56,.56-1.32,.88-2.12,.88H4c-.79,0-1.55-.32-2.12-.88-.56-.56-.88-1.32-.88-2.12V14c0-.79,.32-1.55,.88-2.12,.56-.56,1.32-.88,2.12-.88h1"/><path class="cls-2" d="M14,47h20"/><path class="cls-2" d="M24,43v4"/><path class="cls-1" d="M9,31V3c0-.53,.21-1.04,.59-1.41,.38-.38,.88-.59,1.41-.59h26c.53,0,1.04,.21,1.41,.59,.38,.38,.59,.88,.59,1.41V31"/><path class="cls-1" d="M20,15h-6c-.55,0-1,.45-1,1v5c0,.55,.45,1,1,1h6c.55,0,1-.45,1-1v-5c0-.55-.45-1-1-1Z"/><path class="cls-1" d="M25.2,21h9.8"/><path class="cls-1" d="M25.2,15h9.8"/><path class="cls-1" d="M13,27h22"/><path class="cls-2" d="M1,37H47"/><path class="cls-1" d="M9,10h30"/><g><path class="cls-1" d="M13.5,6c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5"/><path class="cls-1" d="M13.5,6c.28,0,.5-.22,.5-.5s-.22-.5-.5-.5"/></g><g><path class="cls-1" d="M18.5,6c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5"/><path class="cls-1" d="M18.5,6c.28,0,.5-.22,.5-.5s-.22-.5-.5-.5"/></g><g><path class="cls-1" d="M23.5,6c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5"/><path class="cls-1" d="M23.5,6c.28,0,.5-.22,.5-.5s-.22-.5-.5-.5"/></g></svg> \ No newline at end of file diff --git a/docs/images/Value.svg b/docs/images/Value.svg new file mode 100644 index 0000000000..7ef85ed4ac --- /dev/null +++ b/docs/images/Value.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:#db0032;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:10px;}.cls-2{stroke:#ae1164;}</style></defs><path class="cls-2" d="M22.51,3.84c-.05,13.53,6.52,21.18,21.52,21.52-13.91-.05-20.81,7.4-21.52,21.51-.13-13.32-6.07-21.54-21.51-21.51,13.81-.2,21.47-6.86,21.51-21.52Z"/><line class="cls-1" x1="35.14" y1="1.12" x2="35.14" y2="7.27"/><line class="cls-1" x1="32.07" y1="4.2" x2="38.21" y2="4.2"/><line class="cls-1" x1="43.93" y1="33.11" x2="43.93" y2="39.25"/><line class="cls-1" x1="40.85" y1="36.18" x2="47" y2="36.18"/></svg> \ No newline at end of file diff --git a/docs/images/arrow_down.svg b/docs/images/arrow_down.svg new file mode 100644 index 0000000000..2491397d0a --- /dev/null +++ b/docs/images/arrow_down.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16px" height="64px" viewBox="0 0 16 64" version="1.1"> + <g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="strategy/level2/empty" transform="translate(-441.000000, 0.000000)"> + <g id="Group" transform="translate(441.000000, 0.000000)"> + <line x1="8" y1="6.8191286e-13" x2="7.57633366" y2="55.500291" id="Line-3" stroke="#E0E0E8"/> + <path d="M9.78885438,51.5777088 L14.5527864,61.1055728 C15.0467649,62.0935298 14.6463162,63.2948759 13.6583592,63.7888544 C13.3806483,63.9277098 13.0744222,64 12.763932,64 L3.23606798,64 C2.13149848,64 1.23606798,63.1045695 1.23606798,62 C1.23606798,61.6895098 1.30835816,61.3832837 1.4472136,61.1055728 L6.21114562,51.5777088 C6.70512412,50.5897518 7.9064702,50.1893031 8.89442719,50.6832816 C9.281482,50.876809 9.59532698,51.190654 9.78885438,51.5777088 Z" id="Triangle" fill="#E0E0E8" transform="translate(8.000000, 56.000000) scale(1, -1) translate(-8.000000, -56.000000) "/> + </g> + </g> + </g> +</svg> diff --git a/docs/images/caret-down.svg b/docs/images/caret-down.svg new file mode 100644 index 0000000000..36a9d2a5eb --- /dev/null +++ b/docs/images/caret-down.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="12px" viewBox="0 0 24 24" fill="#ae1164"> + <path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path> +</svg> diff --git a/docs/images/caret.svg b/docs/images/caret.svg new file mode 100644 index 0000000000..d8ec32095d --- /dev/null +++ b/docs/images/caret.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="15px" viewBox="0 0 30 30" fill="#ae1164"> + <path d="M13.333 24c-0.001 0-0.001 0-0.002 0-0.736 0-1.333-0.597-1.333-1.333 0-0.41 0.185-0.777 0.477-1.022l0.002-0.002 6.773-5.667-6.767-5.617c-0.28-0.246-0.456-0.604-0.456-1.004 0-0.736 0.597-1.333 1.333-1.333 0.312 0 0.599 0.107 0.826 0.286l-0.003-0.002 8 6.633c0.295 0.245 0.482 0.612 0.483 1.023v0c0 0.004 0 0.010 0 0.015 0 0.409-0.184 0.776-0.475 1.020l-0.002 0.002-8 6.703c-0.227 0.185-0.52 0.297-0.839 0.297-0.006 0-0.013-0-0.019-0h0.001z"></path> +</svg> diff --git a/docs/images/caret_sm.svg b/docs/images/caret_sm.svg new file mode 100644 index 0000000000..6bc07261cf --- /dev/null +++ b/docs/images/caret_sm.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="15px" viewBox="0 0 30 30" fill="#acacac"> + <path d="M13.333 24c-0.001 0-0.001 0-0.002 0-0.736 0-1.333-0.597-1.333-1.333 0-0.41 0.185-0.777 0.477-1.022l0.002-0.002 6.773-5.667-6.767-5.617c-0.28-0.246-0.456-0.604-0.456-1.004 0-0.736 0.597-1.333 1.333-1.333 0.312 0 0.599 0.107 0.826 0.286l-0.003-0.002 8 6.633c0.295 0.245 0.482 0.612 0.483 1.023v0c0 0.004 0 0.010 0 0.015 0 0.409-0.184 0.776-0.475 1.020l-0.002 0.002-8 6.703c-0.227 0.185-0.52 0.297-0.839 0.297-0.006 0-0.013-0-0.019-0h0.001z"></path> +</svg> diff --git a/docs/images/check.svg b/docs/images/check.svg new file mode 100644 index 0000000000..5c7c8c444c --- /dev/null +++ b/docs/images/check.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="12px" height="15px" viewBox="0 0 30 30" fill="#ae1164"><path d="M21 7L9 19l-5.5-5.5 1.41-1.41L9 16.17 19.59 5.59 21 7z"/></svg> diff --git a/docs/images/content.png b/docs/images/content.png new file mode 100644 index 0000000000..8e6963a289 Binary files /dev/null and b/docs/images/content.png differ diff --git a/docs/images/content_edit.svg b/docs/images/content_edit.svg new file mode 100644 index 0000000000..45e6a4999c --- /dev/null +++ b/docs/images/content_edit.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="content_edit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><defs><style>.cls-1{stroke:white;}.cls-1,.cls-2{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px;}.cls-2{stroke:white;}</style></defs><path class="cls-2" d="M9.03,17.02H27.95"/><path class="cls-2" d="M9.03,22.99H27.95"/><path class="cls-2" d="M9.03,11.07h13.94"/><path class="cls-2" d="M9.03,28.97h14.92"/><path class="cls-2" d="M9.03,34.94h7.96"/><path class="cls-2" d="M17,46.87H3.06c-1.09,0-1.98-.88-1.99-1.97V3.08c0-1.1,.89-1.99,1.99-1.99H29.53c.53,0,1.03,.21,1.41,.59l7.38,7.38c.38,.37,.59,.88,.59,1.41v6.56"/><path class="cls-1" d="M31.33,44.52l-8.36,2.35,2.39-8.36,14.3-14.27c1.61-1.69,4.29-1.74,5.97-.13,1.69,1.61,1.74,4.29,.13,5.97-.04,.04-.08,.09-.13,.13l-14.3,14.31Z"/><path class="cls-1" d="M37.87,26.02l5.97,5.97"/><path class="cls-1" d="M25.36,38.53l5.97,5.99"/></svg> \ No newline at end of file diff --git a/docs/images/customer.png b/docs/images/customer.png new file mode 100644 index 0000000000..9c156a62ac Binary files /dev/null and b/docs/images/customer.png differ diff --git a/docs/images/four.png b/docs/images/four.png new file mode 100644 index 0000000000..1ebeda9b87 Binary files /dev/null and b/docs/images/four.png differ diff --git a/docs/images/icons.svg b/docs/images/icons.svg new file mode 100644 index 0000000000..033ce2e546 --- /dev/null +++ b/docs/images/icons.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <symbol id="check-requirements" viewBox="0 0 20 25" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M17.5 24.9993H2.5C1.90326 24.9993 1.33097 24.7623 0.90901 24.3403C0.487053 23.9184 0.25 23.3461 0.25 22.7493V6.99932C0.25 6.40258 0.487053 5.83029 0.90901 5.40833C1.33097 4.98637 1.90326 4.74932 2.5 4.74932H5.56225C5.93275 2.60582 7.7785 0.99707 10 0.99707C12.2215 0.99707 14.0665 2.60657 14.434 4.72307L14.4377 4.75007H17.5C18.0967 4.75007 18.669 4.98712 19.091 5.40908C19.5129 5.83104 19.75 6.40333 19.75 7.00007V22.7501C19.75 23.3468 19.5129 23.9191 19.091 24.3411C18.669 24.763 18.0967 25.0001 17.5 25.0001V24.9993ZM2.5 6.24932C2.30109 6.24932 2.11032 6.32834 1.96967 6.46899C1.82902 6.60964 1.75 6.80041 1.75 6.99932V22.7493C1.75 22.9482 1.82902 23.139 1.96967 23.2797C2.11032 23.4203 2.30109 23.4993 2.5 23.4993H17.5C17.6989 23.4993 17.8897 23.4203 18.0303 23.2797C18.171 23.139 18.25 22.9482 18.25 22.7493V6.99932C18.25 6.80041 18.171 6.60964 18.0303 6.46899C17.8897 6.32834 17.6989 6.24932 17.5 6.24932H13.75C13.5511 6.24932 13.3603 6.1703 13.2197 6.02965C13.079 5.889 13 5.69823 13 5.49932C13 4.70367 12.6839 3.94061 12.1213 3.378C11.5587 2.81539 10.7956 2.49932 10 2.49932C9.20435 2.49932 8.44129 2.81539 7.87868 3.378C7.31607 3.94061 7 4.70367 7 5.49932C7 5.69823 6.92098 5.889 6.78033 6.02965C6.63968 6.1703 6.44891 6.24932 6.25 6.24932H2.5ZM15.4998 19.7493C15.4998 19.5504 15.4207 19.3596 15.2801 19.219C15.1394 19.0783 14.9487 18.9993 14.7498 18.9993H9.25C9.05109 18.9993 8.86032 19.0783 8.71967 19.219C8.57902 19.3596 8.5 19.5504 8.5 19.7493C8.5 19.9482 8.57902 20.139 8.71967 20.2797C8.86032 20.4203 9.05109 20.4993 9.25 20.4993H14.7498C14.9487 20.4993 15.1394 20.4203 15.2801 20.2797C15.4207 20.139 15.4998 19.9482 15.4998 19.7493ZM15.4998 15.2493C15.4998 15.0504 15.4207 14.8596 15.2801 14.719C15.1394 14.5783 14.9487 14.4993 14.7498 14.4993H9.25C9.05109 14.4993 8.86032 14.5783 8.71967 14.719C8.57902 14.8596 8.5 15.0504 8.5 15.2493C8.5 15.4482 8.57902 15.639 8.71967 15.7797C8.86032 15.9203 9.05109 15.9993 9.25 15.9993H14.7498C14.9487 15.9993 15.1394 15.9203 15.2801 15.7797C15.4207 15.639 15.4998 15.4482 15.4998 15.2493ZM15.4998 10.7493C15.4998 10.5504 15.4207 10.3596 15.2801 10.219C15.1394 10.0783 14.9487 9.99932 14.7498 9.99932H9.25C9.05109 9.99932 8.86032 10.0783 8.71967 10.219C8.57902 10.3596 8.5 10.5504 8.5 10.7493C8.5 10.9482 8.57902 11.139 8.71967 11.2797C8.86032 11.4203 9.05109 11.4993 9.25 11.4993H14.7498C14.9487 11.4993 15.1394 11.4203 15.2801 11.2797C15.4207 11.139 15.4998 10.9482 15.4998 10.7493ZM6.25 9.49907C6.00272 9.49907 5.761 9.5724 5.5554 9.70978C5.3498 9.84715 5.18955 10.0424 5.09492 10.2709C5.00029 10.4993 4.97553 10.7507 5.02377 10.9932C5.07201 11.2358 5.19109 11.4585 5.36594 11.6334C5.54079 11.8082 5.76356 11.9273 6.00609 11.9755C6.24861 12.0238 6.5 11.999 6.72845 11.9044C6.9569 11.8098 7.15217 11.6495 7.28955 11.4439C7.42692 11.2383 7.50025 10.9966 7.50025 10.7493C7.50025 10.0593 6.94075 9.49907 6.25 9.49907ZM6.25 13.9991C6.00272 13.9991 5.761 14.0724 5.5554 14.2098C5.3498 14.3472 5.18955 14.5424 5.09492 14.7709C5.00029 14.9993 4.97553 15.2507 5.02377 15.4932C5.07201 15.7358 5.19109 15.9585 5.36594 16.1334C5.54079 16.3082 5.76356 16.4273 6.00609 16.4755C6.24861 16.5238 6.5 16.499 6.72845 16.4044C6.9569 16.3098 7.15217 16.1495 7.28955 15.9439C7.42692 15.7383 7.50025 15.4966 7.50025 15.2493C7.50025 14.5593 6.94075 13.9991 6.25 13.9991ZM6.25 18.4991C6.00272 18.4991 5.761 18.5724 5.5554 18.7098C5.3498 18.8472 5.18955 19.0424 5.09492 19.2709C5.00029 19.4993 4.97553 19.7507 5.02377 19.9932C5.07201 20.2358 5.19109 20.4585 5.36594 20.6334C5.54079 20.8082 5.76356 20.9273 6.00609 20.9755C6.24861 21.0238 6.5 20.999 6.72845 20.9044C6.9569 20.8098 7.15217 20.6495 7.28955 20.4439C7.42692 20.2383 7.50025 19.9966 7.50025 19.7493C7.50025 19.0593 6.94075 18.4991 6.25 18.4991ZM10 3.74957C9.75272 3.74957 9.511 3.8229 9.3054 3.96028C9.0998 4.09765 8.93955 4.29292 8.84492 4.52137C8.75029 4.74982 8.72553 5.00121 8.77377 5.24373C8.82201 5.48626 8.94109 5.70903 9.11594 5.88388C9.29079 6.05873 9.51356 6.17781 9.75609 6.22605C9.99861 6.27429 10.25 6.24953 10.4785 6.1549C10.7069 6.06027 10.9022 5.90002 11.0395 5.69442C11.1769 5.48882 11.2502 5.2471 11.2502 4.99982C11.2502 4.30982 10.6908 3.74957 10 3.74957Z" fill="#976828"/> + </symbol> + <symbol id="install-dxp" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M21.5497 19.3127L24.4147 17.413C24.5272 17.3382 24.6174 17.2345 24.6759 17.1128C24.7343 16.9911 24.7588 16.8558 24.7467 16.7213C24.7346 16.5868 24.6865 16.4581 24.6073 16.3487C24.5282 16.2393 24.421 16.1533 24.297 16.0997L24.2925 16.0975L14.7922 12.0602C14.6582 12.0036 14.5105 11.9872 14.3672 12.0131C14.224 12.0389 14.0913 12.1059 13.9855 12.2059C13.8797 12.3058 13.8052 12.4344 13.7712 12.5759C13.7372 12.7175 13.7451 12.8659 13.794 13.003L13.7925 12.9977L17.445 23.5C17.5433 23.776 17.7922 23.9747 18.09 23.9995H18.1537C18.288 23.9996 18.4199 23.9636 18.5355 23.8953C18.6511 23.8269 18.7462 23.7287 18.8107 23.611L18.813 23.6072L20.538 20.4295L23.4352 23.7497C23.501 23.8238 23.5807 23.8843 23.6698 23.9276C23.7589 23.9709 23.8557 23.9963 23.9545 24.0022C24.0534 24.0081 24.1525 23.9945 24.2462 23.9621C24.3398 23.9297 24.4261 23.8792 24.5002 23.8135C24.5744 23.7477 24.6348 23.668 24.6781 23.5789C24.7214 23.4898 24.7468 23.3931 24.7527 23.2942C24.7586 23.1953 24.745 23.0962 24.7126 23.0026C24.6802 22.9089 24.6297 22.8226 24.564 22.7485L24.5648 22.7492L21.5497 19.3127ZM18.3 21.4L15.765 14.1025L22.425 16.9322L19.9252 18.5875C19.8227 18.6533 19.7376 18.7428 19.677 18.8485L19.6747 18.8522L18.3 21.4ZM11.4998 15.5005H4.5C2.43225 15.5005 0.75 13.9082 0.75 11.9507V3.55073C0.75 1.59323 2.43225 0.000976562 4.5 0.000976562H21C23.0677 0.000976562 24.75 1.59323 24.75 3.55073V11.9515C24.75 12.4375 24.6472 12.9002 24.4613 13.3172L24.4695 13.2955C24.433 13.3899 24.3778 13.4759 24.3072 13.5484C24.2365 13.6209 24.152 13.6784 24.0586 13.7174C23.9651 13.7563 23.8648 13.7759 23.7636 13.7751C23.6624 13.7742 23.5624 13.7529 23.4696 13.7124C23.3769 13.6719 23.2933 13.613 23.2239 13.5393C23.1545 13.4656 23.1008 13.3786 23.0659 13.2836C23.031 13.1886 23.0157 13.0875 23.0209 12.9864C23.0261 12.8854 23.0518 12.7864 23.0963 12.6955L23.0947 12.7007C23.1922 12.4795 23.2493 12.2222 23.25 11.9507V3.55073C23.25 2.42048 22.2502 1.50098 21 1.50098H4.5C3.24975 1.50098 2.25 2.42123 2.25 3.55073V11.9507C2.25 13.081 3.24975 14.0005 4.5 14.0005H11.4998C11.6987 14.0005 11.8894 14.0795 12.0301 14.2201C12.1707 14.3608 12.2498 14.5516 12.2498 14.7505C12.2498 14.9494 12.1707 15.1402 12.0301 15.2808C11.8894 15.4215 11.6987 15.5005 11.4998 15.5005Z" fill="#8B0E50"/> + </symbol> + <symbol id="install-cloud" viewBox="0 0 25 18" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M9.07143 0C4.33756 0 0.5 3.83756 0.5 8.57143C0.5 13.3053 4.33756 17.1429 9.07143 17.1429H19.3571C22.1975 17.1429 24.5 14.8403 24.5 12C24.5 9.15968 22.1975 6.85714 19.3571 6.85714L18.9851 6.87187C18.6148 6.90004 18.2504 6.96828 17.898 7.07447L17.5314 7.2L17.5187 7.10975C16.825 3.07217 13.307 0 9.07143 0ZM9.07143 1.71429C12.7438 1.71429 15.7418 4.60109 15.9202 8.22919L15.9286 8.592C15.9286 9.30271 16.7438 9.70468 17.3076 9.27196C17.8965 8.81997 18.6176 8.57385 19.3599 8.57142C21.2507 8.57143 22.7857 10.1065 22.7857 12C22.7857 13.8935 21.2507 15.4286 19.3571 15.4286H9.07143C5.28433 15.4286 2.21429 12.3585 2.21429 8.57143C2.21429 4.78433 5.28433 1.71429 9.07143 1.71429Z" fill="#2B7283"/> + </symbol> + <symbol id="first-steps" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M5.5 24.0002H5.49475C5.3785 24.0006 5.26393 23.9725 5.161 23.9185L5.16475 23.9207C5.0403 23.8585 4.93561 23.763 4.86236 23.6447C4.78912 23.5264 4.75021 23.3901 4.75 23.251V19.501H2.5C1.90326 19.501 1.33097 19.2639 0.90901 18.842C0.487053 18.42 0.25 17.8477 0.25 17.251L0.25 2.25098C0.25 1.65424 0.487053 1.08194 0.90901 0.659986C1.33097 0.238029 1.90326 0.000976563 2.5 0.000976562H22C22.5967 0.000976563 23.169 0.238029 23.591 0.659986C24.0129 1.08194 24.25 1.65424 24.25 2.25098V17.251C24.25 17.8477 24.0129 18.42 23.591 18.842C23.169 19.2639 22.5967 19.501 22 19.501H11.7498L5.95 23.851C5.82038 23.9487 5.66235 24.0007 5.5 24.0002ZM2.5 1.50023C2.30109 1.50023 2.11032 1.57924 1.96967 1.7199C1.82902 1.86055 1.75 2.05131 1.75 2.25023V17.2502C1.75 17.4491 1.82902 17.6399 1.96967 17.7806C2.11032 17.9212 2.30109 18.0002 2.5 18.0002H5.5C5.69891 18.0002 5.88968 18.0792 6.03033 18.2199C6.17098 18.3605 6.25 18.5513 6.25 18.7502V21.7502L11.05 18.1502C11.1796 18.0525 11.3377 17.9998 11.5 18.0002H22C22.1989 18.0002 22.3897 17.9212 22.5303 17.7806C22.671 17.6399 22.75 17.4491 22.75 17.2502V2.25023C22.75 2.05131 22.671 1.86055 22.5303 1.7199C22.3897 1.57924 22.1989 1.50023 22 1.50023H2.5ZM14.2502 15.25H13.45L13.4042 15.2507C12.8029 15.2507 12.2253 15.0156 11.7949 14.5957C11.3644 14.1757 11.1151 13.6042 11.1003 13.003V9.24998H10.2498C10.0508 9.24998 9.86007 9.17096 9.71942 9.03031C9.57877 8.88965 9.49975 8.69889 9.49975 8.49998C9.49975 8.30106 9.57877 8.1103 9.71942 7.96965C9.86007 7.82899 10.0508 7.74998 10.2498 7.74998H11.0725C11.9065 7.74998 12.5845 8.41823 12.5995 9.24848V13C12.6145 13.2118 12.7123 13.4093 12.8718 13.5496C13.0312 13.69 13.2394 13.7619 13.4515 13.75H13.4493H14.2495C14.4484 13.75 14.6392 13.829 14.7798 13.9696C14.9205 14.1103 14.9995 14.3011 14.9995 14.5C14.9995 14.6989 14.9205 14.8897 14.7798 15.0303C14.6392 15.171 14.4492 15.25 14.2502 15.25ZM11.5 3.75023C11.2527 3.75023 11.011 3.82355 10.8054 3.96093C10.5998 4.09831 10.4395 4.29357 10.3449 4.52203C10.2503 4.75048 10.2255 5.00186 10.2738 5.24439C10.322 5.48691 10.4411 5.70969 10.6159 5.88454C10.7908 6.05939 11.0136 6.17846 11.2561 6.2267C11.4986 6.27494 11.75 6.25019 11.9785 6.15556C12.2069 6.06093 12.4022 5.90068 12.5395 5.69508C12.6769 5.48948 12.7502 5.24775 12.7502 5.00048C12.7502 4.31048 12.1908 3.75023 11.5 3.75023Z" fill="#5A5A5D"/> + </symbol> + <symbol id="arrow" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg"> + <path d="M2.49951 6.99949L9.56826 6.99949L7.62463 9.16586C7.54588 9.25399 7.49751 9.37136 7.49751 9.49961C7.49751 9.77674 7.72213 10.0017 7.99963 10.0017C8.14851 10.0017 8.28238 9.93686 8.37426 9.83411L8.37463 9.83374L10.7259 7.20874C10.8958 7.02461 10.9996 6.77786 10.9996 6.50636C10.9996 6.50411 10.9996 6.50186 10.9996 6.49961L10.9996 6.49999C10.9996 6.49774 10.9996 6.49474 10.9996 6.49211C10.9996 6.22249 10.8961 5.97686 10.7266 5.79311L10.7274 5.79386L8.37501 3.16624C8.28276 3.06274 8.14888 2.99786 8.00001 2.99786C7.72288 2.99786 7.49788 3.22249 7.49788 3.49999C7.49788 3.62824 7.54588 3.74561 7.62538 3.83411L7.62501 3.83374L9.57126 6.00011L2.49988 6.00011C2.22388 6.00011 2.00001 6.22399 2.00001 6.49999C2.00001 6.77599 2.22388 6.99986 2.49988 6.99986L2.49951 6.99949Z" /> + </symbol> + <symbol id="toggler" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg"> + <path d="M6.00001 9.62487C5.90153 9.62496 5.80401 9.60555 5.71307 9.56777C5.62213 9.52999 5.53956 9.47458 5.47013 9.40475L0.108758 4.01487C0.0393029 3.94408 0.000650096 3.8487 0.00122707 3.74953C0.00180405 3.65036 0.041564 3.55544 0.111838 3.48546C0.182113 3.41548 0.277202 3.37612 0.376376 3.37597C0.475549 3.37581 0.570763 3.41487 0.641258 3.48462L6.00001 8.8745L11.3588 3.48462C11.3935 3.44923 11.4349 3.42106 11.4805 3.40174C11.5262 3.38242 11.5752 3.37232 11.6248 3.37203C11.6744 3.37174 11.7235 3.38127 11.7694 3.40006C11.8153 3.41885 11.857 3.44653 11.8921 3.48152C11.9272 3.5165 11.9551 3.55809 11.9741 3.60389C11.9931 3.64969 12.0028 3.69879 12.0027 3.74837C12.0026 3.79795 11.9928 3.84702 11.9736 3.89276C11.9545 3.9385 11.9265 3.98 11.8913 4.01487L6.53138 9.40362C6.39676 9.54012 6.20963 9.62487 6.00263 9.62487H6.00001Z" /> + </symbol> + <symbol id="content-draft" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> + <path d="M4.55005 12.2033C4.47484 12.203 4.40072 12.1854 4.33347 12.1517C4.26621 12.1181 4.20763 12.0694 4.1623 12.0093C4.11698 11.9493 4.08612 11.8797 4.07214 11.8058C4.05816 11.7319 4.06144 11.6558 4.08172 11.5833L4.73005 9.31667C4.75249 9.23726 4.79496 9.16495 4.85338 9.10667L9.48005 4.48C9.55517 4.40441 9.6445 4.34443 9.7429 4.30349C9.84129 4.26256 9.94681 4.24148 10.0534 4.24148C10.16 4.24148 10.2655 4.26256 10.3639 4.30349C10.4623 4.34443 10.5516 4.40441 10.6267 4.48L11.7934 5.64667C11.9447 5.79877 12.0297 6.0046 12.0297 6.21917C12.0297 6.43373 11.9447 6.63957 11.7934 6.79167L7.16672 11.4133C7.10748 11.4714 7.03474 11.5137 6.95505 11.5367L4.68338 12.1833C4.64016 12.1965 4.59523 12.2032 4.55005 12.2033ZM5.63005 9.705L5.25672 11.0083L6.56005 10.6367L10.9851 6.21167L10.0534 5.28167L5.63005 9.705Z" /> + <path d="M10.3767 8C10.313 8.00028 10.2498 7.98791 10.1908 7.96359C10.1319 7.93927 10.0784 7.90349 10.0334 7.85833L8.41505 6.23333C8.36633 6.18907 8.3271 6.13539 8.29972 6.07553C8.27235 6.01567 8.2574 5.95088 8.25578 5.88507C8.25416 5.81927 8.26591 5.75383 8.29032 5.69269C8.31472 5.63156 8.35127 5.57602 8.39776 5.52942C8.44425 5.48282 8.4997 5.44613 8.56077 5.42158C8.62185 5.39703 8.68726 5.38512 8.75307 5.38658C8.81888 5.38803 8.8837 5.40282 8.94363 5.43005C9.00356 5.45728 9.05734 5.49639 9.10172 5.545L10.72 7.16667C10.7653 7.21186 10.8011 7.26551 10.8256 7.32457C10.85 7.38362 10.8626 7.44691 10.8626 7.51083C10.8626 7.57475 10.85 7.63805 10.8256 7.6971C10.8011 7.75615 10.7653 7.80981 10.72 7.855C10.6753 7.90076 10.6219 7.93716 10.563 7.96205C10.504 7.98695 10.4407 7.99985 10.3767 8Z" /> + <path d="M6.81505 11.555C6.75132 11.5551 6.6882 11.5426 6.62928 11.5183C6.57037 11.494 6.51683 11.4583 6.47172 11.4133L4.85338 9.795C4.8083 9.74991 4.77253 9.69639 4.74813 9.63748C4.72373 9.57857 4.71117 9.51543 4.71117 9.45167C4.71117 9.3879 4.72373 9.32477 4.74813 9.26586C4.77253 9.20695 4.8083 9.15342 4.85338 9.10833C4.89847 9.06325 4.952 9.02748 5.01091 9.00308C5.06981 8.97868 5.13295 8.96612 5.19672 8.96612C5.26048 8.96612 5.32362 8.97868 5.38253 9.00308C5.44144 9.02748 5.49496 9.06325 5.54005 9.10833L7.16672 10.7267C7.23656 10.7943 7.28446 10.8814 7.3042 10.9766C7.32394 11.0718 7.31461 11.1707 7.27742 11.2606C7.24023 11.3504 7.17689 11.427 7.09563 11.4804C7.01437 11.5338 6.91894 11.5615 6.82172 11.56L6.81505 11.555Z" /> + <path d="M2.00001 16C1.46958 16 0.960867 15.7893 0.585795 15.4142C0.210722 15.0391 8.21321e-06 14.5304 8.21321e-06 14V2C-0.000747941 1.7368 0.050715 1.47607 0.151417 1.23289C0.25212 0.98972 0.400061 0.768939 0.586675 0.583333C0.96265 0.210454 1.47048 0.000852348 2.00001 4.02488e-08H11.5867C11.9841 -9.19668e-05 12.3654 0.15756 12.6467 0.438333L15.5617 3.35333C15.8424 3.63464 16.0001 4.01588 16 4.41333V14C16 14.5304 15.7893 15.0391 15.4142 15.4142C15.0391 15.7893 14.5304 16 14 16H2.00001ZM2.00001 1C1.73534 1.00087 1.48158 1.10561 1.29334 1.29167C1.20026 1.38464 1.12643 1.49506 1.0761 1.61661C1.02576 1.73815 0.999905 1.86844 1.00001 2V14C1.00001 14.2652 1.10537 14.5196 1.2929 14.7071C1.48044 14.8946 1.73479 15 2.00001 15H14C14.2652 15 14.5196 14.8946 14.7071 14.7071C14.8947 14.5196 15 14.2652 15 14V4.41333C14.9995 4.28135 14.9467 4.15494 14.8533 4.06167L11.94 1.14667C11.8463 1.05287 11.7192 1.00012 11.5867 1H2.00001Z" /> + <path d="M15.5 5H12.5C12.1022 5 11.7207 4.84197 11.4394 4.56066C11.1581 4.27936 11 3.89783 11 3.5V0.5C11 0.367392 11.0527 0.240215 11.1465 0.146447C11.2403 0.0526787 11.3674 2.41498e-07 11.5 2.41498e-07C11.6327 2.41498e-07 11.7598 0.0526787 11.8536 0.146447C11.9474 0.240215 12 0.367392 12 0.5V3.5C12 3.63261 12.0527 3.75979 12.1465 3.85355C12.2403 3.94732 12.3674 4 12.5 4H15.5C15.6327 4 15.7598 4.05268 15.8536 4.14645C15.9474 4.24022 16 4.36739 16 4.5C16 4.63261 15.9474 4.75979 15.8536 4.85355C15.7598 4.94732 15.6327 5 15.5 5Z" /> + <path d="M13.745 13.7667H12.8951V12.9167H13.745V13.7667Z" /> + <path d="M11.8067 13.7667H10.72V13.0667H11.8067V13.7667ZM9.63172 13.7667H8.54338V13.0667H9.63172V13.7667ZM7.46505 13.7667H6.36838V13.0667H7.45672L7.46505 13.7667ZM5.28838 13.7667H4.19338V13.0667H5.28005L5.28838 13.7667Z" /> + <path d="M3.12172 13.7667H2.25505V12.9167H3.12172V13.7667Z" /> + <path d="M3.12172 11.8233H2.25505V10.7317H3.12172V11.8233ZM3.12172 9.63833H2.25505V8.54667H3.12172V9.63833ZM3.12172 7.45333H2.25505V6.36167H3.12172V7.45333ZM3.12172 5.26833H2.25505V4.17667H3.12172V5.26833Z" /> + <path d="M2.25505 3.08333V2.23333H3.10505V3.08333H2.25505Z" /> + <path d="M11.8067 2.93333H10.72V2.23333H11.8067V2.93333ZM9.63172 2.93333H8.54338V2.23333H9.63172V2.93333ZM7.46505 2.93333H6.36838V2.23333H7.45672L7.46505 2.93333ZM5.28838 2.93333H4.19338V2.23333H5.28005L5.28838 2.93333Z" /> + <path d="M13.745 11.8233H13.045V10.7317H13.745V11.8233ZM13.745 9.63833H13.045V8.54667H13.745V9.63833ZM13.745 7.45333H13.045V6.36167H13.745V7.45333ZM13.745 5.26833H13.045V4.17667H13.745V5.26833Z" /> + </symbol> + <symbol id="product" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg"> + <path d="M15.7483 4.501C15.7506 4.48173 15.7506 4.46227 15.7483 4.443V4.444L15.7488 4.4225C15.7488 4.415 15.7488 4.4075 15.7478 4.4V4.401C15.7445 4.38528 15.7403 4.36976 15.7353 4.3545L15.7363 4.3575C15.7363 4.341 15.7248 4.3225 15.7163 4.304V4.3V4.2955L14.063 0.802C13.9307 0.559522 13.7356 0.35711 13.4982 0.215999C13.2608 0.074888 12.9898 0.000279316 12.7136 0L12.6851 0.0005H12.6866H3.78726C3.50954 0.000474106 3.23697 0.0754386 2.99832 0.217479C2.75967 0.35952 2.56379 0.563367 2.43137 0.8075L2.42737 0.8155L0.795996 4.287V4.2955C0.795996 4.314 0.782497 4.332 0.775998 4.349C0.771425 4.36224 0.767751 4.37577 0.764999 4.3895L0.764499 4.392L0.763999 4.4135C0.763999 4.421 0.763999 4.4285 0.764999 4.436V4.435C0.757725 4.45556 0.752696 4.47685 0.75 4.4985V15.5C0.75 15.6326 0.802674 15.7598 0.896435 15.8536C0.990196 15.9473 1.11736 16 1.24996 16H15.2489C15.3815 16 15.5086 15.9473 15.6024 15.8536C15.6962 15.7598 15.7488 15.6326 15.7488 15.5V4.5L15.7483 4.501ZM12.6851 1.001L12.7131 1.0005C12.9005 1.0005 13.066 1.095 13.164 1.2395L13.165 1.2415L14.4614 4.0015H8.74837V1.0015L12.6851 1.001ZM3.3258 1.256C3.37694 1.17378 3.44918 1.10676 3.53499 1.06192C3.6208 1.01707 3.71706 0.996037 3.81376 1.001H3.81226H7.74845V4.001H2.0354L3.3258 1.256ZM14.7484 15.001H1.74942V5.001H14.7484V15.001ZM12.7486 13.001H9.7488C9.6162 13.001 9.48903 12.9483 9.39527 12.8546C9.30151 12.7608 9.24884 12.6336 9.24884 12.501C9.24884 12.3684 9.30151 12.2412 9.39527 12.1474C9.48903 12.0537 9.6162 12.001 9.7488 12.001H12.7486C12.8812 12.001 13.0083 12.0537 13.1021 12.1474C13.1958 12.2412 13.2485 12.3684 13.2485 12.501C13.2485 12.6336 13.1958 12.7608 13.1021 12.8546C13.0083 12.9483 12.8812 13.001 12.7486 13.001Z" /> + </symbol> + <symbol id="cart" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg"> + <path d="M9.66643 16.0005C9.3038 16.0005 8.94931 15.893 8.64779 15.6915C8.34627 15.49 8.11127 15.2037 7.9725 14.8686C7.83372 14.5336 7.79741 14.165 7.86816 13.8093C7.93891 13.4536 8.11353 13.1269 8.36995 12.8705C8.62637 12.6141 8.95307 12.4395 9.30873 12.3687C9.66439 12.298 10.033 12.3343 10.3681 12.4731C10.7031 12.6118 10.9895 12.8468 11.1909 13.1483C11.3924 13.4499 11.4999 13.8044 11.4999 14.167C11.4999 14.6533 11.3068 15.1196 10.9629 15.4635C10.6191 15.8073 10.1527 16.0005 9.66643 16.0005ZM9.66643 13.334C9.50158 13.334 9.34043 13.3829 9.20336 13.4745C9.06629 13.566 8.95946 13.6962 8.89637 13.8485C8.83329 14.0008 8.81678 14.1684 8.84894 14.3301C8.8811 14.4918 8.96049 14.6403 9.07705 14.7569C9.19362 14.8734 9.34214 14.9528 9.50382 14.985C9.6655 15.0171 9.83309 15.0006 9.9854 14.9375C10.1377 14.8745 10.2679 14.7676 10.3595 14.6306C10.451 14.4935 10.4999 14.3323 10.4999 14.1675C10.4999 13.7075 10.1269 13.334 9.66643 13.334ZM4.66643 16.0005C4.3038 16.0005 3.94931 15.893 3.64779 15.6915C3.34627 15.49 3.11127 15.2037 2.9725 14.8686C2.83372 14.5336 2.79741 14.165 2.86816 13.8093C2.93891 13.4536 3.11353 13.1269 3.36995 12.8705C3.62637 12.6141 3.95307 12.4395 4.30873 12.3687C4.66439 12.298 5.03305 12.3343 5.36808 12.4731C5.70311 12.6118 5.98946 12.8468 6.19093 13.1483C6.3924 13.4499 6.49993 13.8044 6.49993 14.167C6.49993 14.6533 6.30676 15.1196 5.96291 15.4635C5.61906 15.8073 5.1527 16.0005 4.66643 16.0005ZM4.66643 13.334C4.50158 13.334 4.34043 13.3829 4.20336 13.4745C4.06629 13.566 3.95946 13.6962 3.89638 13.8485C3.83329 14.0008 3.81678 14.1684 3.84894 14.3301C3.88111 14.4918 3.96049 14.6403 4.07706 14.7569C4.19362 14.8734 4.34214 14.9528 4.50382 14.985C4.6655 15.0171 4.83309 15.0006 4.9854 14.9375C5.1377 14.8745 5.26787 14.7676 5.35946 14.6306C5.45105 14.4935 5.49993 14.3323 5.49993 14.1675C5.49993 13.7075 5.12693 13.334 4.66643 13.334ZM12.1814 10.217L14.3479 2.08048C14.4039 1.86809 14.5282 1.67998 14.7015 1.5451C14.8749 1.41022 15.0878 1.33604 15.3074 1.33398H15.9994C16.132 1.33398 16.2592 1.28131 16.353 1.18754C16.4467 1.09377 16.4994 0.966593 16.4994 0.833984C16.4994 0.701376 16.4467 0.574199 16.353 0.480431C16.2592 0.386663 16.132 0.333984 15.9994 0.333984H15.3079C14.8689 0.334182 14.442 0.478791 14.0933 0.745521C13.7445 1.01225 13.4931 1.38629 13.3779 1.80998L13.3749 1.82398L11.2084 9.96048C11.1509 10.175 10.9599 10.3305 10.7319 10.334H3.51843C3.40612 10.3339 3.2971 10.2961 3.20894 10.2265C3.12077 10.1569 3.0586 10.0597 3.03243 9.95048L3.03193 9.94698L1.48043 4.66698C1.46162 4.60402 1.43059 4.54537 1.38911 4.49439C1.34763 4.44342 1.29652 4.40111 1.23869 4.36988C1.18087 4.33866 1.11745 4.31913 1.05208 4.31241C0.986705 4.3057 0.920648 4.31192 0.857679 4.33073C0.79471 4.34955 0.736063 4.38058 0.685086 4.42205C0.63411 4.46353 0.591802 4.51464 0.560578 4.57247C0.529354 4.6303 0.509827 4.69371 0.503109 4.75908C0.496392 4.82446 0.502617 4.89052 0.521429 4.95348L0.520429 4.94998L2.07043 10.215C2.15513 10.5347 2.34292 10.8176 2.6047 11.0199C2.86648 11.2221 3.18765 11.3323 3.51843 11.3335H10.7319C11.0611 11.3334 11.3811 11.225 11.6426 11.0252C11.9042 10.8253 12.0928 10.545 12.1794 10.2275L12.1819 10.217H12.1814Z" /> + </symbol> + <symbol id="profile" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg"> + <path d="M8.25007 11.9976C10.4218 11.9976 12.4057 13.2286 13.3701 15.1744C13.5102 15.4572 13.3946 15.8 13.1118 15.9402C12.829 16.0803 12.4862 15.9647 12.3461 15.6819C11.5746 14.1253 9.98741 13.1405 8.25007 13.1405C6.51273 13.1405 4.92555 14.1253 4.15407 15.6819C4.01393 15.9647 3.6711 16.0803 3.38833 15.9402C3.10556 15.8 2.98993 15.4572 3.13007 15.1744C4.09441 13.2286 6.07839 11.9976 8.25007 11.9976ZM13.4625 2.02738C16.4117 4.73056 16.8051 9.23979 14.3684 12.4128C14.1762 12.6631 13.8175 12.7102 13.5672 12.518C13.3169 12.3257 13.2698 11.967 13.462 11.7167C15.5377 9.01379 15.2026 5.17259 12.6903 2.86989C10.178 0.56718 6.32218 0.56718 3.80986 2.86989C1.29754 5.17259 0.962468 9.01379 3.03814 11.7167C3.23036 11.967 3.18327 12.3257 2.93297 12.518C2.68266 12.7102 2.32393 12.6631 2.13172 12.4128C-0.304942 9.23979 0.0884042 4.73056 3.03765 2.02738C5.98689 -0.675795 10.5132 -0.675795 13.4625 2.02738ZM8.25007 4.57104C9.98582 4.57104 11.3929 5.97814 11.3929 7.7139C11.3929 9.44965 9.98582 10.8568 8.25007 10.8568C6.51432 10.8568 5.10721 9.44965 5.10721 7.7139C5.10721 5.97814 6.51432 4.57104 8.25007 4.57104ZM8.25007 5.7139C7.1455 5.7139 6.25007 6.60933 6.25007 7.7139C6.25007 8.81847 7.1455 9.71389 8.25007 9.71389C9.35464 9.71389 10.2501 8.81847 10.2501 7.7139C10.2501 6.60933 9.35464 5.7139 8.25007 5.7139Z" /> + </symbol> + <symbol id="search" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> + <path d="M15.805 14.861L11.2585 10.3145C12.1355 9.23652 12.6665 7.84652 12.6665 6.33252C12.6665 2.83452 9.83101 -0.000976562 6.33301 -0.000976562C2.83501 -0.000976562 -0.000488281 2.83452 -0.000488281 6.33252C-0.000488281 9.83052 2.83501 12.666 6.33301 12.666C7.84701 12.666 9.23701 12.135 10.3265 11.2485L10.315 11.2575L14.8615 15.804C14.982 15.9245 15.149 15.9995 15.333 15.9995C15.7015 15.9995 16 15.701 16 15.3325C16 15.1485 15.9255 14.9815 15.8045 14.861V14.861H15.805ZM1.33351 6.33302C1.33351 6.33252 1.33351 6.33202 1.33351 6.33102C1.33351 3.56952 3.57201 1.33102 6.33351 1.33102C9.09501 1.33102 11.3335 3.56952 11.3335 6.33102C11.3335 7.68452 10.796 8.91202 9.92251 9.81252L9.92351 9.81102C9.90351 9.82752 9.88201 9.84252 9.86201 9.86102C9.84501 9.87952 9.82851 9.89952 9.81351 9.92102L9.81201 9.92302C8.91301 10.794 7.68601 11.331 6.33351 11.331C3.57251 11.331 1.33451 9.09352 1.33351 6.33302V6.33302V6.33302Z" fill="white" /> + </symbol> + +</svg> \ No newline at end of file diff --git a/docs/images/minus.svg b/docs/images/minus.svg new file mode 100644 index 0000000000..5334d01101 --- /dev/null +++ b/docs/images/minus.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="15px" viewBox="0 0 24 24" fill="#ae1164"> + <path d="M19,13H5V11H19V13Z"></path> +</svg> diff --git a/docs/images/notification-image.png b/docs/images/notification-image.png new file mode 100644 index 0000000000..ebab5e4a9c Binary files /dev/null and b/docs/images/notification-image.png differ diff --git a/docs/images/number_one.png b/docs/images/number_one.png new file mode 100644 index 0000000000..4013bf0e6a Binary files /dev/null and b/docs/images/number_one.png differ diff --git a/docs/images/one.png b/docs/images/one.png new file mode 100644 index 0000000000..485cdbc626 Binary files /dev/null and b/docs/images/one.png differ diff --git a/docs/images/open-in-new.svg b/docs/images/open-in-new.svg new file mode 100644 index 0000000000..24f9c80777 --- /dev/null +++ b/docs/images/open-in-new.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="12px" height="15px" viewBox="0 0 30 30" fill="rgb(0,71,206)"><path d="M14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3m-2 16H5V5h7V3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7h-2v7z"/></svg> diff --git a/docs/images/page_lg.svg b/docs/images/page_lg.svg new file mode 100644 index 0000000000..7445daca17 --- /dev/null +++ b/docs/images/page_lg.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="15px" viewBox="0 0 30 30" fill="#ae1164"> + <path d="M8.667 16.667c0-0.736 0.597-1.333 1.333-1.333v0h6.667c0.736 0 1.333 0.597 1.333 1.333s-0.597 1.333-1.333 1.333v0h-6.667c-0.736 0-1.333-0.597-1.333-1.333v0zM10 22.667h12c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0zM22 24.667h-12c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h12c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0zM27.667 9.733l-8.21-9.283c-0.245-0.277-0.602-0.45-0.999-0.45-0 0-0.001 0-0.001 0h-13.123c-0.736 0-1.333 0.597-1.333 1.333v0 29.333c0 0.736 0.597 1.333 1.333 1.333v0h21.333c0.736 0 1.333-0.597 1.333-1.333v0-20.050c0-0 0-0.001 0-0.001 0-0.339-0.126-0.648-0.335-0.883l0.001 0.001zM24.93 10.667h-3.597c-1.92 0-2.667-0.753-2.667-2.687v-4.397zM6.667 29.333v-26.667h9.333v5.313c0 3.403 1.953 5.353 5.333 5.353h4v16z"></path> +</svg> diff --git a/docs/images/plus.svg b/docs/images/plus.svg new file mode 100644 index 0000000000..6213eefa62 --- /dev/null +++ b/docs/images/plus.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12px" height="15px" viewBox="0 0 30 30" fill="#ae1164"> + <path d="M27.333 14.667h-10v-10c0-0.736-0.597-1.333-1.333-1.333s-1.333 0.597-1.333 1.333v0 10h-10c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333v0h10v10c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v0-10h10c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333v0z"></path> +</svg> diff --git a/docs/images/product.png b/docs/images/product.png new file mode 100644 index 0000000000..7135b813c6 Binary files /dev/null and b/docs/images/product.png differ diff --git a/docs/images/three.png b/docs/images/three.png new file mode 100644 index 0000000000..337925e5dc Binary files /dev/null and b/docs/images/three.png differ diff --git a/docs/images/two.png b/docs/images/two.png new file mode 100644 index 0000000000..b0b50a4189 Binary files /dev/null and b/docs/images/two.png differ diff --git a/docs/images/user.png b/docs/images/user.png new file mode 100644 index 0000000000..5d47ff34d9 Binary files /dev/null and b/docs/images/user.png differ diff --git a/docs/index.md b/docs/index.md index b0e43bed23..86ed719006 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,109 +1,217 @@ <div class="front-page"> <div class="row"> - <h1>Ibexa developer documentation</h1> - </div> - <div class="row mt-5 pb-4"> - <div class="col-lg px-2 px-lg-4"> - <div class="tile"> - <div class="row"> - <div class="col-lg"> - <h5 class="tile-title"> - <svg class="tile-icon align-middle" width="32" height="32"> - <use fill="var(--ibexa-jazzberry)" xlink:href="images/ez-icons.svg#publish"></use> - </svg> - Installation - </h5> - <div class="tile-body"> - <ol> - <li><a href="getting_started/requirements/">Requirements</a></li> - <li><a href="getting_started/install_ez_platform/">Install Ibexa Platform</a></li> - <li><a href="getting_started/first_steps/">First steps</a></li> - </ol> - </div> + <div class="col-12"> + <h1>Ibexa Developer Documentation</h1> + <h2>How to start?</h2> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <a class="info-tile" href="getting_started/requirements/"> + <div class="info-tile__circle" style="background-color: #fff7ec;"> + <svg width="20" height="25"><use xlink:href="images/icons.svg#check-requirements" /></svg> + </div> + <div class="info-tile__content"> + <div class="info-tile__details"> + Details + <svg class="info-tile__arrow-icon"><use xlink:href="images/icons.svg#arrow" /></svg> + </div> + <div> + Check the <strong>Requirements</strong> + </div> + </div> + </a> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <a class="info-tile" href="getting_started/install_ibexa_dxp/"> + <div class="info-tile__circle" style="background-color: #f6e7ef;"> + <svg width="25" height="25"><use xlink:href="images/icons.svg#install-dxp" /></svg> + </div> + <div class="info-tile__content"> + <div class="info-tile__details"> + Details + <svg class="info-tile__arrow-icon"><use xlink:href="images/icons.svg#arrow" /></svg> + </div> + <div> + Install <strong>Ibexa DXP</strong> + </div> + </div> + </a> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <a class="info-tile" href="getting_started/install_on_ibexa_cloud/"> + <div class="info-tile__circle" style="background-color: #ecf8fb;"> + <svg width="25" height="18"><use xlink:href="images/icons.svg#install-cloud" /></svg> + </div> + <div class="info-tile__content"> + <div class="info-tile__details"> + Details + <svg class="info-tile__arrow-icon"><use xlink:href="images/icons.svg#arrow" /></svg> </div> + <div> + Install on <strong>Ibexa Cloud</strong> + </div> + </div> + </a> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <a class="info-tile" href="getting_started/first_steps/"> + <div class="info-tile__circle" style="background-color: #f3f3f6;"> + <svg width="25" height="24"><use xlink:href="images/icons.svg#first-steps" /></svg> + </div> + <div class="info-tile__content"> + <div class="info-tile__details"> + Details + <svg class="info-tile__arrow-icon"><use xlink:href="images/icons.svg#arrow" /></svg> + </div> + <div> + Go through the <strong>First steps</strong> + </div> + </div> + </a> + </div> + </div> + <div class="row"> + <div class="col-12"> + <div class="notification" id="tile2"> + <div class="notification__content"> + <h2>The latest release is v4.5</h2> + <div>The <a href="releases/ibexa_dxp_v4.5/">latest version of Ibexa DXP is v4.5</a>. You can now update your application to the latest version.</div> + </div> + <div class="notification__cta"> + <a href="updating/from_4.4/update_from_4.4/">Update your application</a> + </div> + <div class="notification__image"> + <img src="images/notification-image.png" alt="The latest release" /> </div> </div> </div> - <div class="col-lg px-2"> - <div class="tile"> - <div class="row"> - <div class="col-lg"> - <h5 class="tile-title"> - <svg class="tile-icon align-middle" width="32" height="32"> - <use fill="var(--ibexa-jazzberry)" xlink:href="images/ez-icons.svg#about"></use> - </svg> - Tutorials - </h5> - <div class="tile-body"> + <div class="col-12"> + <div class="accordion"> + <details> + <summary> + <h2>Notable changes in v4.5</h2> + <div class="accordion__toggler"> + <svg><use xlink:href="images/icons.svg#toggler" /></svg> + </div> + </summary> + <div class="row"> + <div class="col-12 col-lg-6 col-fhd-3"> <ul> - <li><a href="tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform/">Platform beginner</a></li> - <li><a href="tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world/">DXP beginner</a></li> - <li><a href="tutorials/generic_field_type/creating_a_point2d_field_type/">Generic Field Type</a></li> - <li><a href="tutorials/extending_admin_ui/extending_admin_ui/">Extending Admin UI</a></li> + <li><a href="releases/ibexa_dxp_v4.5/#all-new-ibexa-commerce-packages">All-new Ibexa Commerce packages</a></li> + <li><a href="releases/ibexa_dxp_v4.5/#new-commerce-page-blocks">New commerce page blocks</a></li> + <li><a href="releases/ibexa_dxp_v4.5/#page-builder-for-b2b-portals">Page Builder for B2B portals</a></li> + </ul> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <ul> + <li><a href="releases/ibexa_dxp_v4.5/#personalization-improvements">Personalization improvements</a></li> + <li><a href="releases/ibexa_dxp_v4.5/#customer-data-platform-cdp-configuration">Customer Data Platform (CDP) configuration</a></li> + <li><a href="releases/ibexa_dxp_v4.5/#api-improvements">API improvements</a></li> </ul> </div> </div> - </div> + </details> </div> </div> - <div class="col-lg px-2 px-lg-4"> - <div class="tile"> - <div class="row"> - <div class="col-lg"> - <h5 class="tile-title"> - <svg class="tile-icon align-middle" width="32" height="32"> - <use fill="var(--ibexa-jazzberry)" xlink:href="images/ez-icons.svg#settings-config"></use> - </svg> - Guide to Ibexa DXP - </h5> - <div class="tile-body"> - <ul> - <li><a href="guide/content_rendering/">Content rendering</a></li> - <li><a href="guide/search/search/">Search</a></li> - <li><a href="releases/ibexa_dxp_v3.2/">Releases</a></li> - <li><a href="api/public_php_api/">API</a></li> - </ul> + <div class="col-12"> + <div class="accordion"> + <details> + <summary> + <h2>Most popular pages</h2> + <div class="accordion__toggler"> + <svg><use xlink:href="images/icons.svg#toggler" /></svg> + </div> + </summary> + <div class="row"> + <div class="col-12 col-lg-6 col-fhd-3"> + <ul> + <li><a href="api/php_api/php_api/">PHP API</a></li> + <li><a href="search/solr_search_engine/">Solr search engine</a></li> + <li><a href="search/search_api/">Search API</a></li> + </ul> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <ul> + <li><a href="content_management/content_model/">Content model</a></li> + <li><a href="content_management/images/images/">Images</a></li> + <li><a href="content_management/pages/page_blocks/">Page blocks</a></li> + </ul> + </div> </div> - </div> + </details> </div> </div> </div> - </div> - <div class="row mt-5"> - <div class="col-lg-4 mb-5 most-popular"> - <h5> - Most popular topics - <svg class="tile-icon" width="15" height="15"> - <use fill="var(--ibexa-jazzberry)" xlink:href="images/ez-icons.svg#bookmark-active"></use> - </svg> - </h5> - <ul> - <li><a href="guide/twig_functions_reference/">Twig functions</a></li> - <li><a href="guide/http_cache/">HTTP cache</a></li> - <li><a href="guide/search/solr/">Solr search engine</a></li> - <li><a href="guide/images/">Images</a></li> - </ul> + <div class="row"> + <div class="col-12"> + <h2>Manage your DXP</h2> </div> - <div class="col-lg-8 mb-5 latest-release"> - <h5> - The latest release - <span class="badge">v3.2</span> - </h5> - <div class="row mt-3"> - <div class="col-lg-5">The <a href="releases/ibexa_dxp_v3.2/">latest version of Ibexa DXP is v3.2</a>. It is a Fast Track release, and it was released on October 23, 2020. + <div class="col-12 col-lg-6 col-fhd-3"> + <div class="info-tile info-tile--link-card"> + <div class="info-tile__content"> + <h3> + <a href="content_management/content_management/"> + <svg><use xlink:href="images/icons.svg#content-draft" /></svg> + Content + </a> + </h3> + <ul> + <li><a href="content_management/content_model/">Content model</a></li> + <li><a href="content_management/file_management/file_management/">File management</a></li> + <li><a href="guide/page/page_blocks/">Page blocks</a></li> + </ul> </div> - <div class="col-sm-7 features"> + </div> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <div class="info-tile info-tile--link-card"> + <div class="info-tile__content"> + <h3> + <a href="guide/catalog/product/"> + <svg><use xlink:href="images/icons.svg#product" /></svg> + Product + </a> + </h3> + <ul> + <li><a href="guide/basket/basket_configuration/">Basket configuration</a></li> + <li><a href="guide/catalog/catalog/">Catalogs</a></li> + <li><a href="guide/basket/calculating_prices/">Prices</a></li> + </ul> + </div> + </div> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <div class="info-tile info-tile--link-card"> + <div class="info-tile__content"> + <h3> + <a href="guide/shop_process/"> + <svg><use xlink:href="images/icons.svg#cart" /></svg> + Commerce + </a> + </h3> + <ul> + <li><a href="guide/basket/basket/">Basket</a></li> + <li><a href="guide/payment/payment/">Payment</a></li> + <li><a href="guide/checkout/checkout/">Checkout</a></li> + </ul> + </div> + </div> + </div> + <div class="col-12 col-lg-6 col-fhd-3"> + <div class="info-tile info-tile--link-card"> + <div class="info-tile__content"> + <h3> + <a href="cdp/cdp/"> + <svg><use xlink:href="images/icons.svg#profile" /></svg> + Customer + </a> + </h3> <ul> - <li><a href="releases/ibexa_dxp_v3.2/#new-ui">New Back Office UI</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#dam-connector">DAM connector</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#aggregation-api">Aggregation API</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#targeting-block-and-segmentation-api">Targeting block</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#autosave">Autosave</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#twig-helpers-for-content-rendering">New Twig helpers</a></li> - <li><a href="releases/ibexa_dxp_v3.2/#jwt-authentication">JWT authentication</a></li> + <li><a href="guide/customers/customer_templates/">Customer templates</a></li> + <li><a href="guide/customers/customer_api/customer_api/">Customer API</a></li> + <li><a href="guide/customers/customer_api/customer_profile_data/">Customer profile data</a></li> </ul> </div> </div> </div> </div> -</div> +</div> \ No newline at end of file diff --git a/docs/js/custom.js b/docs/js/custom.js index d569c852b9..bb62bee446 100644 --- a/docs/js/custom.js +++ b/docs/js/custom.js @@ -1,11 +1,7 @@ // tmp fix for read-the-docs embeded versions injection let jquery = jQuery; -$(document).ready(function () { - $(document).on("click", "[data-toggle='rst-current-version']", function() { - $('.rst-other-versions').toggle(); - }); - +$(document).ready(function() { // replace edit url var branchName = 'master', branchNameRegexp = /\/en\/([a-z0-9-_.]*)\//g.exec(document.location.href); @@ -15,60 +11,52 @@ $(document).ready(function () { } $('.md-content a.md-icon').each(function() { - $(this).attr('href', $(this).attr('href').replace('master/docs/', branchName + '/docs/')); + $(this).attr( + 'href', + $(this) + .attr('href') + .replace('master/docs/', branchName + '/docs/') + ); }); if (!/^\d+\.\d+$/.test(branchName) && branchName !== 'latest') { branchName = 'master'; } - if (typeof window.doc_version_warning !== 'undefined') { - var doc_version_warning = window.doc_version_warning, - warningMessage = ''; - - if ($.inArray(branchName, doc_version_warning.previous_lts) !== -1) { - warningMessage = 'You are viewing documentation for an older Long-Term Support release. The latest LTS release is <span class="version">' + doc_version_warning.lts[0] + '</span>.'; - } - - if ($.inArray(branchName, doc_version_warning.ft) !== -1) { - warningMessage = 'You are viewing documentation for an older Fast Track release. The latest release is <span class="version">' + doc_version_warning.latest[0] + '</span>.'; - } + // Add version pill to top of navigation + $('#site-name').append('<span class="pill">' + branchName + '</span>'); - if ($.inArray(branchName, doc_version_warning.dev) !== -1) { - warningMessage = 'You are viewing documentation for a development version. The latest stable release is <span class="version">' + doc_version_warning.latest[0] + '</span>.'; - } + $('.rst-current-version.switcher__label').html(branchName); - if (warningMessage) { - $("article").prepend($( - '<div class="md-typeset admonition caution version-warning"> ' + - '<p class="admonition-title">Version warning</p> ' + - '<p> ' + warningMessage + '</p>' + - '</div>' - )); - } - } + // Change navigation icons on onclick + $('.md-nav--primary .md-nav__item--nested .md-nav__link').click(function() { + $(this).addClass('open'); + }); // remove elements, leave only 'versions' var update = setInterval(function() { if ($('.injected .rst-versions').length) { clearInterval(update); - var version = $('.rst-other-versions strong dd a').text(); + var version = $('.rst-other-versions dd.rtd-current-item a').text(); $('.rst-current-version span:first').html(' ' + (version != '' ? version : 'Change version')); $('.rst-other-versions').html($('.injected dl:first').clone()); $('.injected').remove(); //replace url in version switcher - var currentVersion = $('.rst-other-versions strong dd a').attr('href'), + var currentVersion = $('.rst-other-versions dd.rtd-current-item a').attr('href'), resourceUrl = document.location.href.replace(currentVersion, ''); - $('.rst-other-versions dd a').each( function() { + $('.rst-other-versions dd a').each(function() { $(this).attr('href', $(this).attr('href') + resourceUrl); }); if ($('.version-warning').length) { - var url, + var url, version = $('.version-warning .version').html(), - parts = $('.rst-other-versions dd a').first().attr('href').split('/'); + parts = $('.rst-other-versions dd a') + .first() + .attr('href') + .split('/'); parts[4] = version; url = parts.join('/'); @@ -80,39 +68,65 @@ $(document).ready(function () { $('img').each(function() { if ($(this).attr('title')) { - $(this).wrap( "<figure></figure>" ); - $(this).after( "<figcaption>" + $(this).attr('title') + "</figcaption>" ); + $(this).wrap('<figure></figure>'); + $(this).after('<figcaption>' + $(this).attr('title') + '</figcaption>'); } }); - $('.md-content a:not(.md-icon):not(.md-source)').filter(function() { - return this.hostname && this.hostname !== location.hostname; - }).addClass("external"); + $('.md-content a:not(.md-icon):not(.md-source)') + .filter(function() { + return this.hostname && this.hostname !== location.hostname; + }) + .addClass('external'); docsearch({ apiKey: 'bfb5bd7cad971d31ef8be599174334f3', indexName: 'ezplatform', inputSelector: '#search_input', + transformData: function(hits) { + let removedPattern = '¶'; + $.each(hits, function(index, hit) { + for (let lvl=2; lvl<=6; lvl++) { + if (null !== hit.hierarchy['lvl'+lvl]) { + hits[index].hierarchy['lvl' + lvl] = hit.hierarchy['lvl' + lvl].replace(removedPattern, ''); + } + if ('undefined' !== typeof hit._highlightResult.hierarchy['lvl'+lvl]) { + hits[index]._highlightResult.hierarchy['lvl'+lvl].value = hit._highlightResult.hierarchy['lvl'+lvl].value.replace(removedPattern, ''); + } + } + }); + }, algoliaOptions: { - 'facetFilters': ["lang:en", "version:" + branchName], - 'hitsPerPage': 10 + facetFilters: ['lang:en', 'version:' + branchName], + hitsPerPage: 10, }, - debug: false + debug: false, }); - $(document).on("keypress", "#search_input", function(event) { + $(document).on('keypress', '#search_input', function(event) { if (event.keyCode == 13) { event.preventDefault(); } }); - $("#search_input, label.md-search__icon").on("click", function() { - var toggle = document.querySelector("[data-md-toggle=search]"); + $(document).on('blur', '#search_input', function(event) { + setTimeout(() => { + $('#search_input').val(''); + }, 0); + }); + + $('#search_input, label.md-search__icon').on('click', function() { + var toggle = document.querySelector('[data-md-toggle=search]'); toggle.checked = true; }); // Image enlargement modal - $(".md-content img:not(.card-img-top)").click( function(){ + $('body').append('<div id="imageModal"><img class="modal-content" id="enlargedImage"><div id="modalCaption"></div>/div>'); + + //Google Tag Manager code + $('body').prepend('<!-- Google Tag Manager (noscript) --><noscript><iframe src=https://www.googletagmanager.com/ns.html?id=GTM-KKQR5LG height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><!-- End Google Tag Manager (noscript) -->'); + + $('.md-content__inner img').click(function() { $('#enlargedImage').attr('src', $(this).attr('src')); if ($(this).attr('title')) { $('#modalCaption').html($(this).attr('title')); @@ -120,8 +134,25 @@ $(document).ready(function () { $('#imageModal').show(); }); - $("#imageModal").click(function(){ $(this).hide() }); + $('#imageModal').click(function() { + $(this).hide(); + }); - $('.md-sidebar--primary .md-sidebar__scrollwrap')[0].scrollTop = $('.md-sidebar--primary .md-nav__item--active:not(.md-nav__item--nested)')[0].offsetTop - 33; + if ($('.md-sidebar--primary .md-sidebar__scrollwrap')[0] && $('.md-sidebar--primary .md-nav__item--active:not(.md-nav__item--nested)')[0]) { + $('.md-sidebar--primary .md-sidebar__scrollwrap')[0].scrollTop = + $('.md-sidebar--primary .md-nav__item--active:not(.md-nav__item--nested)')[0].offsetTop - 33; + } + // Fix page TOC/hash bug + $('.md-sidebar.md-sidebar--secondary nav a').click(function(event) { + window.setTimeout(function() { + document.location.hash = event.target.hash; + }, 500); + }) + + document.querySelectorAll('.notification__close-btn').forEach((closeBtn) => { + closeBtn.addEventListener('click', () => { + closeBtn.closest('.notification').setAttribute('hidden', 'hidden'); + }); + }); }); diff --git a/docs/js/docs.switcher.js b/docs/js/docs.switcher.js index 7a13c99a7a..60939f572d 100644 --- a/docs/js/docs.switcher.js +++ b/docs/js/docs.switcher.js @@ -1,6 +1,6 @@ (function (doc) { - const CLASS_EXPANDED_LIST = 'ez-docs-switcher__selected-item--expanded'; - const switchers = doc.querySelectorAll('.ez-docs-switcher'); + const CLASS_EXPANDED_LIST = 'switcher__selected-item--expanded'; + const switchers = doc.querySelectorAll('.switcher'); const toggleListExpandedState = (event) => { event.currentTarget.classList.toggle(CLASS_EXPANDED_LIST); }; @@ -11,7 +11,7 @@ }; switchers.forEach((switcher) => { - const selectedItem = switcher.querySelector('.ez-docs-switcher__selected-item'); + const selectedItem = switcher.querySelector('.switcher__selected-item'); selectedItem.addEventListener('click', toggleListExpandedState, false); diff --git a/docs/js/ez-guidelines-modals.js b/docs/js/ez-guidelines-modals.js deleted file mode 100644 index e5b0db6c68..0000000000 --- a/docs/js/ez-guidelines-modals.js +++ /dev/null @@ -1,7 +0,0 @@ -$('.btn-modal-launcher').on("click", function (e) { - $('div').attr('data-backdrop', 'true'); - - $('#trash-location-modal').addClass('modal-backdrop'); - $('#view-notifications').addClass('modal-backdrop'); - $('#ez-modal--custom-url-alias').addClass('modal-backdrop'); -}); diff --git a/docs/js/ez-guidelines-switchers.js b/docs/js/ez-guidelines-switchers.js deleted file mode 100644 index 15ff443872..0000000000 --- a/docs/js/ez-guidelines-switchers.js +++ /dev/null @@ -1,18 +0,0 @@ -(function(global, doc) { - const checkboxIcons = doc.querySelectorAll(".ez-checkbox-icon"); - const toggleChecked = event => { - event.currentTarget.classList.toggle("is-checked"); - event.preventDefault(); - }; - - checkboxIcons.forEach(element => element.addEventListener("click", toggleChecked, false)); - - doc.querySelectorAll(".ez-preview-switcher").forEach(element => { - const CLASS_PREVIEW_ACTION_SELECTED = 'ez-preview-switcher__action--selected'; - const previewActions = [...element.querySelectorAll('.ez-preview-switcher__action')]; - previewActions.forEach(element => element.addEventListener("click", event => { - previewActions.forEach((action) => action.classList.remove(CLASS_PREVIEW_ACTION_SELECTED)); - event.currentTarget.classList.add(CLASS_PREVIEW_ACTION_SELECTED); - }, false)); - }, false); -})(window, window.document); diff --git a/docs/js/ez-guidelines-tooltips.js b/docs/js/ez-guidelines-tooltips.js deleted file mode 100644 index 2852678d04..0000000000 --- a/docs/js/ez-guidelines-tooltips.js +++ /dev/null @@ -1,11 +0,0 @@ -$(function () { - $('[title]').tooltip({ - delay: { show: 150, hide: 75 }, - placement: 'bottom', - template: `<div class="tooltip ez-tooltip"> - <div class="arrow ez-tooltip__arrow"></div> - <div class="tooltip-inner ez-tooltip__inner"></div> - </div>`, - container: ".bootstrap-iso" - }); -}) diff --git a/docs/js/jquery-ui.min.js b/docs/js/jquery-ui.min.js new file mode 100644 index 0000000000..f55d484116 --- /dev/null +++ b/docs/js/jquery-ui.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.1 - 2021-01-20 +* http://jqueryui.com +* Includes: widget.js, position.js, keycode.js, unique-id.js, widgets/tooltip.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +!function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)}(function(C){C.ui=C.ui||{};C.ui.version="1.12.1";var n,i=0,r=Array.prototype.slice;C.cleanData=(n=C.cleanData,function(t){for(var e,i,o=0;null!=(i=t[o]);o++)try{(e=C._data(i,"events"))&&e.remove&&C(i).triggerHandler("remove")}catch(t){}n(t)}),C.widget=function(t,i,e){var o,n,s,l={},r=t.split(".")[0],a=r+"-"+(t=t.split(".")[1]);return e||(e=i,i=C.Widget),C.isArray(e)&&(e=C.extend.apply(null,[{}].concat(e))),C.expr[":"][a.toLowerCase()]=function(t){return!!C.data(t,a)},C[r]=C[r]||{},o=C[r][t],n=C[r][t]=function(t,e){if(!this._createWidget)return new n(t,e);arguments.length&&this._createWidget(t,e)},C.extend(n,o,{version:e.version,_proto:C.extend({},e),_childConstructors:[]}),(s=new i).options=C.widget.extend({},s.options),C.each(e,function(e,o){function n(){return i.prototype[e].apply(this,arguments)}function s(t){return i.prototype[e].apply(this,t)}C.isFunction(o)?l[e]=function(){var t,e=this._super,i=this._superApply;return this._super=n,this._superApply=s,t=o.apply(this,arguments),this._super=e,this._superApply=i,t}:l[e]=o}),n.prototype=C.widget.extend(s,{widgetEventPrefix:o&&s.widgetEventPrefix||t},l,{constructor:n,namespace:r,widgetName:t,widgetFullName:a}),o?(C.each(o._childConstructors,function(t,e){var i=e.prototype;C.widget(i.namespace+"."+i.widgetName,n,e._proto)}),delete o._childConstructors):i._childConstructors.push(n),C.widget.bridge(t,n),n},C.widget.extend=function(t){for(var e,i,o=r.call(arguments,1),n=0,s=o.length;n<s;n++)for(e in o[n])i=o[n][e],o[n].hasOwnProperty(e)&&void 0!==i&&(C.isPlainObject(i)?t[e]=C.isPlainObject(t[e])?C.widget.extend({},t[e],i):C.widget.extend({},i):t[e]=i);return t},C.widget.bridge=function(s,e){var l=e.prototype.widgetFullName||s;C.fn[s]=function(i){var t="string"==typeof i,o=r.call(arguments,1),n=this;return t?this.length||"instance"!==i?this.each(function(){var t,e=C.data(this,l);return"instance"===i?(n=e,!1):e?C.isFunction(e[i])&&"_"!==i.charAt(0)?(t=e[i].apply(e,o))!==e&&void 0!==t?(n=t&&t.jquery?n.pushStack(t.get()):t,!1):void 0:C.error("no such method '"+i+"' for "+s+" widget instance"):C.error("cannot call methods on "+s+" prior to initialization; attempted to call method '"+i+"'")}):n=void 0:(o.length&&(i=C.widget.extend.apply(null,[i].concat(o))),this.each(function(){var t=C.data(this,l);t?(t.option(i||{}),t._init&&t._init()):C.data(this,l,new e(i,this))})),n}},C.Widget=function(){},C.Widget._childConstructors=[],C.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=C(e||this.defaultElement||this)[0],this.element=C(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=C(),this.hoverable=C(),this.focusable=C(),this.classesElementLookup={},e!==this&&(C.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=C(e.style?e.ownerDocument:e.document||e),this.window=C(this.document[0].defaultView||this.document[0].parentWindow)),this.options=C.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:C.noop,_create:C.noop,_init:C.noop,destroy:function(){var i=this;this._destroy(),C.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:C.noop,widget:function(){return this.element},option:function(t,e){var i,o,n,s=t;if(0===arguments.length)return C.widget.extend({},this.options);if("string"==typeof t)if(s={},t=(i=t.split(".")).shift(),i.length){for(o=s[t]=C.widget.extend({},this.options[t]),n=0;n<i.length-1;n++)o[i[n]]=o[i[n]]||{},o=o[i[n]];if(t=i.pop(),1===arguments.length)return void 0===o[t]?null:o[t];o[t]=e}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];s[t]=e}return this._setOptions(s),this},_setOptions:function(t){for(var e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(t){var e,i,o;for(e in t)o=this.classesElementLookup[e],t[e]!==this.options.classes[e]&&o&&o.length&&(i=C(o.get()),this._removeClass(o,e),i.addClass(this._classes({element:i,keys:e,classes:t,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(n){var s=[],l=this;function t(t,e){for(var i,o=0;o<t.length;o++)i=l.classesElementLookup[t[o]]||C(),i=n.add?C(C.unique(i.get().concat(n.element.get()))):C(i.not(n.element).get()),l.classesElementLookup[t[o]]=i,s.push(t[o]),e&&n.classes[t[o]]&&s.push(n.classes[t[o]])}return n=C.extend({element:this.element,classes:this.options.classes||{}},n),this._on(n.element,{remove:"_untrackClassesElement"}),n.keys&&t(n.keys.match(/\S+/g)||[],!0),n.extra&&t(n.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(i){var o=this;C.each(o.classesElementLookup,function(t,e){-1!==C.inArray(i.target,e)&&(o.classesElementLookup[t]=C(e.not(i.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,o){o="boolean"==typeof o?o:i;var n="string"==typeof t||null===t,t={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:o};return t.element.toggleClass(this._classes(t),o),this},_on:function(n,s,t){var l,r=this;"boolean"!=typeof n&&(t=s,s=n,n=!1),t?(s=l=C(s),this.bindings=this.bindings.add(s)):(t=s,s=this.element,l=this.widget()),C.each(t,function(t,e){function i(){if(n||!0!==r.options.disabled&&!C(this).hasClass("ui-state-disabled"))return("string"==typeof e?r[e]:e).apply(r,arguments)}"string"!=typeof e&&(i.guid=e.guid=e.guid||i.guid||C.guid++);var o=t.match(/^([\w:-]*)\s*(.*)$/),t=o[1]+r.eventNamespace,o=o[2];o?l.on(t,o,i):s.on(t,i)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.off(e).off(e),this.bindings=C(this.bindings.not(t).get()),this.focusable=C(this.focusable.not(t).get()),this.hoverable=C(this.hoverable.not(t).get())},_delay:function(t,e){var i=this;return setTimeout(function(){return("string"==typeof t?i[t]:t).apply(i,arguments)},e||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){this._addClass(C(t.currentTarget),null,"ui-state-hover")},mouseleave:function(t){this._removeClass(C(t.currentTarget),null,"ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){this._addClass(C(t.currentTarget),null,"ui-state-focus")},focusout:function(t){this._removeClass(C(t.currentTarget),null,"ui-state-focus")}})},_trigger:function(t,e,i){var o,n,s=this.options[t];if(i=i||{},(e=C.Event(e)).type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),e.target=this.element[0],n=e.originalEvent)for(o in n)o in e||(e[o]=n[o]);return this.element.trigger(e,i),!(C.isFunction(s)&&!1===s.apply(this.element[0],[e].concat(i))||e.isDefaultPrevented())}},C.each({show:"fadeIn",hide:"fadeOut"},function(s,l){C.Widget.prototype["_"+s]=function(e,t,i){var o;"string"==typeof t&&(t={effect:t});var n=t?!0!==t&&"number"!=typeof t&&t.effect||l:s;"number"==typeof(t=t||{})&&(t={duration:t}),o=!C.isEmptyObject(t),t.complete=i,t.delay&&e.delay(t.delay),o&&C.effects&&C.effects.effect[n]?e[s](t):n!==s&&e[n]?e[n](t.duration,t.easing,i):e.queue(function(t){C(this)[s](),i&&i.call(e[0]),t()})}});var o,W,T,s,l,a,h,d,E;C.widget;function x(t,e,i){return[parseFloat(t[0])*(d.test(t[0])?e/100:1),parseFloat(t[1])*(d.test(t[1])?i/100:1)]}function D(t,e){return parseInt(C.css(t,e),10)||0}W=Math.max,T=Math.abs,s=/left|center|right/,l=/top|center|bottom/,a=/[\+\-]\d+(\.[\d]+)?%?/,h=/^\w+/,d=/%$/,E=C.fn.position,C.position={scrollbarWidth:function(){if(void 0!==o)return o;var t,e=C("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),i=e.children()[0];return C("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),o=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.width<t.element[0].scrollWidth;return{width:"scroll"===i||"auto"===i&&t.height<t.element[0].scrollHeight?C.position.scrollbarWidth():0,height:e?C.position.scrollbarWidth():0}},getWithinInfo:function(t){var e=C(t||window),i=C.isWindow(e[0]),o=!!e[0]&&9===e[0].nodeType;return{element:e,isWindow:i,isDocument:o,offset:!i&&!o?C(t).offset():{left:0,top:0},scrollLeft:e.scrollLeft(),scrollTop:e.scrollTop(),width:e.outerWidth(),height:e.outerHeight()}}},C.fn.position=function(u){if(!u||!u.of)return E.apply(this,arguments);u=C.extend({},u);var c,p,f,g,m,t,v=C(u.of),_=C.position.getWithinInfo(u.within),y=C.position.getScrollInfo(_),w=(u.collision||"flip").split(" "),b={},e=9===(t=(e=v)[0]).nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:C.isWindow(t)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:t.preventDefault?{width:0,height:0,offset:{top:t.pageY,left:t.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()};return v[0].preventDefault&&(u.at="left top"),p=e.width,f=e.height,g=e.offset,m=C.extend({},g),C.each(["my","at"],function(){var t,e,i=(u[this]||"").split(" ");1===i.length&&(i=s.test(i[0])?i.concat(["center"]):l.test(i[0])?["center"].concat(i):["center","center"]),i[0]=s.test(i[0])?i[0]:"center",i[1]=l.test(i[1])?i[1]:"center",t=a.exec(i[0]),e=a.exec(i[1]),b[this]=[t?t[0]:0,e?e[0]:0],u[this]=[h.exec(i[0])[0],h.exec(i[1])[0]]}),1===w.length&&(w[1]=w[0]),"right"===u.at[0]?m.left+=p:"center"===u.at[0]&&(m.left+=p/2),"bottom"===u.at[1]?m.top+=f:"center"===u.at[1]&&(m.top+=f/2),c=x(b.at,p,f),m.left+=c[0],m.top+=c[1],this.each(function(){var i,t,l=C(this),r=l.outerWidth(),a=l.outerHeight(),e=D(this,"marginLeft"),o=D(this,"marginTop"),n=r+e+D(this,"marginRight")+y.width,s=a+o+D(this,"marginBottom")+y.height,h=C.extend({},m),d=x(b.my,l.outerWidth(),l.outerHeight());"right"===u.my[0]?h.left-=r:"center"===u.my[0]&&(h.left-=r/2),"bottom"===u.my[1]?h.top-=a:"center"===u.my[1]&&(h.top-=a/2),h.left+=d[0],h.top+=d[1],i={marginLeft:e,marginTop:o},C.each(["left","top"],function(t,e){C.ui.position[w[t]]&&C.ui.position[w[t]][e](h,{targetWidth:p,targetHeight:f,elemWidth:r,elemHeight:a,collisionPosition:i,collisionWidth:n,collisionHeight:s,offset:[c[0]+d[0],c[1]+d[1]],my:u.my,at:u.at,within:_,elem:l})}),u.using&&(t=function(t){var e=g.left-h.left,i=e+p-r,o=g.top-h.top,n=o+f-a,s={target:{element:v,left:g.left,top:g.top,width:p,height:f},element:{element:l,left:h.left,top:h.top,width:r,height:a},horizontal:i<0?"left":0<e?"right":"center",vertical:n<0?"top":0<o?"bottom":"middle"};p<r&&T(e+i)<p&&(s.horizontal="center"),f<a&&T(o+n)<f&&(s.vertical="middle"),W(T(e),T(i))>W(T(o),T(n))?s.important="horizontal":s.important="vertical",u.using.call(this,t,s)}),l.offset(C.extend(h,{using:t}))})},C.ui.position={fit:{left:function(t,e){var i=e.within,o=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,s=t.left-e.collisionPosition.marginLeft,l=o-s,r=s+e.collisionWidth-n-o;e.collisionWidth>n?0<l&&r<=0?(i=t.left+l+e.collisionWidth-n-o,t.left+=l-i):t.left=!(0<r&&l<=0)&&r<l?o+n-e.collisionWidth:o:0<l?t.left+=l:0<r?t.left-=r:t.left=W(t.left-s,t.left)},top:function(t,e){var i=e.within,o=i.isWindow?i.scrollTop:i.offset.top,n=e.within.height,s=t.top-e.collisionPosition.marginTop,l=o-s,r=s+e.collisionHeight-n-o;e.collisionHeight>n?0<l&&r<=0?(i=t.top+l+e.collisionHeight-n-o,t.top+=l-i):t.top=!(0<r&&l<=0)&&r<l?o+n-e.collisionHeight:o:0<l?t.top+=l:0<r?t.top-=r:t.top=W(t.top-s,t.top)}},flip:{left:function(t,e){var i=e.within,o=i.offset.left+i.scrollLeft,n=i.width,s=i.isWindow?i.scrollLeft:i.offset.left,l=t.left-e.collisionPosition.marginLeft,r=l-s,a=l+e.collisionWidth-n-s,h="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,i="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,l=-2*e.offset[0];r<0?((o=t.left+h+i+l+e.collisionWidth-n-o)<0||o<T(r))&&(t.left+=h+i+l):0<a&&(0<(s=t.left-e.collisionPosition.marginLeft+h+i+l-s)||T(s)<a)&&(t.left+=h+i+l)},top:function(t,e){var i=e.within,o=i.offset.top+i.scrollTop,n=i.height,s=i.isWindow?i.scrollTop:i.offset.top,l=t.top-e.collisionPosition.marginTop,r=l-s,a=l+e.collisionHeight-n-s,h="top"===e.my[1]?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,i="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,l=-2*e.offset[1];r<0?((o=t.top+h+i+l+e.collisionHeight-n-o)<0||o<T(r))&&(t.top+=h+i+l):0<a&&(0<(s=t.top-e.collisionPosition.marginTop+h+i+l-s)||T(s)<a)&&(t.top+=h+i+l)}},flipfit:{left:function(){C.ui.position.flip.left.apply(this,arguments),C.ui.position.fit.left.apply(this,arguments)},top:function(){C.ui.position.flip.top.apply(this,arguments),C.ui.position.fit.top.apply(this,arguments)}}};var t;C.ui.position,C.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},C.fn.extend({uniqueId:(t=0,function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&C(this).removeAttr("id")})}});C.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var t=C(this).attr("title")||"";return C("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(t,e){var i=(t.attr("aria-describedby")||"").split(/\s+/);i.push(e),t.data("ui-tooltip-id",e).attr("aria-describedby",C.trim(i.join(" ")))},_removeDescribedBy:function(t){var e=t.data("ui-tooltip-id"),i=(t.attr("aria-describedby")||"").split(/\s+/),e=C.inArray(e,i);-1!==e&&i.splice(e,1),t.removeData("ui-tooltip-id"),(i=C.trim(i.join(" ")))?t.attr("aria-describedby",i):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=C("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=C([])},_setOption:function(t,e){var i=this;this._super(t,e),"content"===t&&C.each(this.tooltips,function(t,e){i._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var o=this;C.each(this.tooltips,function(t,e){var i=C.Event("blur");i.target=i.currentTarget=e.element[0],o.close(i,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var t=C(this);if(t.is("[title]"))return t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")}))},_enable:function(){this.disabledTitles.each(function(){var t=C(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))}),this.disabledTitles=C([])},open:function(t){var i=this,e=C(t?t.target:this.element).closest(this.options.items);e.length&&!e.data("ui-tooltip-id")&&(e.attr("title")&&e.data("ui-tooltip-title",e.attr("title")),e.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&e.parents().each(function(){var t,e=C(this);e.data("ui-tooltip-open")&&((t=C.Event("blur")).target=t.currentTarget=this,i.close(t,!0)),e.attr("title")&&(e.uniqueId(),i.parents[this.id]={element:this,title:e.attr("title")},e.attr("title",""))}),this._registerCloseHandlers(t,e),this._updateContent(e,t))},_updateContent:function(e,i){var t=this.options.content,o=this,n=i?i.type:null;if("string"==typeof t||t.nodeType||t.jquery)return this._open(i,e,t);(t=t.call(e[0],function(t){o._delay(function(){e.data("ui-tooltip-open")&&(i&&(i.type=n),this._open(i,e,t))})}))&&this._open(i,e,t)},_open:function(t,e,i){var o,n,s,l=C.extend({},this.options.position);function r(t){l.of=t,n.is(":hidden")||n.position(l)}i&&((o=this._find(e))?o.tooltip.find(".ui-tooltip-content").html(i):(e.is("[title]")&&(t&&"mouseover"===t.type?e.attr("title",""):e.removeAttr("title")),o=this._tooltip(e),n=o.tooltip,this._addDescribedBy(e,n.attr("id")),n.find(".ui-tooltip-content").html(i),this.liveRegion.children().hide(),(i=C("<div>").html(n.find(".ui-tooltip-content").html())).removeAttr("name").find("[name]").removeAttr("name"),i.removeAttr("id").find("[id]").removeAttr("id"),i.appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:r}),r(t)):n.position(C.extend({of:e},this.options.position)),n.hide(),this._show(n,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(s=this.delayedShow=setInterval(function(){n.is(":visible")&&(r(l.of),clearInterval(s))},C.fx.interval)),this._trigger("open",t,{tooltip:n})))},_registerCloseHandlers:function(t,e){var i={keyup:function(t){t.keyCode===C.ui.keyCode.ESCAPE&&((t=C.Event(t)).currentTarget=e[0],this.close(t,!0))}};e[0]!==this.element[0]&&(i.remove=function(){this._removeTooltip(this._find(e).tooltip)}),t&&"mouseover"!==t.type||(i.mouseleave="close"),t&&"focusin"!==t.type||(i.focusout="close"),this._on(!0,e,i)},close:function(t){var e,i=this,o=C(t?t.currentTarget:this.element),n=this._find(o);n?(e=n.tooltip,n.closing||(clearInterval(this.delayedShow),o.data("ui-tooltip-title")&&!o.attr("title")&&o.attr("title",o.data("ui-tooltip-title")),this._removeDescribedBy(o),n.hiding=!0,e.stop(!0),this._hide(e,this.options.hide,function(){i._removeTooltip(C(this))}),o.removeData("ui-tooltip-open"),this._off(o,"mouseleave focusout keyup"),o[0]!==this.element[0]&&this._off(o,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&C.each(this.parents,function(t,e){C(e.element).attr("title",e.title),delete i.parents[t]}),n.closing=!0,this._trigger("close",t,{tooltip:e}),n.hiding||(n.closing=!1))):o.removeData("ui-tooltip-open")},_tooltip:function(t){var e=C("<div>").attr("role","tooltip"),i=C("<div>").appendTo(e),o=e.uniqueId().attr("id");return this._addClass(i,"ui-tooltip-content"),this._addClass(e,"ui-tooltip","ui-widget ui-widget-content"),e.appendTo(this._appendTo(t)),this.tooltips[o]={element:t,tooltip:e}},_find:function(t){t=t.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){t=t.closest(".ui-front, dialog");return t.length||(t=this.document[0].body),t},_destroy:function(){var o=this;C.each(this.tooltips,function(t,e){var i=C.Event("blur"),e=e.element;i.target=i.currentTarget=e[0],o.close(i,!0),C("#"+t).remove(),e.data("ui-tooltip-title")&&(e.attr("title")||e.attr("title",e.data("ui-tooltip-title")),e.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),!1!==C.uiBackCompat&&C.widget("ui.tooltip",C.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}});C.ui.tooltip}); \ No newline at end of file diff --git a/docs/js/jquery.min.js b/docs/js/jquery.min.js index 644d35e274..7f37b5d991 100644 --- a/docs/js/jquery.min.js +++ b/docs/js/jquery.min.js @@ -1,4 +1,2 @@ -/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), -a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), -null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r}); +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}function fe(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}ce.fn=ce.prototype={jquery:t,constructor:ce,length:0,toArray:function(){return ae.call(this)},get:function(e){return null==e?ae.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=ce.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return ce.each(this,e)},map:function(n){return this.pushStack(ce.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(ae.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(ce.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(ce.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:s,sort:oe.sort,splice:oe.splice},ce.extend=ce.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||v(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(ce.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||ce.isPlainObject(n)?n:{},i=!1,a[t]=ce.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},ce.extend({expando:"jQuery"+(t+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==i.call(e))&&(!(t=r(e))||"function"==typeof(n=ue.call(t,"constructor")&&t.constructor)&&o.call(n)===a)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){m(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(c(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},text:function(e){var t,n="",r=0,i=e.nodeType;if(!i)while(t=e[r++])n+=ce.text(t);return 1===i||11===i?e.textContent:9===i?e.documentElement.textContent:3===i||4===i?e.nodeValue:n},makeArray:function(e,t){var n=t||[];return null!=e&&(c(Object(e))?ce.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:se.call(t,e,n)},isXMLDoc:function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!l.test(t||n&&n.nodeName||"HTML")},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(c(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:le}),"function"==typeof Symbol&&(ce.fn[Symbol.iterator]=oe[Symbol.iterator]),ce.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var pe=oe.pop,de=oe.sort,he=oe.splice,ge="[\\x20\\t\\r\\n\\f]",ve=new RegExp("^"+ge+"+|((?:^|[^\\\\])(?:\\\\.)*)"+ge+"+$","g");ce.contains=function(e,t){var n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(e.contains?e.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))};var f=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;function p(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e}ce.escapeSelector=function(e){return(e+"").replace(f,p)};var ye=C,me=s;!function(){var e,b,w,o,a,T,r,C,d,i,k=me,S=ce.expando,E=0,n=0,s=W(),c=W(),u=W(),h=W(),l=function(e,t){return e===t&&(a=!0),0},f="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",t="(?:\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",p="\\["+ge+"*("+t+")(?:"+ge+"*([*^$|!~]?=)"+ge+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+t+"))|)"+ge+"*\\]",g=":("+t+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+p+")*)|.*)\\)|)",v=new RegExp(ge+"+","g"),y=new RegExp("^"+ge+"*,"+ge+"*"),m=new RegExp("^"+ge+"*([>+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="<a id='"+S+"' href='' disabled='disabled'></a><select id='"+S+"-\r\\' disabled='disabled'><option selected=''></option></select>",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0<I(t,T,null,[e]).length},I.contains=function(e,t){return(e.ownerDocument||e)!=T&&V(e),ce.contains(e,t)},I.attr=function(e,t){(e.ownerDocument||e)!=T&&V(e);var n=b.attrHandle[t.toLowerCase()],r=n&&ue.call(b.attrHandle,t.toLowerCase())?n(e,t,!C):void 0;return void 0!==r?r:e.getAttribute(t)},I.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},ce.uniqueSort=function(e){var t,n=[],r=0,i=0;if(a=!le.sortStable,o=!le.sortStable&&ae.call(e,0),de.call(e,l),a){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)he.call(e,n[r],1)}return o=null,e},ce.fn.uniqueSort=function(){return this.pushStack(ce.uniqueSort(ae.apply(this)))},(b=ce.expr={cacheLength:50,createPseudo:F,match:D,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(v," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(d,e,t,h,g){var v="nth"!==d.slice(0,3),y="last"!==d.slice(-4),m="of-type"===e;return 1===h&&0===g?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u=v!==y?"nextSibling":"previousSibling",l=e.parentNode,c=m&&e.nodeName.toLowerCase(),f=!n&&!m,p=!1;if(l){if(v){while(u){o=e;while(o=o[u])if(m?fe(o,c):1===o.nodeType)return!1;s=u="only"===d&&!s&&"nextSibling"}return!0}if(s=[y?l.firstChild:l.lastChild],y&&f){p=(a=(r=(i=l[S]||(l[S]={}))[d]||[])[0]===E&&r[1])&&r[2],o=a&&l.childNodes[a];while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if(1===o.nodeType&&++p&&o===e){i[d]=[E,a,p];break}}else if(f&&(p=a=(r=(i=e[S]||(e[S]={}))[d]||[])[0]===E&&r[1]),!1===p)while(o=++a&&o&&o[u]||(p=a=0)||s.pop())if((m?fe(o,c):1===o.nodeType)&&++p&&(f&&((i=o[S]||(o[S]={}))[d]=[E,p]),o===e))break;return(p-=g)===h||p%h==0&&0<=p/h}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||I.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?F(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=se.call(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:F(function(e){var r=[],i=[],s=ne(e.replace(ve,"$1"));return s[S]?F(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:F(function(t){return function(e){return 0<I(t,e).length}}),contains:F(function(t){return t=t.replace(O,P),function(e){return-1<(e.textContent||ce.text(e)).indexOf(t)}}),lang:F(function(n){return A.test(n||"")||I.error("unsupported lang: "+n),n=n.replace(O,P).toLowerCase(),function(e){var t;do{if(t=C?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=ie.location&&ie.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===r},focus:function(e){return e===function(){try{return T.activeElement}catch(e){}}()&&T.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:z(!1),disabled:z(!0),checked:function(e){return fe(e,"input")&&!!e.checked||fe(e,"option")&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return q.test(e.nodeName)},input:function(e){return N.test(e.nodeName)},button:function(e){return fe(e,"input")&&"button"===e.type||fe(e,"button")},text:function(e){var t;return fe(e,"input")&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:X(function(){return[0]}),last:X(function(e,t){return[t-1]}),eq:X(function(e,t,n){return[n<0?n+t:n]}),even:X(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:X(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:X(function(e,t,n){var r;for(r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:X(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=B(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=_(e);function G(){}function Y(e,t){var n,r,i,o,a,s,u,l=c[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=y.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=m.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace(ve," ")}),a=a.slice(n.length)),b.filter)!(r=D[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?I.error(e):c(e,s).slice(0)}function Q(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function J(a,e,t){var s=e.dir,u=e.next,l=u||s,c=t&&"parentNode"===l,f=n++;return e.first?function(e,t,n){while(e=e[s])if(1===e.nodeType||c)return a(e,t,n);return!1}:function(e,t,n){var r,i,o=[E,f];if(n){while(e=e[s])if((1===e.nodeType||c)&&a(e,t,n))return!0}else while(e=e[s])if(1===e.nodeType||c)if(i=e[S]||(e[S]={}),u&&fe(e,u))e=e[s]||e;else{if((r=i[l])&&r[0]===E&&r[1]===f)return o[2]=r[2];if((i[l]=o)[2]=a(e,t,n))return!0}return!1}}function K(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Z(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function ee(d,h,g,v,y,e){return v&&!v[S]&&(v=ee(v)),y&&!y[S]&&(y=ee(y,e)),F(function(e,t,n,r){var i,o,a,s,u=[],l=[],c=t.length,f=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)I(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),p=!d||!e&&h?f:Z(f,u,d,n,r);if(g?g(p,s=y||(e?d:c||v)?[]:t,n,r):s=p,v){i=Z(s,l),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(s[l[o]]=!(p[l[o]]=a))}if(e){if(y||d){if(y){i=[],o=s.length;while(o--)(a=s[o])&&i.push(p[o]=a);y(null,s=[],i,r)}o=s.length;while(o--)(a=s[o])&&-1<(i=y?se.call(e,a):u[o])&&(e[i]=!(t[i]=a))}}else s=Z(s===t?s.splice(c,s.length):s),y?y(null,t,s,r):k.apply(t,s)})}function te(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=J(function(e){return e===i},a,!0),l=J(function(e){return-1<se.call(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!=w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[J(K(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return ee(1<s&&K(c),1<s&&Q(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(ve,"$1"),t,s<n&&te(e.slice(s,n)),n<r&&te(e=e.slice(n)),n<r&&Q(e))}c.push(t)}return K(c)}function ne(e,t){var n,v,y,m,x,r,i=[],o=[],a=u[e+" "];if(!a){t||(t=Y(e)),n=t.length;while(n--)(a=te(t[n]))[S]?i.push(a):o.push(a);(a=u(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=E+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==T||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==T||(V(o),n=!C);while(s=v[a++])if(s(o,t||T,n)){k.call(r,o);break}i&&(E=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=pe.call(r));f=Z(f)}k.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&ce.uniqueSort(r)}return i&&(E=h,w=p),c},m?F(r):r))).selector=e}return a}function re(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&Y(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&C&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(O,P),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=D.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(O,P),H.test(o[0].type)&&U(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&Q(o)))return k.apply(n,r),n;break}}}return(l||ne(e,c))(r,t,!C,n,!t||H.test(e)&&U(t.parentNode)||t),n}G.prototype=b.filters=b.pseudos,b.setFilters=new G,le.sortStable=S.split("").sort(l).join("")===S,V(),le.sortDetached=$(function(e){return 1&e.compareDocumentPosition(T.createElement("fieldset"))}),ce.find=I,ce.expr[":"]=ce.expr.pseudos,ce.unique=ce.uniqueSort,I.compile=ne,I.select=re,I.setDocument=V,I.tokenize=Y,I.escape=ce.escapeSelector,I.getText=ce.text,I.isXML=ce.isXMLDoc,I.selectors=ce.expr,I.support=ce.support,I.uniqueSort=ce.uniqueSort}();var d=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&ce(e).is(n))break;r.push(e)}return r},h=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},b=ce.expr.match.needsContext,w=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1<se.call(n,e)!==r}):ce.filter(n,e,r)}ce.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?ce.find.matchesSelector(r,e)?[r]:[]:ce.find.matches(e,ce.grep(t,function(e){return 1===e.nodeType}))},ce.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(ce(e).filter(function(){for(t=0;t<r;t++)if(ce.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)ce.find(e,i[t],n);return 1<r?ce.uniqueSort(n):n},filter:function(e){return this.pushStack(T(this,e||[],!1))},not:function(e){return this.pushStack(T(this,e||[],!0))},is:function(e){return!!T(this,"string"==typeof e&&b.test(e)?ce(e):e||[],!1).length}});var k,S=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(ce.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&ce(e);if(!b.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&ce.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?ce.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?se.call(ce(e),this[0]):se.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(ce.uniqueSort(ce.merge(this.get(),ce(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),ce.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return d(e,"parentNode")},parentsUntil:function(e,t,n){return d(e,"parentNode",n)},next:function(e){return A(e,"nextSibling")},prev:function(e){return A(e,"previousSibling")},nextAll:function(e){return d(e,"nextSibling")},prevAll:function(e){return d(e,"previousSibling")},nextUntil:function(e,t,n){return d(e,"nextSibling",n)},prevUntil:function(e,t,n){return d(e,"previousSibling",n)},siblings:function(e){return h((e.parentNode||{}).firstChild,e)},children:function(e){return h(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(fe(e,"template")&&(e=e.content||e),ce.merge([],e.childNodes))}},function(r,i){ce.fn[r]=function(e,t){var n=ce.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=ce.filter(t,n)),1<this.length&&(j[r]||ce.uniqueSort(n),E.test(r)&&n.reverse()),this.pushStack(n)}});var D=/[^\x20\t\r\n\f]+/g;function N(e){return e}function q(e){throw e}function L(e,t,n,r){var i;try{e&&v(i=e.promise)?i.call(e).done(t).fail(n):e&&v(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}ce.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},ce.each(e.match(D)||[],function(e,t){n[t]=!0}),n):ce.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){ce.each(e,function(e,t){v(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==x(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return ce.each(arguments,function(e,t){var n;while(-1<(n=ce.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<ce.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},ce.extend({Deferred:function(e){var o=[["notify","progress",ce.Callbacks("memory"),ce.Callbacks("memory"),2],["resolve","done",ce.Callbacks("once memory"),ce.Callbacks("once memory"),0,"resolved"],["reject","fail",ce.Callbacks("once memory"),ce.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return ce.Deferred(function(r){ce.each(o,function(e,t){var n=v(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&v(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,v(t)?s?t.call(e,l(u,o,N,s),l(u,o,q,s)):(u++,t.call(e,l(u,o,N,s),l(u,o,q,s),l(u,o,N,o.notifyWith))):(a!==N&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){ce.Deferred.exceptionHook&&ce.Deferred.exceptionHook(e,t.error),u<=i+1&&(a!==q&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(ce.Deferred.getErrorHook?t.error=ce.Deferred.getErrorHook():ce.Deferred.getStackHook&&(t.error=ce.Deferred.getStackHook()),ie.setTimeout(t))}}return ce.Deferred(function(e){o[0][3].add(l(0,e,v(r)?r:N,e.notifyWith)),o[1][3].add(l(0,e,v(t)?t:N)),o[2][3].add(l(0,e,v(n)?n:q))}).promise()},promise:function(e){return null!=e?ce.extend(e,a):a}},s={};return ce.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=ae.call(arguments),o=ce.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?ae.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(L(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||v(i[t]&&i[t].then)))return o.then();while(t--)L(i[t],a(t),o.reject);return o.promise()}});var H=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;ce.Deferred.exceptionHook=function(e,t){ie.console&&ie.console.warn&&e&&H.test(e.name)&&ie.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},ce.readyException=function(e){ie.setTimeout(function(){throw e})};var O=ce.Deferred();function P(){C.removeEventListener("DOMContentLoaded",P),ie.removeEventListener("load",P),ce.ready()}ce.fn.ready=function(e){return O.then(e)["catch"](function(e){ce.readyException(e)}),this},ce.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--ce.readyWait:ce.isReady)||(ce.isReady=!0)!==e&&0<--ce.readyWait||O.resolveWith(C,[ce])}}),ce.ready.then=O.then,"complete"===C.readyState||"loading"!==C.readyState&&!C.documentElement.doScroll?ie.setTimeout(ce.ready):(C.addEventListener("DOMContentLoaded",P),ie.addEventListener("load",P));var M=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n))for(s in i=!0,n)M(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,v(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(ce(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},R=/^-ms-/,I=/-([a-z])/g;function W(e,t){return t.toUpperCase()}function F(e){return e.replace(R,"ms-").replace(I,W)}var $=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function B(){this.expando=ce.expando+B.uid++}B.uid=1,B.prototype={cache:function(e){var t=e[this.expando];return t||(t={},$(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[F(t)]=n;else for(r in t)i[F(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][F(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(F):(t=F(t))in r?[t]:t.match(D)||[]).length;while(n--)delete r[t[n]]}(void 0===t||ce.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!ce.isEmptyObject(t)}};var _=new B,z=new B,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,U=/[A-Z]/g;function V(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(U,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:X.test(i)?JSON.parse(i):i)}catch(e){}z.set(e,t,n)}else n=void 0;return n}ce.extend({hasData:function(e){return z.hasData(e)||_.hasData(e)},data:function(e,t,n){return z.access(e,t,n)},removeData:function(e,t){z.remove(e,t)},_data:function(e,t,n){return _.access(e,t,n)},_removeData:function(e,t){_.remove(e,t)}}),ce.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=z.get(o),1===o.nodeType&&!_.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=F(r.slice(5)),V(o,r,i[r]));_.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){z.set(this,n)}):M(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=z.get(o,n))?t:void 0!==(t=V(o,n))?t:void 0;this.each(function(){z.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){z.remove(this,e)})}}),ce.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=_.get(e,t),n&&(!r||Array.isArray(n)?r=_.access(e,t,ce.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=ce.queue(e,t),r=n.length,i=n.shift(),o=ce._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){ce.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return _.get(e,n)||_.access(e,n,{empty:ce.Callbacks("once memory").add(function(){_.remove(e,[t+"queue",n])})})}}),ce.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?ce.queue(this[0],t):void 0===n?this:this.each(function(){var e=ce.queue(this,t,n);ce._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&ce.dequeue(this,t)})},dequeue:function(e){return this.each(function(){ce.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=ce.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=_.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var G=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Y=new RegExp("^(?:([+-])=|)("+G+")([a-z%]*)$","i"),Q=["Top","Right","Bottom","Left"],J=C.documentElement,K=function(e){return ce.contains(e.ownerDocument,e)},Z={composed:!0};J.getRootNode&&(K=function(e){return ce.contains(e.ownerDocument,e)||e.getRootNode(Z)===e.ownerDocument});var ee=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&K(e)&&"none"===ce.css(e,"display")};function te(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return ce.css(e,t,"")},u=s(),l=n&&n[3]||(ce.cssNumber[t]?"":"px"),c=e.nodeType&&(ce.cssNumber[t]||"px"!==l&&+u)&&Y.exec(ce.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)ce.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,ce.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ne={};function re(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=_.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ee(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ne[s])||(o=a.body.appendChild(a.createElement(s)),u=ce.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ne[s]=u)))):"none"!==n&&(l[c]="none",_.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}ce.fn.extend({show:function(){return re(this,!0)},hide:function(){return re(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ee(this)?ce(this).show():ce(this).hide()})}});var xe,be,we=/^(?:checkbox|radio)$/i,Te=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="<textarea>x</textarea>",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="<option></option>",le.option=!!xe.lastChild;var ke={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n<r;n++)_.set(e[n],"globalEval",!t||_.get(t[n],"globalEval"))}ke.tbody=ke.tfoot=ke.colgroup=ke.caption=ke.thead,ke.th=ke.td,le.option||(ke.optgroup=ke.option=[1,"<select multiple='multiple'>","</select>"]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===x(o))ce.merge(p,o.nodeType?[o]:o);else if(je.test(o)){a=a||f.appendChild(t.createElement("div")),s=(Te.exec(o)||["",""])[1].toLowerCase(),u=ke[s]||ke._default,a.innerHTML=u[1]+ce.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;ce.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<ce.inArray(o,r))i&&i.push(o);else if(l=K(o),a=Se(f.appendChild(o),"script"),l&&Ee(a),n){c=0;while(o=a[c++])Ce.test(o.type||"")&&n.push(o)}return f}var De=/^([^.]*)(?:\.(.+)|)/;function Ne(){return!0}function qe(){return!1}function Le(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Le(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=qe;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return ce().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=ce.guid++)),e.each(function(){ce.event.add(this,t,i,r,n)})}function He(e,r,t){t?(_.set(e,r,!1),ce.event.add(e,r,{namespace:!1,handler:function(e){var t,n=_.get(this,r);if(1&e.isTrigger&&this[r]){if(n)(ce.event.special[r]||{}).delegateType&&e.stopPropagation();else if(n=ae.call(arguments),_.set(this,r,n),this[r](),t=_.get(this,r),_.set(this,r,!1),n!==t)return e.stopImmediatePropagation(),e.preventDefault(),t}else n&&(_.set(this,r,ce.event.trigger(n[0],n.slice(1),this)),e.stopPropagation(),e.isImmediatePropagationStopped=Ne)}})):void 0===_.get(e,r)&&ce.event.add(e,r,Ne)}ce.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.get(t);if($(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&ce.find.matchesSelector(J,i),n.guid||(n.guid=ce.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof ce&&ce.event.triggered!==e.type?ce.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(D)||[""]).length;while(l--)d=g=(s=De.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=ce.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=ce.event.special[d]||{},c=ce.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&ce.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),ce.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=_.hasData(e)&&_.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(D)||[""]).length;while(l--)if(d=g=(s=De.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=ce.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||ce.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)ce.event.remove(e,d+t[l],n,r,!0);ce.isEmptyObject(u)&&_.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=ce.event.fix(e),l=(_.get(this,"events")||Object.create(null))[u.type]||[],c=ce.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=ce.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((ce.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<ce(i,this).index(l):ce.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(ce.Event.prototype,t,{enumerable:!0,configurable:!0,get:v(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[ce.expando]?e:new ce.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click",!0),!1},trigger:function(e){var t=this||e;return we.test(t.type)&&t.click&&fe(t,"input")&&He(t,"click"),!0},_default:function(e){var t=e.target;return we.test(t.type)&&t.click&&fe(t,"input")&&_.get(t,"click")||fe(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},ce.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},ce.Event=function(e,t){if(!(this instanceof ce.Event))return new ce.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ne:qe,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&ce.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[ce.expando]=!0},ce.Event.prototype={constructor:ce.Event,isDefaultPrevented:qe,isPropagationStopped:qe,isImmediatePropagationStopped:qe,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ne,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ne,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ne,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},ce.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},ce.event.addProp),ce.each({focus:"focusin",blur:"focusout"},function(r,i){function o(e){if(C.documentMode){var t=_.get(this,"handle"),n=ce.event.fix(e);n.type="focusin"===e.type?"focus":"blur",n.isSimulated=!0,t(e),n.target===n.currentTarget&&t(n)}else ce.event.simulate(i,e.target,ce.event.fix(e))}ce.event.special[r]={setup:function(){var e;if(He(this,r,!0),!C.documentMode)return!1;(e=_.get(this,i))||this.addEventListener(i,o),_.set(this,i,(e||0)+1)},trigger:function(){return He(this,r),!0},teardown:function(){var e;if(!C.documentMode)return!1;(e=_.get(this,i)-1)?_.set(this,i,e):(this.removeEventListener(i,o),_.remove(this,i))},_default:function(e){return _.get(e.target,r)},delegateType:i},ce.event.special[i]={setup:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i);n||(C.documentMode?this.addEventListener(i,o):e.addEventListener(r,o,!0)),_.set(t,i,(n||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=C.documentMode?this:e,n=_.get(t,i)-1;n?_.set(t,i,n):(C.documentMode?this.removeEventListener(i,o):e.removeEventListener(r,o,!0),_.remove(t,i))}}}),ce.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){ce.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||ce.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),ce.fn.extend({on:function(e,t,n,r){return Le(this,e,t,n,r)},one:function(e,t,n,r){return Le(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,ce(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=qe),this.each(function(){ce.event.remove(this,e,n,t)})}});var Oe=/<script|<style|<link/i,Pe=/checked\s*(?:[^=]|=\s*.checked.)/i,Me=/^\s*<!\[CDATA\[|\]\]>\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)ce.event.add(t,i,s[i][n]);z.hasData(e)&&(o=z.access(e),a=ce.extend({},o),z.set(t,a))}}function $e(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=v(d);if(h||1<f&&"string"==typeof d&&!le.checkClone&&Pe.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),$e(t,r,i,o)});if(f&&(t=(e=Ae(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=ce.map(Se(e,"script"),Ie)).length;c<f;c++)u=e,c!==p&&(u=ce.clone(u,!0,!0),s&&ce.merge(a,Se(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,ce.map(a,We),c=0;c<s;c++)u=a[c],Ce.test(u.type||"")&&!_.access(u,"globalEval")&&ce.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?ce._evalUrl&&!u.noModule&&ce._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):m(u.textContent.replace(Me,""),u,l))}return n}function Be(e,t,n){for(var r,i=t?ce.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||ce.cleanData(Se(r)),r.parentNode&&(n&&K(r)&&Ee(Se(r,"script")),r.parentNode.removeChild(r));return e}ce.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=K(e);if(!(le.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||ce.isXMLDoc(e)))for(a=Se(c),r=0,i=(o=Se(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&we.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||Se(e),a=a||Se(c),r=0,i=o.length;r<i;r++)Fe(o[r],a[r]);else Fe(e,c);return 0<(a=Se(c,"script")).length&&Ee(a,!f&&Se(e,"script")),c},cleanData:function(e){for(var t,n,r,i=ce.event.special,o=0;void 0!==(n=e[o]);o++)if($(n)){if(t=n[_.expando]){if(t.events)for(r in t.events)i[r]?ce.event.remove(n,r):ce.removeEvent(n,r,t.handle);n[_.expando]=void 0}n[z.expando]&&(n[z.expando]=void 0)}}}),ce.fn.extend({detach:function(e){return Be(this,e,!0)},remove:function(e){return Be(this,e)},text:function(e){return M(this,function(e){return void 0===e?ce.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return $e(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Re(this,e).appendChild(e)})},prepend:function(){return $e(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Re(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return $e(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(ce.cleanData(Se(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return ce.clone(this,e,t)})},html:function(e){return M(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Oe.test(e)&&!ke[(Te.exec(e)||["",""])[1].toLowerCase()]){e=ce.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(ce.cleanData(Se(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return $e(this,arguments,function(e){var t=this.parentNode;ce.inArray(this,n)<0&&(ce.cleanData(Se(this)),t&&t.replaceChild(e,this))},n)}}),ce.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){ce.fn[e]=function(e){for(var t,n=[],r=ce(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),ce(r[o])[a](t),s.apply(n,t.get());return this.pushStack(n)}});var _e=new RegExp("^("+G+")(?!px)[a-z%]+$","i"),ze=/^--/,Xe=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=ie),t.getComputedStyle(e)},Ue=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Ve=new RegExp(Q.join("|"),"i");function Ge(e,t,n){var r,i,o,a,s=ze.test(t),u=e.style;return(n=n||Xe(e))&&(a=n.getPropertyValue(t)||n[t],s&&a&&(a=a.replace(ve,"$1")||void 0),""!==a||K(e)||(a=ce.style(e,t)),!le.pixelBoxStyles()&&_e.test(a)&&Ve.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=n.width,u.width=r,u.minWidth=i,u.maxWidth=o)),void 0!==a?a+"":a}function Ye(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",J.appendChild(u).appendChild(l);var e=ie.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),J.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=C.createElement("div"),l=C.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",le.clearCloneStyle="content-box"===l.style.backgroundClip,ce.extend(le,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=C.createElement("table"),t=C.createElement("tr"),n=C.createElement("div"),e.style.cssText="position:absolute;left:-11111px;border-collapse:separate",t.style.cssText="box-sizing:content-box;border:1px solid",t.style.height="1px",n.style.height="9px",n.style.display="block",J.appendChild(e).appendChild(t).appendChild(n),r=ie.getComputedStyle(t),a=parseInt(r.height,10)+parseInt(r.borderTopWidth,10)+parseInt(r.borderBottomWidth,10)===t.offsetHeight,J.removeChild(e)),a}}))}();var Qe=["Webkit","Moz","ms"],Je=C.createElement("div").style,Ke={};function Ze(e){var t=ce.cssProps[e]||Ke[e];return t||(e in Je?e:Ke[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=Qe.length;while(n--)if((e=Qe[n]+t)in Je)return e}(e)||e)}var et=/^(none|table(?!-c[ea]).+)/,tt={position:"absolute",visibility:"hidden",display:"block"},nt={letterSpacing:"0",fontWeight:"400"};function rt(e,t,n){var r=Y.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function it(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0,l=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(l+=ce.css(e,n+Q[a],!0,i)),r?("content"===n&&(u-=ce.css(e,"padding"+Q[a],!0,i)),"margin"!==n&&(u-=ce.css(e,"border"+Q[a]+"Width",!0,i))):(u+=ce.css(e,"padding"+Q[a],!0,i),"padding"!==n?u+=ce.css(e,"border"+Q[a]+"Width",!0,i):s+=ce.css(e,"border"+Q[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u+l}function ot(e,t,n){var r=Xe(e),i=(!le.boxSizingReliable()||n)&&"border-box"===ce.css(e,"boxSizing",!1,r),o=i,a=Ge(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(_e.test(a)){if(!n)return a;a="auto"}return(!le.boxSizingReliable()&&i||!le.reliableTrDimensions()&&fe(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===ce.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===ce.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+it(e,t,n||(i?"border":"content"),o,r,a)+"px"}function at(e,t,n,r,i){return new at.prototype.init(e,t,n,r,i)}ce.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ge(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,aspectRatio:!0,borderImageSlice:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,scale:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeMiterlimit:!0,strokeOpacity:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=F(t),u=ze.test(t),l=e.style;if(u||(t=Ze(s)),a=ce.cssHooks[t]||ce.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=Y.exec(n))&&i[1]&&(n=te(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(ce.cssNumber[s]?"":"px")),le.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=F(t);return ze.test(t)||(t=Ze(s)),(a=ce.cssHooks[t]||ce.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Ge(e,t,r)),"normal"===i&&t in nt&&(i=nt[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),ce.each(["height","width"],function(e,u){ce.cssHooks[u]={get:function(e,t,n){if(t)return!et.test(ce.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?ot(e,u,n):Ue(e,tt,function(){return ot(e,u,n)})},set:function(e,t,n){var r,i=Xe(e),o=!le.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===ce.css(e,"boxSizing",!1,i),s=n?it(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-it(e,u,"border",!1,i)-.5)),s&&(r=Y.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=ce.css(e,u)),rt(0,t,s)}}}),ce.cssHooks.marginLeft=Ye(le.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Ge(e,"marginLeft"))||e.getBoundingClientRect().left-Ue(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),ce.each({margin:"",padding:"",border:"Width"},function(i,o){ce.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+Q[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(ce.cssHooks[i+o].set=rt)}),ce.fn.extend({css:function(e,t){return M(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Xe(e),i=t.length;a<i;a++)o[t[a]]=ce.css(e,t[a],!1,r);return o}return void 0!==n?ce.style(e,t,n):ce.css(e,t)},e,t,1<arguments.length)}}),((ce.Tween=at).prototype={constructor:at,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||ce.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(ce.cssNumber[n]?"":"px")},cur:function(){var e=at.propHooks[this.prop];return e&&e.get?e.get(this):at.propHooks._default.get(this)},run:function(e){var t,n=at.propHooks[this.prop];return this.options.duration?this.pos=t=ce.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):at.propHooks._default.set(this),this}}).init.prototype=at.prototype,(at.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=ce.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){ce.fx.step[e.prop]?ce.fx.step[e.prop](e):1!==e.elem.nodeType||!ce.cssHooks[e.prop]&&null==e.elem.style[Ze(e.prop)]?e.elem[e.prop]=e.now:ce.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=at.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},ce.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},ce.fx=at.prototype.init,ce.fx.step={};var st,ut,lt,ct,ft=/^(?:toggle|show|hide)$/,pt=/queueHooks$/;function dt(){ut&&(!1===C.hidden&&ie.requestAnimationFrame?ie.requestAnimationFrame(dt):ie.setTimeout(dt,ce.fx.interval),ce.fx.tick())}function ht(){return ie.setTimeout(function(){st=void 0}),st=Date.now()}function gt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=Q[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function vt(e,t,n){for(var r,i=(yt.tweeners[t]||[]).concat(yt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function yt(o,e,t){var n,a,r=0,i=yt.prefilters.length,s=ce.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=st||ht(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:ce.extend({},e),opts:ce.extend(!0,{specialEasing:{},easing:ce.easing._default},t),originalProperties:e,originalOptions:t,startTime:st||ht(),duration:t.duration,tweens:[],createTween:function(e,t){var n=ce.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=F(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=ce.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=yt.prefilters[r].call(l,o,c,l.opts))return v(n.stop)&&(ce._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return ce.map(c,vt,l),v(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),ce.fx.timer(ce.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}ce.Animation=ce.extend(yt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return te(n.elem,e,Y.exec(t),n),n}]},tweener:function(e,t){v(e)?(t=e,e=["*"]):e=e.match(D);for(var n,r=0,i=e.length;r<i;r++)n=e[r],yt.tweeners[n]=yt.tweeners[n]||[],yt.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ee(e),v=_.get(e,"fxshow");for(r in n.queue||(null==(a=ce._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,ce.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ft.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||ce.style(e,r)}if((u=!ce.isEmptyObject(t))||!ce.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=_.get(e,"display")),"none"===(c=ce.css(e,"display"))&&(l?c=l:(re([e],!0),l=e.style.display||l,c=ce.css(e,"display"),re([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===ce.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=_.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&re([e],!0),p.done(function(){for(r in g||re([e]),_.remove(e,"fxshow"),d)ce.style(e,r,d[r])})),u=vt(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?yt.prefilters.unshift(e):yt.prefilters.push(e)}}),ce.speed=function(e,t,n){var r=e&&"object"==typeof e?ce.extend({},e):{complete:n||!n&&t||v(e)&&e,duration:e,easing:n&&t||t&&!v(t)&&t};return ce.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in ce.fx.speeds?r.duration=ce.fx.speeds[r.duration]:r.duration=ce.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){v(r.old)&&r.old.call(this),r.queue&&ce.dequeue(this,r.queue)},r},ce.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ee).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=ce.isEmptyObject(t),o=ce.speed(e,n,r),a=function(){var e=yt(this,ce.extend({},t),o);(i||_.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=ce.timers,r=_.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&pt.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||ce.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=_.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=ce.timers,o=n?n.length:0;for(t.finish=!0,ce.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),ce.each(["toggle","show","hide"],function(e,r){var i=ce.fn[r];ce.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(gt(r,!0),e,t,n)}}),ce.each({slideDown:gt("show"),slideUp:gt("hide"),slideToggle:gt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){ce.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),ce.timers=[],ce.fx.tick=function(){var e,t=0,n=ce.timers;for(st=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||ce.fx.stop(),st=void 0},ce.fx.timer=function(e){ce.timers.push(e),ce.fx.start()},ce.fx.interval=13,ce.fx.start=function(){ut||(ut=!0,dt())},ce.fx.stop=function(){ut=null},ce.fx.speeds={slow:600,fast:200,_default:400},ce.fn.delay=function(r,e){return r=ce.fx&&ce.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=ie.setTimeout(e,r);t.stop=function(){ie.clearTimeout(n)}})},lt=C.createElement("input"),ct=C.createElement("select").appendChild(C.createElement("option")),lt.type="checkbox",le.checkOn=""!==lt.value,le.optSelected=ct.selected,(lt=C.createElement("input")).value="t",lt.type="radio",le.radioValue="t"===lt.value;var mt,xt=ce.expr.attrHandle;ce.fn.extend({attr:function(e,t){return M(this,ce.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){ce.removeAttr(this,e)})}}),ce.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?ce.prop(e,t,n):(1===o&&ce.isXMLDoc(e)||(i=ce.attrHooks[t.toLowerCase()]||(ce.expr.match.bool.test(t)?mt:void 0)),void 0!==n?null===n?void ce.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=ce.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!le.radioValue&&"radio"===t&&fe(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(D);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),mt={set:function(e,t,n){return!1===t?ce.removeAttr(e,n):e.setAttribute(n,n),n}},ce.each(ce.expr.match.bool.source.match(/\w+/g),function(e,t){var a=xt[t]||ce.find.attr;xt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=xt[o],xt[o]=r,r=null!=a(e,t,n)?o:null,xt[o]=i),r}});var bt=/^(?:input|select|textarea|button)$/i,wt=/^(?:a|area)$/i;function Tt(e){return(e.match(D)||[]).join(" ")}function Ct(e){return e.getAttribute&&e.getAttribute("class")||""}function kt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(D)||[]}ce.fn.extend({prop:function(e,t){return M(this,ce.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[ce.propFix[e]||e]})}}),ce.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&ce.isXMLDoc(e)||(t=ce.propFix[t]||t,i=ce.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=ce.find.attr(e,"tabindex");return t?parseInt(t,10):bt.test(e.nodeName)||wt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),le.optSelected||(ce.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),ce.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){ce.propFix[this.toLowerCase()]=this}),ce.fn.extend({addClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).addClass(t.call(this,e,Ct(this)))}):(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++)i=e[o],n.indexOf(" "+i+" ")<0&&(n+=i+" ");a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this},removeClass:function(t){var e,n,r,i,o,a;return v(t)?this.each(function(e){ce(this).removeClass(t.call(this,e,Ct(this)))}):arguments.length?(e=kt(t)).length?this.each(function(){if(r=Ct(this),n=1===this.nodeType&&" "+Tt(r)+" "){for(o=0;o<e.length;o++){i=e[o];while(-1<n.indexOf(" "+i+" "))n=n.replace(" "+i+" "," ")}a=Tt(n),r!==a&&this.setAttribute("class",a)}}):this:this.attr("class","")},toggleClass:function(t,n){var e,r,i,o,a=typeof t,s="string"===a||Array.isArray(t);return v(t)?this.each(function(e){ce(this).toggleClass(t.call(this,e,Ct(this),n),n)}):"boolean"==typeof n&&s?n?this.addClass(t):this.removeClass(t):(e=kt(t),this.each(function(){if(s)for(o=ce(this),i=0;i<e.length;i++)r=e[i],o.hasClass(r)?o.removeClass(r):o.addClass(r);else void 0!==t&&"boolean"!==a||((r=Ct(this))&&_.set(this,"__className__",r),this.setAttribute&&this.setAttribute("class",r||!1===t?"":_.get(this,"__className__")||""))}))},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+Tt(Ct(n))+" ").indexOf(t))return!0;return!1}});var St=/\r/g;ce.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=v(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,ce(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=ce.map(t,function(e){return null==e?"":e+""})),(r=ce.valHooks[this.type]||ce.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=ce.valHooks[t.type]||ce.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(St,""):null==e?"":e:void 0}}),ce.extend({valHooks:{option:{get:function(e){var t=ce.find.attr(e,"value");return null!=t?t:Tt(ce.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!fe(n.parentNode,"optgroup"))){if(t=ce(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=ce.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<ce.inArray(ce.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),ce.each(["radio","checkbox"],function(){ce.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<ce.inArray(ce(e).val(),t)}},le.checkOn||(ce.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Et=ie.location,jt={guid:Date.now()},At=/\?/;ce.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{t=(new ie.DOMParser).parseFromString(e,"text/xml")}catch(e){}return n=t&&t.getElementsByTagName("parsererror")[0],t&&!n||ce.error("Invalid XML: "+(n?ce.map(n.childNodes,function(e){return e.textContent}).join("\n"):e)),t};var Dt=/^(?:focusinfocus|focusoutblur)$/,Nt=function(e){e.stopPropagation()};ce.extend(ce.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||C],d=ue.call(e,"type")?e.type:e,h=ue.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||C,3!==n.nodeType&&8!==n.nodeType&&!Dt.test(d+ce.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[ce.expando]?e:new ce.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:ce.makeArray(t,[e]),c=ce.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!y(n)){for(s=c.delegateType||d,Dt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||C)&&p.push(a.defaultView||a.parentWindow||ie)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(_.get(o,"events")||Object.create(null))[e.type]&&_.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&$(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!$(n)||u&&v(n[d])&&!y(n)&&((a=n[u])&&(n[u]=null),ce.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,Nt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,Nt),ce.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=ce.extend(new ce.Event,n,{type:e,isSimulated:!0});ce.event.trigger(r,null,t)}}),ce.fn.extend({trigger:function(e,t){return this.each(function(){ce.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return ce.event.trigger(e,t,n,!0)}});var qt=/\[\]$/,Lt=/\r?\n/g,Ht=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;function Pt(n,e,r,i){var t;if(Array.isArray(e))ce.each(e,function(e,t){r||qt.test(n)?i(n,t):Pt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==x(e))i(n,e);else for(t in e)Pt(n+"["+t+"]",e[t],r,i)}ce.param=function(e,t){var n,r=[],i=function(e,t){var n=v(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!ce.isPlainObject(e))ce.each(e,function(){i(this.name,this.value)});else for(n in e)Pt(n,e[n],t,i);return r.join("&")},ce.fn.extend({serialize:function(){return ce.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=ce.prop(this,"elements");return e?ce.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!ce(this).is(":disabled")&&Ot.test(this.nodeName)&&!Ht.test(e)&&(this.checked||!we.test(e))}).map(function(e,t){var n=ce(this).val();return null==n?null:Array.isArray(n)?ce.map(n,function(e){return{name:t.name,value:e.replace(Lt,"\r\n")}}):{name:t.name,value:n.replace(Lt,"\r\n")}}).get()}});var Mt=/%20/g,Rt=/#.*$/,It=/([?&])_=[^&]*/,Wt=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ft=/^(?:GET|HEAD)$/,$t=/^\/\//,Bt={},_t={},zt="*/".concat("*"),Xt=C.createElement("a");function Ut(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(D)||[];if(v(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Vt(t,i,o,a){var s={},u=t===_t;function l(e){var r;return s[e]=!0,ce.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function Gt(e,t){var n,r,i=ce.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&ce.extend(!0,e,r),e}Xt.href=Et.href,ce.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Et.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Et.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":zt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":ce.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Gt(Gt(e,ce.ajaxSettings),t):Gt(ce.ajaxSettings,e)},ajaxPrefilter:Ut(Bt),ajaxTransport:Ut(_t),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=ce.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?ce(y):ce.event,x=ce.Deferred(),b=ce.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Wt.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Et.href)+"").replace($t,Et.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(D)||[""],null==v.crossDomain){r=C.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Xt.protocol+"//"+Xt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=ce.param(v.data,v.traditional)),Vt(Bt,v,t,T),h)return T;for(i in(g=ce.event&&v.global)&&0==ce.active++&&ce.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ft.test(v.type),f=v.url.replace(Rt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(Mt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(At.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(It,"$1"),o=(At.test(f)?"&":"?")+"_="+jt.guid+++o),v.url=f+o),v.ifModified&&(ce.lastModified[f]&&T.setRequestHeader("If-Modified-Since",ce.lastModified[f]),ce.etag[f]&&T.setRequestHeader("If-None-Match",ce.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+zt+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Vt(_t,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=ie.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&ie.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<ce.inArray("script",v.dataTypes)&&ce.inArray("json",v.dataTypes)<0&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(ce.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(ce.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--ce.active||ce.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return ce.get(e,t,n,"json")},getScript:function(e,t){return ce.get(e,void 0,t,"script")}}),ce.each(["get","post"],function(e,i){ce[i]=function(e,t,n,r){return v(t)&&(r=r||n,n=t,t=void 0),ce.ajax(ce.extend({url:e,type:i,dataType:r,data:t,success:n},ce.isPlainObject(e)&&e))}}),ce.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),ce._evalUrl=function(e,t,n){return ce.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){ce.globalEval(e,t,n)}})},ce.fn.extend({wrapAll:function(e){var t;return this[0]&&(v(e)&&(e=e.call(this[0])),t=ce(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return v(n)?this.each(function(e){ce(this).wrapInner(n.call(this,e))}):this.each(function(){var e=ce(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=v(t);return this.each(function(e){ce(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){ce(this).replaceWith(this.childNodes)}),this}}),ce.expr.pseudos.hidden=function(e){return!ce.expr.pseudos.visible(e)},ce.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},ce.ajaxSettings.xhr=function(){try{return new ie.XMLHttpRequest}catch(e){}};var Yt={0:200,1223:204},Qt=ce.ajaxSettings.xhr();le.cors=!!Qt&&"withCredentials"in Qt,le.ajax=Qt=!!Qt,ce.ajaxTransport(function(i){var o,a;if(le.cors||Qt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(Yt[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&ie.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),ce.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),ce.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return ce.globalEval(e),e}}}),ce.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),ce.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=ce("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=Tt(e.slice(s)),e=e.slice(0,s)),v(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&ce.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?ce("<div>").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var en=/^[\s\uFEFF\xA0]+|([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/g;ce.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),v(e))return r=ae.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(ae.call(arguments)))}).guid=e.guid=e.guid||ce.guid++,i},ce.holdReady=function(e){e?ce.readyWait++:ce.ready(!0)},ce.isArray=Array.isArray,ce.parseJSON=JSON.parse,ce.nodeName=fe,ce.isFunction=v,ce.isWindow=y,ce.camelCase=F,ce.type=x,ce.now=Date.now,ce.isNumeric=function(e){var t=ce.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},ce.trim=function(e){return null==e?"":(e+"").replace(en,"$1")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return ce});var tn=ie.jQuery,nn=ie.$;return ce.noConflict=function(e){return ie.$===ce&&(ie.$=nn),e&&ie.jQuery===ce&&(ie.jQuery=tn),ce},"undefined"==typeof e&&(ie.jQuery=ie.$=ce),ce}); diff --git a/docs/migrating/common_issues.md b/docs/migrating/common_issues.md index 3f4745a7eb..b4d4de4500 100644 --- a/docs/migrating/common_issues.md +++ b/docs/migrating/common_issues.md @@ -1,4 +1,8 @@ -# Common issues +--- +description: Check common issues that occur when updating from earlier version to Ibexa DXP and how to resolve them. +--- + +# Common migration issues Below you will find cleanup commands from the EzPublishMigrationBundle for the most common issues that can occur after migration to [[= product_name =]]. @@ -17,14 +21,23 @@ issues that can occur after migration to [[= product_name =]]. ## Regenerating URL aliases -To regenerate URL aliases, use the `ezplatform:urls:regenerate-aliases` command. +To regenerate URL aliases, use the `ibexa:urls:regenerate-aliases` command. See [Regenerating URL aliases](../guide/url_management.md#regenerating-url-aliases) for more information. !!! note - This command keeps history and replaces the old `ezplatform:regenerate:legacy_storage_url_aliases` command. + This command keeps history and replaces the old `ibexa:regenerate:legacy_storage_url_aliases` command. `legacy_storage_url_aliases` is now deprecated. +## Normalizing images + +If you use image files with unprintable UTF-8 characters, you may come across a problem with images not displaying. +In that case, run the `ezplatform:images:normalize-path` command to normalize them: + +``` bash +php bin/console ezplatform:images:normalize-path +``` + ## Unknown relation type 0 "Unknown relation type 0." error occurs only when using REST API. The issue does not occur @@ -58,7 +71,7 @@ Only affected Fields will be processed by the cleanup command. You can read more about this issue here: [EZP-24882](https://jira.ez.no/browse/EZP-24882) -## Trouble listing sub content +## Listing sub-content It is possible that after upgrade `sort_key_string` is left empty. This may cause problems in searches throughout the API. The cleanup script will check if Fields of given Field diff --git a/docs/migrating/migrating_from_ez_publish.md b/docs/migrating/migrating_from_ez_publish.md index 4b5f93798d..26240b6c98 100644 --- a/docs/migrating/migrating_from_ez_publish.md +++ b/docs/migrating/migrating_from_ez_publish.md @@ -1,3 +1,7 @@ +--- +description: Migrate an older eZ Publish installation to eZ Platform. +--- + # Migrating from eZ Publish eZ Publish was eZ Platform's predecessor, a CMS in development for five major versions and several years. @@ -21,8 +25,8 @@ The specific incompatibilities The specific changes that will be migrated and are incompatible with legacy are:  -- XmlText fields have been replaced with a new [RichText](../api/field_type_reference.md#richtext-field-type) field -- Page field (ezflow) has been replaced by the [LandingPage](../api/field_type_reference.md#landing-page-field-type-enterprise) field, and is now provided by our commercial product [eZ Platform Enterprise Edition](http://ezstudio.com/) +- XmlText fields have been replaced with a new [RichText](../api/field_types_reference/richtextfield.md) field +- Page field (ezflow) has been replaced by the [LandingPage](../api/field_types_reference/pagefield.md) field, and is now provided by our commercial product [eZ Platform Enterprise Edition](http://ezstudio.com/) - Incremental future improvements to the database schema to improve features and scalability of the content repository  Together these major improvements make it practically impossible to run eZ Platform side by side with eZ Publish legacy, like it was possible in 5.x series. *For these reasons we recommend that you use eZ Publish Enterprise 5.4  ([which is supported until end of 2021](https://support.ez.no/Public/Service-Life)) if you don't have the option to remake your web application yet, or want to do it gradually.* @@ -33,18 +37,27 @@ Together these major improvements make it practically impossible to run eZ Platf If you are coming directly from legacy (4.x), you need to follow the best practice 5.x Platform migration path and do the following: -- Rewrite custom Field Types for the new Platform stack. Alternatively you can use Null Field Type as a dummy implementation for the custom Field Types that you don't want to migrate. Using Null Field Type will prevent errors from the Platform Stack, see [Null Field Type Reference](../api/field_type_reference.md#null-field-type) +- Rewrite custom Field Types for the new Platform stack. Alternatively you can use Null Field Type as a dummy implementation for the custom Field Types that you don't want to migrate. Using Null Field Type will prevent errors from the Platform Stack, see [Null Field Type Reference](../api/field_types_reference/nullfield.md) - Rewrite custom web front end to use the new Platform/Symfony stack, see [Beginner Tutorial](../tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md) - Rewrite custom admin modules to use the new Platform/Symfony stack - And if you do this while on 5.x, you can use several of the [available legacy migration features](https://doc.ez.no/display/EZP/Legacy+code+and+features) to make the new code appear in legacy admin -See Upgrade documentation on how to perform the actual upgrade: [Upgrade (eZ Publish Platform page)](https://doc.ez.no/display/EZP/Upgrade) +See Upgrade documentation on how to perform the actual upgrade: [Upgrade (eZ Publish Platform page)](https://doc.ez.no/display/EZP/Upgrade). + +!!! caution "Avoid exception when migrating the database" + + If you plan to migrate from from eZ Publish through eZ Publish Platform 5.4 to eZ Platform and further, an exception may occur when you try to migrate the database while it contains internal drafts of Landing Pages. + This can happen because such drafts do not have an expected row in the `ezcontentobject_name` table.<a id="migration_exception"></a> + + To avoid this exception, you must remove all internal drafts before you migrate. + First, in `content.ini`, set the `InternalDraftsCleanUpLimit` and `InternalDraftsDuration` values to 0. + Then run the [internal drafts cleanup](https://github.com/ezsystems/ezpublish-legacy/blob/2019.03/cronjobs/internal_drafts_cleanup.php) cron job. ### From Platform stack (5.4/2014.11) to eZ Platform As eZ Platform introduced completely new user interfaces with greatly improved user experience, the following custom developments needs to be made if you have customization needs: -- Write UI code for custom Field Types for the new JavaScript-based editorial interface, see  [Extending Admin UI](../tutorials/extending_admin_ui/extending_admin_ui.md)) and [Page blocks](../guide/page_rendering.md#page-blocks) +- Write UI code for custom Field Types for the new JavaScript-based editorial interface, (see [Page blocks](../guide/content_rendering/render_content/render_page.md) - Adjust custom admin modules for the new Symfony-based admin interface For a detailed guide through these developments see [Upgrading from 5.4.x and 2014.11 to 16.xx](migrating_from_ez_publish_platform.md#upgrading-from-54x-and-201411-to-16xx)  diff --git a/docs/migrating/migrating_from_ez_publish_platform.md b/docs/migrating/migrating_from_ez_publish_platform.md index 95d72a6146..4ae1194413 100644 --- a/docs/migrating/migrating_from_ez_publish_platform.md +++ b/docs/migrating/migrating_from_ez_publish_platform.md @@ -1,3 +1,7 @@ +--- +description: Migrate an older eZ Publish Platform installation to eZ Platform. +--- + # Migrating from eZ Publish Platform eZ Publish Platform (5.x) was a transitional version of the Ibexa CMS, bridging the gap between the earlier generation called eZ Publish (sometimes referred to as *legacy*), and eZ Platform, the predecessor to [[= product_name =]]. @@ -42,23 +46,26 @@ This section describes how to upgrade your existing  eZ Publish Platform  5.4 - Browser from 2017 or newer for use with eZ Platform backend UI - This page assumes you have composer installed on the machine and that it is a recent version. -## Upgrade steps +!!! tip "Clearing cache" + + If at any point during the migration procedure you encounter problems with the cache, + refer to [How to clear the cache properly?](../community_resources/support_maintenance_faq.md#how-to-clear-the-cache-properly). -### Step 1: Extract eZ Platform/Enterprise v1.7 +## Step 1: Extract eZ Platform/Enterprise v1.7 The easiest way to upgrade the distribution files is to extract a clean installation of eZ Platform / eZ Enterprise to a separate directory. -### Step 2: Move over code and config +## Step 2: Move over code and config -##### 2.1. Code +### 2.1. Code If you have code in src folder, move that over: `<old-ez-root>/src => <new-ez-root>/src` -##### 2.2. Composer +### 2.2. Composer -###### 2.2.1 Move over own packages +#### 2.2.1 Move over own packages Assuming you have own composer packages *(libraries and bundles, but not eZ Publish legacy packages)*, execute commands like below to add them to new install in `<new-ez-root>`: @@ -66,7 +73,7 @@ Assuming you have own composer packages *(libraries and bundles, but not eZ Publ Adapt the command with your `vendor`, `package`, version number, and add `"–dev"` if a given package is for dev use. Also check if there are other changes in `composer.json` you should move over. -###### 2.2.2 Install XmlText Field Type +#### 2.2.2 Install XmlText Field Type While no longer bundled, the XmlText Field Type still exists and is needed to perform a migration from eZ Publish's XmlText to the new docbook-based format used by the RichText Field Type. If you plan to use Legacy Bridge for a while before migrating content, you'll also need this for rendering content with XMLText. From `<new-ez-root>` execute: @@ -77,7 +84,7 @@ While no longer bundled, the XmlText Field Type still exists and is needed to p As of v1.3, be aware this Field Type now uses the Content View system introduced in eZ Platform 1.0, so make sure you adapt custom templates and override rules if you plan to use this for rendering content _(in Legacy Bridge setup)_. -##### 2.3. Config +### 2.3. Config To move over your own custom configurations, follow the conventions below and manually move the settings over: @@ -110,7 +117,7 @@ To move over your own custom configurations, follow the conventions below and ma In the default configurations in **ezplatform.yaml** you'll find existing SiteAccesses like `site`, and depending on installation perhaps a few others, all under a site group called `site\_group`. Make sure to change those to what you had in **ezpublish.yaml** to avoid issues with having to log in to your website, given user/login policy rules will need to be updated if you change names of SiteAccess as part of the upgrade. -###### 2.3.1 Image aliases +#### 2.3.1 Image aliases Image aliases defined in legacy must also be defined for eZ Platform. Since image aliases in legacy may be scattered around in different `image.ini` files in various extensions, you may find it easier to find all image alias definitions using @@ -153,14 +160,14 @@ ezpublish: - { name: geometry/scaledownonly, params: [170, 220] } ``` -##### 2.4. Bundles +### 2.4. Bundles Move over registration of _your_ bundles you have from src and from composer packages, from old to new kernel: `<old-ez-root>/ezpublish/EzPublishKernel.php => <new-ez-root>/app/AppKernel.php` -##### 2.5. Optional: Install Legacy Bridge +### 2.5. Optional: Install Legacy Bridge If you don't plan to migrate content directly to newer eZ Platform Field Types, you can optionally install Legacy Bridge and gradually handle code and subsequent content migration afterwards. For installation instructions see [here](https://github.com/ezsystems/LegacyBridge/blob/master/INSTALL.md). @@ -168,7 +175,7 @@ If you don't plan to migrate content directly to newer eZ Platform Field Types, The Legacy Bridge integration does not have the same performance, scalability or integrated experience as a pure Platform setup. Like on eZ Publish Platform 5.x there are known edge cases where, for instance, cache or search index won't always be immediately updated across the two systems using the bridge. This is one of the many reasons why we recommend a pure Platform setup where that is possible. -###### 2.5.1 Set up symlinks for legacy folders +#### 2.5.1 Set up symlinks for legacy folders As eZ Publish legacy is installed via composer, we need to take care of placing some files outside its generated `<new-ez-root>/ezpublish_legacy/` folder, and for instance use symlink to place them inside during installation. @@ -176,7 +183,7 @@ As eZ Publish legacy is installed via composer, we need to take care of placing 1. The same goes for the `<new-ez-root>/ezpublish_legacy/var/[<site>/]storage` folder. However, as it is typically not versioned in git, there's no predefined convention for this. If you create a folder within your project structure for symlinking into this folder, as opposed to a mount somewhere else, make sure to mark this folder as ignored by git once it and the corresponding `.keep` file have been added to your checkout. The example below will assume `<new-ez-root>/src/legacy_files/storage` was created for this purpose, if you opt for something else just adjust the instructions. -###### 2.5.2 Upgrade the legacy distribution files +#### 2.5.2 Upgrade the legacy distribution files The easiest way to upgrade the distribution files is to copy the directories that contain site-specific files from the existing 5.4 installation into `/<ezplatform>/ezpublish_legacy`. Make sure you copy the following directories: @@ -193,7 +200,7 @@ The easiest way to upgrade the distribution files is to copy the directories tha Since writable directories and files have been replaced / copied, their permissions might have changed. You most likely need to reconfigure webserver user permissions as instructed further down. -##### 2.6 Binary files +### 2.6 Binary files Binary files can simply be copied from the old to the new installation: @@ -203,7 +210,7 @@ Binary files can simply be copied from the old to the new installation: In the eZ Publish Platform 5.x installation `web/var` is a symlink to `ezpublish_legacy/var`, so if you can't find it in path above you can instead copy the storage files to the similar `ezpublish_legacy/var[/<site_name>]/storage` path. -##### 2.7 Re-apply permissions and update composer +### 2.7 Re-apply permissions and update composer Since writable directories and files have been replaced / copied, their permissions might have changed. You need to re-apply them. @@ -215,15 +222,15 @@ When that is done, execute the following to update and install all packages from At the end of the process, you will be asked for values for parameters.yaml not already moved from old installation, or new *(as defined in parameters.yaml.dist)*. -##### 2.8 Register EzSystemsEzPlatformXmlTextFieldTypeBundle +### 2.8 Register EzSystemsEzPlatformXmlTextFieldTypeBundle Add the following new bundle to your new kernel file, `<new-ez-root>/app/AppKernel.php`: `new EzSystems\EzPlatformXmlTextFieldTypeBundle\EzSystemsEzPlatformXmlTextFieldTypeBundle(),`  -### Step 3: Upgrade the database +## Step 3: Upgrade the database -##### 3.1. Execute update SQL +### 3.1. Execute update SQL Import to your database the changes provided in one of the following files. It's also recommended to read inline comments as you might not need to run some of the queries: @@ -231,11 +238,31 @@ Import to your database the changes provided in one of the following files. It's `Postgres: <new-ez-root>/vendor/ezsystems/ezpublish-kernel/data/update/postgres/dbupdate-5.4.0-to-6.13.0.sql` -##### 3.2. Once you are ready to migrate content to Platform Field Types +### 3.2. Once you are ready to migrate content to Platform Field Types Steps here should only be done once you are ready to move away from legacy and Legacy Bridge, as the following Field Types are not supported by legacy. In other words, content you have migrated will not be editable in legacy admin interface anymore, but rather in the more modern eZ Platform back-end UI, allowing you to take full advantage of what the Platform has to offer. -###### 3.2.1 Migrate XmlText content to RichText +The Field Types unsupported in eZ Platform are: + +- [XmlText](#321-migrate-xmltext-content-to-richtext) +- [Page](#322-migrate-page-field-to-page-ez-enterprise-only) +- Star Rating + +For Field Types which do not have specific procedures below, you must take one of the following actions: + +- implement them yourself in eZ Platform +- remove them from all Content Types that use them + +!!! tip + + To find out which Content Types use a specific Field Type, + you can run the following SQL query on your database (in this case, for the Star Rating Field Type): + + ``` sql + select contentclass_id from ezcontentclass_attribute where data_type_string='ezsrrating' group by contentclass_id; + ``` + +#### 3.2.1 Migrate XmlText content to RichText You should test the XmlText to RichText conversion before you apply it to a production database. RichText has a stricter validation compared to XmlText and you may have to fix some of your XmlText before you are able to convert it to RichText. Run the conversion script on a copy of your production database as the script is rather resource-intensive. @@ -409,7 +436,7 @@ When you are ready to migrate your eZ Publish XmlText content to the eZ Platform eZ Platform now supports custom tags, including inline custom tags, and limited use of custom tag attributes. After migrating to RichText, you need to adapt your custom tag config for eZ Platform and rewrite the custom tags in Twig. -See [Custom tag documentation](../extending/extending_online_editor.md#custom-tags) for more info. +See [Custom tag documentation](../extending/extending_online_editor.md#configure-custom-tags) for more info. If you configured custom attributes in legacy in OE using [ezoe_attributes.ini](https://github.com/ezsystems/ezpublish-legacy/blob/master/extension/ezoe/settings/ezoe_attributes.ini#L33-L48), note that not all types are supported. @@ -417,7 +444,7 @@ Below is a table of the tags that are currently supported, and their correspondi | [XmlText](https://github.com/ezsystems/ezpublish-legacy/blob/2019.03/extension/ezoe/settings/ezoe_attributes.ini#L33-L48) | [RichText](https://github.com/ezsystems/ezplatform-richtext/blob/v1.1.5/src/bundle/DependencyInjection/Configuration.php#L17) | Note | | ------------- | ------------- | ----- | -| `link` | [`link`](../extending/extending_online_editor.md#example-link-tag) | | +| `link` | [`link`](../extending/extending_online_editor.md#link-tag) | | | `number` | `number` | | | `int` | `number` | | | `checkbox` | `boolean` | | @@ -433,29 +460,29 @@ Below is a table of the tags that are currently supported, and their correspondi | `cssborder` | Not supported | Use `string` as workaround | -###### 3.2.2 Migrate Page field to Page (eZ Enterprise only) +#### 3.2.2 Migrate Page field to Page (eZ Enterprise only) **If** you use Page field (ezflow) and an eZ Enterprise subscription, and are ready to migrate your eZ Publish Flow content to the eZ Enterprise Page format, you can use a script to migrate your old Page content to new Page, to start using a pure eZ Enterprise setup. See [Migrating legacy Page field (ezflow) to new Page (Enterprise)](#migrating-legacy-page-field-ezflow-to-new-page-enterprise) for more information. -###### 3.2.3 Add other eZ Enterprise schemas (eZ Enterprise only) +#### 3.2.3 Add other eZ Enterprise schemas (eZ Enterprise only) -For date-based publisher and form builder, there are additional tables, you can import them to your database using the following sql files: +For date-based publisher and Form Builder, there are additional tables, you can import them to your database using the following sql files: `<new-ez-root>/vendor/ezsystems/date-based-publisher/bundle/Resources/install/datebasedpublisher_scheduled_version.sql`, `<new-ez-root>/vendor/ezsystems/ezstudio-form-builder/bundle/Resources/install/form_builder.sql`, `<new-ez-root>/vendor/ezsystems/ezstudio-notifications/bundle/Resources/install/ezstudio-notifications.sql` -### Step 4: Re-configure web server and proxy +## Step 4: Re-configure web server and proxy -#### Varnish *(optional)* +### Varnish *(optional)* -If you use Varnish, the recommended Varnish (4 or higher) VCL configuration can be found in the `doc/varnish` folder. See also the [Using Varnish](../guide/http_cache.md#using-varnish) page. +If you use Varnish, the recommended Varnish (4 or higher) VCL configuration can be found in the [Using Varnish](../guide/cache/symfony_reverse_proxy.md#configure-varnish-or-fastly) page. -#### Web server configuration +### Web server configuration The officially recommended virtual configuration is now shipped in the `doc` folder, for both apache2 (`doc/apache2`) and nginx (`doc/nginx`). Both are built to be easy to understand and use, but aren't meant as drop-in replacements for your existing configuration. As was the case starting 5.4, one notable change is that `SetEnvIf` is now used to dynamically change rewrite rules depending on the Symfony environment. It is currently used for the assetic production rewrite rules. -### Step 5: Link assets +## Step 5: Link assets Assets from the various bundles need to be made available for the webserver through the web/ document root. Execute the following commands from `<new-ez-root>`: diff --git a/docs/references.md b/docs/references.md index 671f2ff88f..19a56d9d25 100644 --- a/docs/references.md +++ b/docs/references.md @@ -2,19 +2,18 @@ ## API -- [REST API](https://doc.ezplatform.com/rest-api-reference) +- [REST API](api/rest_api_reference/rest_api_reference.html) - [Public PHP API](api/public_php_api.md) - [Field Types](api/field_type_reference.md) -- [Shop Field Types](api/commerce_api/field_type_reference/field_type_reference.md) - [GraphQL](api/graphql_queries.md) ## Content model and rendering -- [Twig functions](guide/twig_functions_reference.md) -- [Twig helpers list](guide/content_rendering.md#twig-helper) -- [Image variation filters](guide/images.md#available-filters) -- [SiteAccess matchers](guide/siteaccess_matching.md#available-matchers) -- [View matchers](guide/content_rendering.md#available-matchers) +- [Twig functions](guide/content_rendering/twig_function_reference/twig_functions_reference.md) +- [Template variables](guide/content_rendering/templates/templates.md#template-variables) +- [Image variation filters](guide/content_rendering/image_variations.md#available-variation-filters) +- [SiteAccess matchers](guide/multisite/siteaccess_matching.md#available-siteaccess-matchers) +- [View matcher reference](guide/content_rendering/templates/view_matcher_reference.md) ## Permissions @@ -23,6 +22,6 @@ ## Search -- [Search Criteria](guide/search/search_criteria_reference.md) -- [Sort Clauses](guide/search/sort_clause_reference.md) -- [Aggregation](guide/search/aggregation_reference.md) +- [Search Criteria](guide/search/criteria_reference/search_criteria_reference.md) +- [Sort Clauses](guide/search/sort_clause_reference/sort_clause_reference.md) +- [Aggregation](guide/search/aggregation_reference/aggregation_reference.md) diff --git a/docs/releases/ez_platform_v1.10.0.md b/docs/releases/ez_platform_v1.10.0.md index 59aac0d726..0d221bf2ec 100644 --- a/docs/releases/ez_platform_v1.10.0.md +++ b/docs/releases/ez_platform_v1.10.0.md @@ -3,11 +3,7 @@ **The FAST TRACK v1.10.0 release of eZ Platform and eZ Platform Enterprise Edition is available as of June 28, 2017.** -If you are looking for the Long Term Support (LTS) release, see[ https://ezplatform.com/Blog/Long-Term-Support-is-Here](https://ezplatform.com/Blog/Long-Term-Support-is-Here) - -Upgrade notes - -This release contains special steps to follow further described in [Updating eZ Platform](../updating/updating_ez_platform.md). +If you are looking for the Long Term Support (LTS) release, see [https://ezplatform.com/Blog/Long-Term-Support-is-Here](https://ezplatform.com/Blog/Long-Term-Support-is-Here) ## Notable changes since v1.9.0 @@ -68,9 +64,9 @@ $value = $content->getFieldValue('body'); #### SOLR: Index time boosting & Improved Facets support -One of the new features in 1.10 *(Solr Bundle 1.4)* is the possibility to [configure index time boosting](../guide/search/solr.md), which enables you to properly tune the search results to be relevant for your content architecture. +One of the new features in 1.10 *(Solr Bundle 1.4)* is the possibility to [configure index time boosting](https://doc.ibexa.co/en/latest/guide/search/solr/#boost-configuration), which enables you to properly tune the search results to be relevant for your content architecture. -In addition to that, we made progress on providing native support for faceted search within eZ Platform when using the Solr Bundle. You can now use facets based on ContentTypes, Sections and Users, see [Performing a Faceted Search](../api/public_php_api_search.md#performing-a-faceted-search) page for how to use them. We plan to provide more facets natively in the coming releases. +In addition to that, we made progress on providing native support for faceted search within eZ Platform when using the Solr Bundle. You can now use facets based on ContentTypes, Sections and Users, see [Performing a Faceted Search](https://doc.ibexa.co/en/latest/api/public_php_api_search/#faceted-search) page for how to use them. We plan to provide more facets natively in the coming releases. #### Cluster migration script @@ -93,8 +89,8 @@ Starting with 1.10, a new command `ezplatform:io:migrate-files` has been added ### eZ Platform Enterprise Edition - Studio Demo -- [DEMO-102](https://jira.ez.no/browse/DEMO-102): [NovaeZSEOBundle](https://github.com/Novactive/NovaeZSEOBundle/) is now included in Studio Demo. [NovaeZSEOBundle](https://github.com/Novactive/NovaeZSEOBundle/) includes a new Field Type that lets you manage your SEO strategy in very advanced and powerful ways. -- [DEMO-100](https://jira.ez.no/browse/DEMO-100): We also improved the way we provide personalization in the site using a profiling block ([DEMO-94](https://jira.ez.no/browse/DEMO-94)) and letting the end user manage their preferences by themselves. In this new version, the end user, once logged on the site, can access a page where they can define their content preferences. See [here](https://ez.no/Blog/Personalization-Does-Not-Have-to-Be-that-Complex) for more information. +- [DEMO-102](https://jira.ez.no/browse/DEMO-102): [NovaeZSEOBundle](https://github.com/Novactive/NovaeZSEOBundle/) is now included in Studio Demo. NovaeZSEOBundle includes a new Field Type that lets you manage your SEO strategy in very advanced and powerful ways. +- [DEMO-100](https://jira.ez.no/browse/DEMO-100): We also improved the way we provide personalization in the site using a profiling block and letting the end user manage their preferences by themselves. In this new version, the end user, once logged on the site, can access a page where they can define their content preferences. See [here](https://ez.no/Blog/Personalization-Does-Not-Have-to-Be-that-Complex) for more information. ## Full list of new features, improvements and bug fixes since v1.9.0 @@ -106,32 +102,23 @@ Starting with 1.10, a new command `ezplatform:io:migrate-files` has been added ### Acknowledgements -Kudos to [@emodric](https://twitter.com/emodric) for the Tags bundle, [@pspanja]() for the work Solr index-time boosting, [@plopix](https://twitter.com/Plopix) for the NovaeZSEOBundle, [@](https://twitter.com/jvieilledent)[jvieilledent](https://twitter.com/jvieilledent)[ ](https://twitter.com/jvieilledent)for the initial work on the design engine and to all others who contributed bug reports, feedback and comments that made this release possible. +Kudos to [@emodric](https://twitter.com/emodric) for the Tags bundle, [@pspanja](https://twitter.com/pspanja) for the work Solr index-time boosting, [@plopix](https://twitter.com/Plopix) for the NovaeZSEOBundle, [@jvieilledent](https://twitter.com/jvieilledent) for the initial work on the design engine and to all others who contributed bug reports, feedback and comments that made this release possible. ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download #### eZ Platform -- Download at [eZPlatform.com](http://ezplatform.com/#download) +- Download at [eZPlatform.com](http://ezplatform.com/#download) #### eZ Enterprise -- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads)* - * -- [Partners: Test & Trial software access (TTL License)](https://support.ez.no/Downloads) - -If you would like to request an eZ Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> - -### Updating +- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) +- Partners: Test & Trial software access (TTL License) -To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.10.0 as `<version>`. - -**Note:** When updating eZ Platform Enterprise Edition, you need to [add the new EzSystemsPlatformEEAssetsBundle](../updating/3_update_app.md) - -  +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). diff --git a/docs/releases/ez_platform_v1.11.0.md b/docs/releases/ez_platform_v1.11.0.md index 2928378d40..559f7bdc2a 100644 --- a/docs/releases/ez_platform_v1.11.0.md +++ b/docs/releases/ez_platform_v1.11.0.md @@ -19,7 +19,7 @@ Note that the former way will be removed in a future major version. [EZP-24800](https://jira.ez.no/browse/EZP-24800): you can now specify a Content Type limitation for the Relation field, just like with the Relation List field. This enables you to limit what kind of relations Editors can select also on singular relation fields. -![Adding a new Relation (single) Field with allowed Content Types](img\relation_single_allowed_cts.png) +![Adding a new Relation (single) Field with allowed Content Types](img/relation_single_allowed_cts.png) This has been made possible by initial legacy contribution from [@peterkeung](https://github.com/peterkeung), and [@slaci](https://github.com/slaci) who ported this feature over to eZ Platform so that both could go in. @@ -40,7 +40,7 @@ It enables you to manually select a set of Content items to be displayed. To enable adding content to a Collection block in a clean installation, you need to configure the views for the block and define which Content Types can be embedded in it. - See [block templates](../guide/page_rendering.md#block-templates) for more information and an example. + See [block templates](https://doc.ibexa.co/en/latest/content_management/pages/page_blocks/#block-templates) for more information and an example. #### RecommendationBundle adapted for YooChoose v2 @@ -81,9 +81,9 @@ full support, maintenance, and priority security patch handling as they are used ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download @@ -94,10 +94,10 @@ full support, maintenance, and priority security patch handling as they are used #### eZ Enterprise - [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) -- [Partners: Test & Trial software access (TTL License)](https://ez.no/Partner-Portal/Software-Downloads-Release-Info) +- Partners: Test & Trial software access (TTL License) -If you would like to request an eZ Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.11.0 as `<version>`. +To update the product, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). diff --git a/docs/releases/ez_platform_v1.12.0.md b/docs/releases/ez_platform_v1.12.0.md index 09364c2de8..df9785c5c8 100644 --- a/docs/releases/ez_platform_v1.12.0.md +++ b/docs/releases/ez_platform_v1.12.0.md @@ -24,7 +24,7 @@ See [EZP-26806](https://jira.ez.no/browse/EZP-26806) for more information. You can now remove translations from Content item Versions through the PHP API. -See the [section on deleting translations](../api/public_php_api_managing_content.md#delete-translations) for more information. +See the section on [deleting translations](https://doc.ibexa.co/en/latest/api/public_php_api_creating_content/#deleting-a-translation) for more information. You also have a new endpoint available for deleting a single Version, see [EZP-27864](https://jira.ez.no/browse/EZP-27864) for more information. @@ -51,26 +51,26 @@ For Varnish users be aware thus change implies new VCL and requriment for varnis ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download #### eZ Platform -- Download at [eZPlatform.com](http://ezplatform.com/#download) +- Download at eZPlatform.com #### eZ Enterprise - [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) -- [Partners: Test & Trial software access (TTL License)](https://ez.no/Partner-Portal/Software-Downloads-Release-Info) +- Partners: Test & Trial software access (TTL License) -If you would like to request an eZ Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.12.0 as `<version>`. +To update to this version, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). !!! caution "BC: Change for Varnish users" diff --git a/docs/releases/ez_platform_v1.13.0_lts.md b/docs/releases/ez_platform_v1.13.0_lts.md index 4863a92a9d..cd191771a2 100644 --- a/docs/releases/ez_platform_v1.13.0_lts.md +++ b/docs/releases/ez_platform_v1.13.0_lts.md @@ -21,7 +21,7 @@ You can edit a link in the manager and it will be updated automatically in all C Following [EZP-27759](https://jira.ez.no/browse/EZP-27759) you can now copy a Content item with all of its sub-items in the back office. -The maximum number of Content items that can be copied this way can be set in configuration, see [Copy subtree limit](../guide/config_back_office.md#copy-subtree-limit). +The maximum number of Content items that can be copied this way can be set in configuration, see [Copy subtree limit](https://doc.ibexa.co/en/latest/guide/config_back_office/#copy-subtree-limit). ![Copy subtree option in the menu](img/copy_subtree_button.png) @@ -36,7 +36,7 @@ The maximum number of Content items that can be copied this way can be set in co ### Fastly -Following [EZEE-1781](https://jira.ez.no/browse/EZEE-1781) you can [serve Varnish through Fastly](../guide/http_cache.md#serving-varnish-through-fastly). +Following [EZEE-1781](https://jira.ez.no/browse/EZEE-1781) you can [serve Varnish through Fastly](https://doc.ibexa.co/en/latest/infrastructure_and_maintenance/cache/http_cache/reverse_proxy/). ## Full list of new features, improvements and bug fixes since v1.12.0 @@ -48,9 +48,9 @@ Following [EZEE-1781](https://jira.ez.no/browse/EZEE-1781) you can [serve Varnis ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download @@ -61,10 +61,10 @@ Following [EZEE-1781](https://jira.ez.no/browse/EZEE-1781) you can [serve Varnis #### eZ Enterprise - [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) -- [Partners: Test & Trial software access (TTL License)](https://ez.no/Partner-Portal/Software-Downloads-Release-Info) +- Partners: Test & Trial software access (TTL License) -If you would like to request an eZ Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.13.0 as `<version>`. +To update the product, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). diff --git a/docs/releases/ez_platform_v1.7.0_lts.md b/docs/releases/ez_platform_v1.7.0_lts.md index 7ba2147a65..483adb8192 100644 --- a/docs/releases/ez_platform_v1.7.0_lts.md +++ b/docs/releases/ez_platform_v1.7.0_lts.md @@ -85,36 +85,25 @@ The Enterprise demo site has been significantly improved featuring a new **Produ   -## Full list of new features, improvements and bug fixes since v1.6.0: - - - ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download #### eZ Platform -- Download at [eZPlatform.com](http://ezplatform.com/#download) - -  - -  - -#### eZ Platform Enterprise Edition +- Download at [eZPlatform.com](http://ezplatform.com/#download) -- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) +#### eZ Enterprise -- [Partners: Test & Trial software access (TTL License)](https://support.ez.no/Downloads) +- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) +- Partners: Test & Trial software access (TTL License) -If you would like to request an Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> - -  +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -**eZ Platform**: To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.7.0 as `<version>`. +To update the product, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). diff --git a/docs/releases/ez_platform_v1.8.0.md b/docs/releases/ez_platform_v1.8.0.md index 4deaa44d80..dc6766cf07 100644 --- a/docs/releases/ez_platform_v1.8.0.md +++ b/docs/releases/ez_platform_v1.8.0.md @@ -1,17 +1,9 @@ - - # eZ Platform v1.8.0 **The FAST TRACK v1.8.0 release of eZ Platform and eZ Platform Enterprise Edition is available as of February 16, 2017.** If you are looking for the Long Term Support (LTS) release, see[ https://ezplatform.com/Blog/Long-Term-Support-is-Here](https://ezplatform.com/Blog/Long-Term-Support-is-Here) -Upgrade notes - -This release contains special steps to follow further described in [Updating eZ Platform](../updating/updating_ez_platform.md). - - - ## Notable Changes Since v1.7.0 LTS ### eZ Platform @@ -89,31 +81,23 @@ This release contains special steps to follow further described in [Updating eZ ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) - [Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download #### eZ Platform -- Download at [eZPlatform.com](http://ezplatform.com/#download) - -  - -  +- Download at [eZPlatform.com](http://ezplatform.com/#download) -#### eZ Platform Enterprise Edition +#### eZ Enterprise -- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) -- [Partners: Test & Trial software access (TTL License)](https://support.ez.no/Downloads) +- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) +- Partners: Test & Trial software access (TTL License) -If you would like to request an Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> - -  +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -**eZ Platform**: To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.8.0 as `<version>`. - -  +To update the product, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). diff --git a/docs/releases/ez_platform_v1.9.0.md b/docs/releases/ez_platform_v1.9.0.md index 19ab9d37df..9d9fcaccb6 100644 --- a/docs/releases/ez_platform_v1.9.0.md +++ b/docs/releases/ez_platform_v1.9.0.md @@ -34,7 +34,7 @@ In version 1.8 we introduced a new Content Browser in the Universal Discovery Wi - You can now restore from Trash content whose original Location has been deleted. - Pasted thead/tfood tags are now kept in RichText Field Type, and its Online Editor -- Solr 6 is now supported in [Solr Bundle](../guide/search/solr.md) +- Solr 6 is now supported in [Solr Bundle](https://doc.ibexa.co/en/latest/guide/search/solr) ### eZ Platform Enterprise Edition - Studio @@ -65,26 +65,23 @@ The eZ Enterprise Demo now uses the [Netgen Tags bundle](https://github.com/netg ### Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation Guide](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical Requirements](https://doc.ibexa.co/en/latest/getting_started/requirements) ### Download #### eZ Platform -- Download at [eZPlatform.com](http://ezplatform.com/#download) +- Download at [eZPlatform.com](http://ezplatform.com/#download) #### eZ Enterprise -- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads)* - * -- [Partners: Test & Trial software access (TTL License)](https://support.ez.no/Downloads) +- [Customers: eZ Enterprise subscription (BUL License)](https://support.ez.no/Downloads) +- Partners: Test & Trial software access (TTL License) -If you would like to request an eZ Enterprise Demo instance: <http://ez.no/Forms/Discover-eZ-Studio> +If you would like to become familiar with the products, [request a demo](https://www.ibexa.co/forms/request-a-demo). ### Updating -To update to this version, follow the [Updating eZ Platform](../updating/updating_ez_platform.md) guide and use v1.9.0 as `<version>`. - -  +To update the product, follow the [updating guide](https://doc.ibexa.co/en/latest/updating/updating/). diff --git a/docs/releases/ez_platform_v2.0.0.md b/docs/releases/ez_platform_v2.0.0.md index 623104d6f4..4bf99372e0 100644 --- a/docs/releases/ez_platform_v2.0.0.md +++ b/docs/releases/ez_platform_v2.0.0.md @@ -45,6 +45,6 @@ eZ Platform v2.0.0 requires PHP version 7.1, instead of 5.6, as before. Together ## Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation guide](https://doc.ibexa.co/en/2.5/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical requirements](https://doc.ibexa.co/en/2.5/getting_started/requirements) diff --git a/docs/releases/ez_platform_v2.1.0.md b/docs/releases/ez_platform_v2.1.0.md index b02ec80ba3..9e2eb27be4 100644 --- a/docs/releases/ez_platform_v2.1.0.md +++ b/docs/releases/ez_platform_v2.1.0.md @@ -14,7 +14,7 @@ You can now add custom tags to RichText Fields. Custom tags enable you to extend the menu of available elements when editing a RichText Field with the Online Editor. -See [Custom tags](../extending/extending_online_editor.md#custom-tags) for more information. +See [Custom tags](https://doc.ibexa.co/en/2.5/guide/extending/extending_online_editor/#custom-tags) for more information. ### Object states @@ -22,7 +22,7 @@ Object states enable you to create sets of custom states and then assign them to !["Lock" Object state](img/object_state_lock.png) -Object states can be used in conjunction with [permissions](../guide/limitation_reference.md#state-limitation). +Object states can be used in conjunction with [permissions](https://doc.ibexa.co/en/2.5/guide/limitation_reference/#state-limitation). ### Content on the fly @@ -80,6 +80,6 @@ You can now perform REST search via `POST /views` using custom `FieldCriterion`. ## Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation guide](https://doc.ibexa.co/en/2.5/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical requirements](https://doc.ibexa.co/en/2.5/getting_started/requirements) diff --git a/docs/releases/ez_platform_v2.2.0.md b/docs/releases/ez_platform_v2.2.0.md index 151a8c6705..1ce3d24ed5 100644 --- a/docs/releases/ez_platform_v2.2.0.md +++ b/docs/releases/ez_platform_v2.2.0.md @@ -37,9 +37,9 @@ In the Page block config you can now specify the CSS class with its own style fo !!! caution "Updating to 2.2" - Refer to [Updating eZ Platform](../updating/4_update_2.2.md) for a database update script. + Refer to [Updating eZ Platform](https://doc.ibexa.co/en/2.5/updating/5_update_2.2) for a database update script. - To update to 2.2 with existing Content you will need a [dedicated script for converting the Landing Page into the new Page](../updating/4_update_2.2.md#migrate-landing-pages). + To update to 2.2 with existing Content you will need a [dedicated script for converting the Landing Page into the new Page](https://doc.ibexa.co/en/2.5/updating/5_update_2.2/#migrate-landing-pages). ### Bookmarks @@ -51,14 +51,14 @@ You can find the list of all bookmarks in *Browse content* section. There, you c ### Image placeholders -[Placeholder generator](../guide/images.md#setting-placeholder-generator) enables you to replace any missing image with downloaded or generated image placeholder. It can be used when you are working on an existing database and you are not able to download uploaded images to your local development environment because of their large size. +[Placeholder generator](https://doc.ibexa.co/en/2.5/guide/images/#setting-placeholder-generator) enables you to replace any missing image with downloaded or generated image placeholder. It can be used when you are working on an existing database and you are not able to download uploaded images to your local development environment because of their large size. ![Placeholder GenericProvider](img/placeholder_generic_provider.png) ### Standard design -eZ Platform now comes with two designs using the [design engine](../guide/design_engine.md): `standard` for content view and `admin` for the Back Office. -See [default designs](../guide/design_engine.md#default-designs) for more information. +eZ Platform now comes with two designs using the [design engine](https://doc.ibexa.co/en/2.5/guide/design_engine): `standard` for content view and `admin` for the Back Office. +See [default designs](https://doc.ibexa.co/en/2.5/guide/design_engine/#default-designs) for more information. !!! caution @@ -71,7 +71,7 @@ When viewing User or User Group Content items you can now preview what permissio ![Preview of permissions assigned to a User](img/2.2_permissions_in_user_view.png) -You can also [select which Content Types are treated the same way as User of User Group](../guide/configuration.md#user-identifiers) for these purposes. +You can also [select which Content Types are treated the same way as User of User Group](https://doc.ibexa.co/en/2.5/guide/config_repository/#user-identifiers) for these purposes. ### Change from UTF8 to UTF8MB4 @@ -79,13 +79,13 @@ Database charset is changed from UTF8 to UTF8MB4, in order to support 4-byte cha !!! caution - To cover this change when upgrading, follow the instructions in the [update guide](../updating/4_update_2.2.md). + To cover this change when upgrading, follow the instructions in the [update guide](https://doc.ibexa.co/en/2.5/updating/5_update_2.2). ### URL generation pattern You can now select the pattern that will be used to generate URL patterns. -See [URL alias patterns](../guide/url_management.md#url-alias-patterns) for more information about the available settings. +See [URL alias patterns](https://doc.ibexa.co/en/2.5/guide/url_management/#url-alias-patterns) for more information about the available settings. !!! caution "Default URL generation pattern" @@ -118,8 +118,8 @@ New Bookmark service had been added. Bookmark operations are now available via t This release introduces a few notable simplifications to API use. Here are some highlights: -- [Location object now gives access to Content](../api/public_php_api_browsing.md#location-object-with-access-to-content) -- [Optional SiteAccessAware Repository](../api/public_php_api_browsing.md#siteaccess-aware-repository-optional) +- [Location object now gives access to Content](https://doc.ibexa.co/en/2.5/api/public_php_api_browsing/#getting-content-from-a-location) +- [Optional SiteAccessAware Repository](https://doc.ibexa.co/en/2.5/api/public_php_api_browsing/#siteaccess-aware-repository) ## Full list of new features, improvements and bug fixes since v2.1.0 @@ -131,6 +131,6 @@ This release introduces a few notable simplifications to API use. Here are some ## Installation -[Installation Guide](../getting_started/install_ez_platform.md) +[Installation guide](https://doc.ibexa.co/en/2.5/getting_started/install_ez_platform) -[Technical Requirements](../getting_started/requirements.md) +[Technical requirements](https://doc.ibexa.co/en/2.5/getting_started/requirements) diff --git a/docs/releases/ez_platform_v2.3.md b/docs/releases/ez_platform_v2.3.md index 4bb274c809..f677e73f7e 100644 --- a/docs/releases/ez_platform_v2.3.md +++ b/docs/releases/ez_platform_v2.3.md @@ -33,7 +33,7 @@ !!! tip "User documentation" - You can also check user documentation on [advanced publishing options](https://doc.ezplatform.com/projects/userguide/en/latest/publishing/#advanced-publishing-options) + You can also check user documentation on [advanced publishing options](https://doc.ibexa.co/projects/userguide/en/2.5/publishing/advanced_publishing_options) ### Form Builder @@ -47,17 +47,17 @@ ![Form Builder submissions](img/2.3_form_builder_submissions.png) - See [Extending Form Builder](../extending/extending_form_builder.md) for information on how to modify and create Form fields. + See [Extending Form Builder](https://doc.ibexa.co/en/2.5/guide/extending/extending_form_builder) for information on how to modify and create Form fields. !!! tip "User documentation" - You can also check user documentation on [forms](https://doc.ezplatform.com/projects/userguide/en/latest/creating_content_advanced/#forms) + You can also check user documentation on [forms](https://doc.ibexa.co/projects/userguide/en/2.5/creating_content_advanced/#forms) ### ImageAsset Field Type You can now create a single source media library with images that can be reused across the system. -See [Reusing images](../guide/images.md#reusing-images) and [ImageAsset Field Type reference](../api/field_type_reference.md#imageasset-field-type) for more information. +See [Reusing images](https://doc.ibexa.co/en/2.5/guide/images/#reusing-images) and [ImageAsset Field Type reference](https://doc.ibexa.co/en/2.5/api/field_types_reference/imageassetfield) for more information. ![Set up multiple relations with image](img/2.3_image_asset.png) @@ -66,7 +66,7 @@ See [Reusing images](../guide/images.md#reusing-images) and [ImageAsset Field Ty A new `ezplatform:urls:regenerate-aliases` command enables you to regenerate all URL aliases. You can use it after changing URL alias configuration, or in case of database corruption. -See [Regenerating URL aliases](../guide/url_management.md#regenerating-url-aliases) for more information. +See [Regenerating URL aliases](https://doc.ibexa.co/en/2.5/guide/url_management/#regenerating-url-aliases) for more information. ### User preferences @@ -102,7 +102,7 @@ There are three new ways you can now contribute to Back Office translations: - translate in-context with console - translate directly on the Crowdin website -See [How to translate the interface using Crowdin](../community_resources/translations.md#how-to-translate-the-interface-using-crowdin) for more information. +See [How to translate the interface using Crowdin](https://doc.ibexa.co/en/2.5/community_resources/translations/#how-to-translate-the-interface-using-crowdin) for more information. ## Full list of new features, improvements and bug fixes since v2.2.0 @@ -115,6 +115,6 @@ See [How to translate the interface using Crowdin](../community_resources/transl ## Installation -[Installation guide](../getting_started/install_ez_platform.md) +[Installation guide](https://doc.ibexa.co/en/2.5/getting_started/install_ez_platform) -[Technical requirements](../getting_started/requirements.md) +[Technical requirements](https://doc.ibexa.co/en/2.5/getting_started/requirements) diff --git a/docs/releases/ez_platform_v2.4.md b/docs/releases/ez_platform_v2.4.md index 455f681de0..d4e793fbdb 100644 --- a/docs/releases/ez_platform_v2.4.md +++ b/docs/releases/ez_platform_v2.4.md @@ -12,12 +12,12 @@ ### Editorial workflow - [Editorial Workflow](../guide/workflow.md) enables you to pass content through a series of stages. + [Editorial Workflow](https://doc.ibexa.co/en/2.5/guide/workflow) enables you to pass content through a series of stages. Each step can be used to represent for example contributions and approval of different teams and editors. For instance, an article can pass through draft, design and proofreading stages. - The workflow mechanism is [permission-aware](../guide/workflow.md#permissions). + The workflow mechanism is [permission-aware](https://doc.ibexa.co/en/2.5/guide/workflow/#permissions). You can limit access to content in different workflow stages, or the ability to pass content through specific transitions. ![Workflow event timeline](img/2.4_workflow_events_timeline.png "Timeline of workflow stages a Content item has gone through") @@ -33,7 +33,7 @@ RichText Field Type has been extracted to a separate bundle, [ezsystems/ezplatfo If you're implementing any interface or extending any base class from the old namespace, refer to its PHPDoc to see what to implement or extend instead. Make sure to enable the new eZ Platform RichTextBundle. -See [RichText Field Type Reference](../api/field_type_reference.md#richtext-field-type). +See [RichText Field Type Reference](https://doc.ibexa.co/en/2.5/api/field_types_reference/richtextfield). #### RichText block @@ -67,14 +67,14 @@ The new `embed-inline` built-in view type enables embedding Content items within #### Custom tag - `ezcontent` The `ezcontent` property is now editable in the UI and can be used to store the output/preview of a custom tag. -To learn how it works, see [FactBox tag](../extending/extending_online_editor/#factbox-tag). +To learn how it works, see [FactBox tag](https://doc.ibexa.co/en/2.5/guide/extending/extending_online_editor/#example-factbox-tag). ### Content Type translation You can now translate Content Type names and Field definitions. This possibility is available automatically when you have the target language configured -(in the same way as for translating content, see [Languages](../guide/internationalization.md)). +(in the same way as for translating content, see [Languages](https://doc.ibexa.co/en/2.5/guide/internationalization)). ![Content type with existing translations](img/2.4_content_type_translations.png "Available translation of a Content Type") @@ -86,7 +86,7 @@ When you translate Content of this type, the Content Type information will be di New multi-file content management functionalities enable you to move and delete multiple files at the same time. -See [Multi-file content management](https://doc.ezplatform.com/projects/userguide/en/latest/multi_file_content_management/) for more information. +See [Multi-file content management](https://doc.ibexa.co/projects/userguide/en/2.5/multi_file_content_management/#multi-file-content-management) for more information. !!! dxp @@ -108,13 +108,13 @@ The list of all drafts can now be found in the **Administrator User** menu under ![Administrator User list of all Drafts](img/2.4_drafts_admin_user.png "Administrator User list of all Drafts") -See [Reviewing a draft](https://doc.ezplatform.com/projects/userguide/en/latest/publishing/#reviewing-a-draft) for more information. +See [Reviewing a draft](https://doc.ibexa.co/projects/userguide/en/2.5/publishing/flex_workflow/#reviewing-a-draft) for more information. ### Subtree search filter A new filter enables you to filter search results by Subtree. -See [Simplified Filtered search](https://doc.ezplatform.com/projects/userguide/en/latest/search/#simplified-filtered-search) for more information. +See [Simplified Filtered search](https://doc.ibexa.co/projects/userguide/en/2.5/search/#simplified-filtered-search) for more information. ### Sub-items limit @@ -156,7 +156,7 @@ You are now able to load multiple Locations at once, using `LocationService->loa - Online Editor format for `ezlink` inside `ezembed` tag changed to an anchor tag. See [ezplatform-richtext/pull/20](https://github.com/ezsystems/ezplatform-richtext/pull/20). - The merge order of content edit forms has been changed. It can affect you if you extended the content edit template. See [ezplatform-admin-ui/pull/720](https://github.com/ezsystems/ezplatform-admin-ui/pull/720). -- Changes to the handling of multilingual Content Types, see [BC notes in the kernel](https://github.com/ezsystems/ezpublish-kernel/blob/master/doc/bc/changes-7.4.md). +- Changes to the handling of multilingual Content Types, see [BC notes in the kernel](https://github.com/ezsystems/ezpublish-kernel/blob/7.5/doc/bc/changes-7.4.md). ## Full list of new features, improvements and bug fixes since v2.3 diff --git a/docs/releases/ez_platform_v2.5.md b/docs/releases/ez_platform_v2.5.md index 3534a82af6..f5fa8f0c0d 100644 --- a/docs/releases/ez_platform_v2.5.md +++ b/docs/releases/ez_platform_v2.5.md @@ -16,9 +16,9 @@ Each Content item has a unique icon that helps you identify it without opening. ![Content Tree in the menu](img/left_menu_tree.png "Content Tree in the menu") -For more information on custom configuration, see [Content Tree](../guide/config_back_office/#content-tree) in the developer documentation. +For more information on custom configuration, see [Content Tree](https://doc.ibexa.co/en/2.5/guide/config_back_office/#content-tree) in the developer documentation. -For full description of the interface, see [Content Tree](https://doc.ezplatform.com/projects/userguide/en/latest/content_model/#content-tree) in the user documentation. +For full description of the interface, see [Content Tree](https://doc.ibexa.co/projects/userguide/en/2.5/content_model/#content-tree) in the user documentation. ### Webpack Encore @@ -30,20 +30,20 @@ Assetic is still in use, but it will be deprecated in a future version. ### PostgreSQL -This release enables you to [use PostgreSQL](../guide/databases.md#using-postgresql) for database instead of the default MySQL. +This release enables you to [use PostgreSQL](https://doc.ibexa.co/en/2.5/guide/databases/#using-postgresql) for database instead of the default MySQL. Database schema is now created based on [YAML configuration](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml). ### GraphQL -You can now take advantage of [GraphQL](../api/graphql.md) to query and operate on content. +You can now take advantage of [GraphQL](https://doc.ibexa.co/en/2.5/api/graphql) to query and operate on content. It uses a domain schema based on your content model. See [GraphQL documentation](https://graphql.org/) for more information about GraphQL in general. ### Matrix Field Type -The new [Matrix Field Type](../api/field_type_reference.md#matrix-field-type) enables you to store a table of data. +The new [Matrix Field Type](https://doc.ibexa.co/en/2.5/api/field_types_reference/matrixfield) enables you to store a table of data. Columns in the matrix are defined in the Field definition. ![Configuring a Matrix Field Type](img/2.5_matrix_ft.png) @@ -81,16 +81,16 @@ You can now link fragments of text by adding Anchors in Rich Text Fields. #### Inline custom tags -You can now create [inline custom tags](../extending/extending_online_editor.md#inline-custom-tags) in Rich Text Fields. +You can now create [inline custom tags](https://doc.ibexa.co/en/2.5/guide/extending/extending_online_editor/#inline-custom-tags) in Rich Text Fields. #### Custom CK Editor plugins -You can now easily use [custom CK Editor plugins](../extending/extending_online_editor.md#plugins-configuration) in AlloyEditor. +You can now easily use [custom CK Editor plugins](https://doc.ibexa.co/en/2.5/guide/extending/extending_online_editor/#plugins-configuration) in AlloyEditor. ### Hiding and revealing content You can now hide and reveal Content items from the Back Office. -Hidden content will be unavailable on the front page regardless of permissions or [Location visibility](../guide/content_management.md#location-visibility). +Hidden content will be unavailable on the front page regardless of permissions or [Location visibility](https://doc.ibexa.co/en/2.5/guide/content_management/#location-visibility). ![Icon for hiding content](img/2.5_hide_content_icon.png) @@ -114,7 +114,7 @@ The User Settings menu has been expanded with the following options: This release introduced several Back Office improvements to facilitate editorial experience, including: -- [Icons for Content Types and the ability to define them](../extending/custom_icons.md) +- [Icons for Content Types and the ability to define them](https://doc.ibexa.co/en/2.5/guide/extending/extending_back_office/#custom-content-type-icons) - Ability to collapse and expand content preview to have easier access to the Sub-items list - Responsive Sub-items table with selectable column layout - Simpler assigning of Object States to content @@ -150,7 +150,7 @@ New API improvements include: ## Requirements changes -Due to using Webpack Encore, you now need [Node.js and yarn](../updating/3_update_app.md) +Due to using Webpack Encore, you now need [Node.js and yarn](https://doc.ibexa.co/en/2.5/updating/updating) to install or update eZ Platform. This release also changes support for versions of the following third-party software: @@ -159,7 +159,7 @@ This release also changes support for versions of the following third-party soft - Apache 2.2 is no longer supported. Use Apache 2.4 instead. - Varnish 4 is no longer supported. Use Varnish 5.1 or higher (6.0LTS recommended). -For full list of supported versions, see [Requirements](../getting_started/requirements.md). +For full list of supported versions, see [Requirements](https://doc.ibexa.co/en/2.5/getting_started/requirements). ### Password requirements @@ -168,7 +168,7 @@ This version introduces stricter default password quality requirements. Passwords must be at least 10 characters long, and must include upper and lower case letters, and digits. Existing passwords are not changed. -See [backwards compatibility changes](https://github.com/ezsystems/ezpublish-kernel/blob/master/doc/bc/changes-7.5.md) +See [backwards compatibility changes](https://github.com/ezsystems/ezpublish-kernel/blob/7.5/doc/bc/changes-7.5.md) for detailed information. ## Full changelog @@ -190,14 +190,14 @@ If you use classes from the `Leafo\ScssPhp` namespace, change them to `ScssPhp\S ### SolrCloud -You can now take advantage of [SolrCloud in eZ Platform Solr search engine](../guide/search/solr.md#solrcloud). +You can now take advantage of [SolrCloud in eZ Platform Solr search engine](https://doc.ibexa.co/en/2.5/guide/search/solr/#solrcloud). It enables you to set up a cluster of Solr servers for highly available and fault tolerant environment. ### Online Editor #### Custom attributes -It is now possible to add [custom data attributes and CSS classes](../extending/extending_online_editor.md#custom-data-attributes-and-classes) to elements in the Online Editor. +It is now possible to add [custom data attributes and CSS classes](https://doc.ibexa.co/en/2.5/guide/extending/extending_online_editor/#custom-data-attributes-and-classes) to elements in the Online Editor. #### Translatable custom tag choice attributes @@ -205,7 +205,7 @@ You can now translate labels of choice attributes in Custom tags using the `ezri ### URL Wildcards -[URL wildcards](../guide/url_management.md#url-wildcards) enable you to set up global URL redirections. +[URL wildcards](https://doc.ibexa.co/en/2.5/guide/url_management/#url-wildcards) enable you to set up global URL redirections. ## eZ Platform v2.5.3 @@ -239,8 +239,8 @@ This section provides a list of deprecated features to be removed in eZ Platform #### Custom Installers -- The `\EzSystems\PlatformInstallerBundle\Installer\CleanInstaller` class and its Service Container definition (`ezplatform.installer.clean_installer`) have been deprecated in favor of `EzSystems\PlatformInstallerBundle\Installer\CoreInstaller` which requires the [Doctrine Schema Bundle](https://github.com/ezsystems/doctrine-dbal-schema) to be enabled. -- The `ezplatform.installer.db_based_installer` Service Container definition has been deprecated in favor of its FQCN-named equivalent (`EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller`). +- The `\EzSystems\PlatformInstallerBundle\Installer\CleanInstaller` class and its [service container](https://doc.ibexa.co/en/2.5/api/service_container) definition (`ezplatform.installer.clean_installer`) have been deprecated in favor of `EzSystems\PlatformInstallerBundle\Installer\CoreInstaller` which requires the [Doctrine Schema Bundle](https://github.com/ezsystems/doctrine-dbal-schema) to be enabled. +- The `ezplatform.installer.db_based_installer` service container definition has been deprecated in favor of its FQCN-named equivalent (`EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller`). - `vendor/ezsystems/ezpublish-kernel/data/mysql/schema.sql` has been deprecated and is not used by the installation process anymore. diff --git a/docs/releases/ez_platform_v3.0.md b/docs/releases/ez_platform_v3.0.md index 5b30855669..2d1fc02b1f 100644 --- a/docs/releases/ez_platform_v3.0.md +++ b/docs/releases/ez_platform_v3.0.md @@ -15,7 +15,7 @@ The version 3.0 moves eZ Platform to Symfony 5.0 from the previously used Symfony 3.4. This entails several changes to the way projects are organized. -For details, see [Symfony 4.0](https://github.com/symfony/symfony/blob/5.0/UPGRADE-4.0.md) +For details, see [Symfony 4.0](https://github.com/symfony/symfony/blob/4.0/UPGRADE-4.0.md) and [Symfony 5.0 upgrade documentation](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) ### Using Events instead of SignalSlots @@ -37,94 +37,94 @@ The list of bundles in v3.0 has been extended by the following ones: - [`ezplatform-site-factory`](https://github.com/ezsystems/ezplatform-site-factory) - [`ezplatform-version-comparison`](https://github.com/ezsystems/ezplatform-version-comparison) -For details, see [Bundles](../guide/bundles.md). +For details, see [Bundles](https://doc.ibexa.co/en/latest/guide/bundles). ## New features !!! dxp ### Site Factory - - The new Site management User Interface is now integrated with Admin UI. + + The new Site management User Interface is now integrated with Back Office. It enables you to easily create and manage multiple sites from the Back Office without editing the configuration files. - - For more information on: - - - enabling and configuring, see [Site Factory in developer documentation](../guide/site_factory.md) - - using the Site Factory, see [User Guide](https://doc.ezplatform.com/projects/userguide/en/3.0/site_organization/site_factory/) + + For more information about: + + - enabling and configuring, see [Enable Site Factory](https://doc.ibexa.co/en/latest/guide/multisite/site_factory/#enable-site-factory) + - using the Site Factory, see [User Guide]([[= user_doc =]]/site_organization/site_factory) ### Scheduling - + #### Schedule calendar - + You can now easily view and perform scheduling actions using the Calendar widget available in the Back Office. By default, the widget displays Content items scheduled for future publication, but custom events can be configured as well. You can also filter displayed events and toggle through a day, week, and month view. - + #### Manage planned publications with Dashboard - + You can now reschedule or cancel planned future publications right from the Dashboard. - + #### Schedule hiding a Content item - + You can now schedule hiding Content items. Using Calendar widget available in the Back Office you can also reschedule or cancel hiding a Content item. - + ### Defining buttons in Online Editor - - You can now reorder and disable buttons in Online Editor using [YAML configuration](../extending/extending_online_editor.md#customizing-buttons). + + You can now reorder and disable buttons in Online Editor using [YAML configuration](https://doc.ibexa.co/en/latest/extending/extending_online_editor/#customizing-buttons). ### Workflow improvements - + #### Workflow actions - - You can now configure your workflows to [automatically publish content](../guide/workflow.md#publishing-content-with-workflow). - - You can also create [custom workflow actions](../extending/extending_workflow.md#adding-custom-actions). - + + You can now configure your workflows to [automatically publish content](https://doc.ibexa.co/en/latest/guide/workflow/#publishing-content-with-workflow). + + You can also create [custom workflow actions](https://doc.ibexa.co/en/latest/extending/extending_workflow/#adding-custom-actions). + #### Reviewers - + When sending content through a workflow, the user can now select reviewers. You can require the user to select reviewers when sending content through the workflow. - - In the configuration, you can also set the workflow to [automatically notify the selected reviewers](../guide/workflow.md#sending-notifications). - + + In the configuration, you can also set the workflow to [automatically notify the selected reviewers](https://doc.ibexa.co/en/latest/guide/workflow/#sending-notifications). + #### Quick review - + A built-in Quick Review offers a quick workflow configuration for your basic needs. - + #### Custom transition color - + You can configure a custom color for each of the transitions defined in the Workflow. - + ## Version comparison - + You can now compare two versions of the same Content item and preview changes in their Fields: - + ![Version comparison](img/compare_results.png "Version comparison in one-column view") - + ### Universal Discovery Widget The Universal Discovery Widget (UDW) has been re-designed and re-written. New functionalities and changes include: - new configuration -- filtered search +- filtered search - resizable column with custom sort order - editing content from UDW (Enterprise only) -For full list of changes, see [Backwards compatibility doc](ez_platform_v3.0_deprecations.md#universal-discovery-widget) and [Configuration](../extending/extending_udw.md#configuration). +For full list of changes, see [Backwards compatibility doc](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#universal-discovery-widget) and [Configuration](https://doc.ibexa.co/en/latest/extending/extending_udw/#configuration). ### Field Types #### Content query Field Type -The new [Content query Field Type](../api/field_type_reference.md#content-query-field-type) +The new [Content query Field Type](https://doc.ibexa.co/en/latest/api/field_types_reference/contentqueryfield) enables you to configure a Content query that will use parameters from a Field definition. #### Field Type creation -You can now use [Generic Field Type](../extending/extending_field_type.md) as a template for your custom Field Types. +You can now use [Generic Field Type](https://doc.ibexa.co/en/latest/extending/extending_field_type) as a template for your custom Field Types. #### Keyword Field Type @@ -134,11 +134,11 @@ The `keyword` Field Type can now recognize versions of a Content item. #### Login by User name or email -You can now give your users th ability to [log in with User name or with email](../guide/user_management.md#login-methods). +You can now give your users th ability to [log in with User name or with email](https://doc.ibexa.co/en/latest/guide/user_management/user_management/#login-methods). #### Password rules -You can now set [password expiration rules](../guide/user_management.md#password-expiration) +You can now set [password expiration rules](https://doc.ibexa.co/en/latest/guide/user_management/user_management/#password-expiration) for user passwords. ### Duplicate a role @@ -149,7 +149,7 @@ You can now duplicate a role with a single click in the Back Office. ### REST API reference -The REST reference has been moved from Kernel to a new page, [eZ Platform REST API.](https://doc.ezplatform.com/rest-api-reference) +The REST reference has been moved from Kernel to a new page, [eZ Platform REST API](https://ezsystems.github.io/ezplatform-rest-reference). ### Search Criteria @@ -157,15 +157,15 @@ The following new Search Criteria have been added: |Search Criterion|Search based on| |-----|-----| -|[IsUserBased](../guide/search/criteria_reference/isuserbased_criterion.md)|Whether content represents a User account| -|[IsUserEnabled](../guide/search/criteria_reference/isuserenabled_criterion.md)|Whether a User account is enabled| -|[ObjectStateIdentifier](../guide/search/criteria_reference/objectstateidentifier_criterion.md)|Object State Identifier| -|[SectionId](../guide/search/criteria_reference/sectionid_criterion.md)|ID of the Section content is assigned to| -|[SectionIdentifier](../guide/search/criteria_reference/sectionidentifier_criterion.md)|Identifier of the Section content is assigned to| -|[UserEmail](../guide/search/criteria_reference/useremail_criterion.md)|Email address of a User account| -|[Sibling](../guide/search/criteria_reference/sibling_criterion.md)|Locations that are children of the same parent| -|[UserId](../guide/search/criteria_reference/userid_criterion.md)|User ID| -|[UserLogin](../guide/search/criteria_reference/userlogin_criterion.md)|User login| +|[IsUserBased](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/isuserbased_criterion)|Whether content represents a User account| +|[IsUserEnabled](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/isuserenabled_criterion)|Whether a User account is enabled| +|[ObjectStateIdentifier](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/objectstateidentifier_criterion)|Object State Identifier| +|[SectionId](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/sectionid_criterion)|ID of the Section content is assigned to| +|[SectionIdentifier](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/sectionidentifier_criterion)|Identifier of the Section content is assigned to| +|[UserEmail](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/useremail_criterion)|Email address of a User account| +|[Sibling](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/sibling_criterion)|Locations that are children of the same parent| +|[UserId](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/userid_criterion)|User ID| +|[UserLogin](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/userlogin_criterion)|User login| ### Random sorting @@ -173,7 +173,7 @@ The list of common Sort Clauses has been extended by the Random sorting option. ### Contextual Twig variables -You can now create [contextual Twig variables](../guide/templates.md#contextual-twig-variables) for use in templates. +You can now create [custom Twig variables](https://doc.ibexa.co/en/latest/guide/content_rendering/templates/templates/#custom-template-variables) for use in templates. They can be defined per SiteAccess, or per content view. ### Built-in Query Types @@ -194,22 +194,21 @@ You can now use the Sub-items list to quickly hide, reveal, to add Locations to ### Tooltips You can now add custom tooltips to provide more information for the users when they hover over, focus on, or tap an element. -For more information, see [Tooltips UI documentation](../guidelines/components/tooltips.md). ### Thumbnails The new thumbnails API allows you to easily choose an image for each content. -For more information, see [Extending thumbnails](../extending/extending_thumbnails.md). +For more information, see [Extending thumbnails](https://doc.ibexa.co/en/latest/extending/extending_thumbnails). ### Type hints for Public API -Strict types have been added to Public API methods, for full list see [backwards compatibility breaks](ez_platform_v3.0_deprecations.md#strict-types-for-php-api). +Strict types have been added to Public API methods. For a complete list, see [backwards compatibility breaks](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#strict-types-for-php-api). ## Other changes ### GraphQL -In GraphQL, you can now [query Locations and their children](../api/graphql_queries.md#querying-locations). +In GraphQL, you can now [query Locations and their children](https://doc.ibexa.co/en/latest/api/graphql_queries/#querying-locations). ### Translations @@ -225,12 +224,12 @@ New multilingual content route for internal translations has been added. ### Renamed templates and parameters Templates and parameters used by the Back Office have been renamed for consistency. -For A full list of changes, see [Backwards compatibility doc](ez_platform_v3.0_deprecations.md). +For A full list of changes, see [Backwards compatibility doc](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations). ### HTTP Cache HTTP cache bundle now uses FOS Cache Bundle v2. -For a full list of changes this entails, see [Backwards compatibility doc](ez_platform_v3.0_deprecations.md#ezplatform-http-cache). +For a full list of changes this entails, see [Backwards compatibility doc](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#ezplatform-http-cache). ### Helpers @@ -265,7 +264,7 @@ New methods have been introduced to the PHP API: ## Deprecations and removals -For full list of deprecations and removals, see [eZ Platform v3.0 deprecations and backwards compatibility breaks](ez_platform_v3.0_deprecations.md). +For full list of deprecations and removals, see [eZ Platform v3.0 deprecations and backwards compatibility breaks](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations). ### SignalSlots @@ -276,7 +275,7 @@ Use [Event Listeners](https://symfony.com/doc/5.0/event_dispatcher.html) in your The deprecated `ezprice` and `ezpage` Field Types have been removed. Nameable field type interface has been removed and replaced by `eZ\Publish\SPI\FieldType\FieldType::getName`. -For a full list of changes on Field Types, see [Backwards compatibility doc](ez_platform_v3.0_deprecations.md#field-types). +For a full list of changes on Field Types, see [Backwards compatibility doc](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#field-types). ### Elastic Search @@ -289,12 +288,12 @@ Following the change, the REST client has been removed from Kernel. ### Kernel -`ezpublish-kernel` has been replaced by [`ezplatform-kernel`.](https://github.com/ezsystems/ezplatform-kernel) +`ezpublish-kernel` has been replaced by [`ezplatform-kernel`](https://github.com/ezsystems/ezplatform-kernel). ### Online Editor Online Editor front-end code and assets have been moved to the `ezplatform-richtext` repository. -For a full list of resulting changes, see [Backwards compatibility doc](ez_platform_v3.0_deprecations.md#online-editor). +For a full list of resulting changes, see [Backwards compatibility doc](https://doc.ibexa.co/en/latest/releases/ez_platform_v3.0_deprecations/#online-editor). ### Configuration through `ezplatform` @@ -311,7 +310,7 @@ The Symfony Service definitions, providing extension point to create custom inst ## Requirements changes -eZ Platform now requires using PHP 7.3. For full list of, see [eZ Platform requirements](../getting_started/requirements.md). +eZ Platform now requires using PHP 7.3. For full list of, see [eZ Platform requirements](https://doc.ibexa.co/en/latest/getting_started/requirements). !!! note @@ -320,7 +319,7 @@ eZ Platform now requires using PHP 7.3. For full list of, see [eZ Platform requi ## Updating -For the upgrade details, see [eZ Platform v3.0 project update instructions](../upgrading/upgrading_to_v3.md). +For the upgrade details, see [eZ Platform v3.0 project update instructions](https://doc.ibexa.co/en/latest/updating/updating). ## Full changelog diff --git a/docs/releases/ez_platform_v3.0_deprecations.md b/docs/releases/ez_platform_v3.0_deprecations.md index c56c10ffdc..9c482123b2 100644 --- a/docs/releases/ez_platform_v3.0_deprecations.md +++ b/docs/releases/ez_platform_v3.0_deprecations.md @@ -5,7 +5,7 @@ This page lists backwards compatibility breaks and deprecations introduced in eZ !!! tip "Upgrade to v3" For a guide on moving your project to v3, - see [Upgrading eZ Platform to v3](../updating/upgrading_to_v3.md). + see [eZ Platform v3.0 project update instructions](https://doc.ibexa.co/en/latest/updating/updating). ## Symfony 5 @@ -16,8 +16,8 @@ as well as [Symfony upgrade guides for 4.0](https://github.com/symfony/symfony/b and [for 5.0](https://github.com/symfony/symfony/blob/master/UPGRADE-5.0.md) to learn about all changes it entails. -See [v3.0 project update](../upgrading/upgrading_to_v3.md) for the steps you need to take to update your project to Symfony 5. -See also [full requirements for installing eZ Platform](../getting_started/requirements.md). +See [v3.0 project update](../updating/from_2.5/adapt_code_to_v3.md) for the steps you need to take to update your project to Symfony 5. +See also [full requirements for installing eZ Platform](https://doc.ibexa.co/en/latest/getting_started/requirements). ### Template configuration @@ -38,11 +38,11 @@ Example 2: Following the upgrade to Symfony 5, the DFS IO handler must be configured in a different way. -For more information, see the Doctrine connection configuration example in the [Clustering](../../guide/clustering/#configuring-the-dfs-io-handler) article. +For more information, see the Doctrine connection configuration example in the [Clustering](https://doc.ibexa.co/en/latest/guide/clustering/#configuring-the-dfs-io-handler) article. ## Field Types -The following tags used to register Field Type features in the dependency injection container have been renamed: +The following tags used to register Field Type features in the [service container](https://doc.ibexa.co/en/latest/api/public_php_api/#service-container) have been renamed: |Former name|New name| |-----------|--------| @@ -50,14 +50,14 @@ The following tags used to register Field Type features in the dependency inject |`ezpublish.fieldType.indexable`|`ezplatform.field_type.indexable`| |`ezpublish.storageEngine.legacy.converter`|`ezplatform.field_type.legacy_storage.converter`| |`ezpublish.fieldType.parameterProvider`|`ezplatform.field_type.parameter_provider`| -|`ezpublish_rest.field_type_processor`|`ezplatform.field_type.rest.processor`| +|`ezpublish_rest.field_type_processor`|`ibexa.rest.field_type.processor`| |`ez.fieldFormMapper.value`|`ezplatform.field_type.form_mapper.value`| |`ez.fieldFormMapper.definition`|`ezplatform.field_type.form_mapper.definition`| |`ezpublish.fieldType.externalStorageHandler`|`ezplatform.field_type.external_storage_handler`| |`ezpublish.fieldType.externalStorageHandler.gateway`|`ezplatform.field_type.external_storage_handler.gateway`| Deprecated method `eZ\Publish\SPI\FieldType\FieldType::getName` is now supported with a new signature similar to `eZ\Publish\SPI\FieldType\Nameable::getFieldName()`, which has been removed. -See [eZ Platform v3.0 project update](../upgrading/4_3_upgrade_field_types.md) for further information. +See [eZ Platform v3.0 project update](https://doc.ibexa.co/en/latest/updating/4_3_upgrade_field_types) for further information. The deprecated `eZ\Publish\Core\FieldType\RichText` namespace has been removed, as it was moved to a separate bundle in v2.4. @@ -90,9 +90,9 @@ The following Symfony Service definitions that provide extension point to create The `ezstudio.installer.studio_installer` service has been renamed to the FQCN-named service `EzSystems\EzPlatformEnterpriseEditionInstallerBundle\Installer\Installer`. -Deprecated `ezplatform.ee.installer.class` DIC parameter has been removed. +Deprecated `ezplatform.ee.installer.class` [service container](https://doc.ibexa.co/en/latest/api/public_php_api/#service-container) parameter has been removed. -See [eZ Platform v3.0 project update instructions](../upgrading/4_8_upgrade_rest.md#custom-installers) for upgrade details. +See [eZ Platform v3.0 project update instructions](https://doc.ibexa.co/en/latest/updating/4_8_upgrade_rest/#custom-installers) for upgrade details. ## ezplatform-admin-ui @@ -282,7 +282,7 @@ The `@ezdesign/account/error/credentials_expired.html.twig` has been relocated f ### Universal Discovery Widget The UDW configuration has been changed. -For the full list of UDW configuration keys and their descriptions, see [UDW configuration](../extending/extending_udw.md#configuration). +For the full list of UDW configuration keys and their descriptions, see [UDW configuration](https://doc.ibexa.co/en/latest/extending/extending_udw/#configuration). ### Online Editor @@ -292,14 +292,14 @@ have been moved from `ezplatform-admin-ui` to `ezplatform-richtext`. ### Adding new tabs in the Back Office The way of adding custom tab groups in the Back Office has changed. -You now need to [make use of the `TabsComponent`](../extending/extending_tabs.md#adding-a-new-tab-group). +You now need to [make use of the `TabsComponent`](https://doc.ibexa.co/en/latest/extending/extending_tabs/#adding-a-new-tab-group). ### Content Type forms Content Type editing, including Action Dispatchers, Form Processors, Types and Data classes related to Content Types/Limitations, has been moved to `ezplatform-admin-ui` from `repository-forms`. -### Code cleanup in Admin UI +### Code cleanup in Back Office The following deprecated items have been removed: @@ -409,7 +409,7 @@ Instances of the following deprecated event classes have been replaced: |`Twig_SimpleFunction`|`Twig\TwigFunction`| Selected deprecated Role Service and permission-related methods have been removed. -For details, see [code cleanup in kernel](#code-cleanup-in-kernel). +For details, see [code cleanup in kernel](#code-cleanup-in-ez-platform-kernel). ## ezplatform-kernel replacing ezpublish-kernel @@ -465,7 +465,7 @@ Following SPI methods have been removed: ### Dynamic settings -Using dynamic settings (through `$setting$`) and getting settings from the ConfigResolver in a class constructor +Using dynamic settings (through `$setting$`) and getting settings from the [ConfigResolver](https://doc.ibexa.co/en/latest/guide/config_dynamic/#configresolver) in a class constructor or method call has been dropped. You should use the ConfigResolver instead. @@ -475,8 +475,8 @@ Do not store the values globally. Every time the value is needed call `ConfigRes #### AbstractController -The `eZ\Bundle\EzPublishCoreBundle\Controller` now extends `Symfony\Bundle\FrameworkBundle\Controller\AbstractController` instead of `Symfony\Bundle\FrameworkBundle\Controller\Controller` which has limited access to the dependency injection container. -For details, see [Service Subscribers Locators.](https://symfony.com/doc/5.0/service_container/service_subscribers_locators.html) +The `eZ\Bundle\EzPublishCoreBundle\Controller` now extends `Symfony\Bundle\FrameworkBundle\Controller\AbstractController` instead of `Symfony\Bundle\FrameworkBundle\Controller\Controller` which has limited access to the [service container](https://doc.ibexa.co/en/latest/api/public_php_api/#service-container). +For details, see [Service Subscribers Locators](https://symfony.com/doc/5.0/service_container/service_subscribers_locators.html). The `Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand` is deprecated, use `Symfony\Component\Console\Command\Command` instead. @@ -689,12 +689,12 @@ Instead, you can inject `eZ\Publish\API\Repository\PermissionResolver` and rely The deprecated `Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesserInterface` has been replaced with `Symfony\Component\Mime\MimeTypesInterface`. -### Symfony Dependency Injection Container +### Symfony service container -The deprecated Symfony Dependency Injection Container parameters ending with `.class` have been removed, services relying on them now have their classes defined explicitly. +The deprecated Symfony [service container](https://doc.ibexa.co/en/latest/api/public_php_api/#service-container) parameters ending with `.class` have been removed, services relying on them now have their classes defined explicitly. To properly decorate a Symfony service, use the `decorates` attribute instead. For the full list of the dropped parameters, see -[kernel documentation.](https://github.com/ezsystems/ezpublish-kernel/blob/master/doc/bc/8.0/dropped-container-parameters.md) +[kernel documentation.](https://github.com/ezsystems/ezpublish-kernel/blob/master/doc/bc/1.0/dropped-container-parameters.md) ### Template parameter names @@ -738,12 +738,16 @@ The following deprecated Twig classes have been replaced: ### Twig intl extension -Twig intl extension [has been dropped.](https://github.com/twigphp/Twig-extensions/blob/master/README.rst) +Twig intl extension [has been dropped](https://github.com/twigphp/Twig-extensions/blob/master/README.rst). ### EzPublishMigration The `EzPublishMigration` bundle has been dropped. +### EzMigrationBundle + +As of v3.3.3, the `ezsystems/EzMigrationBundle` bundle has been dropped. Use `ibexa/migrations` instead. + ### Password hashes Insecure password hash types deprecated since v1.13 have been removed: @@ -825,7 +829,7 @@ The following namespaces have been changed: ### Code cleanup in eZ Platform REST Selected deprecated Role Service and permission-related methods have been removed. -For details, see [code cleanup in kernel](#code-cleanup-in-kernel). +For details, see [code cleanup in kernel](#code-cleanup-in-ez-platform-kernel). Using the Criteria element in REST input query (search view) payload has been deprecated since eZ Platform v1.6 and was dropped in this release. @@ -834,7 +838,7 @@ Using the Criteria element in REST input query (search view) payload has been de ### Code cleanup in eZ Platform RichText Selected deprecated permission-related methods have been removed. -For details, see [code cleanup in kernel](#code-cleanup-in-kernel). +For details, see [code cleanup in kernel](#code-cleanup-in-ez-platform-kernel). ### Input and output converters @@ -851,7 +855,7 @@ Configuration providers exposing the following JavaScript variables have been dr - `eZ.adminUiConfig.alloyEditor` replaced by `eZ.richText.alloyEditor` - `eZ.adminUiConfig.richTextCustomTags` replaced by `eZ.richText.customTags` -- `eZ.adminUiConfig.richTextCustomStyles` replaced by `eZ.richtext.customStyles` +- `eZ.adminUiConfig.richTextCustomStyles` replaced by `eZ.richText.customStyles` The following Webpack Encore entries have been changed: @@ -864,7 +868,7 @@ have been moved from `ezplatform-admin-ui` to `ezplatform-richtext`. #### Custom button configuration Configuring custom Online Editor buttons with `ezrichtext.alloy_editor.extra_buttons` is deprecated. -Use [`ezplatform.system.<siteacces>.fieldtypes.ezrichtext.toolbars.<toolbar_identifier>.buttons`](../extending/online_editor_button.md) instead. +Use [`ezplatform.system.<siteacces>.fieldtypes.ezrichtext.toolbars.<toolbar_identifier>.buttons`](https://doc.ibexa.co/en/latest/extending/online_editor_button) instead. ### View matching diff --git a/docs/releases/ez_platform_v3.1.md b/docs/releases/ez_platform_v3.1.md index 17d7c235ab..a02b5d2aae 100644 --- a/docs/releases/ez_platform_v3.1.md +++ b/docs/releases/ez_platform_v3.1.md @@ -24,20 +24,20 @@ This release of eZ Platform introduces the following new features: You can now create multiple content structures that can be used as Site skeletons for the new sites. - For more information about Site skeleton, see [Configure Site skeleton](../guide/site_factory.md#configure-site-skeleton). + For more information about Site skeleton, see [Configure Site skeleton](https://doc.ibexa.co/en/latest/guide/multisite/site_factory_configuration/#site-skeletons). #### Defining parent Location You can now define the parent Location for every new site in the template configuration. - For more information about defining parent Location, see [Configure parent Location](../guide/site_factory.md#configure-parent-location). + For more information about defining parent Location, see [Configure parent Location](https://doc.ibexa.co/en/latest/guide/multisite/site_factory_configuration/#parent-location). ### Elasticsearch You can now use [Elasticsearch](https://www.elastic.co/) in your eZ Platform installation through the `PlatformElasticSearchEngineBundle`. - See [Elasticsearch documentation](../guide/search/elastic.md) to learn how to set up, configure and user Elasticsearch with eZ Platform. + See [Elasticsearch documentation](https://doc.ibexa.co/en/latest/guide/search/elastic) to learn how to set up, configure and user Elasticsearch with eZ Platform. ### Page Builder @@ -47,7 +47,7 @@ This release of eZ Platform introduces the following new features: ### Field Group permissions - The new [Field Group Limitation](../guide/limitation_reference.md#field-group-limitation) + The new [Field Group Limitation](https://doc.ibexa.co/en/latest/guide/limitation_reference/#field-group-limitation) enables you to control who can edit content Fields per Field group. ### Version comparison @@ -59,7 +59,7 @@ This release of eZ Platform introduces the following new features: - Matrix - Media - For overview of additional Fields, see [User documentation on Comparing versions.](https://doc.ezplatform.com/projects/userguide/en/3.1/publishing/publishing/#comparing-versions) + For overview of additional Fields, see [User documentation on Comparing versions.]([[= user_doc =]]/publishing/publishing/#comparing-versions) ### URL management UI @@ -71,7 +71,7 @@ You can now manage URL addresses and URL wildcards with a comfortable user inter ![URL Management UI](img/3_1_URL_Management.png "URL Management UI") -For more information on how to manage URLs, see [URL management](../guide/url_management.md). +For more information on how to manage URLs, see [URL management](https://doc.ibexa.co/en/latest/guide/url_management). ### Tree view in the Universal Discovery Widget @@ -81,7 +81,7 @@ Selections that you make in one view survive when you switch to the other view. ![Tree view in the Content Browser](img/3_1_Content_browser_Tree_view.png "Tree view in Content Browser") -For more information about configuring the Universal Discovery Widget, see [Extending Universal Discovery Widget](../extending/extending_udw.md). +For more information about configuring the Universal Discovery Widget, see [Extending Universal Discovery Widget](https://doc.ibexa.co/en/latest/extending/extending_udw). ### Field group display @@ -119,11 +119,11 @@ A customizable search controller has been extracted and placed in `ezplatform-se You can now search through the contents of Trash and sort the search results based on a number of Search Criteria and Sort Clauses that can be used by the `\eZ\Publish\API\Repository\TrashService::findTrashItems` method only. -For more information, see [Searching in trash](../api/public_php_api_search.md#searching-in-trash). +For more information, see [Searching in trash](https://doc.ibexa.co/en/latest/api/public_php_api_search/#searching-in-trash). ### Repository filtering -[Repository filtering](../api/public_php_api_search.md#repository-filtering) enables you to filter content and Locations using a defined Filter, +[Repository filtering](https://doc.ibexa.co/en/latest/api/public_php_api_search/#repository-filtering) enables you to filter content and Locations using a defined Filter, without the `SearchService`. ### PermissionResolver diff --git a/docs/releases/ibexa_dxp_v3.2.md b/docs/releases/ibexa_dxp_v3.2.md index ae95a94a2f..f2c863ba19 100644 --- a/docs/releases/ibexa_dxp_v3.2.md +++ b/docs/releases/ibexa_dxp_v3.2.md @@ -21,7 +21,7 @@ including eCommerce administration. ### DAM connector -You can now [connect your installation to a Digital Asset Management (DAM) system](../guide/config_connector.md#dam-cofniguration) +You can now [connect your installation to a Digital Asset Management (DAM) system](https://doc.ibexa.co/en/latest/guide/config_connector/#dam-cofniguration) and use assets such as images directly from the DAM in your content. ### Autosave @@ -37,18 +37,18 @@ to group search results and get the count of results per aggregation type. You can aggregate results by general conditions such as Content Type or Section, or by Field aggregations such as the value of specific Fields. -See [Aggregation API](../api/public_php_api_search.md#aggregation-api) for more information. +See [Aggregation API](https://doc.ibexa.co/en/latest/api/public_php_api_search/#aggregation) for more information. -### Targeting block and Segmentation API +### Targeting block and Segmentation API [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] Targeting block for the Page Builder enables you to display different Content items to different users depending on the Segments they belong to. ![Targeting block](img/3.2_targeting_block.png) -You can [configure Segments](../guide/admin_panel.md#segments) in the Back Office. +You can [configure Segments](https://doc.ibexa.co/en/latest/guide/admin_panel/#segments) in the Back Office. -[Segmentation API](../api/public_php_api_managing_users.md#segments) enables you to create and edit Segments and Segment Groups, +[Segmentation API](https://doc.ibexa.co/en/latest/api/public_php_api_managing_users/#segments) enables you to create and edit Segments and Segment Groups, as well as assign Users to Segments. ### Twig helpers for content rendering @@ -59,30 +59,30 @@ Use `ez_render_content(content)` and `ez_render_location(location)` to render th You can also use `ez_render()` and provide it with either a content or Location object. -See [Using `ez_render` Twig helpers](../guide/templates.md#using-ez_render-twig-helpers) for more information. +See [Using `ez_render` Twig helpers](https://doc.ibexa.co/en/latest/guide/templates/#using-ez_render-twig-helpers) for more information. ### JWT authentication -You can now use JWT tokens to authenticate in [REST API](../api/general_rest_usage.md#jwt-authentication) -and [GraphQL](../api/graphql.md#jwt-authentication). +You can now use JWT tokens to authenticate in [REST API](https://doc.ibexa.co/en/latest/api/general_rest_usage/#jwt-authentication) +and [GraphQL](https://doc.ibexa.co/en/latest/api/graphql/#jwt-authentication). -See [JWT authentication](../guide/security.md#jwt-authentication) to learn how to configure this authentication method. +See [JWT authentication](https://doc.ibexa.co/en/latest/guide/security/#jwt-authentication) to learn how to configure this authentication method. -### Searching in Ibexa Commerce with Elasticsearch +### Searching in Ibexa Commerce with Elasticsearch [[% include 'snippets/commerce_badge.md' %]] You can now use Elasticsearch for searching in Ibexa Commerce. -See [Install Ibexa Platform](../getting_started/install_ez_platform.md#install-and-configure-a-search-engine) to learn how to install and configure the search engine. +See [Install Ibexa Platform](https://doc.ibexa.co/en/latest/getting_started/install_ez_platform/#install-and-configure-a-search-engine) to learn how to install and configure the search engine. ## Other changes -### Site Factory improvements +### Site Factory improvements [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] You can now define User Group skeletons where you define Policies and Limitations that apply to a specific User Group. You can then associate a number of such skeletons with a site template. User Group skeletons survive deleting a site. -See [Configure User Group skeleton](../guide/site_factory.md#configure-user-group-skeleton) for more information. +See [Configure User Group skeleton](https://doc.ibexa.co/en/latest/guide/multisite/site_factory_configuration/#user-group-skeletons) for more information. ### Calendar widget improvements @@ -104,7 +104,7 @@ Then, refresh the view to see an updated list of Content Types. ### Object state API improvements You can now use `ObjectStateService::loadObjectStateByIdentifier()` and `ObjectStateService::loadObjectStateGroupByIdentifier()` -to [get Object states and Object state groups](../api/public_php_api_managing_repository.md#getting-object-state-information) in the PHP API. +to [get Object states and Object state groups](https://doc.ibexa.co/en/latest/api/public_php_api_managing_repository/#getting-object-state-information) in the PHP API. ## Full changelog diff --git a/docs/releases/ibexa_dxp_v3.3.md b/docs/releases/ibexa_dxp_v3.3.md new file mode 100644 index 0000000000..2b8791d5d7 --- /dev/null +++ b/docs/releases/ibexa_dxp_v3.3.md @@ -0,0 +1,88 @@ +# Ibexa DXP v3.3 + +**Version number**: v3.3 + +**Release date**: January 18, 2021 + +**Release type**: [Long Term Support](../community_resources/release_process.md#release-process) + +## Notable changes + +### New Personalization UI + +This release brings a completely reconstructed user interface of the Personalization feature. + +![Personalization dashboard](img/3.3_perso_ui.png "Personalization dashboard") + +### Symfony Flex + +Ibexa DXP is now installed using [Symfony Flex](https://symfony.com/doc/current/quick_tour/flex_recipes.html). + +See [the updated installation instruction](https://doc.ibexa.co/en/3.3/getting_started/install_ez_platform) for a new guide to installing the product. + +### Image Editor + +With the Image Editor, users can now perform basic operations, such as cropping or flipping an image, +or setting a point of focus. +The Image Editor is available when browsing the Media library, or creating or editing Content items +that contain an `ezimage` or `ezimageasset` Field. + +You can modify the Image Editor's default settings to change its appearance or behavior. +For more information, see [Configuring the Image Editor](https://doc.ibexa.co/en/3.3/guide/image_editor). + +### Migration bundle + +The new [migration bundle](https://doc.ibexa.co/en/3.3/guide/data_migration) enables you to export and import your Repository data by using YAML files. + +## Other changes + +### Extended Search API capabilities + +Search API has been extended with the following capabilities: + +- [Score Sort Clause](https://doc.ibexa.co/en/3.3/guide/search/sort_clause_reference/score_sort_clause) orders search results by their score. +- [CustomField Sort Clause](https://doc.ibexa.co/en/3.3/guide/search/sort_clause_reference/customfield_sort_clause) sorts search results by raw search index fields. +- [ContentTranslatedName Sort Clause](https://doc.ibexa.co/en/3.3/guide/search/sort_clause_reference/contenttranslatedname_sort_clause) sorts search results by the Content items' translated names. + +You can now access [additional search result data from PagerFanta](https://doc.ibexa.co/en/3.3/api/public_php_api_search/#additional-search-result-data). + +### PHP API improvements + +You can now use the following new PHP API methods: + +- [`UserService::loadUserGroupByRemoteId`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/UserService.php#L71) +- [`PasswordHashService::getDefaultHashType`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/PasswordHashService.php#L18) +- [`PasswordHashService::getSupportedHashTypes`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/PasswordHashService.php#L25) +- [`PasswordHashService::isHashTypeSupported`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/PasswordHashService.php#L30) +- [`PasswordHashService::createPasswordHash`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/PasswordHashService.php#L37) +- [`PasswordHashService::isValidPassword`](https://github.com/ezsystems/ezplatform-kernel/blob/master/eZ/Publish/API/Repository/PasswordHashService.php#L44) + +### Query Field Location handling + +The [Query Field Type](https://doc.ibexa.co/en/3.3/guide/content_rendering/queries_and_controllers/content_queries/#content-query-field) now enables getting results for the current Location of a Content item. + +## Deprecations + +### Trusted proxy configuration + +If you configure trusted proxies in the `.env` file, you now need to add them to the configuration in the following way: + +``` yaml +framework: + trusted_proxies: '%env(TRUSTED_PROXIES)%' +``` + +## Full changelog + +See [list of changes in Symfony 5.2.](https://symfony.com/blog/symfony-5-2-curated-new-features) + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|--------------|------------|------------| +| [Ibexa Content v3.3.0](https://github.com/ibexa/content/releases/tag/v3.3.0) | [Ibexa Experience v3.3.0](https://github.com/ibexa/experience/releases/tag/v3.3.0) | [Ibexa Commerce v3.3.0](https://github.com/ibexa/commerce/releases/tag/v3.3.0)| + +## v3.3.15 + +### Symfony 5.4 + +The version v3.3.15 moves Ibexa DXP to Symfony 5.4. +For more information, see [Symfony 5.4 documentation](https://symfony.com/releases/5.4) and [update documentation](../updating/from_3.3/update_from_3.3.md#3315). \ No newline at end of file diff --git a/docs/releases/ibexa_dxp_v4.0.md b/docs/releases/ibexa_dxp_v4.0.md new file mode 100644 index 0000000000..cce59abd0a --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.0.md @@ -0,0 +1,123 @@ +# Ibexa DXP v4.0 + +**Version number**: v4.0 + +**Release date**: February 4, 2022 + +**Release type**: [Fast Track](https://doc.ibexa.co/en/4.0/community_resources/release_process/#release-process) + +## Notable changes + +### Redesigned user interface + +The the Back Office has undergone a complete redesign, including revised look and feel, +simplified navigation and more streamlined workflows. + +![New UI](img/4.0_new_ui.png) + +!!! tip + + Read more about the rationale and process for the redesign on [Ibexa blog](https://www.ibexa.co/blog/ibexa-dxp-v4.0-preview-redesigned-user-interface-elevates-the-user-experience). + +### New product catalog + +New product catalog enables easy management of products, stock and prices. + +Products are now organized into product types, each offering a specific set of attributes +that you can use to provide information about a product. +You can also set VAT rates per product type. + +![Product catalog](img/4.0_catalog.png) + +#### Price management + +You can now configure prices with discounts per product and per customer group. +Separate currencies enable you to set different price rules for different currencies. + +![Price management](img/4.0_product_price.png "Managing prices in the new product catalog") + +### Taxonomy management + +You can now organize content adding tags and create taxonomy categories to make it easy for your +site users to browse and to deliver content appropriate for them. + +### Separate recommendations for different websites + +Personalization service has been enhanced to allow returning separate recommendations +for different websites. +This way you can eliminate irrelevant recommendations when you set up stores that +operate on different markets or under different brands. + +For more information, see [Support for multiple websites](https://doc.ibexa.co/projects/userguide/en/latest/personalization/use_cases/#multiple-website-hosting). + +## Other changes + +### Draft locking + +You can now configure and use the locking feature to lock a draft of a Content item, +so that only an assigned person can edit it, and no other user can take it over. + +For more information, see the [Draft locking](https://doc.ibexa.co/en/latest/guide/workflow/workflow/#draft-locking) +and the relevant [user documentation](https://doc.ibexa.co/projects/userguide/en/latest/publishing/editorial_workflow/#releasing-locked-drafts). + +### Online Editor is now based on CKEditor + +You can now edit content of RichText Fields using CKEditor and extend its functionality with many elements. + +For more information, see [Extend Online Editor](https://doc.ibexa.co/en/4.0/extending/extending_online_editor/). + +### Enhanced GraphQL location handling + +GraphQL now enables better querying of Locations and URLs. + +### Migration API + +You can now manage [data migrations](https://doc.ibexa.co/en/4.0/guide/data_migration/data_migration/) by using the PHP API, +including getting migration information and running individual migration files. + +See [Managing migrations](https://doc.ibexa.co/en/4.0/api/public_php_api_managing_migrations/) for more information. + +### Decide whether alternative text for Image field is optional + +Alternative text for an Image field is now optional by default. +You can set it as required when adding the Image field to a Content Type. + +### Configure what elements are available in the Page Builder for the Content type [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +You can now select which page blocks, page layout and what edit mode are available in the Editor mode for the Content type. +For more information, see [Working with Page](https://doc.ibexa.co/projects/userguide/en/latest/site_organization/working_with_page/#configure-block-display). + +### Purge all submissions of given form [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +You can purge all submissions of a given form. +For more information, see [Forms](https://doc.ibexa.co/en/4.0/guide/form_builder/forms/#form-submission-purging). + +### Hidden eCommerce features + +Commerce tab and all its features are now disabled by default. +For more information, see [Enable Commerce features](https://doc.ibexa.co/en/4.0/guide/config_back_office/#enable-commerce-features). + +### External datasource handling + +Pesronalization has been given an option to fetch content feed from external sources. + +### Category exclusion + +Personalization service has been enhanced with a feature which allows to exclude categories from the recommendation response. +See [Exclusions](https://doc.ibexa.co/projects/userguide/en/latest/personalization/filters/#exclusions). + +## Deprecations + +### Code cleanup results + +v4.0 sees significant code cleanup, including renaming of namespaces, services, REST API endpoints +and many other internal names. + +Refer to [Ibexa DXP v4.0 deprecations and backwards compatibility breaks](ibexa_dxp_v4.0_deprecations.md) +for full details of changes and how they influence your project. + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|--------------|------------|------------| +| [Ibexa Content v4.0](https://github.com/ibexa/content/releases/tag/v4.0.0) | [Ibexa Experience v4.0](https://github.com/ibexa/experience/releases/tag/v4.0.0) | [Ibexa Commerce v4.0](https://github.com/ibexa/commerce/releases/tag/v4.0.0) diff --git a/docs/releases/ibexa_dxp_v4.0_deprecations.md b/docs/releases/ibexa_dxp_v4.0_deprecations.md new file mode 100644 index 0000000000..bcd71debce --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.0_deprecations.md @@ -0,0 +1,269 @@ +# Ibexa DXP v4.0 deprecations and backwards compatibility breaks + +Ibexa DXP v4.0 introduces changes to significant parts of the code +to align with the product name change from earlier eZ Platform. + +These changes include changing repository names, namespaces, filenames, function names, and others. + +A backwards compatibility layer ensures that custom implementations and extensions +using the older naming should function without change. + +## Namespaces + +Namespaces in the product which referred to old product names now use the Ibexa name. + +All namespace changes are listed in the `ibexa/compatibility-layer` repository. + +Refer to [mapping reference](https://github.com/ibexa/compatibility-layer/tree/main/src/bundle/Resources/mappings) +for a full comparison of old and new bundle names and namespaces. + +!!! tip + + To make sure your code is up to date with the new namespaces, + you can use the [Ibexa PhpStorm plugin](../community_resources/phpstorm_plugin.md). + The plugin indicates deprecated namespaces and suggests updating them to new ones. + +### Richtext namespace + +The internal format of richtext has changed. + +All namespace changes are listed in the +[richtext](https://github.com/ibexa/fieldtype-richtext/blob/bf45e57ea1d2933cc02eb8d8bff76c0925de92de/src/bundle/Resources/config/default_settings.yaml#L60-L67) repository. + +## Configuration keys + +`ezplatform` and `ezpublish` configuration keys have been replaced with `ibexa`. + +Other package-specific configuration keys have also been updated. + +| Old name | New name | +| --- | --- | +| `ezplatform` | `ibexa` | +| `ezpublish` | `ibexa` | +| `ez_doctrine_schema` | `ibexa_doctrine_schema` | +| `ez_io` | `ibexa_io` | +| `ez_platform_fastly_cache` | `ibexa_fastly` | +| `ez_platform_http_cache` | `ibexa_http_cache` | +| `ez_platform_page_builder` | `ibexa_page_builder` | +| `ez_platform_standard_design` | `ibexa_standard_design` | +| `ez_search_engine_legacy` | `ibexa_legacy_search_engine` | +| `ez_search_engine_solr` | `ibexa_solr` | +| `ezdesign` | `ibexa_design_engine` | +| `ezplatform_elastic_search_engine` | `ibexa_elasticsearch` | +| `ezplatform_form_builder` | `ibexa_form_builder` | +| `ezplatform_graphql` | `ibexa_graphql` | +| `ezplatform_page_fieldtype` | `ibexa_fieldtype_page` | +| `ezplatform_support_tools` | `ibexa_system_info` | +| `ezrecommendation` | `ibexa_personalization_client` | +| `ezrichtext` | `ibexa_fieldtype_richtext` | +| `ibexa_platform_commerce_field_types` | `ibexa_commerce_field_types` | +| `one_sky` | `ibexa_commerce_one_sky` | +| `ses_specificationstypefieldtype` | `ibexa_commerce_specifications_type` | +| `shop_price_engine_plugin` | `ibexa_commerce_price_engine` | +| `silversolutions_eshop` | `ibexa_commerce_eshop` | +| `silversolutions_tools` | `ibexa_commerce_shop_tools` | +| `silversolutions_translation` | `ibexa_commerce_translation` | +| `siso_admin_erp` | `ibexa_commerce_erp_admin` | +| `siso_basket` | `ibexa_commerce_basket` | +| `siso_checkout` | `ibexa_commerce_checkout` | +| `siso_comparison` | `ibexa_commerce_comparison` | +| `siso_content_plugin` | `ibexa_commerce_base_design` | +| `siso_ez_studio` | `ibexa_commerce_ez_studio` | +| `siso_local_order_management` | `ibexa_commerce_local_order_management` | +| `siso_newsletter` | `ibexa_commerce_newsletter` | +| `siso_order_history` | `ibexa_commerce_order_history` | +| `siso_payment` | `ibexa_commerce_payment` | +| `siso_price` | `ibexa_commerce_price` | +| `siso_quick_order` | `ibexa_commerce_quick_order` | +| `siso_search` | `ibexa_commerce_search` | +| `siso_shop_frontend` | `ibexa_commerce_shop_frontend` | +| `siso_test` | `ibexa_commerce_test_tools` | +| `siso_tools` | `ibexa_commerce_tools` | +| `siso_voucher` | `ibexa_commerce_voucher` | + +## Service names + +Service names which referred to old product names now use the Ibexa name. + +All service name changes are listed in the `ibexa/compatibility-layer` repository. + +Refer to [mapping reference](https://github.com/ibexa/compatibility-layer/blob/main/src/bundle/Resources/mappings/services-to-fqcn-map.php) +for a full comparison of old and new names. + +## Service tags + +Service tag which referred to old product names now use the Ibexa name. + +All service tag changes are listed in the `ibexa/compatibility-layer` repository. + +Refer to [mapping reference](https://github.com/ibexa/compatibility-layer/blob/main/src/bundle/Resources/mappings/symfony-service-tag-name-map.php) +for a full comparison of old and new service tags. + +## CSS classes for Back Office + +CSS classes with the `ez-` prefix have been modified with an `ibexa-` prefix. + +## JavaScript event names + +JavaScript event names with the `ez-` prefix have been modified with an `ibexa-` prefix, for example: + +`ez-notify` > `ibexa-notify` +`ez-content-tree-refresh` > `ibexa-content-tree-refresh` + +## REST API + +REST API route prefix has changed from `/api/ezp/v2/` to `/api/ibexa/v2/`. + +REST API media types have changed from `application/vnd.ez.api.*` to `application/vnd.ibexa.api.*`. + +## Twig functions and filters + +The following Twig functions and filter have been renamed, including: + +| Old name | New name | +| --- | --- | +| `ez_content_name` | `ibexa_content_name` | +| `ez_render_field` | `ibexa_render_field` | +| `ez_render` | `ibexa_render` | +| `ez_field` | `ibexa_field` | +| `ez_image_alias` | `ibexa_image_alias` | + +??? note "Full list of changed Twig function and filter names" + + | Old name | New name | + | --- | --- | + | `calculate_shipping` | `ibexa_commerce_calculate_shipping` | + | `code_label` | `ibexa_commerce_code_label` | + | `date_format` | `ibexa_commerce_date_format` | + | `ez_content_field_identifier_first_filled_image` | `ibexa_content_field_identifier_first_filled_image` | + | `ez_content_field_identifier_image_asset` | `ibexa_content_field_identifier_image_asset` | + | `ez_content_name` | `ibexa_content_name` | + | `ez_content_type_icon` | `ibexa_content_type_icon` | + | `ez_data_attributes_serialize` | `ibexa_data_attributes_serialize` | + | `ez_datetime_diff` | `ibexa_datetime_diff` | + | `ez_field_description` | `ibexa_field_description` | + | `ez_field_is_empty` | `ibexa_field_is_empty` | + | `ez_field_name` | `ibexa_field_name` | + | `ez_field_value` | `ibexa_field_value` | + | `ez_field` | `ibexa_field` | + | `ez_file_size` | `ibexa_file_size` | + | `ez_full_date` | `ibexa_full_date` | + | `ez_full_datetime` | `ibexa_full_datetime` | + | `ez_full_time` | `ibexa_full_time` | + | `ez_http_cache_tag_location` | `ibexa_http_cache_tag_location` | + | `ez_http_tag_location` | `ibexa_http_cache_tag_location` | + | `ez_http_tag_relation_ids` | `ibexa_http_cache_tag_relation_ids` | + | `ez_http_tag_relation_location_ids` | `ibexa_http_cache_tag_relation_location_ids` | + | `ez_image_alias` | `ibexa_image_alias` | + | `ez_page_layout` | `ibexa_page_layout` | + | `ez_path_to_locations` | `ibexa_path_to_locations` | + | `ez_path` | `ibexa_path` | + | `ez_recommendation_enabled` | `ibexa_recommendation_enabled` | + | `ez_recommendation_track_user` | `ibexa_recommendation_track_user` | + | `ez_render_*_query_*` | `ibexa_render_*_query_` | + | `ez_render_*_query` | `ibexa_render_*_query` | + | `ez_render_comparison_result` | `ibexa_render_comparison_result `| + | `ez_render_content` | `ibexa_render_content` | + | `ez_render_field_definition_settings` | `ibexa_render_field_definition_settings` | + | `ez_render_field` | `ibexa_render_field` | + | `ez_render_limitation_value` | `ibexa_render_limitation_value` | + | `ez_render_location` | `ibexa_render_location` | + | `ez_render` | `ibexa_render` | + | `ez_richtext_to_html5_edit` | `ibexa_richtext_to_html5_edit` | + | `ez_richtext_to_html5` | `ibexa_richtext_to_html5` | + | `ez_richtext_youtube_extract_id` | `ibexa_richtext_youtube_extract_id` | + | `ez_route` | `ibexa_route` | + | `ez_short_date` | `ibexa_short_date` | + | `ez_short_datetime` | `ibexa_short_datetime` | + | `ez_short_time` | `ibexa_short_time` | + | `ez_url` | `ibexa_url` | + | `get_characteristics_b2b` | `ibexa_commerce_get_characteristics_b2b` | + | `get_relation_content` | `ibexa_commerce_get_relation_content` | + | `get_search_query` | `ibexa_commerce_get_search_query` | + | `get_shipping_free_value` | `ibexa_commerce_get_shipping_free_value` | + | `get_siteaccess_locale` | `ibexa_commerce_get_siteaccess_locale` | + | `get_stored_baskets` | `ibexa_commerce_get_stored_baskets` | + | `ibexa_commerce_render_stock` | `ibexa_commerce_render_stock` | + | `ibexa_platform_asset` | `ibexa_dam_asset` | + | `ibexa_platform_dam_image_transformation` | `ibexa_dam_image_transformation` | + | `is_shipping_free` | `ibexa_commerce_is_shipping_free` | + | `price_format` | `ibexa_commerce_price_format` | + | `ses_assets_by_group` | `ibexa_commerce_assets_by_group` | + | `ses_assets_image_list` | `ibexa_commerce_assets_image_list` | + | `ses_assets_main_image` | `ibexa_commerce_assets_main_image` | + | `ses_basket` | `ibexa_commerce_basket` | + | `ses_check_product_in_comparison` | `ibexa_commerce_check_product_in_comparison` | + | `ses_check_product_in_wish_list` | `ibexa_commerce_check_product_in_wish_list` | + | `ses_comparison_category` | `ibexa_commerce_comparison_category` | + | `ses_config_parameter` | `ibexa_commerce_config_parameter` | + | `ses_contains_basket_vouchers` | `ibexa_commerce_contains_basket_vouchers` | + | `ses_content_pagination` | `ibexa_commerce_content_pagination` | + | `ses_correct_url` | `ibexa_commerce_correct_url` | + | `ses_erp_to_default` | `ibexa_commerce_erp_to_default` | + | `ses_format_args` | `ibexa_commerce_format_args` | + | `ses_get_basket_vouchers` | `ibexa_commerce_get_basket_vouchers` | + | `ses_invoice_number` | `ibexa_commerce_invoice_number` | + | `ses_navigation` | `ibexa_commerce_navigation` | + | `ses_pagination` | `ibexa_commerce_pagination` | + | `ses_product` | `ibexa_commerce_product` | + | `ses_render_field` | `ibexa_commerce_render_field` | + | `ses_render_price` | `ibexa_commerce_render_price` | + | `ses_render_specification_matrix` | `ibexa_commerce_render_specification_matrix` | + | `ses_render_stock` | `ibexa_commerce_render_stock` | + | `ses_scope_request_active` | `ibexa_commerce_scope_request_active` | + | `ses_to_float` | `ibexa_commerce_to_float` | + | `ses_total_comparison` | `ibexa_commerce_total_comparison` | + | `ses_track_base` | `ibexa_commerce_track_base` | + | `ses_track_basket` | `ibexa_commerce_track_basket` | + | `ses_track_product` | `ibexa_commerce_track_product` | + | `ses_user_menu` | `ibexa_commerce_user_menu` | + | `ses_variant_product_by_sku` | `ibexa_commerce_variant_product_by_sku` | + | `ses_wish_list` | `ibexa_commerce_wish_list` | + | `sort_characteristic_codes` | `ibexa_commerce_sort_characteristic_codes` | + | `sort_characteristics` | `ibexa_commerce_sort_characteristics` | + | `st_image` | `ibexa_commerce_image` | + | `st_imageconverter` | `ibexa_commerce_imageconverter` | + | `st_resolve_template` | `ibexa_commerce_resolve_template` | + | `st_siteaccess_lang` | `ibexa_commerce_siteaccess_lang` | + | `st_siteaccess_path` | `ibexa_commerce_siteaccess_path` | + | `st_siteaccess_url` | `ibexa_commerce_siteaccess_url` | + | `st_tag` | `ibexa_commerce_tag` | + | `st_translate` | `ibexa_commerce_translate` | + | `truncate` | `ibexa_commerce_truncate` | + | `unserialize` | `ibexa_commerce_unserialize` | + | `youtube_video_id` | `ibexa_commerce_youtube_video_id` | + + +## URL Alias route name +URL Alias route name has changed from `ez_urlalias` to `ibexa.url.alias`. + +## Configuration file names + +Built-in configuration files starting with `ezplatform` now use names with `ibexa`, including: + +| Old name | New name | +| --- | --- | +| `ezplatform.yaml` | `ibexa.yaml` | +| `ezplatform_admin_ui.yaml` | `ibexa_admin_ui.yaml` | +| `ezplatform_assets.yaml` | `ibexa_assets.yaml` | +| `ezplatform_doctrine_schema.yaml` | `ibexa_doctrineschema.yaml` | +| `ezplatform_elastic_search_engine.yaml` | `ibexa_elasticsearch.yaml` | +| `ezplatform_form_builder.yaml` | `ibexa_form_builder.yaml` | +| `ezplatform_http_cache.yaml` | `ibexa_http_cache.yaml` | +| `ezplatform_http_cache_fastly.yaml` | `ibexa_fastly.yaml` | +| `ezplatform_page_builder.yaml` | `ibexa_page_builder.yaml` | +| `ezplatform_site_factory.yaml` | `ibexa_site_factory.yaml` | +| `ezplatform_solr.yaml` | `ibexa_solr.yaml` | +| `ezplatform_welcome_page.yaml` | `ibexa_welcome_page.yaml` | + +## Minor changes + +- `AbstractBuilder::createMenuItem` return type is now `ItemInterface` only. +- Meaningless properties added to the Doctrine schema now throw an exception. +This is prevents indexes from being placed erroneously in the root table. +- The following deprecated service tags have been dropped: `ezsystems.platformui.application_config_provider`, +`ezpublish.content_view_provider`, `ezpublish.fieldType`, `ezpublish.fieldType.parameterProvider`, +`ezpublish.fieldType.indexable`, `ezpublish.fieldType.externalStorageHandler`, `ezpublish.fieldType.externalStorageHandler.gateway`, +`ezpublish.location_view_provider`, `ezpublish.query_type`, `ezpublish.searchEngineIndexer`, +`ezpublish.searchEngine`, `ezpublish.storageEngine.legacy.converter` +- `Ibexa\Contracts\Core\MVC\EventSubscriber\onConfigScopeChange::onConfigScopeChange` now takes `ScopeChangeEvent $event` instead of `SiteAccess $siteAccess` as argument. diff --git a/docs/releases/ibexa_dxp_v4.1.md b/docs/releases/ibexa_dxp_v4.1.md new file mode 100644 index 0000000000..83eaaf939b --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.1.md @@ -0,0 +1,86 @@ +# Ibexa DXP v4.1 + +**Version number**: v4.1 + +**Release date**: April 15, 2022 + +**Release type**: [Fast Track](../community_resources/release_process.md#release-process) + +## Notable changes + +### Product catalog enhancements + +With this release, product catalog brings new PHP APIs, productivity boost from new product search criteria and sort classes, advanced filtering in REST endpoints, auto-generated identifiers, product list sorting, and more. + +You can now use [advanced filtering on products, product types, attributes, and others in REST endpoints](https://doc.ibexa.co/en/latest/api/rest_api_reference/rest_api_reference.html#product-catalog-filter-currencies). + +Currencies, regions and customer groups can now be resolved automatically in the PHP API +based on the current context (for example, selected locale). + +A new Color attribute enables adding a product attribute that uses the color picker to select a precise color. + +The product catalog is now fully integrated with the transactional system integration, enabling a full purchasing process. + +### Measurement Field Type and attribute + +With the new Measurement Field Type users can now add a Measurement Field, with different pre-built units, to content: + +![Adding a Measurement Field to Content Type definition](img/4.1_measurement_ft.png) + +The new Measurement product attribute enables describing products with different types and units of measurement: + +![Adding measurement attribute values to product](img/4.1_measurement_attribute.png) + +### Dynamic targeting block [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +[Dynamic targeting block](https://doc.ibexa.co/projects/userguide/en/latest/site_organization/working_with_page/#dynamic-targeting-block) for the Page Builder provides recommendation items based on users related to the configured Segments. + +![Dynamic targeting block](img/4.1_page_builder_dynamic_targeting.png) + +### User interface improvements + +Several improvements to the Back Office interface enhance the user experience. +These include: + +- "Go to top" button +- new DateTime widget +- view switcher between lists, grids and calendar. + +Several new options have been added to the Content Tree's contextual menu, including Hide/Reveal, Create, Edit and Add translation, Add/Remove from bookmarks. + +![New Content Tree options](img/4.1_content_tree.png) + +## Other changes + +### GraphlQL + +Product catalog is now fully covered in GraphQL API. + +### Taxonomy language switcher + +A language switcher in Taxonomy view enables quick switching between different translations of the tag tree. + +![Language switcher in Taxonomy tree](img/4.1_taxonomy_lang_switcher.png) + +### Image optimization + +Images modified in the Image Editor are now optimized for reduced file size. +You can use external libraries to [optimize different image formats](https://doc.ibexa.co/en/latest/guide/images/#image-optimization). + +### Expanded data migrations + +[Data migration](../guide/data_migration/data_migration.md) now covers additional objects: + +- [database settings](https://doc.ibexa.co/en/latest/guide/data_migration/importing_data/#settings) +- [segments](https://doc.ibexa.co/en/latest/guide/data_migration/importing_data/#segments) +- [prices](https://doc.ibexa.co/en/latest/guide/data_migration/importing_data/#prices) with `create` mode +- [settings](https://doc.ibexa.co/en/latest/guide/data_migration/importing_data/#settings) + +Data migration now also offers a locking capability, +which prevents multiple processes from executing the same migration and causing duplicated records. + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|--------------|------------|------------| +| [Ibexa Content v4.1](https://github.com/ibexa/content/releases/tag/v4.1.0) | [Ibexa Experience v4.1](https://github.com/ibexa/experience/releases/tag/v4.1.0) | [Ibexa Commerce v4.1](https://github.com/ibexa/commerce/releases/tag/v4.1.0) diff --git a/docs/releases/ibexa_dxp_v4.2.md b/docs/releases/ibexa_dxp_v4.2.md new file mode 100644 index 0000000000..461257c4bd --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.2.md @@ -0,0 +1,252 @@ +# Ibexa DXP v4.2 + +**Version number**: v4.2 + +**Release date**: August 9, 2022 + +**Release type**: [Fast Track](../community_resources/release_process.md#release-process) + +**Update**: [v4.1.x to v4.2](https://doc.ibexa.co/en/latest/update_and_migration/from_4.1/update_from_4.1/) + +## Notable changes + +### Customer Portal [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +The new Customer Portal allows you to create and manage a business account for your company. +With this new feature, you can easily manage members of your organization, +your shipping information and view your past orders. +You can invite members to your company, activate or deactivate their accounts, +assign them specific roles and limitations, such as a buyer, or sales representative, and group them into teams. + +![Customer Portal Back Office](img/4.2_customer_portal.png) + +For more information, see [Back Office company management documentation](https://doc.ibexa.co/projects/userguide/en/latest/shop_administration/manage_users). + +On their personal accounts in Customer Portal, members of your organisation can view their order history, +other members of their team and information regarding their company, for example, billing addresses. +They can also edit their profile information. + +![Customer Portal Frontend](img/4.2_customer_center.png) + +For more information, see [Customer Portal documentation](https://doc.ibexa.co/projects/userguide/en/latest/shop_administration/customer_portal). + +### User management + +#### Inviting users + +You can [invite users to create their account](https://doc.ibexa.co/projects/userguide/en/latest/users/user_management/#inviting-users) in the frontend as customers or in the Back Office as members of your team. + +![Inviting members of your team](img/4.2_invite_users.png) + +#### Configure register form + +Register forms for new users can now be [configured straight in the YAML file](https://doc.ibexa.co/en/latest/guide/content_rendering/layout/add_register_user_template/#configure-existing-form). + +### Catalogs + +You can now create catalogs containing sub-sets of products. +Choose products for a catalog by applying filters which enable you to select products, +for example, by product type, price range, availability or category. + +![List of products in a catalog](img/4.2_catalogs_product_list.png) + +Catalogs are useful when creating special lists for B2B and B2C uses, for retailers and distributors or for different regions, +or other situations where you need to present a selected set of products. + +### Product variants + +To cover use cases of products with variable characteristics (such as colors, technical parameters or sizes), +you can now create product variants based on selected attributes. +The system automatically generates variants for the attribute values you select. + +![Generating product variants](img/4.2_product_variants_generate.png) + +You can set prices, including custom pricing, as well as availability and stock for each variant separately. + +### Product assets + +To provide your products with images, you can now upload multiple assets to each product. +Assets are grouped into collections based on attribute values + and, in this way, are connected to product variants which have these attributes. + +![Asset images in product view](img/4.2_product_assets.png) + +### Product completeness + +The new product completeness tab, in product view, lists all the parts of a product you can configure: +attributes, assets, prices, availability, and so on. +You can use it to get a quick overview of missing parts in the product configuration +and to instantly move to the proper screen to fill the gaps. + +![Product completeness tab](img/4.2_product_completeness.png) + +### Product categories + +With product categories, you can organize products that populate the Product Catalog. +You do it, for example, to assist users in searching for products. + +For more information, see [Product categories](https://doc.ibexa.co/projects/userguide/en/latest/shop_administration/product_categories/). + +![Product categories](img/4.2_product_categories_rn.png) + +### Cross-content type (CCT) recommendations + +If a recommendation scenario has more than one Content Type configured, with cross-content type (CCT) parameter in the request, +you can now get recommendations for all these Content Types. + +### Taxonomy Field Type + +Taxonomy is now [configured with a Field Type](https://doc.ibexa.co/projects/userguide/en/latest/taxonomy/#add-tag), +so you can use many Fields to add different taxonomy categories, for example, tags and product categories in the same Content Type. + +### Address Field Type + +With the [new Address Field Type](https://doc.ibexa.co/en/latest/content_management/field_types/field_type_reference/addressfield), you can now customize address Fields and configure them per country. + +![Address Field Type](img/4.2_address_field_type.png) + +### Repeatable migration steps + +Data migration now offers [repeatable migration steps](https://doc.ibexa.co/en/latest/guide/data_migration/importing_data/#repeatable-steps), +especially useful when creating large amounts of data, for example for testing. + +You can vary the migration values by using the iteration counter, or by generating random data by using [`FakerPHP`](https://fakerphp.github.io/). + +## Other changes + +### New product Search Criteria and Sort Clauses + +New Search Criteria and Sort Clauses help better fine-tune searches for products. + +Price-related Search Criteria enable you to search by base or custom product price: + +- [BasePrice](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/baseprice_criterion/) +- [CustomPrice](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/customprice_criterion/) + +Attribute Criteria search for products based on their attribute values, per attribute type: + +- [CheckboxAttribute](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/checkboxattribute_criterion/) +- [ColorAttribute](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/colorattribute_criterion/) +- [FloatAttribute](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/floatattribute_criterion/) +- [IntegerAttribute](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/integerattribute_criterion/) +- [SelectionAttribute](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/selectionattribute_criterion/) +- SimpleMeasurementAttribute +- RangeMeasurementAttributeMinimum +- RangeMeasurementAttributeMaximum + +Creation date Criteria and Sort Clauses allow searching by date of the product's creation: + +- [CreatedAt](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/createdat_criterion/) +- [CreatedAtRange](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/createdatrange_criterion/) +- [CreatedAt](https://doc.ibexa.co/en/latest/guide/search/sort_clause_reference/createdat_sort_clause/) + +Finally, you can search product by product category: + +- [ProductCategory](https://doc.ibexa.co/en/latest/guide/search/criteria_reference/productcategory_criterion/) + +### API improvements + +#### GraphQL + +Taxonomy is now covered with GraphQL API. + +Querying product attributes with GraphQL is improved with the option to [query by attribute type](https://doc.ibexa.co/en/latest/api/graphql_queries/#querying-product-attributes). + +### New ways to add images in Online Editor + +You can now drag and drop images directly into the Online Editor. +To achieve the same result, you can also click the **Upload image** button and select a file from the disk. +Images that you upload this way are automatically added to the Media library. + +!!! note + + In Media library, to avoid potential conflicts, + if several images are added with identical file names, + each of them is modified by appending a unique prefix. + +![Drag and drop image into the Online Editor](img/4.2_online_editor_dnd_image.png) + +### Content edit tabs + +Content editing screen is now enriched with a [tab switcher](https://doc.ibexa.co/en/latest/administration/back_office/content_tab_switcher/), allowing easy access to metadata such as taxonomies. +The view can be extended with custom tabs. + +![Tabs in content edit view](img/4.2_content_edit_tabs.png) + +### Grouped attributes in Page block + +If a Page block has multiple attributes, you can now group them with the [`nested_attribute` parameter](https://doc.ibexa.co/en/latest/content_management/pages/page_block_attributes/#nested-attribute-configuration). + +![Grouped attributes](img/4.2_page_block_nested.png) + +### Search in URL wildcards + +You can now search through the **URL wildcards** table in the Back Office. + +### Product price events + +The price engine now dispatches [events related to creating, updating and deleting prices](https://doc.ibexa.co/en/latest/guide/repository/event_reference/catalog_events/#price). + +### Data migration + +#### Migrations for attributes and attribute groups + +Data migration now supports `attribute` and `attribute_group` types when generating migration files. + +#### Hide and reveal content actions + +You can now hide and reveal Content items in data migrations by using the [`hide` and `reveal` actions](https://doc.ibexa.co/en/latest/guide/data_migration/data_migration_actions/#available-migration-actions). + +### Fastly shielding + +Ibexa DXP now supports Fastly shielding. + +## Deprecations + +### Segmentation + +- `SegmentationService::loadSegmentGroup()` and `SegmentationService::loadSegment()` are now deprecated. +Use `SegmentationService::loadSegmentGroupByIdentifier()` and `SegmentationService::loadSegmentByIdentifier()` instead, +which take `SegmentGroup` and `Segment` identifier respectively, instead of numerical IDs. +- `SegmentationService::updateSegmentGroup()` and `SegmentationService::updateSegment()` now take +a `SegmentGroup` and `Segment` objects respectively, instead of numerical IDs. + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|--------------|------------|------------| +| [Ibexa Content v4.2](https://github.com/ibexa/content/releases/tag/v4.2.0) | [Ibexa Experience v4.2](https://github.com/ibexa/experience/releases/tag/v4.2.0) | [Ibexa Commerce v4.2](https://github.com/ibexa/commerce/releases/tag/v4.2.0)| + +## v4.2.1 + +### Ibexa CDP + +Ibexa Customer Data Center allows you to collect, connect and organize customer data from multiple sources. +You can use them to build segments that will allow you to create personalized customer +experience for your brand. + +This is a standalone package that you can install along every product edition (Content, Experience, Commerce). +Ibexa CDP is also compatible with Ibexa v3.3. + +![CDP Control Panel](img/4.2_cdp_control_panel.png) + +For more information, see [Customer Data Platform](https://doc.ibexa.co/en/latest/cdp/cdp/). + +### SEO + +With Search Engine Optimization (SEO) tool, you can optimize your website or online store for both visitors and search engines. +The implementation of SEO brings in more organic traffic and improves your website visibility in SERPs. This is a core feature od Digital Experience Platform. +SEO bundle provides meta tags and meta titles with a description which helps to control search result's appearance of your website on the search engine pages. +Now you can share your content on the social networks using OpenGraph and Twitter cards. + +### Separate product edition directories + +Thanks to splitting SQL upgrade files into separate product editions, the product update is easier. + +### Event layer for TaxonomyService + +Now, Events are sent while performing operations within Taxonomy, which considerably extends the Taxonomy feature. + +### Protected Segment Groups + +You can now set existing Segment Groups as protected and make them unable to be modified through the user interface. diff --git a/docs/releases/ibexa_dxp_v4.3.md b/docs/releases/ibexa_dxp_v4.3.md new file mode 100644 index 0000000000..c25f3de074 --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.3.md @@ -0,0 +1,170 @@ +--- +description: Ibexa DXP v4.3 adds the improvements to the Customer Portal, PIM and SEO. +--- + +# Ibexa DXP v4.3 + +**Version number**: v4.3 + +**Release date**: November 10, 2022 + +**Release type**: [Fast Track](https://support.ibexa.co/Public/service-life) + +**Update**: [v4.2.x to v4.3](https://doc.ibexa.co/en/4.3/update_and_migration/from_4.2/update_from_4.2/) + +## Notable changes + +### Customer Portal [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +#### Company self-registration + +Now, a prospective buyer can apply to [create a company account](https://doc.ibexa.co/projects/userguide/en/latest/shop_administration/company_self_registration/) on a seller's website. + +The application goes through an approval process +where admin specifies the customer group and sales representative for the new company account. +Finally, the invitation link is sent back to the applicant to finish the registration process +and give them access to the Customer Portal. + +For more information, see [Customer Portal applications documentation](https://doc.ibexa.co/en/latest/customer_management/cp_applications/). + +![Company self-registration](img/4.3_self_registration.png) + +#### Customization of approval process + +You can now [customize the approval process](https://doc.ibexa.co/en/latest/customer_management/cp_applications/#customization-of-an-approval-process) for company self-registration. +By adding additional steps and options, you can build a process that perfectly meets your business needs. + +### SEO configuration exposed + +SEO configuration gains a more prominent place on the Content Type editing screen. +For example, to enable SEO, you now have to edit the Content Type that you want to modify, +scroll down to the SEO section and switch the **Enable SEO for this content type** toggle. + +For more information, see [Work with SEO](https://doc.ibexa.co/projects/userguide/en/latest/search_engine_optimization/work_with_seo/). + +!!! note + + This change is also implemented in v4.2. + +## Other changes + +### PIM improvements + +#### Price Sort Clauses + +When querying for products, you can now use one of two price-related Sort Clauses: + +- [`BasePrice` Sort Clause](https://doc.ibexa.co/en/master/search/sort_clause_reference/baseprice_sort_clause/) sorts results by the products' base prices +- [`CustomPrice` Sort Clause](https://doc.ibexa.co/en/master/search/sort_clause_reference/customprice_sort_clause/) enables sorting by the custom price configured for the provided customer group. + +#### Usability improvements + +This release also includes a number of usability improvements in PIM, +such as full information about available attribute values or improved display of Selection attributes. + +You can now move assets between collections by using drag and drop. + +![Moving assets between collection with drag and drop](img/4.3_collection_dnd.png) + +From product's Completeness tab you can now jump directly to editing the product prices in all configured currencies. + +![Editing product price from Completeness tab](img/4.3_edit_product_price.png) + +#### Catalog filters + +In catalogs, you can now [configure default filters](https://doc.ibexa.co/en/master/pim/pim_configuration/#catalog-filters) that are always added to a catalog, +as well as define filter order and group custom filters. +Built-in filters are also divided into groups now for easier browsing. + +Filtering by the Color attribute is now possible. + +#### Integration with recommendation engine + +Now, during product creation, edition, or deletion, information about the selected product categories (Taxonomies) is sent to the recommendation engine as an attribute +and can be used for recommendation engine filtering. + +### Users + +#### New User Content Type + +This release brings you a new Content Type for private customers registering from the front page. +We also prepared a migration command for already existing users to ease your upgrade process. +For more information, refer to upgrade documentation. + +### API improvements + +The catalogs functionality in PIM is now covered in REST API, including: + +- [Getting catalog list](https://doc.ibexa.co/en/4.3/api/rest_api/rest_api_reference/rest_api_reference.html#product-catalog-filter-catalogs) +- [Creating, modifying, copying and deleting catalogs](https://doc.ibexa.co/en/4.3/api/rest_api/rest_api_reference/rest_api_reference.html#product-catalog-create-catalog) +- [Changing catalog status](https://doc.ibexa.co/en/4.3/api/rest_api/rest_api_reference/rest_api_reference.html#product-catalog-update-catalog) +- [Getting catalog filters and sorting options](https://doc.ibexa.co/en/4.3/api/rest_api/rest_api_reference/rest_api_reference.html#product-catalog-load-catalog-filters) + +### Personalization improvements + +Now, as a Personalization admin, after editing a model in the Back Office, +[you can build this model](https://doc.ibexa.co/projects/userguide/en/master/personalization/recommendation_models/#trigger-model-build), use the **Trigger model build** button to build this model with your modifications. + +### Taxonomy improvements + +Objects of `Ibexa\Contracts\Taxonomy\Value\TaxonomyEntry` type, +which are returned by `TaxonomyService`, now contain the information about nesting level in the tree. + +The `TaxonomyEntryId` Search Criterion is not available in Legacy search Engine. + +### Other improvements + +- You can now [customize Elasticsearch index structure](https://doc.ibexa.co/en/master/search/extensibility/customize_elasticsearch_index_structure/) to manage how documents in the index are grouped. +- A new [`ibexa_seo_is_empty()` Twig function](https://doc.ibexa.co/en/master/templating/twig_function_reference/content_twig_functions/#ibexa_content_name) checks whether SEO data is available for a Content item. + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|--------------|------------|------------| +| [Ibexa Content v4.3](https://github.com/ibexa/content/releases/tag/v4.3.0) | [Ibexa Experience v4.3](https://github.com/ibexa/experience/releases/tag/v4.3.0) | [Ibexa Commerce v4.3](https://github.com/ibexa/commerce/releases/tag/v4.3.0)| + +## v4.3.1 + +### New REST API endpoints + +You can now use new REST API routes that confirm whether the User is logged in, +without invoking any other route: + +- GET `/user/current` - redirects to current User API load. +- GET `/user/sessions/current` - returns a current User Session object. + +You can retrieve, add and remove users from a Segment with: + +- GET `/user/users/{userId}/segments` - retrieves Segments for a given User. +- POST `/user/users/{userId}/segments` - assigns User to one or more Segments. +- DELETE `/user/users/{userId}/segments/{segmentIdentifier}` - unassigns User from a Segment. + +You can retrieve the defined languages with: + +- GET `/languages`- returns a defined language list. +- GET `/languages/{languageCode}` - returns a single language. + +### New service for token-based authentication + +The new release adds `Ibexa\Contracts\Rest\Security\AuthorizationHeaderRESTRequestMatcher` service that can be used instead of `Ibexa\AdminUi\REST\Security\NonAdminRESTRequestMatcher`. +It allows REST API endpoints to work with cookie-based authentication. + +### PIM improvements + +#### HTTP cache support for product-related responses + +Customer group is now part of user context, which enables HTTP cache to support +product-related responses. + +#### Ability to retrieve a customer group + +You can now retrieve customer group by implementing the `Ibexa\Contracts\ProductCatalog\CustomerGroupResolverInterface` interface and tagging it with `ibexa.product_catalog.customer_group.resolver`. + +## v4.3.5 + +- When `UserService::updateUserPassword` method throws `ContentFieldValidationException`, +it now uses the format accessible via `ContentFieldValidationException::getFieldErrors`: + +``` +array<<int fieldId>, array<<string language code>, array<\Ibexa\Contracts\Core\FieldType\ValidationError>>> +``` diff --git a/docs/releases/ibexa_dxp_v4.4.md b/docs/releases/ibexa_dxp_v4.4.md new file mode 100644 index 0000000000..5813201566 --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.4.md @@ -0,0 +1,159 @@ +--- +description: Ibexa DXP v4.4 adds the improvements to the Welcome Page, All-new Ibexa Commerce packages and Fastly IO. +--- + +# Ibexa DXP v4.4 + +**Version number**: v4.4 + +**Release date**: February 2, 2023 + +**Release type**: [Fast Track](https://support.ibexa.co/Public/service-life) + +**Update**: [v4.3.x to v4.4](https://doc.ibexa.co/en/latest/update_and_migration/from_4.3/update_from_4.3/) + +## Notable changes + +### New welcome page + +A new welcome page greets you when opening Ibexa Digital Experience Platform. + +![New Welcome Page](4.4_welcome_page.png) + +### All-new Ibexa Commerce packages [[% include 'snippets/commerce_badge.md' %]] + +This release deprecates all Commerce packages that you've known from previous releases +and brings a redesigned and reconstructed Commerce offering: + +- `ibexa/cart` +- `ibexa/checkout` +- `ibexa/storefront` + +As part of this effort, two all-new components have been created: Cart and Checkout, +that you can use to build your own e-commerce presence. + +![The new cart view](img/4.4_new_cart.png "The new cart view") + +![The new checkout](img/4.4_new_checkout.png "The new checkout") + +For more information, see [Commerce](https://doc.ibexa.co/en/4.4/commerce/commerce/). + +#### Storefront + +Another addition is the Storefront package that provides a starting kit +for the developers. +It is a working set of components, which you can use to test the new capabilities, +and then customize and extend to create your own implementation of a web store. + +For more information, see [Storefront](https://doc.ibexa.co/en/4.4/commerce/storefront/storefront). + +### Fastly Image Optimizer (Fastly IO) + +You can now use Fastly IO to serve optimized versions of your images in real time and cache them. +Fastly can perform multiple transformations on your image, +for example, cropping, resizing and trimming before serving it to end user. +Fastly is an external service that requires a separate subscription, +to learn more see, [Fastly Image Optimizer website](https://docs.fastly.com/en/guides/about-fastly-image-optimizer). + +If you already have Fastly IO subscription, you can move to [Fastly IO configuration in Ibexa DXP](https://doc.ibexa.co/en/4.4/content_management/images/fastly_io/). + +#### Fastly VCL upload + +With this release, you can manipulate your Fastly VCL configuration directly from the command line. +For example, you can define formats or source path for images. + +### New page blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +This release introduces new page blocks that rely on Personalization and PIM features +to let editors visually organize products on a page: + +- [Catalog block](https://doc.ibexa.co/projects/userguide/en/4.4/content_management/block_reference/#catalog-block) displays products from a specific catalog to a selected customer group. +- [Last purchased](https://doc.ibexa.co/projects/userguide/en/4.4/content_management/block_reference/#last-purchased-block) displays a list of products that were recently purchased, either generally, or by a specific user. +- [Last viewed](https://doc.ibexa.co/projects/userguide/en/4.4/content_management/block_reference/#last-viewed-block) displays a list of products that were recently viewed. +- [Product collection](https://doc.ibexa.co/projects/userguide/en/4.4/content_management/block_reference/#product-collection-block) displays a collection of specifically selected products. +- [Recently added](https://doc.ibexa.co/projects/userguide/en/4.4/content_management/block_reference/#recently-added-block) displays a list of products that were recently added to PIM. + +### Personalization improvements + +#### Automated way of creating Personalization service account + +The Personalization service has been enhanced to speed up the process of creating a new customer account. +Now, to create an account in the new, automated way, you have to fill out the form, select an account type, and send a request to the Personalization endpoint. +Shortly after, you receive the credentials. + +For more information, see [Requesting access to the server](https://doc.ibexa.co/projects/userguide/en/4.4/personalization/enable_personalization/#request-access-to-the-server). + +#### New models in Personalization engine + +Personalization engine introduces two new recommendation models: [predictive](https://doc.ibexa.co/projects/userguide/en/4.4/personalization/recommendation_models/#predictive) and [recurring purchase](https://doc.ibexa.co/projects/userguide/en/4.4/personalization/recommendation_models/#recurring-purchase). These two new models, based on mathematical approach, help to predict clients behavior and +provide the best recommendations. + +## Ibexa Connect + +You can now take advantage of [Ibexa Connect](https://www.ibexa.co/products/ibexa-connect), +an iPaaS (integration platform-as-a-service) which allows you to connect Ibexa DXP with third-party applications. +Ibexa Connect features a low-code drag-and-drop interface and hundreds of connectors to different services +that help you automate business processes. + +See [Ibexa Connect documentation](https://doc.ibexa.co/projects/connect/en/latest/). + +![Example of an Ibexa Connect scenario](4.4_connect_scenario_example.png) + +## Other changes + +### Flysystem v2 + +The codebase has undergone significant upgrades to rely on Flysystem v2. +The Flysystem Adapter implementation now supports dynamic paths +described by complex settings resolvable for the SiteAccess context. +For more information, see [Configuring the DFS IO handler](https://doc.ibexa.co/en/4.4/infrastructure_and_maintenance/clustering/clustering/#configuring-the-dfs-io-handler). + +If your custom project relies directly on a Flysystem features instead of using our IO abstraction, +it will require an upgrade as well, +using [these instructions](https://flysystem.thephpleague.com/docs/upgrade-from-1.x/). + +### Dedicated migration type for Corporate Accounts + +To simplify data migration, you can now create a corporate account with underlying objects such as members group and address book. +You can also extract those objects as references. +For more information on data migration actions, see [Data migration actions](https://doc.ibexa.co/en/4.4/content_management/data_migration/data_migration_actions/#data-migration-actions). + +### API improvements + +### Item age in Recently added model + +In a Recently added model (previously Random model), you can now manually [set the age of items](https://doc.ibexa.co/projects/userguide/en/4.4/personalization/recommendation_models/#recently-added) which are displayed in recommendations. + +### Deprecations + +#### Commerce packages + +The following Commerce packages are deprecated as of this release and will be removed in v5: + +- `ibexa/commerce-admin-ui` +- `ibexa/commerce-erp-admin` +- `ibexa/commerce-order-history` +- `ibexa/commerce-page-builder` +- `ibexa/commerce-rest` +- `ibexa/commerce-transaction` +- `ibexa/commerce-base-design` +- `ibexa/commerce-checkout` +- `ibexa/commerce-fieldtypes` +- `ibexa/commerce-price-engine` +- `ibexa/commerce-shop` +- `ibexa/commerce-shop-ui` + +They will be maintained by Ibexa with fixes, including security fixes, but they won't be further developed. +Old packages are replaced by [the all-new Ibexa Commerce packages](#all-new-ibexa-commerce-packages) with more +to come in the upcoming releases. + +#### Flysystem + +- Support for overwriting existing files has been dropped (catch block of `\Ibexa\Core\IO\IOBinarydataHandler\Flysystem::create` and test). The new native Flysystem v2 Local Adapter performs this out of the box. +- Support for no last modified timestamp has been dropped (in the form of a test case). The new Flysystem v2 throws `UnableToRetrieveMetadata` exception in such case. + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce | +|------------------------|---------------------------|-------------------------| +| [Ibexa Content v4.4](https://github.com/ibexa/content/releases/tag/v4.4.0) | [Ibexa Experience v4.4](https://github.com/ibexa/experience/releases/tag/v4.4.0) | [Ibexa Commerce v4.4](https://github.com/ibexa/commerce/releases/tag/v4.4.0) | diff --git a/docs/releases/ibexa_dxp_v4.5.md b/docs/releases/ibexa_dxp_v4.5.md new file mode 100644 index 0000000000..bff15f6ef2 --- /dev/null +++ b/docs/releases/ibexa_dxp_v4.5.md @@ -0,0 +1,245 @@ +--- +description: Ibexa DXP v4.5 adds new features to Ibexa Commerce, translation comparison, and a number of improvements to Customer Portal and Personalization. +--- + +# Ibexa DXP v4.5 + +**Version number**: v4.5 + +**Release date**: May 12, 2023 + +**Release type**: [Fast Track](https://support.ibexa.co/Public/service-life) + +**Update**: [v4.4.x to v4.5](https://doc.ibexa.co/en/latest/update_and_migration/from_4.4/update_from_4.4/) + +## Notable changes + +### All-new Ibexa Commerce packages [[% include 'snippets/commerce_badge.md' %]] + +This release brings new packages to complement the redesigned and reconstructed Commerce offering. +You can use them to further enhance your e-commerce presence: + +- `ibexa/order-management` +- `ibexa/payment` +- `ibexa/shipping` + +Modules can interact with each other, for example, to decrease stock as a result of a sale, or cancel shipments and payments when orders are cancelled, and so on. + +#### Order management + +With order management in place, it is now possible to create orders, configure and customize the order processing workflow, as well as manage orders by using the APIs. + +New screens added to the Back Office user interface let [[= product_name =]] users search for orders and filter search results. +Users can also review order details and completion status, as well as cancel orders. + +![The order list screen](img/4.5_order_list.png "The order list screen") + +#### Payment + +The all-new Payment module brings a possibility of tracking payment progress and defining a custom payment processing workflow. +New Back Office screens allow users to search for payment methods and payments, as well as define, enable and disable offline payment methods. + +Additionally, new APIs are available, which can be used for managing payment methods and payments. + +![The payment methods screen](img/4.5_payment_methods.png "The payment methods screen") + +#### Shipping + +With the arrival of the Shipping module, it is now possible to define and manage shipping methods of different types, together with their related costs, on a dedicated Back Office screen, as well as configure and customize the shipment workflow. + +New APIs enable managing shipping methods and payments, while an extension point can be used to expand the default list of shipping method types. + +![The shipping methods screen](img/4.5_shipping_methods.png "The shipping methods screen") + +For more information, see [Commerce](https://doc.ibexa.co/en/4.5/commerce/commerce/). + +### New commerce page blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +This release introduces new page blocks: + +- [Bestsellers block](https://doc.ibexa.co/projects/userguide/en/4.5/content_management/block_reference/#bestsellers-block) displays a list of products from PIM that were recently a bestseller. + +![Bestsellers block](img/4.5_bestsellers_block.png "Bestsellers block") + +- [React app block](https://doc.ibexa.co/en/4.5/content_management/pages/react_app_block/) allows an editor to embed a preconfigured React application in a page. React app block requires configuration. For more information, see [React App Block configuration](https://doc.ibexa.co/en/4.5/content_management/pages/react_app_block/#react-app-block-configuration). + +![React app block](img/4.5_react_app_block.png "React app block") + +### Translation comparison + +With this release, you can compare different versions of translations of a Content item, including comparison between different languages. + +You can now choose between two new options of the view: + +- Split - default, side by side view to compare versions of the same or different languages +- Unified - single column view to compare versions of the same language + +Now, when you compare different versions within the same language, the system highlights the changes using colors: + +- yellow - content updated +- blue - content added +- red - content deleted + +![Translation comparison](img/4.5_comparison_view.png "Translation comparison") + +For more information, see [Translation comparison](https://doc.ibexa.co/projects/userguide/en/4.5/content_management/translate_content/#translation-comparison). + +### Page Builder for B2B portals [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +With this release, you will be able to use Page Builder to create custom Customer Portals for your clients. +With new Sales rep page block and using all available blocks from the original Page Builder, you can create a unique experience for each customer group. +Additionally, you can assign each customer group to a specific Customer Portal or create an availability hierarchy based on rules and configuration. + +![Page Builder for B2B portals](img/4.5_page_builder_b2b.png "Page Builder for B2B portals") + +For more information, see [backend configuration](https://doc.ibexa.co/en/4.5/customer_management/cp_page_builder/) +and [user guide](https://doc.ibexa.co/projects/userguide/en/4.5/customer_management/build_customer_portal/) on how to create and edit Customer Portals. + +### Personalization improvements + +#### New B2B models in Personalization engine + +Personalization engine introduces two new types of models: [last clicked and last purchased B2B, and B2B recurring purchase models](https://doc.ibexa.co/projects/userguide/en/4.5/personalization/recommendation_models/#b2b-model), dedicated to B2B users. +Built on the fly, and based on segment groups, the models return actual items clicked by users with the same segment ID and actual bought items. +B2B recurring purchase model anticipates and predicts purchase of products that were bought recursively within the same segment ID. + +### Segment management + +Now you can use segmentation logic with operators to build complex segment groups which enable precise filtering. +With intuitive drag-and-drop interface, define rules, add logic operators and nest segments in segment +groups to get the most accurate, precise and targeted recommendations for your customers. + +![Segment management](img/4.5_segment_management.png "Segment management logic") + +For more information, see [Segment management](https://doc.ibexa.co/projects/userguide/en/4.5/personalization/segment_management/). + +## Other changes + +### Customer Data Platform (CDP) configuration + +In this release, the CDP configuration becomes more generic +and allows supporting other transport types accepted by CDP. +Currently, only `stream_file` transport is supported and can be initialized from the configuration. + +Ibexa DXP v4.5 adds the abstraction that allows you to implement other transport types from third parties. +For more information, see [CDP configuration](https://doc.ibexa.co/en/4.5/cdp/cdp_activation/#configuration). + +### API improvements + +#### REST API for company accounts [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +This release adds new endpoints that allow you to manage companies in your platform with REST API: + +- GET `/corporate/companies` - supports pagination and existing Content Criteria and Sort Clauses but via query parameters +- POST `/corporate/companies` - creates a company +- GET `/corporate/companies/{companyId}` - loads a company +- DELETE `/corporate/companies/{companyId}` - deletes a company +- PATCH `/corporate/companies/{companyId}` - updates company data +- GET `/corporate/companies/{companyId}/members` - supports filtering, sorting, and pagination +- POST `/corporate/companies/{companyId}/members` - creates new member in a company +- GET `/corporate/companies/{companyId}/members/{memberId}` - loads a member from a company +- DELETE `/corporate/companies/{companyId}/members/{memberId}` - deletes a member from a company +- PATCH `/corporate/companies/{companyId}/members/{memberId}` - updates member data + +#### PHP API for company accounts [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +To create a company with proper structure and shipping address by using PHP API, we recommend new +`\Ibexa\Contracts\CorporateAccount\Service\CorporateAccountService::createCompany` service instead of +`\Ibexa\Contracts\CorporateAccount\Service\CompanyService::createCompany`. + +#### REST API for order management [[% include 'snippets/commerce_badge.md' %]] + +This release adds new endpoints that allow you to manage orders by using REST API: + +- GET `/orders/orders` - loads a list of orders +- POST `/orders/orders` - creates an order +- GET `/orders/order` - loads an order by its identifier +- GET `/orders/order/{id}` - loads an order +- POST `/orders/orders/{id}` - cancels an order +- PATCH `/orders/orders/{id}` - updates an order + +#### PHP API for order management [[% include 'snippets/commerce_badge.md' %]] + +The Order Management package provides the `Ibexa\Contracts\OrderManagement\OrderServiceInterface` service, which is the entrypoint for calling the backend API for managing orders. + +#### PHP API for shipping methods and shipments [[% include 'snippets/commerce_badge.md' %]] + +The Checkout package provides the following services that are entrypoints to the backend API: + +- `Ibexa\Contracts\Shipping\ShipmentServiceInterface` for managing shipments +- `Ibexa\Contracts\Shipping\ShippingMethodServiceInterface` for managing shipment methods + +#### PHP API for payment methods and payments [[% include 'snippets/commerce_badge.md' %]] + +The Payment package provides the following services that are entrypoints to the backend API: + +- `Ibexa\Contracts\Payment\PaymentServiceInterface` for managing payments +- `Ibexa\Contracts\Payment\PaymentMethodServiceInterface` for managing payment methods + +### Category filter in product search + +To help users search for products, products in the main catalog view can now be filtered by product category. + +![Product categories filter](img/4.5_product_categories.png "Product categories filter") + +### Product aggregations + +Product search now supports aggregations, with the following aggregations available: + +- [Product attribute](https://doc.ibexa.co/en/4.5/search/aggregation_reference/product_attribute_aggregations/) - based on product attribute values +- [ProductAvailabilityTerm](https://doc.ibexa.co/en/4.5/search/aggregation_reference/productavailabilityterm_aggregation/) - based on product availability +- [ProductPriceRange](https://doc.ibexa.co/en/4.5/search/aggregation_reference/productpricerange_aggregation/) - based on product price +- [ProductTypeTerm](https://doc.ibexa.co/en/4.5/search/aggregation_reference/producttypeterm_aggregation/) - based on product type + +The new [TaxonomyEntryIdAggregation](https://doc.ibexa.co/en/4.5/search/aggregation_reference/taxonomyentryid_aggregation/) aggregates results based on content taxonomy entries or product categories. + +### Password security + +You can now enhance password security with a setting that prevents using passwords that have been exposed in a public breach. +To do it, the system checks the password against known password dumps by using the https://haveibeenpwned.com/ API. + +See [Breached passwords](https://doc.ibexa.co/en/4.5/users/user_management/#breached-passwords) for more information. + +### Ibexa Connect + +For list of changes in Ibexa Connect, see [Ibexa app release notes](https://doc.ibexa.co/projects/connect/en/latest/general/ibexa_app_release_notes/). + +### Deprecations + +#### `ibexa/admin-ui` + +Changes: + +- `\Ibexa\PageBuilder\Siteaccess\SiteaccessService::resolveSiteAccessForContent` moved to `\Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface` + +Deprecations: + +- `\Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface::getSiteaccessesForLocation` + replaced by `\Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface::getSiteAccessesList` +- `\Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface::getSiteaccesses` replaced by `\Ibexa\AdminUi\Siteaccess\SiteaccessResolverInterface::getSiteAccessesListForLocation` + +## Full changelog + +| Ibexa Content | Ibexa Experience | Ibexa Commerce| +|---------------|------------------|---------------| +| [Ibexa Content v4.5](https://github.com/ibexa/content/releases/tag/v4.5.0) | [Ibexa Experience v4.5](https://github.com/ibexa/experience/releases/tag/v4.5.0) | [Ibexa Commerce v4.5](https://github.com/ibexa/commerce/releases/tag/v4.5.0) | + +## v4.5.1 + +### Product category tree filter + +In the main catalog view, the tree of categories now has a search input to reduce the tree to matching categories. + +![Product category tree filter](img/4.5_product_category_tree.png "Product category tree filter") + +### Product stock criteria and aggregation + +Product search now supports stock availability: + +- [ProductStock Criterion](https://doc.ibexa.co/en/4.5/search/criteria_reference/productstock_criterion/) - searches for products with a stock compared to a given number +- [ProductStockRange Criterion](https://doc.ibexa.co/en/4.5/search/criteria_reference/productstockrange_criterion/) - searches for products with a stock in a given range +- [ProductStockRangeAggregation](https://doc.ibexa.co/en/4.5/search/aggregation_reference/productstockrange_aggregation/) - aggregates search results by products' stock ranges + +### `X-Expected-User` REST request header + +The [`X-Expected-User` header](https://doc.ibexa.co/en/4.5/api/rest_api/rest_api_usage/rest_requests/#expected-user) checks that the REST request is executed with the desired user (and not, for example, the Anonymous user because of an expired authentication). diff --git a/docs/releases/img/2.4_subtree_filter.png b/docs/releases/img/2.4_subtree_filter.png deleted file mode 100644 index 83334250a4..0000000000 Binary files a/docs/releases/img/2.4_subtree_filter.png and /dev/null differ diff --git a/docs/releases/img/3.3_perso_ui.png b/docs/releases/img/3.3_perso_ui.png new file mode 100644 index 0000000000..cc80ce4538 Binary files /dev/null and b/docs/releases/img/3.3_perso_ui.png differ diff --git a/docs/releases/img/4.0_catalog.png b/docs/releases/img/4.0_catalog.png new file mode 100644 index 0000000000..19fb0be151 Binary files /dev/null and b/docs/releases/img/4.0_catalog.png differ diff --git a/docs/releases/img/4.0_new_ui.png b/docs/releases/img/4.0_new_ui.png new file mode 100644 index 0000000000..bdf40db3bf Binary files /dev/null and b/docs/releases/img/4.0_new_ui.png differ diff --git a/docs/releases/img/4.0_product_price.png b/docs/releases/img/4.0_product_price.png new file mode 100644 index 0000000000..4c6c8f76c5 Binary files /dev/null and b/docs/releases/img/4.0_product_price.png differ diff --git a/docs/releases/img/4.1_content_tree.png b/docs/releases/img/4.1_content_tree.png new file mode 100644 index 0000000000..f45eeaa263 Binary files /dev/null and b/docs/releases/img/4.1_content_tree.png differ diff --git a/docs/releases/img/4.1_measurement_attribute.png b/docs/releases/img/4.1_measurement_attribute.png new file mode 100644 index 0000000000..adce55c735 Binary files /dev/null and b/docs/releases/img/4.1_measurement_attribute.png differ diff --git a/docs/releases/img/4.1_measurement_ft.png b/docs/releases/img/4.1_measurement_ft.png new file mode 100644 index 0000000000..60f7cd4469 Binary files /dev/null and b/docs/releases/img/4.1_measurement_ft.png differ diff --git a/docs/releases/img/4.1_page_builder_dynamic_targeting.png b/docs/releases/img/4.1_page_builder_dynamic_targeting.png new file mode 100644 index 0000000000..b3314b8763 Binary files /dev/null and b/docs/releases/img/4.1_page_builder_dynamic_targeting.png differ diff --git a/docs/releases/img/4.1_taxonomy_lang_switcher.png b/docs/releases/img/4.1_taxonomy_lang_switcher.png new file mode 100644 index 0000000000..89d4878626 Binary files /dev/null and b/docs/releases/img/4.1_taxonomy_lang_switcher.png differ diff --git a/docs/releases/img/4.2_address_field_type.png b/docs/releases/img/4.2_address_field_type.png new file mode 100644 index 0000000000..7c4dd2338f Binary files /dev/null and b/docs/releases/img/4.2_address_field_type.png differ diff --git a/docs/releases/img/4.2_catalogs_product_list.png b/docs/releases/img/4.2_catalogs_product_list.png new file mode 100644 index 0000000000..53f5498aef Binary files /dev/null and b/docs/releases/img/4.2_catalogs_product_list.png differ diff --git a/docs/releases/img/4.2_cdp_control_panel.png b/docs/releases/img/4.2_cdp_control_panel.png new file mode 100644 index 0000000000..51a253a083 Binary files /dev/null and b/docs/releases/img/4.2_cdp_control_panel.png differ diff --git a/docs/releases/img/4.2_content_edit_tabs.png b/docs/releases/img/4.2_content_edit_tabs.png new file mode 100644 index 0000000000..e86d14f3b6 Binary files /dev/null and b/docs/releases/img/4.2_content_edit_tabs.png differ diff --git a/docs/releases/img/4.2_customer_center.png b/docs/releases/img/4.2_customer_center.png new file mode 100644 index 0000000000..a8b8bdab62 Binary files /dev/null and b/docs/releases/img/4.2_customer_center.png differ diff --git a/docs/releases/img/4.2_customer_portal.png b/docs/releases/img/4.2_customer_portal.png new file mode 100644 index 0000000000..6c96236725 Binary files /dev/null and b/docs/releases/img/4.2_customer_portal.png differ diff --git a/docs/releases/img/4.2_invite_users.png b/docs/releases/img/4.2_invite_users.png new file mode 100644 index 0000000000..82c4bc91c2 Binary files /dev/null and b/docs/releases/img/4.2_invite_users.png differ diff --git a/docs/releases/img/4.2_online_editor_dnd_image.png b/docs/releases/img/4.2_online_editor_dnd_image.png new file mode 100644 index 0000000000..3e01d71802 Binary files /dev/null and b/docs/releases/img/4.2_online_editor_dnd_image.png differ diff --git a/docs/releases/img/4.2_page_block_nested.png b/docs/releases/img/4.2_page_block_nested.png new file mode 100644 index 0000000000..f274c0e8cb Binary files /dev/null and b/docs/releases/img/4.2_page_block_nested.png differ diff --git a/docs/releases/img/4.2_product_assets.png b/docs/releases/img/4.2_product_assets.png new file mode 100644 index 0000000000..d897fff72c Binary files /dev/null and b/docs/releases/img/4.2_product_assets.png differ diff --git a/docs/releases/img/4.2_product_categories_rn.png b/docs/releases/img/4.2_product_categories_rn.png new file mode 100644 index 0000000000..b820597217 Binary files /dev/null and b/docs/releases/img/4.2_product_categories_rn.png differ diff --git a/docs/releases/img/4.2_product_completeness.png b/docs/releases/img/4.2_product_completeness.png new file mode 100644 index 0000000000..c2d1c4e67f Binary files /dev/null and b/docs/releases/img/4.2_product_completeness.png differ diff --git a/docs/releases/img/4.2_product_variants_generate.png b/docs/releases/img/4.2_product_variants_generate.png new file mode 100644 index 0000000000..1a9422b84e Binary files /dev/null and b/docs/releases/img/4.2_product_variants_generate.png differ diff --git a/docs/releases/img/4.2_upload_images.png b/docs/releases/img/4.2_upload_images.png new file mode 100644 index 0000000000..afe7bf6fd9 Binary files /dev/null and b/docs/releases/img/4.2_upload_images.png differ diff --git a/docs/releases/img/4.3_collection_dnd.png b/docs/releases/img/4.3_collection_dnd.png new file mode 100644 index 0000000000..5af07f88a8 Binary files /dev/null and b/docs/releases/img/4.3_collection_dnd.png differ diff --git a/docs/releases/img/4.3_edit_product_price.png b/docs/releases/img/4.3_edit_product_price.png new file mode 100644 index 0000000000..4f75e2abb9 Binary files /dev/null and b/docs/releases/img/4.3_edit_product_price.png differ diff --git a/docs/releases/img/4.3_self_registration.png b/docs/releases/img/4.3_self_registration.png new file mode 100644 index 0000000000..92e981de47 Binary files /dev/null and b/docs/releases/img/4.3_self_registration.png differ diff --git a/docs/releases/img/4.4_connect_scenario_example.png b/docs/releases/img/4.4_connect_scenario_example.png new file mode 100644 index 0000000000..1951b3cce9 Binary files /dev/null and b/docs/releases/img/4.4_connect_scenario_example.png differ diff --git a/docs/releases/img/4.4_new_cart.png b/docs/releases/img/4.4_new_cart.png new file mode 100644 index 0000000000..0923a6ebce Binary files /dev/null and b/docs/releases/img/4.4_new_cart.png differ diff --git a/docs/releases/img/4.4_new_checkout.png b/docs/releases/img/4.4_new_checkout.png new file mode 100644 index 0000000000..dbe6fdccd7 Binary files /dev/null and b/docs/releases/img/4.4_new_checkout.png differ diff --git a/docs/releases/img/4.4_welcome_page.png b/docs/releases/img/4.4_welcome_page.png new file mode 100644 index 0000000000..a69bc67f23 Binary files /dev/null and b/docs/releases/img/4.4_welcome_page.png differ diff --git a/docs/releases/img/4.5_bestsellers_block.png b/docs/releases/img/4.5_bestsellers_block.png new file mode 100644 index 0000000000..74f55c3465 Binary files /dev/null and b/docs/releases/img/4.5_bestsellers_block.png differ diff --git a/docs/releases/img/4.5_comparison_view.png b/docs/releases/img/4.5_comparison_view.png new file mode 100644 index 0000000000..faec3b0c1f Binary files /dev/null and b/docs/releases/img/4.5_comparison_view.png differ diff --git a/docs/releases/img/4.5_order_list.png b/docs/releases/img/4.5_order_list.png new file mode 100644 index 0000000000..15389d0039 Binary files /dev/null and b/docs/releases/img/4.5_order_list.png differ diff --git a/docs/releases/img/4.5_page_builder_b2b.png b/docs/releases/img/4.5_page_builder_b2b.png new file mode 100644 index 0000000000..c545974218 Binary files /dev/null and b/docs/releases/img/4.5_page_builder_b2b.png differ diff --git a/docs/releases/img/4.5_payment_methods.png b/docs/releases/img/4.5_payment_methods.png new file mode 100644 index 0000000000..e0c52ac2a5 Binary files /dev/null and b/docs/releases/img/4.5_payment_methods.png differ diff --git a/docs/releases/img/4.5_product_categories.png b/docs/releases/img/4.5_product_categories.png new file mode 100644 index 0000000000..a7c0f437ac Binary files /dev/null and b/docs/releases/img/4.5_product_categories.png differ diff --git a/docs/releases/img/4.5_product_category_tree.png b/docs/releases/img/4.5_product_category_tree.png new file mode 100644 index 0000000000..b9d0cd8bdf Binary files /dev/null and b/docs/releases/img/4.5_product_category_tree.png differ diff --git a/docs/releases/img/4.5_react_app_block.png b/docs/releases/img/4.5_react_app_block.png new file mode 100644 index 0000000000..563a5b3247 Binary files /dev/null and b/docs/releases/img/4.5_react_app_block.png differ diff --git a/docs/releases/img/4.5_segment_management.png b/docs/releases/img/4.5_segment_management.png new file mode 100644 index 0000000000..8f7ac1dcdc Binary files /dev/null and b/docs/releases/img/4.5_segment_management.png differ diff --git a/docs/releases/img/4.5_shipping_methods.png b/docs/releases/img/4.5_shipping_methods.png new file mode 100644 index 0000000000..3b1a83a89f Binary files /dev/null and b/docs/releases/img/4.5_shipping_methods.png differ diff --git a/docs/releases/img/LP_drag_and_drop_improved.png b/docs/releases/img/LP_drag_and_drop_improved.png deleted file mode 100644 index 1049864c38..0000000000 Binary files a/docs/releases/img/LP_drag_and_drop_improved.png and /dev/null differ diff --git a/docs/releases/img/LP_in_view_mode.png b/docs/releases/img/LP_in_view_mode.png deleted file mode 100644 index 900194db7e..0000000000 Binary files a/docs/releases/img/LP_in_view_mode.png and /dev/null differ diff --git a/docs/releases/img/adding_siteaccess_limitations_rn16.02.png b/docs/releases/img/adding_siteaccess_limitations_rn16.02.png deleted file mode 100644 index d5773ab261..0000000000 Binary files a/docs/releases/img/adding_siteaccess_limitations_rn16.02.png and /dev/null differ diff --git a/docs/releases/img/aligned_image.png b/docs/releases/img/aligned_image.png deleted file mode 100644 index 5a74913dd2..0000000000 Binary files a/docs/releases/img/aligned_image.png and /dev/null differ diff --git a/docs/releases/img/approval_timeline.png b/docs/releases/img/approval_timeline.png deleted file mode 100644 index 7bd85178db..0000000000 Binary files a/docs/releases/img/approval_timeline.png and /dev/null differ diff --git a/docs/releases/img/better_udw.png b/docs/releases/img/better_udw.png deleted file mode 100644 index ba94719743..0000000000 Binary files a/docs/releases/img/better_udw.png and /dev/null differ diff --git a/docs/releases/img/choose_translation.png b/docs/releases/img/choose_translation.png deleted file mode 100644 index 3235661d57..0000000000 Binary files a/docs/releases/img/choose_translation.png and /dev/null differ diff --git a/docs/releases/img/cookiebundle.png b/docs/releases/img/cookiebundle.png deleted file mode 100644 index 87cda00470..0000000000 Binary files a/docs/releases/img/cookiebundle.png and /dev/null differ diff --git a/docs/releases/img/demow.jpg b/docs/releases/img/demow.jpg deleted file mode 100644 index fc348f9f67..0000000000 Binary files a/docs/releases/img/demow.jpg and /dev/null differ diff --git a/docs/releases/img/dev_mode.png b/docs/releases/img/dev_mode.png deleted file mode 100644 index 1dea512ccd..0000000000 Binary files a/docs/releases/img/dev_mode.png and /dev/null differ diff --git a/docs/releases/img/draft_conflict_screen.png b/docs/releases/img/draft_conflict_screen.png deleted file mode 100644 index 49bcaca170..0000000000 Binary files a/docs/releases/img/draft_conflict_screen.png and /dev/null differ diff --git a/docs/releases/img/ez_cookie.png b/docs/releases/img/ez_cookie.png deleted file mode 100644 index 87ff28fbf6..0000000000 Binary files a/docs/releases/img/ez_cookie.png and /dev/null differ diff --git a/docs/releases/img/featuring_articles.png b/docs/releases/img/featuring_articles.png deleted file mode 100644 index eefe2a3976..0000000000 Binary files a/docs/releases/img/featuring_articles.png and /dev/null differ diff --git a/docs/releases/img/field_categories.png b/docs/releases/img/field_categories.png deleted file mode 100644 index 38bb007e61..0000000000 Binary files a/docs/releases/img/field_categories.png and /dev/null differ diff --git a/docs/releases/img/flex_sending_for_review.png b/docs/releases/img/flex_sending_for_review.png deleted file mode 100644 index 8b4c5bcbe1..0000000000 Binary files a/docs/releases/img/flex_sending_for_review.png and /dev/null differ diff --git a/docs/releases/img/flex_workflow_notification_in_profile.png b/docs/releases/img/flex_workflow_notification_in_profile.png deleted file mode 100644 index ec5b66ad3b..0000000000 Binary files a/docs/releases/img/flex_workflow_notification_in_profile.png and /dev/null differ diff --git a/docs/releases/img/flex_workflow_notification_window.png b/docs/releases/img/flex_workflow_notification_window.png deleted file mode 100644 index 407ebaf955..0000000000 Binary files a/docs/releases/img/flex_workflow_notification_window.png and /dev/null differ diff --git a/docs/releases/img/landing_page_elements_menu.png b/docs/releases/img/landing_page_elements_menu.png deleted file mode 100644 index cbb7970001..0000000000 Binary files a/docs/releases/img/landing_page_elements_menu.png and /dev/null differ diff --git a/docs/releases/img/landing_page_general_screen.png b/docs/releases/img/landing_page_general_screen.png deleted file mode 100644 index 64a112ceaa..0000000000 Binary files a/docs/releases/img/landing_page_general_screen.png and /dev/null differ diff --git a/docs/releases/img/location_swap.png b/docs/releases/img/location_swap.png deleted file mode 100644 index 115e387cff..0000000000 Binary files a/docs/releases/img/location_swap.png and /dev/null differ diff --git a/docs/releases/img/locations_tab.png b/docs/releases/img/locations_tab.png deleted file mode 100644 index 1db3419d9c..0000000000 Binary files a/docs/releases/img/locations_tab.png and /dev/null differ diff --git a/docs/releases/img/m_c_t.png b/docs/releases/img/m_c_t.png deleted file mode 100644 index 07faadc2e9..0000000000 Binary files a/docs/releases/img/m_c_t.png and /dev/null differ diff --git a/docs/releases/img/new_datepicker.png b/docs/releases/img/new_datepicker.png deleted file mode 100644 index 74b140c434..0000000000 Binary files a/docs/releases/img/new_datepicker.png and /dev/null differ diff --git a/docs/releases/img/oe_list.png b/docs/releases/img/oe_list.png deleted file mode 100644 index 4235bfa36d..0000000000 Binary files a/docs/releases/img/oe_list.png and /dev/null differ diff --git a/docs/releases/img/online_editor_image_options.png b/docs/releases/img/online_editor_image_options.png deleted file mode 100644 index db810e3884..0000000000 Binary files a/docs/releases/img/online_editor_image_options.png and /dev/null differ diff --git a/docs/releases/img/options_menu.png b/docs/releases/img/options_menu.png deleted file mode 100644 index 110fbea946..0000000000 Binary files a/docs/releases/img/options_menu.png and /dev/null differ diff --git a/docs/releases/img/pick_cont.png b/docs/releases/img/pick_cont.png deleted file mode 100644 index ee095e88e9..0000000000 Binary files a/docs/releases/img/pick_cont.png and /dev/null differ diff --git a/docs/releases/img/platform_custom_policies.png b/docs/releases/img/platform_custom_policies.png deleted file mode 100644 index f59f5ec4bc..0000000000 Binary files a/docs/releases/img/platform_custom_policies.png and /dev/null differ diff --git a/docs/releases/img/platformui_navigation_bar.png b/docs/releases/img/platformui_navigation_bar.png deleted file mode 100644 index 30a347fc4d..0000000000 Binary files a/docs/releases/img/platformui_navigation_bar.png and /dev/null differ diff --git a/docs/releases/img/premium_content.png b/docs/releases/img/premium_content.png deleted file mode 100644 index b33c56c78b..0000000000 Binary files a/docs/releases/img/premium_content.png and /dev/null differ diff --git a/docs/releases/img/recommended_articles.png b/docs/releases/img/recommended_articles.png deleted file mode 100644 index 4ed405f2b8..0000000000 Binary files a/docs/releases/img/recommended_articles.png and /dev/null differ diff --git a/docs/releases/img/register.png b/docs/releases/img/register.png deleted file mode 100644 index 78218ccc79..0000000000 Binary files a/docs/releases/img/register.png and /dev/null differ diff --git a/docs/releases/img/richtext.png b/docs/releases/img/richtext.png deleted file mode 100644 index 6cee633301..0000000000 Binary files a/docs/releases/img/richtext.png and /dev/null differ diff --git a/docs/releases/img/roles_ui.png b/docs/releases/img/roles_ui.png deleted file mode 100644 index a9fd32c2c0..0000000000 Binary files a/docs/releases/img/roles_ui.png and /dev/null differ diff --git a/docs/releases/img/rt_edit.png b/docs/releases/img/rt_edit.png deleted file mode 100644 index 6cee633301..0000000000 Binary files a/docs/releases/img/rt_edit.png and /dev/null differ diff --git a/docs/releases/img/schedule_block_airtime_settings.png b/docs/releases/img/schedule_block_airtime_settings.png deleted file mode 100644 index 69aa4adfb8..0000000000 Binary files a/docs/releases/img/schedule_block_airtime_settings.png and /dev/null differ diff --git a/docs/releases/img/setting_priorities.png b/docs/releases/img/setting_priorities.png deleted file mode 100644 index 918c32fd14..0000000000 Binary files a/docs/releases/img/setting_priorities.png and /dev/null differ diff --git a/docs/releases/img/studio_demo_bundle_demo.png b/docs/releases/img/studio_demo_bundle_demo.png deleted file mode 100644 index cfff5498f4..0000000000 Binary files a/docs/releases/img/studio_demo_bundle_demo.png and /dev/null differ diff --git a/docs/releases/img/sub-items_ordering.png b/docs/releases/img/sub-items_ordering.png deleted file mode 100644 index f11ca29b06..0000000000 Binary files a/docs/releases/img/sub-items_ordering.png and /dev/null differ diff --git a/docs/releases/img/symfony_black_02.png b/docs/releases/img/symfony_black_02.png deleted file mode 100644 index d0a0d093f6..0000000000 Binary files a/docs/releases/img/symfony_black_02.png and /dev/null differ diff --git a/docs/releases/img/timeline_releasenotes.png b/docs/releases/img/timeline_releasenotes.png deleted file mode 100644 index 98e7d3e11e..0000000000 Binary files a/docs/releases/img/timeline_releasenotes.png and /dev/null differ diff --git a/docs/releases/img/timeline_view_list.png b/docs/releases/img/timeline_view_list.png deleted file mode 100644 index 935e8a6761..0000000000 Binary files a/docs/releases/img/timeline_view_list.png and /dev/null differ diff --git a/docs/releases/img/trash.png b/docs/releases/img/trash.png deleted file mode 100644 index 930c393789..0000000000 Binary files a/docs/releases/img/trash.png and /dev/null differ diff --git a/docs/releases/img/trash_mgmt.png b/docs/releases/img/trash_mgmt.png deleted file mode 100644 index 6cb725da3d..0000000000 Binary files a/docs/releases/img/trash_mgmt.png and /dev/null differ diff --git a/docs/releases/img/tuxi.png b/docs/releases/img/tuxi.png deleted file mode 100644 index 870c720e23..0000000000 Binary files a/docs/releases/img/tuxi.png and /dev/null differ diff --git a/docs/releases/img/ui_simple_search.png b/docs/releases/img/ui_simple_search.png deleted file mode 100644 index adefea4244..0000000000 Binary files a/docs/releases/img/ui_simple_search.png and /dev/null differ diff --git a/docs/releases/img/user_registration_form.png b/docs/releases/img/user_registration_form.png deleted file mode 100644 index bed12bbd3c..0000000000 Binary files a/docs/releases/img/user_registration_form.png and /dev/null differ diff --git a/docs/releases/img/variations_purging.png b/docs/releases/img/variations_purging.png deleted file mode 100644 index 08e6606f30..0000000000 Binary files a/docs/releases/img/variations_purging.png and /dev/null differ diff --git a/docs/releases/img/verions_window.png b/docs/releases/img/verions_window.png deleted file mode 100644 index f0a20bbd44..0000000000 Binary files a/docs/releases/img/verions_window.png and /dev/null differ diff --git a/docs/releases/img/versions_tab.png b/docs/releases/img/versions_tab.png deleted file mode 100644 index 8cf50a7d2d..0000000000 Binary files a/docs/releases/img/versions_tab.png and /dev/null differ diff --git a/docs/snippets/commerce_badge.md b/docs/snippets/commerce_badge.md index daac62884a..0aa977a29b 100644 --- a/docs/snippets/commerce_badge.md +++ b/docs/snippets/commerce_badge.md @@ -1 +1 @@ -<span class="badge commerce-badge"></span> +<span class="pill commerce-pill"></span> diff --git a/docs/snippets/experience_badge.md b/docs/snippets/experience_badge.md index 4b4b105256..df97d86805 100644 --- a/docs/snippets/experience_badge.md +++ b/docs/snippets/experience_badge.md @@ -1 +1 @@ -<span class="badge experience-badge"></span> +<span class="pill experience-pill"></span> diff --git a/docs/snippets/forms_caution.md b/docs/snippets/forms_caution.md new file mode 100644 index 0000000000..f752fb5ddc --- /dev/null +++ b/docs/snippets/forms_caution.md @@ -0,0 +1,4 @@ +!!! caution "Known limitation" + + To have multiple instances of the same form on one page, create several identical form blocks. + Otherwise, you may encounter issues with submitting data from all forms at the same time. diff --git a/docs/snippets/rendering_dump_variable.md b/docs/snippets/rendering_dump_variable.md new file mode 100644 index 0000000000..441bdd07c5 --- /dev/null +++ b/docs/snippets/rendering_dump_variable.md @@ -0,0 +1,8 @@ +!!! tip + + For development purposes, you can list all available variables, or a single variable, and their values, by using the `dump()` Twig function: + + ``` html+twig + {{ dump() }} + {{ dump(content) }} + ``` diff --git a/docs/snippets/search_term_aggregation_settings.md b/docs/snippets/search_term_aggregation_settings.md new file mode 100644 index 0000000000..e67da0f71d --- /dev/null +++ b/docs/snippets/search_term_aggregation_settings.md @@ -0,0 +1,10 @@ +## Settings + +You can define additional limits to the results using the `setLimit()` and `setMinCount()` methods. +The following example limits the number of terms returned to 5 and only considers terms that have 10 or more results: + +``` php +$aggregation = new //... +$aggregation->setLimit(5); +$aggregation->setMinCount(10); +``` diff --git a/docs/snippets/simple_hash_value_caution.md b/docs/snippets/simple_hash_value_caution.md new file mode 100644 index 0000000000..40fa9b2f31 --- /dev/null +++ b/docs/snippets/simple_hash_value_caution.md @@ -0,0 +1,4 @@ +!!! caution "Simple hash values" + + A simple hash value always means an array of scalar values and/or nested arrays of scalar values. + To avoid issues with format conversion, don't use objects inside the simple hash values. \ No newline at end of file diff --git a/docs/snippets/update/check_out_version.md b/docs/snippets/update/check_out_version.md new file mode 100644 index 0000000000..1ebf442daa --- /dev/null +++ b/docs/snippets/update/check_out_version.md @@ -0,0 +1,88 @@ +### A. Create branch + +Create a new branch for handling update changes from the branch you are updating on: + +``` bash +git checkout -b update-[[= target_version =]] +``` + +This creates a new project branch (`update-[[= target_version =]]`) for the update based on your current project branch. + +### B. Add `upstream` remote + +If it is not added as a remote yet, add an `upstream` remote: + +=== "ezplatform" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezplatform.git + ``` + +=== "ezplatform-ee" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezplatform-ee.git + ``` + +=== "ezcommerce" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezcommerce.git + ``` + +### C. Prepare for pulling changes + +??? note "Adding `sort-packages` option when updating from <=v1.13.4, v2.2.3, v2.3.2" + + Composer sorts packages listed in `composer.json`. + If your packages are not sorted yet, you should prepare for this update to make it clearer which changes you introduce. + + Assuming you have installed packages on your installation (`composer install`), do the following steps: + + 1\. Add [sort-packages](https://getcomposer.org/doc/06-config.md#sort-packages) to the `config` section in `composer.json`. + + ``` json hl_lines="3" + "config": { + "bin-dir": "bin", + "sort-packages": true, + "preferred-install": { + "ezsystems/*": "dist" + } + }, + ``` + + 2\. Use `composer require` to get Composer to sort your packages. + + The following example updates a few requirements with what you can expect in the upcoming change: + + ``` bash hl_lines="1 2 4" + composer require --no-scripts --no-update doctrine/doctrine-bundle:^1.9.1 + composer require --dev --no-scripts --no-update behat/behat:^3.5.0 + # The upcoming change also moves security-advisories to dev as advised by the package itself + composer require --dev --no-scripts --no-update roave/security-advisories:dev-master + ``` + + 3\. Check that you can install/update packages. + + ``` bash + composer update + ``` + + If Composer says there were no updates, or if it updates packages without stopping with conflicts, + your preparation was successful. + + 4\. Save your work. + + ``` bash + git commit -am "Sort my existing composer packages in anticipation of update with sorted merge" + ``` + +### D. Pull the tag into your branch + +Pull the latest v[[= target_version =]] tag into the `update-[[= target_version =]]` branch with the following command: + +``` bash +git pull upstream v[[= latest_tag =]] +``` + +At this stage you may get conflicts, which are a normal part of the update procedure. diff --git a/docs/snippets/update/db/db_backup_warning.md b/docs/snippets/update/db/db_backup_warning.md new file mode 100644 index 0000000000..e7392a19de --- /dev/null +++ b/docs/snippets/update/db/db_backup_warning.md @@ -0,0 +1,9 @@ +!!! caution + + Always back up your data before running any database update scripts. + + After updating the database, clear the cache. + + Do not use `--force` argument for `mysql` / `psql` commands when performing update queries. + If there is any problem during the update, it is best if the query fails immediately, so you can fix the underlying problem before you execute the update again. + If you leave this for later you risk ending up with an incompatible database, though the problems might not surface immediately. diff --git a/docs/snippets/update/db/update_db_2.5-3.3.md b/docs/snippets/update/db/update_db_2.5-3.3.md new file mode 100644 index 0000000000..f9c7202787 --- /dev/null +++ b/docs/snippets/update/db/update_db_2.5-3.3.md @@ -0,0 +1,144 @@ +Apply the following database update script: + +### Ibexa DXP + +=== "MySQL" + + ``` bash + mysql -u <username> -p <password> <database_name> < vendor/ibexa/installer/upgrade/db/mysql/ezplatform-2.5-to-ibexa-3.3.0.sql + ``` + +=== "PosgreSQL" + + ``` bash + psql <database_name> < vendor/ibexa/installer/upgrade/db/postgresql/ezplatform-2.5-to-ibexa-3.3.0.sql + ``` + +If you are updating from an installation based on the `ezsystems/ezplatform-ee` metarepository, +run the following command to upgrade your database: + +``` bash +php bin/console ibexa:upgrade +``` + +!!! caution + + You can only run this command once. + +Check the Location ID of the "Components" Content item and set it as a value of the `content_tree_module.contextual_tree_root_location_ids` key in `config/ezplatform.yaml`: + +``` +- 60 # Components +``` + +If you are upgrading between [[= product_name_com =]] versions, +add the `content/read` Policy with the Owner Limitation set to `self` to the "Ecommerce registered users" Role. + +### Ibexa Open Source + +If you are upgrading to Ibexa Open Source v3.3 and have no access to `ibexa/installer` package, run the following SQL commands: + +=== "MySQL" + + ``` sql + START TRANSACTION; + DELETE FROM `ezsite_data` WHERE `name` IN ('ezpublish-version', 'ezpublish-release', 'ezplatform-release'); + INSERT INTO ezsite_data (`name`, `value`) VALUES ('ibexa-release', '3.3'); + COMMIT; + + ALTER TABLE `ezcontentclass_attribute` MODIFY `data_text1` VARCHAR(255); + ALTER TABLE `ezcontentclass_attribute` ADD COLUMN `is_thumbnail` BOOLEAN NOT NULL DEFAULT false; + + ALTER TABLE `ezkeyword_attribute_link` + ADD COLUMN `version` INT(11) DEFAULT '0', + ADD KEY `ezkeyword_attr_link_oaid_ver` (`objectattribute_id`, `version`); + UPDATE `ezkeyword_attribute_link` + SET `version` = COALESCE( + ( + SELECT `current_version` + FROM `ezcontentobject_attribute` AS `a` + JOIN `ezcontentobject` AS `c` + ON `a`.`contentobject_id` = `c`.`id` AND `a`.`version` = `c`.`current_version` + WHERE `a`.`id` = `ezkeyword_attribute_link`.`objectattribute_id` + ), 0); + ALTER TABLE `ezkeyword_attribute_link` MODIFY `version` INT(11) NOT NULL DEFAULT '0'; + + UPDATE `ezcontentclass_attribute` + SET `data_text2` = '^[^@]+$' + WHERE `data_type_string` = 'ezuser' AND `data_text2` IS NULL; + + UPDATE `ezuser` + SET `email` = + CASE + WHEN `contentobject_id` = 10 THEN 'anonymous@link.invalid' + WHEN `contentobject_id` = 14 THEN 'admin@link.invalid' + END + WHERE `contentobject_id` IN (10, 14) AND `email` IN ('nospam@ibexa.co', 'nospam@ez.no'); + + DROP TABLE IF EXISTS `ibexa_setting`; + CREATE TABLE `ibexa_setting` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `group` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL, + `identifier` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL, + `value` text COLLATE utf8mb4_unicode_520_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ibexa_setting_group_identifier` (`group`, `identifier`) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_520_ci; + + UPDATE ezcontentobject_attribute + SET data_text=NULL + WHERE data_type_string='ezimageasset' + AND data_text IN ('null', '{"destinationContentId":null,"alternativeText":"null"}'); + ``` + +=== "PostgreSQL" + + ``` sql + START TRANSACTION; + DELETE FROM "ezsite_data" WHERE "name" IN ('ezpublish-version', 'ezpublish-release', 'ezplatform-release'); + INSERT INTO "ezsite_data" ("name", "value") VALUES ('ibexa-release', '3.3'); + COMMIT; + + ALTER TABLE "ezcontentclass_attribute" ALTER COLUMN "data_text1" TYPE varchar(255); + ALTER TABLE "ezcontentclass_attribute" ADD "is_thumbnail" boolean DEFAULT false NOT NULL; + + ALTER TABLE "ezkeyword_attribute_link" ADD COLUMN "version" INT; + UPDATE "ezkeyword_attribute_link" + SET "version" = COALESCE( + ( + SELECT "current_version" + FROM "ezcontentobject_attribute" AS a + JOIN "ezcontentobject" AS c + ON a.contentobject_id = c.id AND a.version = c.current_version + WHERE a.id = ezkeyword_attribute_link.objectattribute_id + ), 0); + ALTER TABLE "ezkeyword_attribute_link" ALTER COLUMN "version" SET NOT NULL; + CREATE INDEX "ezkeyword_attr_link_oaid_ver" ON "ezkeyword_attribute_link" ("objectattribute_id", "version"); + + UPDATE "ezcontentclass_attribute" + SET "data_text2" = '^[^@]+$' + WHERE "data_type_string" = 'ezuser' AND "data_text2" IS NULL; + + UPDATE "ezuser" + SET "email" = + CASE + WHEN "contentobject_id" = 10 THEN 'anonymous@link.invalid' + WHEN "contentobject_id" = 14 THEN 'admin@link.invalid' + END + WHERE "contentobject_id" IN (10, 14) AND "email" IN ('nospam@ibexa.co', 'nospam@ez.no'); + + DROP TABLE IF EXISTS "ibexa_setting"; + CREATE TABLE "ibexa_setting" ( + "id" SERIAL NOT NULL, + "group" varchar(128) NOT NULL, + "identifier" varchar(128) NOT NULL, + "value" json NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "ibexa_setting_group_identifier" UNIQUE ("group", identifier) + ); + + UPDATE ezcontentobject_attribute + SET data_text=NULL + WHERE data_type_string='ezimageasset' + AND data_text IN ('null', '{"destinationContentId":null,"alternativeText":"null"}'); + ``` diff --git a/docs/snippets/update/finish_the_update.md b/docs/snippets/update/finish_the_update.md new file mode 100644 index 0000000000..19b838766f --- /dev/null +++ b/docs/snippets/update/finish_the_update.md @@ -0,0 +1,63 @@ +### A. Platform.sh changes + +If you are hosting your site on Ibexa Cloud be aware of the fact that Varnish is enabled by default as of v1.13.5, v2.4.3 and v2.5.0. +If you are using Fastly, read about [how to disable Varnish](https://docs.platform.sh/frameworks/ibexa/fastly.html#remove-varnish-configuration). + +### B. Dump assets + +Dump web assets if you are using the `prod` environment. In `dev` this happens automatically: + +``` sh +yarn install +yarn encore prod +``` + +If you encounter problems, additionally clear the cache and install assets: + +``` sh +php bin/console cache:clear -e prod +php bin/console assets:install --symlink -e prod +yarn install +yarn encore prod +``` + +### C. Commit, test and merge + +When you resolve all conflicts and update `composer.lock`, commit the merge. + +You may or may not keep `composer.lock`, depending on your version management workflow. +If you do not want to keep it, run `git reset HEAD composer.lock` to remove it from the changes. +Run `git commit`, and adapt the message if necessary. + +Go back to `master`, and merge the `update-[[= target_version =]]` branch: + +``` sh +git checkout master +git merge update-[[= target_version =]] +``` + +!!! note "Insecure password hashes" + + To ensure that no users have unsupported, insecure password hashes, run the following command: + + ``` bash + # In v1 and v2: + php bin/console ezplatform:user:validate-password-hashes + # In v3: + php bin/console ibexa:user:validate-password-hashes + ``` + + This command checks if all user hashes are up-to-date and informs you if any of them need to be updated. + +### D. Complete the update + +Complete the update by running the following commands: + +``` bash +# In v2.5: +php bin/console ezplatform:graphql:generate-schema +# In v3: +php bin/console ibexa:graphql:generate-schema + +composer run post-install-cmd +``` diff --git a/docs/snippets/update/merge_composer.md b/docs/snippets/update/merge_composer.md new file mode 100644 index 0000000000..2713ba4633 --- /dev/null +++ b/docs/snippets/update/merge_composer.md @@ -0,0 +1,56 @@ +### A. Resolve conflicts + +If you get a lot of conflicts and you installed from the [support.ez.no / support.ibexa.co](https://support.ibexa.co) tarball +or from [ezplatform.com](https://ezplatform.com), you may have incomplete history. + +To load the full history, run `git fetch upstream --unshallow` from the `update-[[= target_version =]]` branch, and run the merge again. + +Ignore the conflicts in `composer.lock`, because this file is regenerated when you execute `composer update` later. +It is easiest to check out the version of `composer.lock` from the tag and add it to the changes: + +``` bash +git checkout --theirs composer.lock && git add composer.lock +``` + +If you do not keep a copy of `composer.lock` in the branch, you may also remove it by running: + +``` bash +git rm composer.lock +``` + +### B. Resolve conflicts in `composer.json` + +You need to fix conflicts in `composer.json` manually. + +If you're not familiar with the diff output, you may check out the tag's version from the `update-[[= target_version =]]` branch and inspect the changes. + +``` bash +git checkout --theirs composer.json && git diff HEAD composer.json +``` + +This command shows the differences between the target `composer.json` and your own in the diff output. + +Updating `composer.json` changes the requirements for all of the `ezsystems` / `ibexa` packages. Keep those changes. +The other changes remove what you added for your own project. +Use `git checkout -p` to selectively cancel those changes (and retain your additions): + +``` bash +git checkout -p composer.json +``` + +Answer `no` (do not discard) to the requirement changes of `ezsystems` / `ibexa` dependencies. +Answer `yes` (discard) to removals of your changes. + +After you are done, inspect the file (you can use an editor or run `git diff composer.json`). +You may also test the file with `composer validate`, +and test the dependencies by running `composer update --dry-run` +(it outputs what it would do to the dependencies, without applying the changes). + +When finished, run `git add composer.json` and commit. + +### C. Fix other conflicts + +Depending on the local changes you have done, you may get other conflicts on configuration files, kernel, etc. + +For each change, edit the file, identify the conflicting changes and resolve the conflict. +Run `git add <conflicting-file>` to add the changes. diff --git a/docs/snippets/update/notify_support.md b/docs/snippets/update/notify_support.md new file mode 100644 index 0000000000..73cda8e0e6 --- /dev/null +++ b/docs/snippets/update/notify_support.md @@ -0,0 +1,5 @@ +## Notify support + +Please tell support that you have updated your installation. They will update your support portal to match the new version. +This ensures that you receive notifications about new maintenance releases and security advisories for the correct version. +You can contact support at support@ibexa.co or through your [Support portal](https://support.ibexa.co). diff --git a/docs/snippets/update/update_app.md b/docs/snippets/update/update_app.md new file mode 100644 index 0000000000..46626dc9e2 --- /dev/null +++ b/docs/snippets/update/update_app.md @@ -0,0 +1,14 @@ +At this point, you should have a `composer.json` file with the correct requirements and you can update dependencies. + +If you want to first test how the update proceeds without actually updating any packages, you can try the command with the `--dry-run` switch: + +``` bash +composer update --dry-run +``` + +Then, run `composer update` to update the dependencies. + +``` bash +composer update +``` + diff --git a/docs/snippets/update/vcl_configuration_for_fastly_v3.md b/docs/snippets/update/vcl_configuration_for_fastly_v3.md new file mode 100644 index 0000000000..77b066f4a8 --- /dev/null +++ b/docs/snippets/update/vcl_configuration_for_fastly_v3.md @@ -0,0 +1,13 @@ +If you use Fastly, deploy the most up-to-date VCL configuration. + +Locate the `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_main.vcl` file, make sure that it has been updated with the following changes, and upload it to your Fastly: + +- Add the following lines: + +``` vcl +if (req.restarts == 0 && resp.status == 301 && req.http.x-fos-original-url) { + set resp.http.location = regsub(resp.http.location, "/_fos_user_context_hash", req.http.x-fos-original-url); +} +``` + +- Move the `#FASTLY recv` macro call to a new location, right after the `Preserve X-Forwarded-For in all requests` section. diff --git a/docs/snippets/update/vcl_configuration_for_fastly_v4.md b/docs/snippets/update/vcl_configuration_for_fastly_v4.md new file mode 100644 index 0000000000..5994551241 --- /dev/null +++ b/docs/snippets/update/vcl_configuration_for_fastly_v4.md @@ -0,0 +1,14 @@ +If you use Fastly, deploy the most up-to-date VCL configuration. + +Locate the `vendor/ibexa/fastly/fastly/ez_main.vcl` file, +make sure that it has been updated with the following changes, and upload it to your Fastly: + +- Add the following lines: + +``` vcl +if (req.restarts == 0 && resp.status == 301 && req.http.x-fos-original-url) { + set resp.http.location = regsub(resp.http.location, "/_fos_user_context_hash", req.http.x-fos-original-url); +} +``` + +- Move the `#FASTLY recv` macro call to a new location, right after the `Preserve X-Forwarded-For in all requests` section. diff --git a/docs/tutorials/enterprise_beginner/1_get_a_starter_website.md b/docs/tutorials/enterprise_beginner/1_get_a_starter_website.md index 6cf1e49010..b4b5a967c6 100644 --- a/docs/tutorials/enterprise_beginner/1_get_a_starter_website.md +++ b/docs/tutorials/enterprise_beginner/1_get_a_starter_website.md @@ -1,4 +1,9 @@ -# Step 1 — Get a starter website [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Start the tutorial by getting a clean installation of Ibexa Experience and preparing initial content. +edition: experience +--- + +# Step 1 — Get a starter website !!! tip @@ -6,13 +11,13 @@ To set up the starter website, you need to follow these steps: -## Get a clean [[= product_name_exp =]] installation +## Get a clean [[= product_name =]] installation To begin the tutorial, you need a clean installation of [[= product_name_exp =]]. Get it by following the [Install [[= product_name =]]](../../getting_started/install_ez_platform.md) guide. -## Create Content Types +## Add Content Types Log in to the Back Office – add `/admin` to your installation's address (`<yourdomain>/admin`) and log in using `admin` as the login and `publish` as the password. In Admin go to Content types tab and (under the Content category) create two Content Types with the following settings: @@ -26,7 +31,7 @@ Log in to the Back Office – add `/admin` to your installation's address (`<y |------------|-------------------|---------------------|----------|------------|--------------| | Text line | Name | `name` | yes | yes | yes | | Text line | Short Description | `short_description` | yes | yes | yes | -| Image | Photo | `photo` | yes | no  | no  | +| Image Asset | Photo | `photo` | yes | no  | no  | | RichText | Full Description | `description` | yes | yes | yes | ### Tip @@ -47,19 +52,24 @@ Edit it to remove the Image Field that has a Content Relation (ezobjectrelation) | Field Type | Name | Identifier | Required | Searchable | Translatable | |------------|-------|------------|----------|------------|--------------| -| Image | Image | `image` |yes   |no | yes | +| Image Asset | Image | `image` |yes   |no | no | ![New image Field in the Article Content Type](img/enterprise_tut_image_in_article_ct.png) +!!! note "Alternative text requirement" + + It is recommended that you require an alternative text for the image. + To do this, select the "Alternative text is required" checkbox. + ## Add template, configuration and style files !!! tip - For an introduction on how to use templates in [[= product_name =]], take a look at the [Building a Bicycle Route Tracker in [[= product_name =]] tutorial](../platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md) + For an introduction on how to use templates in [[= product_name =]], take a look at the [Building a Bicycle Route Tracker in [[= product_name =]] tutorial](../platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md). -First, delete the `config/packages/ezplatform_welcome_page.yaml` file to remove the welcome page. +First, to remove the welcome page, go to `config/packages/` and delete the `ezplatform_welcome_page.yaml` file. -Place the [`pagelayout.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/pagelayout.html.twig) and [`pagelayout_menu.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/pagelayout_menu.html.twig) files in the `templates` folder. Create a new folder, called `full`, under `templates`. Place further template files in it: +Place the [`pagelayout.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/pagelayout.html.twig) and [`pagelayout_menu.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/pagelayout_menu.html.twig) files in the `templates` folder. Create a new folder, called `full`, in `templates`. Place further template files in it: - [`article.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/full/article.html.twig) - [`dog_breed.html.twig`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/templates/full/dog_breed.html.twig) @@ -76,7 +86,11 @@ In the `assets` folder in the project root: - create a `css` folder and add the following stylesheet: [`style.css`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/assets/css/style.css) to it - create an `images` subfolder and add the [`header.jpg`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/assets/images/header.jpg) file to it -Replace the `webpack.config.js` file in the project root folder with the [provided file](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/webpack.config.js). +In the `webpack.config.js` file in the project root folder, add the following line after `Encore.addEntry('app', './assets/app.js');`: + +``` js +Encore.addStyleEntry('tutorial', [path.resolve(__dirname, './assets/css/style.css')]); +``` Next, in the terminal run the commands: @@ -91,7 +105,7 @@ php bin/console cache:clear In the `src` folder create a `QueryType` subfolder and add [`QueryType/MenuQueryType.php`](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/blob/v3-step1/src/QueryType/MenuQueryType.php) to it. -This file takes care of displaying the top menu (read up on it [in the documentation](../../guide/controllers.md#query-controller)). +This file takes care of displaying the top menu (read up on it [in the documentation](../../guide/content_rendering/queries_and_controllers/content_queries.md#query-types)). It is not the scope of this tutorial and we won't go here into detail on how it works. This is what the structure of the new and modified files should look like (excluding pre-existing files): @@ -102,7 +116,17 @@ This is what the structure of the new and modified files should look like (exclu Now return to the Back Office and create some content for your website. -First, make three Folders under the `Content/Content structure` tab. Call them 'All Articles', 'Dog Breed Catalog' and 'All Tips'. Remember that you save and close them by using the 'Publish' button. +First, you are not implementing a shop in this tutorial, +so you can hide shop-related Content items from the project root. + +Go to **Content** -> **Content structure** and select "Ibexa Digital Experience Platform". +In the **Sub-items** section, select all the current sub-items +and click the **Hide selected Locations** icon: + +![Hiding Content items you do not need](img/enterprise_tut_hide_content.png) + +Next, under "Ibexa Digital Experience Platform", create three Folders. Call them 'All Articles', 'Dog Breed Catalog' and 'All Tips'. +Remember that you save and close them by using the 'Publish' button. Next, create a few Content items of proper Content Types in each of these folders: @@ -110,8 +134,6 @@ Next, create a few Content items of proper Content Types in each of these folder - 3 Dog Breeds - 3 Tips -Finally, remove the "Ibexa Platform" folder. You will not need it for this tutorial. - ### Add images When you need an image, you can use one from [this image pack](img/photos.zip). diff --git a/docs/tutorials/enterprise_beginner/2_prepare_the_landing_page.md b/docs/tutorials/enterprise_beginner/2_prepare_the_landing_page.md index 919f9990c8..b900176330 100644 --- a/docs/tutorials/enterprise_beginner/2_prepare_the_landing_page.md +++ b/docs/tutorials/enterprise_beginner/2_prepare_the_landing_page.md @@ -1,4 +1,9 @@ -# Step 2 — Prepare the Page [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Learn how to build a Page with a custom layout. +edition: experience +--- + +# Step 2 — Prepare the Page !!! tip @@ -20,7 +25,7 @@ Go to the front page of your website (`<yourdomain>`). You can see that it looks `yarn encore <dev|prod>` -Log in to the Back Office. Go to Content > Content Structure. +Log in to the Back Office. Go to **Content** -> **Content Structure**. The **Ibexa Digital Experience Platform** Content item is the first page that is shown to the visitor. Here you can check what Content Type it belongs to: it is a Landing Page. @@ -40,11 +45,11 @@ The design for the website you are making needs a layout with two zones: a main Preparing a new layout requires three things: -- **entry in configuration** -- **thumbnail**  -- **template**  +- entry in configuration +- thumbnail +- template -#### Create entry in configuration +#### Add entry in configuration First create a new file for layout configuration, `config/packages/ezplatform_page_fieldtype.yaml`: @@ -68,7 +73,7 @@ ezplatform_page_fieldtype: !!! tip - For a detailed description of creating a Page layout, see [Page layouts](../../guide/page_rendering.md#page-layouts). + For a detailed description of creating a Page layout, see [Page layouts](../../guide/content_rendering/render_content/render_page.md#render-a-layout). The `sidebar` (line 3) is the internal key of the layout. `name` (line 5) is displayed in the interface when the user selects a layout. The `thumbnail` (line 7) points to an image file that is shown when creating a new Landing Page next to the name. @@ -132,7 +137,7 @@ The above template creates two columns and defines their widths. Each column is !!! note A zone in a layout template **must have** the `data-ez-zone-id` attribute (lines 2 and 19). - A block **must have** the `data-ez-block-id` attribute (lines 7 and 24) + A block **must have** the `data-ez-block-id` attribute (lines 7 and 24). With these three elements: configuration, icon and template, the new layout is ready to use. @@ -151,7 +156,7 @@ The empty zones you defined in the template will be visible in the editor. ![Empty page with new layout](img/enterprise_tut_new_layout.png) Publish the Home Page. You will notice that it still has some additional text information. -This is because the looks of a Page are controller by two separate template files, and you have only prepared one of those. +This is because the looks of a Page are controlled by two separate template files, and you have only prepared one of those. The `sidebar.html.twig` file defines how zones are organized and how content is displayed in them. But you also need a general template file that will be used for every Page, regardless of its layout. diff --git a/docs/tutorials/enterprise_beginner/3_use_existing_blocks.md b/docs/tutorials/enterprise_beginner/3_use_existing_blocks.md index 09de7b1c79..d7d8bc7928 100644 --- a/docs/tutorials/enterprise_beginner/3_use_existing_blocks.md +++ b/docs/tutorials/enterprise_beginner/3_use_existing_blocks.md @@ -1,4 +1,9 @@ -# Step 3 — Use existing blocks [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Learn how to use and customize built-in Page blocks. +edition: experience +--- + +# Step 3 — Use existing blocks !!! tip @@ -49,7 +54,7 @@ blocks: name: Content List ``` -The template makes use of an [image variation](../../guide/images.md) (line 10). +The template makes use of an [image variation](../../guide/images/images.md) (line 10). It is the thumbnail of the Dog Breed image that will be displayed in the block. To configure this variation, open the `config/packages/image_variations.yaml` file and add the following code under the `image_variations` key: diff --git a/docs/tutorials/enterprise_beginner/4_create_a_custom_block.md b/docs/tutorials/enterprise_beginner/4_create_a_custom_block.md index e28f3a2f31..a600b8c5e0 100644 --- a/docs/tutorials/enterprise_beginner/4_create_a_custom_block.md +++ b/docs/tutorials/enterprise_beginner/4_create_a_custom_block.md @@ -1,4 +1,9 @@ -# Step 4 — Create a custom block [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Try creating a custom Page block with specific logic. +edition: experience +--- + +# Step 4 — Create a custom block !!! tip @@ -78,90 +83,90 @@ use eZ\Publish\API\Repository\SearchService; class RandomBlockListener implements EventSubscriberInterface { -/** @var \eZ\Publish\API\Repository\ContentService */ -private $contentService; - -/** @var \eZ\Publish\API\Repository\LocationService */ -private $locationService; - -/** @var \eZ\Publish\API\Repository\SearchService */ -private $searchService; - -/** - * @param \eZ\Publish\API\Repository\ContentService $contentService - * @param \eZ\Publish\API\Repository\LocationService $locationService - * @param \eZ\Publish\API\Repository\SearchService $searchService - */ -public function __construct( - ContentService $contentService, - LocationService $locationService, - SearchService $searchService -) { - $this->contentService = $contentService; - $this->locationService = $locationService; - $this->searchService = $searchService; -} + /** @var \eZ\Publish\API\Repository\ContentService */ + private $contentService; -/** - * @return array The event names to listen to - */ -public static function getSubscribedEvents() -{ - return [ - BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender', - ]; -} + /** @var \eZ\Publish\API\Repository\LocationService */ + private $locationService; -/** - * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ -public function onBlockPreRender(PreRenderEvent $event): void -{ - $blockValue = $event->getBlockValue(); - $renderRequest = $event->getRenderRequest(); + /** @var \eZ\Publish\API\Repository\SearchService */ + private $searchService; - $parameters = $renderRequest->getParameters(); + /** + * @param \eZ\Publish\API\Repository\ContentService $contentService + * @param \eZ\Publish\API\Repository\LocationService $locationService + * @param \eZ\Publish\API\Repository\SearchService $searchService + */ + public function __construct( + ContentService $contentService, + LocationService $locationService, + SearchService $searchService + ) { + $this->contentService = $contentService; + $this->locationService = $locationService; + $this->searchService = $searchService; + } - $contentIdAttribute = $blockValue->getAttribute('parent'); - $location = $this->loadLocationByContentId((int) $contentIdAttribute->getValue()); - $contents = $this->findContentItems($location); - shuffle($contents); + /** + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return [ + BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender', + ]; + } - $parameters['randomContent'] = reset($contents); + /** + * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event + * + * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + */ + public function onBlockPreRender(PreRenderEvent $event): void + { + $blockValue = $event->getBlockValue(); + $renderRequest = $event->getRenderRequest(); - $renderRequest->setParameters($parameters); -} + $parameters = $renderRequest->getParameters(); -/** - * @param Location $location - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ -private function findContentItems(Location $location): array -{ - $query = new Query(); - $query->query = new Criterion\LogicalAnd( - [ - new Criterion\ParentLocationId($location->id), - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - ] - ); - - $searchHits = $this->searchService->findContent($query)->searchHits; - - $contentArray = []; - foreach ($searchHits as $searchHit) { - $contentArray[] = $searchHit->valueObject; + $contentIdAttribute = $blockValue->getAttribute('parent'); + $location = $this->loadLocationByContentId((int) $contentIdAttribute->getValue()); + $contents = $this->findContentItems($location); + shuffle($contents); + + $parameters['randomContent'] = reset($contents); + + $renderRequest->setParameters($parameters); } - return $contentArray; -} + /** + * @param Location $location + * + * @return \eZ\Publish\API\Repository\Values\Content\Content[] + * + * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + */ + private function findContentItems(Location $location): array + { + $query = new Query(); + $query->query = new Criterion\LogicalAnd( + [ + new Criterion\ParentLocationId($location->id), + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + ] + ); + + $searchHits = $this->searchService->findContent($query)->searchHits; + + $contentArray = []; + foreach ($searchHits as $searchHit) { + $contentArray[] = $searchHit->valueObject; + } + + return $contentArray; + } /** * @param int $contentId diff --git a/docs/tutorials/enterprise_beginner/5_create_newsletter_form.md b/docs/tutorials/enterprise_beginner/5_create_newsletter_form.md index 3e2c565bee..0bdd639b57 100644 --- a/docs/tutorials/enterprise_beginner/5_create_newsletter_form.md +++ b/docs/tutorials/enterprise_beginner/5_create_newsletter_form.md @@ -1,15 +1,22 @@ -# Step 5 — Create a newsletter form [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Learn how to create a sign-up form and how to view and manage its submissions. +edition: experience +--- + +# Step 5 — Create a newsletter form !!! tip You can find all files used and modified in this step on [GitHub.](https://github.com/ezsystems/ezplatform-ee-beginner-tutorial/tree/v3-master) The final step of this tutorial assists you in adding to the home page a Form block for signing up to a newsletter. +[[% include 'snippets/forms_caution.md' %]] + ### Add a Form block Start with creating a Form Content item. In the **Forms** panel, click **Create** and select **Form**. -Provide the title, e.g. "Sign up for Newsletter" and click **Build form**. +Provide the title, for example, "Sign up for Newsletter" and click **Build form**. In the Form Builder, add and configure (using the **Basic** and **Validation** tabs) the following form fields: @@ -39,7 +46,7 @@ It clearly differs from the page design, so you also need to customize the block ### Change the block template First, add a new template for the Form block to align it with the Random block design. -Create an `templates/blocks/form/newsletter.html.twig` file: +Create a `newsletter.html.twig` file in `templates/blocks/form/`: ``` html+twig hl_lines="1" <div class="row"> @@ -71,17 +78,17 @@ blocks: Now you have to apply the template to the block. Go back to editing the Page. -Edit the Form block again. +Edit the Form block again. In the **Design** tab, select the **Newsletter Form View** and click **Submit**. The block remains unchanged, but the results will be visible when you add CSS styling. -### Change the field template +### Change the field template At this point, you need to change the field template. This results in alternating the position and design of the Form fields. -Create a `templates/fields/form_field.html.twig` file: +Create a `form_field.html.twig` file in `templates/fields/`: ``` html+twig {% block ezform_field %} @@ -110,12 +117,12 @@ Clear the cache by running `bin/console cache:clear` and refresh the Page to see ### Configure the Form field -Before applying the final styling of the block, you need to configure the [CAPTCHA field](../../extending/extending_form_builder.md#captcha-field). -In `config/packages/gregwar_captcha.yaml`, under the `gregwar_captcha` key, add the following configuration: +Before applying the final styling of the block, you need to configure the [CAPTCHA field](../../guide/form_builder/forms.md#captcha-field). +In `config/packages`, add a `gregwar_captcha.yaml` file with the following configuration: ``` yaml gregwar_captcha: - # existing keys + as_url: true width: 150 invalid_message: Please, enter again. reload: true @@ -193,13 +200,13 @@ Refresh the Page and enter a couple of mock submissions. ### Manage the submissions You can view all submissions in the Back Office. -Go to **Forms** panel. From the Content Tree, select the Form and view the **Submissions** tab. +Go to **Forms** panel. From the Content Tree, select the Form and click the **Submissions** tab. There, after selecting submission(s), click **Download** or **Delete**. To see details about a submission, click **View**. ![Collect Form Submissions](img/enterprise_tut_form_collect_sub.png "Collect Form Submissions") -For more details, see [viewing form results.](https://doc.ezplatform.com/projects/userguide/en/latest/creating_content_advanced/#viewing-results) +For more details, see [viewing form results.](https://doc.ezplatform.com/projects/userguide/en/latest/creating_forms/#viewing-results) ## Congratulations! diff --git a/docs/tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md b/docs/tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md index 73cdeeb022..a72f362ab2 100644 --- a/docs/tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md +++ b/docs/tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md @@ -1,12 +1,17 @@ -# [[= product_name_exp =]] Beginner Tutorial - It's a Dog's World [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] +--- +description: Go through a Page and Form tutorial to learn how to create modular Sites and how to manage forms and their submissions. +edition: experience +--- -This tutorial is a step-by-step guide to building a website with [[= product_name_exp =]]. -It focuses on creating a front page using a feature called **Page Builder**.  +# Page and Form tutorial + +This tutorial is a step-by-step guide to building an advanced website with [[= product_name_exp =]]. +It focuses on creating a front page using a feature called **Page Builder**. ### Intended audience This tutorial is intended for users who have basic knowledge of [[= product_name =]]. -Ideally, you should be familiar with the concepts covered in the [Building a Bicycle Route Tracker in [[= product_name =]] tutorial](../platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md). +Ideally, you should be familiar with the concepts covered in the [Beginner tutorial](../platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md). ### Learning outcomes diff --git a/docs/tutorials/enterprise_beginner/img/diagram_source/enterprise_tut_file_structure.xml b/docs/tutorials/enterprise_beginner/img/diagram_source/enterprise_tut_file_structure.xml index 15793083f1..c267c3a12d 100644 --- a/docs/tutorials/enterprise_beginner/img/diagram_source/enterprise_tut_file_structure.xml +++ b/docs/tutorials/enterprise_beginner/img/diagram_source/enterprise_tut_file_structure.xml @@ -1 +1 @@ -<mxfile modified="2019-07-11T07:24:59.068Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" etag="tcVvoTfO5Q_dRKF2gQHe" version="10.9.4" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7V1bc5s4FP41fowHhITgMXba7nbabbvdbXefMsQoNi02XhnHpr9+hbkLxXFAApoy05nCAUtC59O56RxlYszXxzfU2a7eBy7xJ0BzjxPjZgKADgGYxP80N0ooGMOEsKSem75UED57P0hK1FLq3nPJrvJiGAR+6G2rxEWw2ZBFWKE5lAaH6mv3gV/tdessSY3weeH4depXzw1XCdVCWkH/jXjLVdazrqVP1k72ckrYrRw3OJRIxquJMadBECZX6+Oc+PHkZfOS/O71I0/zgVGyCS/5Qfjh++bPL3sMP0TRJ7r/+u73yLrCSSsPjr9PPzgdbBhlM0A27nU8kexuE2wYcbYK1z6709llfRDpuIhbmdd0SG9IsCYhjdgLh2I2s8lclSYyo1HiO6H3UOWGkzJ1mTeX9/Ax8NhIgJYC0E6biaq3WQO7YE8XJP1Nee64ZjA620zo0CUJa82waXOi0mvb+IVd+Q12UZqUgnRi3TPYaLVkIzl64T/s+kqbajpMCf8ygjZFKL29iedCy26i0s1HQj32BYSmtEdBkUz3me8A1qDQky/mlO/5Wm6JH9wWPxf1AkzjOYPSq/KDXST9SwWqbgqQavqMbbP74DS4ArLmf/sge3C1O2mGa/aCDrfH4iG7Wsb/O7sdiecmaYuNLWkueVhbDCE5htUVQAnrwbk7vRBj2NmHQdLn6bHje8sNu14wSMc4nz0QGnpMTVynD9ae68Y/nqVMYt2g2QTdxOP3fH8e+AEtlt4upMF3whFpsN+4xE0HEI//tbP2/Jg9c4Yzj/ULtD/IIX34Of0cgSJISfEYyfHsOsrEpF6BgpXis7TKsGCV8auhvKAqEu7ZKBGpJRkoWexGiDSEiA7NqrjQ6yCBnYLEVgQSb82MusdxkpDv6IicpsjB1hT1K2AAbGkwPTotQ7FcUNVy0fkmLrVcdJ23EriGerd9gcj4laIsgs29txzlgCIjAyC9bzFgtPWbzs7f4GUE5JZ2U++YlxG21VJEXCiKbLkOi/52GWnW9eu75f3f4dtZ+PZwNRN61jKEy9ZZfC+bGWU5MhqojcwMTuUBLJIvliL5IgSPDhSh50DuYgDdks0ioGQaOQwDI5AkAQlwUR9DwwIg6d0iSWSwykDSg0cOuxFAagEE665yHlrsBj6qAio7Qh+8BRkRJFuXVW1lCGyRCAJdYij/CiXGMpsrGsVbENrUgEZGiLcgYi2e3hZbEKe7qHx38SZEYo2e8QqGtgnB2TUQ5nZNW0OduWR8U4ps9WLQSq31DJIvNq5TUy261RgMJnqqqS5jO2J+GiplztCZrdt2lUNWUwfd5Nc9UrXueVBZHfjoQNWm4mkn4PbBoR5jY7AZrRzFdrLQY8/DUt1YOS89I6bunGBpGqTeVO8axGhrtZZTY0CWC5OkxuCJkswY8XeYg0IRl1eFLNAURLWkFVUmKdcR0lAXFqmhSjft6GLcmpKlibitKWiLFJGq7AYhbqAou2F4cmsg4kgv5E+2vHHNxL1crWG+sbrLLc1ervWFO5FL2cRLl0tz9hINfD9eraOhLCuDpiqfEBAayp2GlJHIsJIBoPdksy9ANN2utiOQpAEJVyMKyBQCSVUShhhILz4XC4LqpBuNjWVdw0811bvHhVBLfv7cMTuO2abePFrPM1uzVVkhXE/FoNXaIKp8o097QqO/oi0ZNYc0zcHtSJqaSHN0GqpDLz9UZ3DOgQlhc6/Gerqx/pXHL52RKWA4NiQyHKhzY2t95QNXq0JUFYG8CxanPZ/5yvNdhpdcpYwOidwtIA42hkitMG3ToV4xVYVGYs92xFE3OBI7tt3iSFWEJCTrLdMzY42aPPxAbivTMvu1bU2gCDv3e3/MYJAGG5QVEubVcaLSRpHFqA45opQpKRX2MeN8Mo2ZPw0PRWXcCKPWMMIcjEzYt+5SVW/gBsvbO0qIO+KoAxzZuI6jTutWTFEwWE793JL52lGwD2/XzKwe0aReuRmGqA4KGJ3iSWl8qFSEAJA5qRQhgEmDIoRyEoYGJ+UkDBMrOljp7DosFz+cdVoGEhGrYZA/OunSaBi0qjufOuQakhQJg5ZVHTDqIAHabJss9CiwhgIDXrE1D4siw36qqd6j4FgUgVIh5QwgUchpVfkGupRvGF8q38CwgM2JJYC1xsBG1TUCJNTzPNJTJjtrg1Yq5bAoJvKipByv7DRbjrKrNdS/hGtb2DV0XvIBxTw579m85Mo/cVtWXtZNNl61K1pVAkh8zC+ho1PIO4Xs3ve2X55aQ80jDrk3WI44dBr/xKqOOgi97Qio7gGVxbN7O/cAtw05DF1V1aYcNs5P5Z0rQVO9mx5W24rAn46fmimNn/Wm+uenqg2vIsQ8yn310WVg93/KltW2FCEPrZyq7sqxFW2ax5Oln2LzMwQ6IFcxrMPGgY6aa2UM7+ASq20RxNCVDM8Eo+nhobzjqejwUL4bo5PDQ1X5t+n5j9PkiOLptzETTFkmmGH1n0lota10+LWKwTGfRZ/n0zw7hx7xee26wTFZVgZ9vad00GollKrzjU8/m5b+3sZoMbeVTHlZbgYQVI+1dZqkmnU2iqVmYgmAWkFnC8Gk7ky3Wl/FwJUKJ1sUqZEhnFbEibcHvm1Hf16ZdGKwUXhGBbst/nJigrfi708ar/4H</diagram></mxfile> \ No newline at end of file +<mxfile modified="2021-08-16T09:31:24.174Z" host="www.draw.io" agent="5.0 (Macintosh)" etag="U4ftiq1Xu7UYg5lmiuUu" version="14.9.6" type="device"><diagram id="I6n2ew1ox3OroGUpLBHz" name="Page-1">7V1bc5s4FP41fowHCcTlsUk33e200+52t919yhCj2DQYsQLHZn/9CpurUBoHIy4dvSQg4EgWn75z0ZFY6DfbwzvqRpuPxMPBAmreYaG/XUAIDGiyf1lJeiqxioI19b38pqrgi/8fzgu1vHTnezhu3JgQEiR+1CxckTDEq6RR5lJK9s3bHkjQrDVy17hV8GXlBu3Sb76XbE6lNtKq8l+xv94UNQMtv7J1i5vzgnjjemRfK9J/Weg3lJDkdLQ93OAg67yiX07P3T5ztWwYxWFyzgPJp8fwj687y/iUpr/T3bcPv6X2lXWS8uQGu/wH541N0qIHcOi9yTqSnYUkZIXXm2QbsDPADh9ImNy6Wz/IXu+e0MfsWTeM80tfyI6uMjmbJGEvDCL9DfvDmpj9yW6Il2tC1gF2Iz9ersj2eGEVH2+9fTgJZoe5aASv68LzNmrtnsg7B3uNl5v3yztMtjihTLC2r15p8UY3tbdZlFEcuIn/1ISEmyNrXYora/hMfNYSqOWjwMnFpM3TQkB87KT8mfoL5MRY6IdiEpeucdISw96dm9Zui7Ib4vod7KDWKVXRET+vwJJ9IZbwwU/+ZsdX2lIDRl7wT/aClwjlp28Pxxeen6S1k8+Y+uwXYJqXTRuZcVH9c50J7UlBuKS1HHwlq10IYutSEJ9VCzT11zQKNJmUHZzq73W0AFMwXMwgySHUGDfmvztSXLiKjzqSoZUNkuhQXWRH6+y/G8c465uTrMUJzcXF1ohM8CFpDkOKWQ3ufQ5gJm+XkFOdx8tu4K9DdrxikM4G2/UTponPFOab/MLW97zs4ev8JbFq0PUCvc3a7wfBDQkIrcZ/nFDyiLlCSnahh70zR3I52CSP7GcHc9YD+LD40SgtNIHZAJqdo782hk3BGObHWn24Nkj81RgUqf8+MHjsTQXA6QEQGE0EAtCGoDEoBB1JEPS3zDR/HoWn4nuqcDkNXCLQxKVlL9G45AiNC+3ZaVugUzEsUdOwBLyIcw1LAHgjjhM0un8ERQ5SL9qWhA/+WlHdPKiOswEhAmMznX6p566Y7hymMziC6hoH4pnOsS8kujMJ1enXKwbv16lmv7m9Xz/8lby/Tt7vr66FMaQ+KDJyV491e7DOhspPmR5J8mYBtEQsaUtiSSE0AZSEzT2+z+B5h8MVoXiZugxhCqazgCnUmhypa5YApsAaFKciv6UPnD75eB8reM4ZnkY72uMMik1ZAccY0yd/hRU85wRPgJqukAEdEXuCIRFa/goJvhDrKZpmM5zaUjf0oiCb4cyMm/y0muE8nqX1M9Ec58jO1cnZWLwc9ZiKF8YZloZRGpaX+mHQaYmS5IpVjZbqjBXQV3EBmYiE3NS6DuzOiDTRS6KGjIKKQaUrUA1Ac47TRILdNdhk8iSHZJEcD157gHgTlJWFcZx+vHtyqc9eIwmVYTonwxRy+lYXRp+ArCC9GKkqVXMMZWxYvSnjtqjRlbF+qbdTz9mERZLmKWfTWvy8KZvizjQnBWUu6xjZsCuSW9mUsvwcriKkoSHcHF2WDRDTlZqUn4fGd5rpR4YjUviyEuOEqDREiXGKmqdsPICKYgsGs1re0vnmg8ULa4eqenO9WnVZg1Bv0fG9U+8Nu4mSIMBU+VzzYGA+AxTB0Wf8kcg67gOdH3G4qxC6jDaRQulMUGo342rIFEYGREpGHkxVovIg6Xuw+eb1zv4U0KyXRI0eGUBIgWp4UJmg+2wkDyrNkWUtcjVVjZZrK8py03/fYZr+mUZYKeGZKGHOVDQ1kRIeNLEJqej8INF5nfNTTcPo7mDbLwsbXw+rtRnjAMvSewQWlBe5adVVNlyuNpa1bvcDWR1nzG82fuAxvJTaWbnJ89HQpaItQKmLNDTUh1TRpqxYYxbNUSidI0oBh9IJBHNMWTHHBG8jpiPVlghzQadhNdFpmy1oDhoNN6EkZD7sApUZNxNQItiMfwMo2qdj0IlyU5RQ3MtGWhksArzMoLVM9tUOCwqkEwcpN0kDTKOt1Ye1PWWtyfTI+u6eYuwplM4epY41tu0pmvPpZ/eFNQ7clOySuy1zlhRWZ4dVnVtOoovWuZfJ08OgVWJktLZSEyJz0VipCRcdVmrWk/E0Y1FPxjOtbpvbTj0dumCT+grRH7q8E4kFo2Ljt3LxR8elUwZP8AYnqKcYsGHbzQajARZOmZemp6p5iXOwaDWxCLrPSvAULhA1+mSXJQrR9k/pOuyR0bUmmcOfk8wt61wyh9MaQBwHQ0vrPIBQcyzCHhZXP1OToT3TaKmUbokCfIrSZZsXmtOPedESND6dq6X+AwDK4NaZlp9qeDWguK1RrEvxdF41RXvlcpus5MHsg0KYqohGK6IR+NHX2jCedHSDNxKK2HA9EgeHjG1YsvZJS/xIYXXeWOWs0GL6q47VQeNwlspQHCESAI3Oy0/4SIBA1Oimoy1xGz4FqmdBpZm9gaotanxQycoUqOa3lHKd39QWNwYc4RbOg+71VOj47ns/FMHO4+YP9Wintiyns1670+h06XRuUUmD25sHGJ2jki3vX5/elo+2Wks6QhxI7/p9ET5AI+n7Inw1+iDfF5EVB8o/4rA8fYtp+V2lWc9E/xvcpmO6LVwEMGhKq33palK199PgK/ktfgVhmXX66vWDiF/TVyar9r16sF1T3mi5JCzrK0/Hx5a1j88q52va5AtszhBG7cj7oB/PKWwmxbwzZl4IW1ufXMC98jY8b9VVNVwq/zqi4Gof/LvBbjYZ+j1S0a+5EDC/ty9AAuu3p61U2CklGWAqMFM32nwkHs7u+B8=</diagram></mxfile> \ No newline at end of file diff --git a/docs/tutorials/enterprise_beginner/img/enterprise_tut_empty_single_block.png b/docs/tutorials/enterprise_beginner/img/enterprise_tut_empty_single_block.png index 32da0ce62a..0d3aaeb009 100644 Binary files a/docs/tutorials/enterprise_beginner/img/enterprise_tut_empty_single_block.png and b/docs/tutorials/enterprise_beginner/img/enterprise_tut_empty_single_block.png differ diff --git a/docs/tutorials/enterprise_beginner/img/enterprise_tut_file_structure.png b/docs/tutorials/enterprise_beginner/img/enterprise_tut_file_structure.png index 5f066a81d8..667cd2ab61 100644 Binary files a/docs/tutorials/enterprise_beginner/img/enterprise_tut_file_structure.png and b/docs/tutorials/enterprise_beginner/img/enterprise_tut_file_structure.png differ diff --git a/docs/tutorials/enterprise_beginner/img/enterprise_tut_hide_content.png b/docs/tutorials/enterprise_beginner/img/enterprise_tut_hide_content.png new file mode 100644 index 0000000000..ee51b654ba Binary files /dev/null and b/docs/tutorials/enterprise_beginner/img/enterprise_tut_hide_content.png differ diff --git a/docs/tutorials/enterprise_beginner/img/enterprise_tut_image_in_article_ct.png b/docs/tutorials/enterprise_beginner/img/enterprise_tut_image_in_article_ct.png index 04e22fbba1..04835e5d39 100644 Binary files a/docs/tutorials/enterprise_beginner/img/enterprise_tut_image_in_article_ct.png and b/docs/tutorials/enterprise_beginner/img/enterprise_tut_image_in_article_ct.png differ diff --git a/docs/tutorials/extending_admin_ui/1_creating_a_dashboard_tab.md b/docs/tutorials/extending_admin_ui/1_creating_a_dashboard_tab.md deleted file mode 100644 index 95b829d07c..0000000000 --- a/docs/tutorials/extending_admin_ui/1_creating_a_dashboard_tab.md +++ /dev/null @@ -1,136 +0,0 @@ -# Step 1 - Creating a My dashboard tab - -**My dashboard** is the front page that you visit after logging in to the Back Office. -You can also get to it from any other page by selecting the site logo in the top left corner. -By default, the **My dashboard** page contains following blocks: "My content" and "Common content", which list Content items and Media. - -![Unmodified dashboard](img/dashboard.png) - -In this step you will add a new tab to the "Common content" block in the dashboard. -This tab, called "Articles", will list ten most recently modified Content items of the Content Type `article`. - -!!! tip - - To be able to view the results of this step, create a few Content items of the type "Article". - -## Register a service - -First, add the following block to `config/services.yaml`. Place the block indented, under the `services` key: - -``` yml -App\Tab\Dashboard\Everyone\EveryoneArticleTab: - autowire: true - autoconfigure: true - public: false - tags: - - { name: ezplatform.tab, group: dashboard-everyone } -``` - -The tags indicate that this is a tab on the **My dashboard** page that will be placed in the "Common content" block. - -This configuration points to the `EveryoneArticleTab.php` file, which you now need to create. - -## Create a tab - -Create an `EveryoneArticleTab.php` file in `src/Tab/Dashboard/Everyone`: - -``` php hl_lines="17 47" -<?php - -namespace App\Tab\Dashboard\Everyone; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter; -use EzSystems\EzPlatformAdminUi\Tab\AbstractTab; -use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface; -use EzSystems\EzPlatformAdminUi\Tab\Dashboard\PagerContentToDataMapper; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use Pagerfanta\Pagerfanta; -use Symfony\Contracts\Translation\TranslatorInterface; -use Twig\Environment; - -class EveryoneArticleTab extends AbstractTab implements OrderedTabInterface -{ - /** @var PagerContentToDataMapper */ - protected $pagerContentToDataMapper; - - /** @var SearchService */ - protected $searchService; - - public function __construct( - Environment $twig, - TranslatorInterface $translator, - PagerContentToDataMapper $pagerContentToDataMapper, - SearchService $searchService - ) { - parent::__construct($twig, $translator); - - $this->pagerContentToDataMapper = $pagerContentToDataMapper; - $this->searchService = $searchService; - } - - public function getIdentifier(): string - { - return 'everyone-article'; - } - - public function getName(): string - { - return 'Articles'; - } - - public function getOrder(): int - { - return 300; - } - - public function renderView(array $parameters): string - { - $page = 1; - $limit = 10; - - $query = new LocationQuery(); - - $query->sortClauses = [new SortClause\DateModified(LocationQuery::SORT_DESC)]; - $query->query = new Criterion\LogicalAnd([ - new Criterion\ContentTypeIdentifier('article'), - ]); - - $pager = new Pagerfanta( - new ContentSearchAdapter($query, - $this->searchService - ) - ); - $pager->setMaxPerPage($limit); - $pager->setCurrentPage($page); - - return $this->twig->render('@ezdesign/ui/dashboard/tab/all_content.html.twig', [ - 'data' => $this->pagerContentToDataMapper->map($pager), - ]); - } -} -``` - -!!! tip - - The tab extends `AbstractTab`. - There are also [other tab types that you can extend](../../extending/extending_tabs.md). - -The tab also implements `OrderedTabInterface` (see line 17), which enables you to define the order in which the tab is displayed on the **My dashboard** page. -This is done using the `getOrder` method (see line 47). - -The rendering is done using the built-in `all_content.html.twig` template, -which ensures the tab looks the same as the existing tabs. - -## Check results - -!!! tip - - If you cannot see the results or encounter an error, clear the cache and reload the application. - -At this point you can go to the **My dashboard** page in the Back Office: select the site logo in the top left corner. -In the "Common content" block you can see the new "Articles" tab with the first ten articles in the Repository. - -![Articles tab in My dashboard](img/dashboard_articles_tab.png "Articles tab in My dashboard") diff --git a/docs/tutorials/extending_admin_ui/2_creating_a_content_list.md b/docs/tutorials/extending_admin_ui/2_creating_a_content_list.md deleted file mode 100644 index cea9e369d6..0000000000 --- a/docs/tutorials/extending_admin_ui/2_creating_a_content_list.md +++ /dev/null @@ -1,183 +0,0 @@ -# Step 2 - Creating a top menu item - -The next thing you will extend in this tutorial is the top menu. - -![Top menu](img/top_menu.png) - -You will add a "Content list" item under "Content". It will list all Content items existing in the Repository. -You will be able to filter the list by Content Types using a drop-down menu. - -## Add an event subscriber - -The first step is to add an event subscriber. - -Create a `MyMenuSubscriber.php` file in `src/EventSubscriber`. It will be registered automatically: - -``` php hl_lines="14 26" -<?php - -namespace App\EventSubscriber; - -use EzSystems\EzPlatformAdminUi\Menu\Event\ConfigureMenuEvent; -use EzSystems\EzPlatformAdminUi\Menu\MainMenuBuilder; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class MyMenuSubscriber implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return [ - ConfigureMenuEvent::MAIN_MENU => ['onMenuConfigure', 0], - ]; - } - - public function onMenuConfigure(ConfigureMenuEvent $event) - { - $menu = $event->getMenu(); - - $menu[MainMenuBuilder::ITEM_CONTENT]->addChild( - 'all_content_list', - [ - 'label' => 'Content List', - 'route' => 'all_content_list.list', - ] - ); - } -} -``` - -This subscriber subscribes to the `ConfigureMenuEvent::MAIN_MENU` event (see line 14). - -Line 26 points to the new route that you need to add to the routing file. - -## Add routing - -Add the following block to `config/routes.yaml`: - -``` yaml hl_lines="5" -all_content_list.list: - path: /all_content_list/{page} - defaults: - page: 1 - _controller: App\Controller\AllContentListController::listAction -``` - -## Create a controller - -As you can see in the code above, the next step is creating a controller that will take care of the article list view. - -In `src/Controller` create an `AllContentListController.php` file (it will be registered automatically): - -```php hl_lines="41" -<?php - -namespace App\Controller; - -use EzSystems\EzPlatformAdminUiBundle\Controller\Controller; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter; -use Pagerfanta\Pagerfanta; - -class AllContentListController extends Controller -{ - private $searchService; - - private $contentTypeService; - - public function __construct(SearchService $searchService, ContentTypeService $contentTypeService) - { - $this->searchService = $searchService; - $this->contentTypeService = $contentTypeService; - } - - public function listAction($page = 1) - { - $query = new LocationQuery(); - - $criterions = [ - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - ]; - - $query->query = new Criterion\LogicalAnd($criterions); - - $paginator = new Pagerfanta( - new LocationSearchAdapter($query, $this->searchService) - ); - $paginator->setMaxPerPage(8); - $paginator->setCurrentPage($page); - - return $this->render('list/all_content_list.html.twig', [ - 'totalCount' => $paginator->getNbResults(), - 'articles' => $paginator, - ]); - } -} -``` - -The highlighted line 41 indicates the template that will be used to display the list. - -## Add a template - -Finally, create an `all_content_list.html.twig` file in `templates/list`: - -``` html+twig -{% extends '@ezdesign/ui/layout.html.twig' %} - -{% block title %}{{ 'Content List'|trans }}{% endblock %} - -{%- block breadcrumbs -%} - {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ - { value: 'url.list'|trans|desc('Content List') } - ]} %} -{%- endblock -%} - -{%- block page_title -%} - {% include '@ezdesign/ui/page_title.html.twig' with { - title: 'url.list'|trans|desc('Content List'), - icon_name: 'article' - } %} -{%- endblock -%} - -{%- block content -%} - <section class="container my-4"> - <div class="ez-table-header"> - <div class="ez-table-header__headline">{{ "Content List"|trans }}</div> - </div> - <table class="table"> - <thead> - <tr> - <th>{{ 'Content name'|trans }}</th> - <th>{{ 'Content Type'|trans }}</th> - <th>{{ 'Modified'|trans }}</th> - <th>{{ 'Published'|trans }}</th> - </tr> - </thead> - <tbody> - {% for article in articles %} - <tr> - <td><a href={{ez_path(article)}}>{{ ez_content_name(article.contentInfo) }}</a></td> - <td>{{ article.contentInfo.contentTypeId }}</td> - <td>{{ article.contentInfo.modificationDate|ez_full_datetime }}</td> - <td>{{ article.contentInfo.publishedDate|ez_full_datetime }}</td> - </tr> - {% endfor %} - </tbody> - </table> - {{ pagerfanta(articles, 'ez') }} - </section> -{%- endblock -%} -``` - -## Check results - -!!! tip - - If you cannot see the results or encounter an error, clear the cache and reload the application. - -At this point you can go to the Back Office and under "Content" you will see the new "Content list" item. -Select it and you will see the list of all Content items in the Repository. - -![Content list with unfiltered results](img/content_list_unfiltered.png "Content list with unfiltered results") diff --git a/docs/tutorials/extending_admin_ui/3_filtering_the_content_list.md b/docs/tutorials/extending_admin_ui/3_filtering_the_content_list.md deleted file mode 100644 index fe08337aed..0000000000 --- a/docs/tutorials/extending_admin_ui/3_filtering_the_content_list.md +++ /dev/null @@ -1,211 +0,0 @@ -# Step 3a - Filtering query results - -In this step you will enable the content list to be filtered by Content Types. The following tutorial requires the completed [Step 2 - Creating a top menu item](2_creating_a_content_list.md). - -## Update routing - -First, modify the route to the content list page. -In `config/routes.yaml` add the `contentTypeIdentifier` parameter and set its default value: - -``` yml hl_lines="2 5" -all_content_list.list: - path: /all_content_list/{page}/{contentTypeIdentifier} - defaults: - page: 1 - contentTypeIdentifier: false - _controller: App\Controller\AllContentListController::listAction -``` - -## Modify the controller - -Introduce changes to `src/Controller/AllContentListController.php`, so that it takes the selected Content Type into account. - -First, provide the new `contentTypeIdentifier` parameter in the `listAction` function: -`public function listAction($contentTypeIdentifier = false, $page = 1)`. - -Add the following block inside the `listAction` function, after defining `$criterions`: - -``` php -if ($contentTypeIdentifier) { - $criterions[] = new Criterion\ContentTypeIdentifier($contentTypeIdentifier); -} -``` - -After the lines setting the `$paginator` parameters, add the following code block: - -``` php -$contentTypes = []; -$contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); -foreach ($contentTypeGroups as $group) { - $contentTypes[$group->identifier] = $this->contentTypeService->loadContentTypes($group); -} -``` - -Finally, provide the new parameter to `$this->render`, after `articles`: - -``` php -'contentTypes' => $contentTypes, -``` - -??? tip "Complete controller code" - - ``` php hl_lines="25 33 34 35 45 46 47 48 49 54" - <?php - - namespace App\Controller; - - use EzSystems\EzPlatformAdminUiBundle\Controller\Controller; - use eZ\Publish\API\Repository\SearchService; - use eZ\Publish\API\Repository\ContentTypeService; - use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - use eZ\Publish\API\Repository\Values\Content\LocationQuery; - use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter; - use Pagerfanta\Pagerfanta; - - class AllContentListController extends Controller - { - private $searchService; - - private $contentTypeService; - - public function __construct(SearchService $searchService, ContentTypeService $contentTypeService) - { - $this->searchService = $searchService; - $this->contentTypeService = $contentTypeService; - } - - public function listAction($contentTypeIdentifier = false, $page = 1) - { - $query = new LocationQuery(); - - $criterions = [ - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - ]; - - if ($contentTypeIdentifier) { - $criterions[] = new Criterion\ContentTypeIdentifier($contentTypeIdentifier); - } - - $query->query = new Criterion\LogicalAnd($criterions); - - $paginator = new Pagerfanta( - new LocationSearchAdapter($query, $this->searchService) - ); - $paginator->setMaxPerPage(8); - $paginator->setCurrentPage($page); - - $contentTypes = []; - $contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); - foreach ($contentTypeGroups as $group) { - $contentTypes[$group->identifier] = $this->contentTypeService->loadContentTypes($group); - } - - return $this->render('list/all_content_list.html.twig', [ - 'totalCount' => $paginator->getNbResults(), - 'articles' => $paginator, - 'contentTypes' => $contentTypes, - ]); - } - } - ``` - -## Change the template - -The last thing to do is to update the template by adding a drop-down menu for choosing Content Types. - -Add the following block to `templates/list/all_content_list.html.twig` -inside `<section class="container my-4">`: - -``` html+twig -<div class="my-4"> - <div class="dropdown"> - <button class="btn btn-secondary dropdown-toggle" type="button" id="contentTypeFilter" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - Content Type - </button> - <div class="dropdown-menu" aria-labelledby="contentTypeFilter"> - {% for group, types in contentTypes %} - <h6 class="dropdown-header">{{ group }}</h6> - {% for type in types %} - <a class="dropdown-item" href="{{ path('all_content_list.list', { 'contentTypeIdentifier': type.identifier }) }}">{{ type.name }}</a> - {% endfor %} - {% endfor %} - </div> - </div> -</div> -``` - -??? tip "Complete template code" - - ``` html+twig hl_lines="20 21 22 23 24 25 26 27 28 29 30 31 32 33 34" - {% extends '@ezdesign/ui/layout.html.twig' %} - - {% block title %}{{ 'Content List'|trans }}{% endblock %} - - {%- block breadcrumbs -%} - {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ - { value: 'url.list'|trans|desc('Content List') } - ]} %} - {%- endblock -%} - - {%- block page_title -%} - {% include '@ezdesign/ui/page_title.html.twig' with { - title: 'url.list'|trans|desc('Content List'), - icon_name: 'article' - } %} - {%- endblock -%} - - {%- block content -%} - <section class="container my-4"> - <div class="my-4"> - <div class="dropdown"> - <button class="btn btn-secondary dropdown-toggle" type="button" id="contentTypeFilter" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - Content Type - </button> - <div class="dropdown-menu" aria-labelledby="contentTypeFilter"> - {% for group, types in contentTypes %} - <h6 class="dropdown-header">{{ group }}</h6> - {% for type in types %} - <a class="dropdown-item" href="{{ path('all_content_list.list', { 'contentTypeIdentifier': type.identifier }) }}">{{ type.name }}</a> - {% endfor %} - {% endfor %} - </div> - </div> - </div> - <div class="ez-table-header"> - <div class="ez-table-header__headline">{{ "Content List"|trans }}</div> - </div> - <table class="table"> - <thead> - <tr> - <th>{{ 'Content name'|trans }}</th> - <th>{{ 'Content Type'|trans }}</th> - <th>{{ 'Modified'|trans }}</th> - <th>{{ 'Published'|trans }}</th> - </tr> - </thead> - <tbody> - {% for article in articles %} - <tr> - <td><a href={{ez_path(article)}}>{{ ez_content_name(article.contentInfo) }}</a></td> - <td>{{ article.contentInfo.contentTypeId }}</td> - <td>{{ article.contentInfo.modificationDate|ez_full_datetime }}</td> - <td>{{ article.contentInfo.publishedDate|ez_full_datetime }}</td> - </tr> - {% endfor %} - </tbody> - </table> - {{ pagerfanta(articles, 'ez') }} - </section> - {%- endblock -%} - ``` - -## Check results - -!!! tip - - If you cannot see the results or encounter an error, clear the cache and reload the application. - -At this point you should see a drop-down menu at the top of the content list. -Select a Content Type and the list will filter to show only Content items of this type. - -![Filtered content list](img/content_list_dropdown.png "Filtered content list") diff --git a/docs/tutorials/extending_admin_ui/3b_adding_edit_button_content_list.md b/docs/tutorials/extending_admin_ui/3b_adding_edit_button_content_list.md deleted file mode 100644 index e24c7aeb92..0000000000 --- a/docs/tutorials/extending_admin_ui/3b_adding_edit_button_content_list.md +++ /dev/null @@ -1,242 +0,0 @@ -# Step 3b - Adding an edit button - -In this step you will add an edit button to the content list. The following tutorial is requires the completed [Step 2 - Creating a top menu item](2_creating_a_content_list.md). - -## Modify the controller - -Introduce changes to `src/Controller/AllContentListController.php`, so that it enables content editing from the content list. - -First, inject the `EzSystems\EzPlatformAdminUi\Form\Factory\FormFactory` service into your controller. - -To do so, add the new `FormFactory $formFactory` parameter and the `$this->formFactory = $formFactory;` argument to the `__construct` function: - -```php hl_lines="4 8" -public function __construct( - SearchService $searchService, - ContentTypeService $contentTypeService, - FormFactory $formFactory -) { - $this->searchService = $searchService; - $this->contentTypeService = $contentTypeService; - $this->formFactory = $formFactory; -} -``` - -Next, provide a new use statement for `FormFactory` parameter: - -```php -use EzSystems\EzPlatformAdminUi\Form\Factory\FormFactory; -``` - -Create an underlying form for handling requests for content editing. -Add the following code line after e.g. the lines setting the `$paginator` parameters: - -```php -$editForm = $this->formFactory->contentEdit(); -``` - -Finally, provide the new parameter to `$this->render`. It can be added after e.g. `articles`: - -``` php -'form_edit' => $editForm->createView(), -``` - -??? tip "Complete controller code" - - ```php hl_lines="6 20 25 30 45 49" - <?php - - namespace App\Controller; - - use EzSystems\EzPlatformAdminUiBundle\Controller\Controller; - use EzSystems\EzPlatformAdminUi\Form\Factory\FormFactory; - use eZ\Publish\API\Repository\SearchService; - use eZ\Publish\API\Repository\ContentTypeService; - use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - use eZ\Publish\API\Repository\Values\Content\LocationQuery; - use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter; - use Pagerfanta\Pagerfanta; - - class AllContentListController extends Controller - { - private $searchService; - - private $contentTypeService; - - private $formFactory; - - public function __construct( - SearchService $searchService, - ContentTypeService $contentTypeService, - FormFactory $formFactory - ) - { - $this->searchService = $searchService; - $this->contentTypeService = $contentTypeService; - $this->formFactory = $formFactory; - } - - public function listAction($page = 1) - { - $query = new LocationQuery(); - $criterions = [ - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - ]; - $query->query = new Criterion\LogicalAnd($criterions); - $paginator = new Pagerfanta( - new LocationSearchAdapter($query, $this->searchService) - ); - $paginator->setMaxPerPage(8); - $paginator->setCurrentPage($page); - $editForm = $this->formFactory->contentEdit(); - return $this->render('list/all_content_list.html.twig', [ - 'totalCount' => $paginator->getNbResults(), - 'articles' => $paginator, - 'form_edit' => $editForm->createView(), - ]); - } - } - ``` - -## Change the template - -The last thing to do is to add the edit button to the content list template. -All the code blocks below should be added to `templates/list/all_content_list.html.twig`. - -First, add a `<th>{{ 'Edit'|trans }}</th>` header to the content list table inside `<section class="container my-4">`: - -```html+twig hl_lines="8" -<table class="table"> - <thead> - <tr> - <th>{{ 'Content name'|trans }}</th> - <th>{{ 'Content Type'|trans }}</th> - <th>{{ 'Modified'|trans }}</th> - <th>{{ 'Published'|trans }}</th> - <th>{{ 'Edit'|trans }}</th> - </tr> - </thead> -``` - -Next, add the edit button as a new `<td>` tag inside `<section class="container my-4">`: - -```html+twig -<td> - <button class="btn btn-icon mx-2 ez-btn--content-edit" - title="{{ 'dashboard.table.all.content.edit'|trans|desc('Edit Content') }}" - data-content-id="{{ article.contentInfo.id }}" - data-version-no="{{ article.contentInfo.currentVersionNo }}" - data-language-code="{{ article.contentInfo.mainLanguageCode }}"> - <svg class="ez-icon ez-icon-edit"> - <use xlink:href="{{ asset('bundles/ezplatformadminui/img/ez-icons.svg') }}#edit"></use> - </svg> - </button> -</td> -``` - -After that, add a hidden form for redirecting to the content edit page by adding the following code block inside `{%- block content -%}`, right under `<section class="container my-4">`: - -```html+twig -{{ form_start(form_edit, { - 'action': path('ezplatform.content.edit'), - 'attr': - { 'class': 'ez-edit-content-form'} -}) }} -{{ form_widget(form_edit.language, {'attr': {'hidden': 'hidden', 'class': 'language-input'}}) }} -{{ form_end(form_edit) }} -{% include '@ezdesign/content/modal/version_conflict.html.twig' %} -``` - -Finally, add a JavaScript block with js listeners at the end of the twig file: - -```html+twig -{% block javascripts %} - {{ encore_entry_script_tags('ezplatform-admin-ui-dashboard-js', null, 'ezplatform') }} -{%- endblock -%} - -``` - -??? tip "Complete template code" - - ``` html+twig hl_lines="30 40 41 42 43 44 45 46 47 48 49 50 57 58 59 60 61 62 63 64 67 68 69" - {% extends '@ezdesign/ui/layout.html.twig' %} - - {% block title %}{{ 'Content List'|trans }}{% endblock %} - - {%- block breadcrumbs -%} - {% include '@ezdesign/ui/breadcrumbs.html.twig' with { items: [ - { value: 'url.list'|trans|desc('Content List') } - ]} %} - {%- endblock -%} - - {%- block page_title -%} - {% include '@ezdesign/ui/page_title.html.twig' with { - title: 'url.list'|trans|desc('Content List'), - icon_name: 'article' - } %} - {%- endblock -%} - - {%- block content -%} - <section class="container my-4"> - <div class="ez-table-header"> - <div class="ez-table-header__headline">{{ "Content List"|trans }}</div> - </div> - <table class="table"> - <thead> - <tr> - <th>{{ 'Content name'|trans }}</th> - <th>{{ 'Content Type'|trans }}</th> - <th>{{ 'Modified'|trans }}</th> - <th>{{ 'Published'|trans }}</th> - <th>{{ 'Edit'|trans }}</th> - </tr> - </thead> - <tbody> - {% for article in articles %} - <tr> - <td><a href={{ez_path(article)}}>{{ ez_content_name(article.contentInfo) }}</a></td> - <td>{{ article.contentInfo.contentTypeId }}</td> - <td>{{ article.contentInfo.modificationDate|ez_full_datetime }}</td> - <td>{{ article.contentInfo.publishedDate|ez_full_datetime }}</td> - <td> - <button class="btn btn-icon mx-2 ez-btn--content-edit" - title="{{ 'dashboard.table.all.content.edit'|trans|desc('Edit Content') }}" - data-content-id="{{ article.contentInfo.id }}" - data-version-no="{{ article.contentInfo.currentVersionNo }}" - data-language-code="{{ article.contentInfo.mainLanguageCode }}"> - <svg class="ez-icon ez-icon-edit"> - <use xlink:href="{{ asset('bundles/ezplatformadminui/img/ez-icons.svg') }}#edit"></use> - </svg> - </button> - </td> - </tr> - {% endfor %} - </tbody> - </table> - {{ pagerfanta(articles, 'ez') }} - </section> - {{ form_start(form_edit, { - 'action': path('ezplatform.content.edit'), - 'attr': - { 'class': 'ez-edit-content-form'} - }) }} - {{ form_widget(form_edit.language, {'attr': {'hidden': 'hidden', 'class': 'language-input'}}) }} - {{ form_end(form_edit) }} - {% include '@ezdesign/content/modal/version_conflict.html.twig' %} - {%- endblock -%} - - {% block javascripts %} - {{ encore_entry_script_tags('ezplatform-admin-ui-dashboard-js', null, 'ezplatform') }} - {%- endblock -%} - ``` - -## Check results - -!!! tip - - If you cannot see the results or encounter an error, clear the cache and reload the application. - -At this point you should see the edit button beside each Content item in the content list. -Select the edit button to change your Content items. - -![Edit button in content list](img/content_list_edit.png "Edit button in content list") diff --git a/docs/tutorials/extending_admin_ui/4_adding_a_custom_tag.md b/docs/tutorials/extending_admin_ui/4_adding_a_custom_tag.md deleted file mode 100644 index df385237aa..0000000000 --- a/docs/tutorials/extending_admin_ui/4_adding_a_custom_tag.md +++ /dev/null @@ -1,102 +0,0 @@ -# Step 4 - Creating a custom tag - -The RichText Field contains some built-in elements you can use, such as an image or a table. -You can also add custom tags that will enable you to add additional elements to RichText Fields. - -In this step you will add a custom tag, which will enable you to create a special note box called a Factbox. - -See full documentation of custom tags in [Extending the Online Editor](../../extending/extending_online_editor.md#custom-tags). - -## Configure the custom tag - -First, create a file that will contain the configuration for the custom tags. -Add file `custom_tags.yaml` to `config/packages`: - -``` yaml hl_lines="5 25" -ezplatform: - system: - admin_group: - fieldtypes: - ezrichtext: - custom_tags: [factbox] - toolbars: - ezadd: - buttons: - factbox: - priority: 5 - factbox: - buttons: - ezmoveup: - priority: 40 - ezmovedown: - priority: 30 - ezcustomtagedit: - priority: 20 - ezblockremove: - priority: 10 -ezrichtext: - custom_tags: - factbox: - template: field_type/ezrichtext/custom_tag/factbox.html.twig - icon: '/bundles/ezplatformadminui/img/ez-icons.svg#warning' - attributes: - name: - type: string - required: true - style: - type: choice - required: true - default_value: light - choices: [light, dark] -``` - -The configuration first lists all custom tags that you have in the configuration (line 5) - in this case `factbox`. -`factbox` is then configured. Line 25 points to the template used to render the tag. -Then attributes of the custom tag are listed. These attributes can be set when adding the tag to a RichText Field. - -## Create a template - -Next, create the template that is referred to in the configuration. -In `templates/field_type/ezrichtext/custom_tag` add the following `factbox.html.twig` file: - -``` html+twig -<div class="ez-factbox ez-factbox--{{ params.style }}"> - <p>{{ params.name }}</p> - <div> - {{ content|raw }} - </div> -</div> -``` - -## Add labels - -Finally, add labels to the custom tag's editing interface. -Provide them in a `translations/custom_tags.en.yaml` file: - -``` yaml -ezrichtext.custom_tags.factbox.label: FactBox -ezrichtext.custom_tags.factbox.description: '' -ezrichtext.custom_tags.factbox.attributes.name.label: Name -ezrichtext.custom_tags.factbox.attributes.style.label: Style -ezrichtext.custom_tags.factbox.attributes.style.choices.light.label: Light style -ezrichtext.custom_tags.factbox.attributes.style.choices.dark.label: Dark style -``` - -## Check results - -!!! tip - - If you cannot see the results or encounter an error, clear the cache and reload the application. - -At this point you can go to the Back Office and start editing any Content with a RichText Field (e.g. a Folder or an Article). -Click Add (plus icon) on the left side of the RichText Field, and from the list of available tags select the FactBox tag icon (exclamation mark icon). - -![List of available tags](img/custom_tag_available_tags.png "Previewing a list of available tags") - -Provide a name and select a style. - -![Example of a Factbox name and style selection](img/custom_tag_factbox.png "Previewing a Factbox custom tag") - -You can now edit the content of the Factbox: - -![Example of a Factbox custom tag](img/custom_tag.png "Previewing a Content item with a Factbox custom tag") diff --git a/docs/tutorials/extending_admin_ui/extending_admin_ui.md b/docs/tutorials/extending_admin_ui/extending_admin_ui.md deleted file mode 100644 index 6d18583b0c..0000000000 --- a/docs/tutorials/extending_admin_ui/extending_admin_ui.md +++ /dev/null @@ -1,28 +0,0 @@ -# Extending Admin UI - -## Introduction - -This tutorial aims at providing a step-by-step guide on how to extend the [[= product_name =]] Back Office provided by [the [[= product_name =]] Admin UI bundle](https://github.com/ezsystems/ezplatform-admin-ui).  - -You will learn to extend a few points of the Back Office: - -- Add a tab in the **My dashboard** page that lists all Articles -- Add a menu item that lists all Content items filtered by Content Type -- Add an Edit button that allows you to edit Content items from the content list -- Add a custom tag to the Online Editor for creating note boxes - -To follow this tutorial you need to: - -- be comfortable with [[= product_name =]]'s content model (Content, Location, Content Type, etc.) -- have basic to intermediate level in PHP -- have a basic knowledge of Symfony's concepts (such as Controllers and routing) - -## Steps - -In this tutorial you will go through the following steps: - -- [1. Creating a My dashboard tab](1_creating_a_dashboard_tab.md) -- [2. Creating a top menu item](2_creating_a_content_list.md) -- [3a. Filtering query results](3_filtering_the_content_list.md) -- [3b. Adding an edit button](3b_adding_edit_button_content_list.md) -- [4. Creating a custom tag](4_adding_a_custom_tag.md) diff --git a/docs/tutorials/extending_admin_ui/img/content_list_dropdown.png b/docs/tutorials/extending_admin_ui/img/content_list_dropdown.png deleted file mode 100644 index 8c7ea43be5..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/content_list_dropdown.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/content_list_edit.png b/docs/tutorials/extending_admin_ui/img/content_list_edit.png deleted file mode 100644 index 6c63bea2d2..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/content_list_edit.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/content_list_unfiltered.png b/docs/tutorials/extending_admin_ui/img/content_list_unfiltered.png deleted file mode 100644 index 9708d83229..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/content_list_unfiltered.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/custom_tag.png b/docs/tutorials/extending_admin_ui/img/custom_tag.png deleted file mode 100644 index 61f23eeb1c..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/custom_tag.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/custom_tag_available_tags.png b/docs/tutorials/extending_admin_ui/img/custom_tag_available_tags.png deleted file mode 100644 index 2e7004b012..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/custom_tag_available_tags.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/custom_tag_factbox.png b/docs/tutorials/extending_admin_ui/img/custom_tag_factbox.png deleted file mode 100644 index f945c98cc9..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/custom_tag_factbox.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/dashboard.png b/docs/tutorials/extending_admin_ui/img/dashboard.png deleted file mode 100644 index 3816a638e1..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/dashboard.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/dashboard_articles_tab.png b/docs/tutorials/extending_admin_ui/img/dashboard_articles_tab.png deleted file mode 100644 index ff6aa4b321..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/dashboard_articles_tab.png and /dev/null differ diff --git a/docs/tutorials/extending_admin_ui/img/top_menu.png b/docs/tutorials/extending_admin_ui/img/top_menu.png deleted file mode 100644 index 38df2ac094..0000000000 Binary files a/docs/tutorials/extending_admin_ui/img/top_menu.png and /dev/null differ diff --git a/docs/tutorials/generic_field_type/1_implement_the_point2d_value_class.md b/docs/tutorials/generic_field_type/1_implement_the_point2d_value_class.md index 8511ff1637..6f015c08e6 100644 --- a/docs/tutorials/generic_field_type/1_implement_the_point2d_value_class.md +++ b/docs/tutorials/generic_field_type/1_implement_the_point2d_value_class.md @@ -1,8 +1,8 @@ -# Step 1 - Implement the Point 2D Value class - -!!! tip +--- +description: Learn how to create a Value class that stores the value of the Field. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_1). +# Step 1 - Implement the Point 2D Value class ## Project installation @@ -25,12 +25,14 @@ For more information about Field Type Value, see [Value handling](../../api/fiel According to the convention, the class representing Field Type Value should be named `Value` and should be placed in the same namespace as the Type definition. -The Point 2D Value class will contain: +[[= include_file('docs/snippets/simple_hash_value_caution.md') =]] + +The Point 2D Value class contains: - private properties, used to store the actual data - an implementation of the `__toString()` method, required by the Value interface -By default, the constructor from `FieldType\Value` will be used. +By default, the constructor from `FieldType\Value` is used. The Point 2D is going to store two elements (coordinates for point 2D): @@ -42,62 +44,16 @@ At this point, it does not matter where they are stored. You want to focus on wh `src/FieldType/Point2D/Value.php` should have the following properties: ```php -{ -//Properties of the class Value - - /** @var float|null */ - private $x; - /** @var float|null */ - private $y; -} +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_1/Value.php', 9, 13) =]] ``` A Value class must also implement the `eZ\Publish\SPI\FieldType\Value` interface. To match the `FieldType\Value` interface, you need to implement `__toString()` method. You also need to add getters and setters for `x` and `y` properties. -This class will represent the point 2D. +This class represents the point 2D. The final code should look like this: ```php -<?php -declare(strict_types=1); - -namespace App\FieldType\Point2D; - -use eZ\Publish\SPI\FieldType\Value as ValueInterface; - -final class Value implements ValueInterface -{ - /** @var float|null */ - private $x; - /** @var float|null */ - private $y; - public function __construct(?float $x = null, ?float $y = null) - { - $this->x = $x; - $this->y = $y; - } - - public function getX(): ?float - { - return $this->x; - } - public function setX(?float $x): void - { - $this->x = $x; - } - public function getY(): ?float - { - return $this->y; - } - public function setY(?float $y): void - { - $this->y = $y; - } - public function __toString() - { - return "({$this->x}, {$this->y})"; - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_1/Value.php') =]] ``` diff --git a/docs/tutorials/generic_field_type/2_define_point2d_field_type.md b/docs/tutorials/generic_field_type/2_define_point2d_field_type.md index 52cb1af498..1ff4d8dfd1 100644 --- a/docs/tutorials/generic_field_type/2_define_point2d_field_type.md +++ b/docs/tutorials/generic_field_type/2_define_point2d_field_type.md @@ -1,8 +1,8 @@ -# Step 2 - Define the Point 2D Field Type - -!!! tip +--- +description: Learn how to create the Type class which contains the logic for the Field. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_2). +# Step 2 - Define the Point 2D Field Type ## The Type class @@ -16,20 +16,7 @@ First, create `src/FieldType/Point2D/Type.php`. Add a `getFieldTypeIdentifier()` method to it. The new method will return the string that **uniquely** identifies your Field Type, in this case `point2d`: ```php -<?php -declare(strict_types=1); - -namespace App\FieldType\Point2D; - -use eZ\Publish\SPI\FieldType\Generic\Type as GenericType; - -final class Type extends GenericType -{ - public function getFieldTypeIdentifier(): string - { - return 'point2d'; - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_2/Type.php') =]] ``` ## Add a new service definition @@ -38,8 +25,5 @@ Next, add the `ezplatform.field_type` tag to `config/services.yaml`: ```yaml services: - # ... - App\FieldType\Point2D\Type: - tags: - - { name: ezplatform.field_type, alias: point2d } +[[= include_file('code_samples/field_types/2dpoint_ft/config/services.yaml', 33, 36) =]] ``` diff --git a/docs/tutorials/generic_field_type/3_create_form_for_point2d.md b/docs/tutorials/generic_field_type/3_create_form_for_point2d.md index 3e9024913b..b06f32d406 100644 --- a/docs/tutorials/generic_field_type/3_create_form_for_point2d.md +++ b/docs/tutorials/generic_field_type/3_create_form_for_point2d.md @@ -1,8 +1,8 @@ -# Step 3 - Create form for editing Field Type +--- +description: Learn how to create a form used for editing a custom Field definition. +--- -!!! tip - - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_3). +# Step 3 - Create a form for editing Field Type ## Create a form @@ -11,25 +11,8 @@ Next, add a `Point2DType` class that extends the `AbstractType` and implements t This method adds fields for `x` and `y` coordinates. ```php -<?php -declare(strict_types=1); - -namespace App\Form\Type; - -use App\FieldType\Point2D\Value; -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; - -final class Point2DType extends AbstractType -{ - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $builder->add('x', NumberType::class); - $builder->add('y', NumberType::class); - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_3/Point2DType.php', 0, 18) =]] +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_3/Point2DType.php', 25, 26) =]] ``` ## Add a Form Mapper Interface @@ -48,64 +31,15 @@ Next, implement a `mapFieldValueForm()` method and invoke `FormInterface::add` m Final version of the Type class should have the following statements and functions: -```php hl_lines="7 10 18 19 20 21 22 23 24 25" -<?php -declare(strict_types=1); - -namespace App\FieldType\Point2D; - -use App\Form\Type\Point2DType; -use eZ\Publish\SPI\FieldType\Generic\Type as GenericType; -use EzSystems\EzPlatformContentForms\Data\Content\FieldData; -use EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface; -use Symfony\Component\Form\FormInterface; - -final class Type extends GenericType implements FieldValueFormMapperInterface -{ - public function getFieldTypeIdentifier(): string - { - return 'point2d'; - } - public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) - { - $definition = $data->fieldDefinition; - $fieldForm->add('value', Point2DType::class, [ - 'required' => $definition->isRequired, - 'label' => $definition->getName() - ]); - } -} +```php hl_lines="7 10 19 20 21 22 23 24 25 26" +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_3/Type.php') =]] ``` Finally, add a `configureOptions` method and set default value of `data_class` to `Value::class` in `src/Form/Type/Point2DType.php`. It will allow your form to work on this object. -```php hl_lines="19 20 21 22 23 24" -<?php -declare(strict_types=1); - -namespace App\Form\Type; - -use App\FieldType\Point2D\Value; -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; - -final class Point2DType extends AbstractType -{ - public function buildForm(FormBuilderInterface $builder, array $options): void - { - $builder->add('x', NumberType::class); - $builder->add('y', NumberType::class); - } - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'data_class' => Value::class - ]); - } -} +```php hl_lines="20 21 22 23 24 25" +[[= include_file('code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DType.php') =]] ``` ## Add a new tag @@ -113,8 +47,5 @@ final class Point2DType extends AbstractType Next, add the `ezplatform.field_type.form_mapper.value` tag to `config/services.yaml`: ```yaml hl_lines="4" -App\FieldType\Point2D\Type: - tags: - - { name: ezplatform.field_type, alias: point2d } - - { name: ezplatform.field_type.form_mapper.value, fieldType: point2d } +[[= include_file('code_samples/field_types/2dpoint_ft/config/services.yaml', 33, 37) =]] ``` diff --git a/docs/tutorials/generic_field_type/4_introduce_a_template.md b/docs/tutorials/generic_field_type/4_introduce_a_template.md index cd8a1ed081..3dcdaf0a53 100644 --- a/docs/tutorials/generic_field_type/4_introduce_a_template.md +++ b/docs/tutorials/generic_field_type/4_introduce_a_template.md @@ -1,8 +1,8 @@ -# Step 4 - Introduce a template - -!!! tip +--- +description: Learn how to add a template for rendering the custom Field on the site front. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_4). +# Step 4 - Introduce a template ## Point 2D template @@ -11,7 +11,7 @@ Each Field Type template receives a set of variables that can be used to achieve In this case the most important variable is the `field`, an instance of `eZ\Publish\API\Repository\Values\Content\Field`. In addition to its own metadata (`id`, `fieldDefIdentifier`, etc.), it exposes the Field Value through the `value` property. -Remember that [Field Type templates can be overridden](../../guide/twig_functions_reference.md#override-a-field-template-block) in order to tweak what is displayed and how. +Remember that Field Type templates can be overridden in order to tweak what is displayed and how. For more information, see the documentation about [Field Type templates](../../api/field_type_form_and_template.md#content-view-templates). First, create a `point2d_field.html.twig` template in the `templates` directory. @@ -19,9 +19,7 @@ It will define the default display of a Point 2D. Your basic template for Point 2D should look like this: ```html+twig -{% block point2d_field %} - ({{ field.value.getX() }}, {{ field.value.getY() }}) -{% endblock %} +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_4/point2d_field.html.twig') =]] ``` ## Template mapping @@ -29,10 +27,5 @@ Your basic template for Point 2D should look like this: Next, provide the template mapping in `config/packages/ezplatform.yaml`: ```yaml -ezplatform: - system: - default: - # ... - field_templates: - - { template: 'point2d_field.html.twig', priority: 0 } +[[= include_file('code_samples/field_types/2dpoint_ft/config/packages/field_templates.yaml', 0, 5) =]] ``` diff --git a/docs/tutorials/generic_field_type/5_add_a_field.md b/docs/tutorials/generic_field_type/5_add_a_field.md index 79f4f00bca..0f337c0158 100644 --- a/docs/tutorials/generic_field_type/5_add_a_field.md +++ b/docs/tutorials/generic_field_type/5_add_a_field.md @@ -1,4 +1,8 @@ -# Step 5 - Add a new Point 2D field +--- +description: Learn how to use your custom Field Type by adding a Field to a Content Type and creating an instance. +--- + +# Step 5 - Add a new Point 2D Field All actions in this step are done in the admin interface also called the Back Office. Go to the admin interface (`<yourdomain>/admin`) and log in with the default username: `admin` and the default password: `publish`. diff --git a/docs/tutorials/generic_field_type/6_settings.md b/docs/tutorials/generic_field_type/6_settings.md index c32c7de7a4..c96e12f92f 100644 --- a/docs/tutorials/generic_field_type/6_settings.md +++ b/docs/tutorials/generic_field_type/6_settings.md @@ -1,8 +1,8 @@ -# Step 6 - Implement Point 2D settings - -!!! tip +--- +description: Learn how to add settings that format the Field value. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_6). +# Step 6 - Implement Point 2D settings Implementing settings enables you to define the format for displaying the Field on the page. To do so, you will create the `format` field where you will be able to change the way coordinates for Point 2D are displayed. @@ -15,48 +15,8 @@ You will also specify coordinates as placeholder values `%x%` and `%y%`. Open `src/FieldType/Point2D/Type.php` and add a `getSettingsSchema` method according to the following code block: -```php hl_lines="23 24 25 26 27 28 29 30 31" -<?php -declare(strict_types=1); - -namespace App\FieldType\Point2D; - -use App\Form\Type\Point2DSettingsType; -use App\Form\Type\Point2DType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\SPI\FieldType\Generic\Type as GenericType; -use eZ\Publish\SPI\FieldType\Value; -use EzSystems\EzPlatformContentForms\Data\Content\FieldData; -use EzSystems\EzPlatformAdminUi\Form\Data\FieldDefinitionData; -use EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface; -use Symfony\Component\Form\FormInterface; - -final class Type extends GenericType -{ - public function getFieldTypeIdentifier(): string - { - return 'point2d'; - } - - public function getSettingsSchema(): array - { - return [ - 'format' => [ - 'type' => 'string', - 'default' => '(%x%, %y%)', - ], - ]; - } - - public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) - { - $definition = $data->fieldDefinition; - $fieldForm->add('value', Point2DType::class, [ - 'required' => $definition->isRequired, - 'label' => $definition->getName() - ]); - } -} +```php hl_lines="18-26" +[[= include_file('code_samples/field_types/2dpoint_ft/steps/step_6/Type.php') =]] ``` ## Add a format field @@ -66,23 +26,7 @@ In this part you will define and implement the edit form for your Field Type. Define a `Point2DSettingsType` class and add a `format` field in `src/Form/Type/Point2DSettingsType.php`: ```php -<?php -declare(strict_types=1); - -namespace App\Form\Type; - -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; - -final class Point2DSettingsType extends AbstractType - -{ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('format', TextType::class); - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/src/Form/Type/Point2DSettingsType.php') =]] ``` ## FieldDefinitionFormMapper Interface @@ -94,85 +38,31 @@ In `src/FieldType/Point2D/Type.php` you will: - add a `mapFieldDefinitionForm` method at the end that will define the field settings ```php -<?php -namespace App\FieldType\Point2D; - -use EzSystems\EzPlatformAdminUi\FieldType\FieldDefinitionFormMapperInterface; +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php', 0, 4) =]] +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php', 7, 8) =]] // ... -final class Type extends GenericType implements FieldValueFormMapperInterface, FieldDefinitionFormMapperInterface +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php', 14, 15) =]] // ... - public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data): void - { - $fieldDefinitionForm->add('fieldSettings', Point2DSettingsType::class, [ - 'label' => false - ]); - } +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php', 40, 46) =]] ``` -??? tip "Complete `Type.php` code" - - ```php - <?php - declare(strict_types=1); - - namespace App\FieldType\Point2D; - - use App\Form\Type\Point2DSettingsType; - use App\Form\Type\Point2DType; - use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; - use eZ\Publish\SPI\FieldType\Generic\Type as GenericType; - use eZ\Publish\SPI\FieldType\Value; - use EzSystems\EzPlatformContentForms\Data\Content\FieldData; - use EzSystems\EzPlatformAdminUi\Form\Data\FieldDefinitionData; - use EzSystems\EzPlatformAdminUi\FieldType\FieldDefinitionFormMapperInterface; - use EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface; - use Symfony\Component\Form\FormInterface; - - final class Type extends GenericType implements FieldValueFormMapperInterface, FieldDefinitionFormMapperInterface - { - public function getFieldTypeIdentifier(): string - { - return 'point2d'; - } - public function getSettingsSchema(): array - { - return [ - 'format' => [ - 'type' => 'string', - 'default' => '(%x%, %y%)', - ], - ]; - } - - public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data) - { - $definition = $data->fieldDefinition; - $fieldForm->add('value', Point2DType::class, [ - 'required' => $definition->isRequired, - 'label' => $definition->getName() - ]); - } - - public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data): void - { - $fieldDefinitionForm->add('fieldSettings', Point2DSettingsType::class, [ - 'label' => false - ]); - } - } - ``` +<details class="tip"> +<summary>Complete Type.php code</summary> +```php +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Type.php') =]] +``` +</details> ## Add a new tag Next, add `FieldDefinitionFormMapper` as an extra tag definition for `App\FieldType\Point2D\Type` in `config/services.yaml`: -```yaml -tags: - - { name: ezplatform.field_type.form_mapper.definition, fieldType: point2d } +```yaml hl_lines="5" +[[= include_file('code_samples/field_types/2dpoint_ft/config/services.yaml', 33, 38) =]] ``` ## Field Type definition @@ -181,26 +71,15 @@ To be able to display the new `format` field, you need to add a template for it. Create `templates/point2d_field_type_definition.html.twig`: ```html+twig -{% block point2d_field_definition_edit %} - <div class="{% if group_class is not empty %}{{ group_class }}{% endif %}"> - {{- form_label(form.fieldSettings.format) -}} - {{- form_errors(form.fieldSettings.format) -}} - {{- form_widget(form.fieldSettings.format) -}} - </div> -{% endblock %} +[[= include_file('code_samples/field_types/2dpoint_ft/templates/point2d_field_type_definition.html.twig') =]] ``` ### Add configuration for the format field Next, provide the template mapping in `config/packages/ezplatform.yaml`: -```yaml -ezplatform: - system: - default: - # ... - fielddefinition_edit_templates: - - { template: 'point2d_field_type_definition.html.twig', priority: 0 } +```yaml hl_lines="6 7" +[[= include_file('code_samples/field_types/2dpoint_ft/config/packages/field_templates.yaml') =]] ``` ## Redefine template @@ -210,15 +89,10 @@ Finally, redefine the Point 2D template, so it accommodates the new `format` fie In `templates/point2d_field.html.twig` replace the content with: ```html+twig -{% block point2d_field %} - {{ fieldSettings.format|replace({ - '%x%': field.value.x, - '%y%': field.value.y - }) }} -{% endblock %} +[[= include_file('code_samples/field_types/2dpoint_ft/templates/point2d_field.html.twig') =]] ``` -## Add a new Content Type +## Edit the Content Type Now, you can go to Admin in the Back Office and see the results of your work by editing the Point 2D Content Type. diff --git a/docs/tutorials/generic_field_type/7_add_a_validation.md b/docs/tutorials/generic_field_type/7_add_a_validation.md index ac8e4def94..3ac8fa4ba7 100644 --- a/docs/tutorials/generic_field_type/7_add_a_validation.md +++ b/docs/tutorials/generic_field_type/7_add_a_validation.md @@ -1,35 +1,14 @@ -# Step 7 - Add basic validation - -!!! tip +--- +description: Learn how to validate custom Field data. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_7). +# Step 7 - Add basic validation To provide basic validation that ensures both coordinates are provided, add assertions to the `src/FieldType/Point2D/Value.php`: ```php -<?php - -use Symfony\Component\Validator\Constraints as Assert; - -final class Value implements ValueInterface - -{ - /** - * @var float|null - * - * @Assert\NotBlank() - */ - private $x; - - /** - * @var float|null - * - * @Assert\NotBlank() - */ - private $y; - - // ... -} +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Value.php', 6, 23) =]] +// ... ``` As a result, if a user tries to publish the Point 2D with just one value, they will receive an error message. diff --git a/docs/tutorials/generic_field_type/8_data_migration.md b/docs/tutorials/generic_field_type/8_data_migration.md index 07d5a824bf..ad9907d70c 100644 --- a/docs/tutorials/generic_field_type/8_data_migration.md +++ b/docs/tutorials/generic_field_type/8_data_migration.md @@ -1,43 +1,22 @@ -# Step 8 - Data migration between Field Type versions - -!!! tip +--- +description: Learn how to serialize and deserialize Field data to enable sorting or search. +--- - You can find all files used and modified in this step on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/Step_8). +# Step 8 - Data migration between Field Type versions Adding data migration enables you to easily change the output of the Field Type to fit your current needs. This process is important when a Field Type needs to be compared for sorting and searching purposes. Serialization allows changing objects to array by normalizing them, and then to the selected format by encoding them. In reverse, deserialization changes different formats into arrays by decoding and then denormalizing them into objects. -For more information on Serializer Components, see [Symfony documentation.](https://symfony.com/doc/5.0/components/serializer.html) +For more information on Serializer Components, see [Symfony documentation.]([[= symfony_doc =]]/components/serializer.html) ## Normalization First, you need to add support for normalization in a `src/Serializer/Point2D/ValueNormalizer.php`: ```php -<?php -declare(strict_types=1); - -namespace App\Serializer\Point2D; - -use App\FieldType\Point2D\Value; -use Symfony\Component\Serializer\Normalizer\NormalizerInterface; - -final class ValueNormalizer implements NormalizerInterface -{ - public function normalize($object, string $format = null, array $context = []) - { - return [ - $object->getX(), - $object->getY() - ]; - } - public function supportsNormalization($data, string $format = null) - { - return $data instanceof Value; - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/src/Serializer/Point2D/ValueNormalizer.php') =]] ``` ## Add Normalizer definition @@ -46,10 +25,7 @@ Next, add the `ValueNormalizer` service definition to the `config/services.yaml` ```yaml services: - # ... - App\Serializer\Point2D\ValueNormalizer: - tags: - - { name: serializer.normalizer } +[[= include_file('code_samples/field_types/2dpoint_ft/config/services.yaml', 39, 42) =]] ``` ## Backward compatibility @@ -57,29 +33,7 @@ services: To accept old versions of the Field Type you need to add support for denormalization in a `src/Serializer/Point2D/ValueDenormalizer.php`: ```php -<?php -declare(strict_types=1); - -namespace App\Serializer\Point2D; - -use App\FieldType\Point2D\Value; -use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; - -final class ValueDenormalizer implements DenormalizerInterface -{ - public function denormalize($data, string $class, string $format = null, array $context = []) - { - if (isset($data['x']) && isset($data['y'])) { - // Support for old format - $data = [ $data['x'], $data['y'] ]; - } - return new $class($data); - } - public function supportsDenormalization($data, string $type, string $format = null) - { - return $type === Value::class; - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/src/Serializer/Point2D/ValueDenormalizer.php') =]] ``` ## Add Denormalizer definition @@ -88,10 +42,7 @@ Next, add the `ValueDenormalizer` service definition to `config/services.yaml` w ```yaml services: - # ... - App\Serializer\Point2D\ValueDenormalizer: - tags: - - { name: serializer.denormalizer } +[[= include_file('code_samples/field_types/2dpoint_ft/config/services.yaml', 43, 46) =]] ``` ## Change format on the fly @@ -99,13 +50,7 @@ services: To change the format on the fly, you need to replace the constructor in `src/FieldType/Point2D/Value.php`: ```php -public function __construct(array $coords = []) -{ - if (!empty($coords)) { - $this->x = $coords[0]; - $this->y = $coords[1]; - } -} +[[= include_file('code_samples/field_types/2dpoint_ft/src/FieldType/Point2D/Value.php', 24, 31) =]] ``` Now you can easily change the internal representation format of the Point 2D Field Type. diff --git a/docs/tutorials/generic_field_type/creating_a_point2d_field_type.md b/docs/tutorials/generic_field_type/creating_a_point2d_field_type.md index d3e0fc6607..88c080cf66 100644 --- a/docs/tutorials/generic_field_type/creating_a_point2d_field_type.md +++ b/docs/tutorials/generic_field_type/creating_a_point2d_field_type.md @@ -1,10 +1,10 @@ -# Creating a Point 2D Field Type - -!!! tip "Getting the code" +--- +description: Go through a Field Type tutorial to learn how to create a custom Field Type based on the built-in Generic Field Type. +--- - The code created in this tutorial is available on [GitHub](https://github.com/ezsystems/generic-field-type-tutorial/tree/master). +# Creating a Point 2D Field Type -This tutorial covers the creation and development of a custom [[= product_name =]] [Field Type](../../api/field_type_reference.md) based on a [Generic Field Type](../../extending/extending_field_type.md). +This tutorial covers the creation and development of a custom [[= product_name =]] [Field Type](../../api/field_type_reference.md) based on a [Generic Field Type](../../api/field_type/create_custom_generic_field_type.md). The Generic Field Type is a very powerful extension point. It enables you to easily build complex solutions on a ready-to-go Field Type template. Field Types are responsible for: diff --git a/docs/tutorials/generic_field_type/img/point2d_field_definition.png b/docs/tutorials/generic_field_type/img/point2d_field_definition.png index dcb2496b59..ad24473c15 100644 Binary files a/docs/tutorials/generic_field_type/img/point2d_field_definition.png and b/docs/tutorials/generic_field_type/img/point2d_field_definition.png differ diff --git a/docs/tutorials/platform_beginner/1_get_ready.md b/docs/tutorials/platform_beginner/1_get_ready.md index 6ac1c2c019..90bae0f942 100644 --- a/docs/tutorials/platform_beginner/1_get_ready.md +++ b/docs/tutorials/platform_beginner/1_get_ready.md @@ -1,3 +1,7 @@ +--- +description: Start the tutorial by getting a clean installation of Ibexa DXP. +--- + # Step 1 — Get ready To begin the tutorial, you need a clean installation of [[= product_name =]]. @@ -10,6 +14,7 @@ The clean installation contains only a root Content item which displays a welcom ![Front page after clean installation](img/bike_tutorial_homepage_install_clean.png) You will replace the welcome page with your own in step 3. -To remove it for now, delete the `config/packages/ezplatform_welcome_page.yaml` file. + +To remove it for now, go to `config/packages/` and delete the `ezplatform_welcome_page.yaml` file. You can now start creating the content model. diff --git a/docs/tutorials/platform_beginner/2_create_the_content_model.md b/docs/tutorials/platform_beginner/2_create_the_content_model.md index 78f5b3feb5..0c40e79782 100644 --- a/docs/tutorials/platform_beginner/2_create_the_content_model.md +++ b/docs/tutorials/platform_beginner/2_create_the_content_model.md @@ -1,3 +1,7 @@ +--- +description: Learn how to create a content model consisting of Content Types and a few sample Content items. +--- + # Step 2 — Create the content model How your content is structured is a very important part of an [[= product_name =]] project. Think of it as the database design of your application. @@ -17,13 +21,13 @@ Each Field Type is built to represent a specific type of data: a text line, a bl You can find a complete list in the [Field Types reference](../../api/field_type_reference.md) section. Every Field Type may have its own options, and comes with its own editing and viewing interfaces. -## Create a Content Type +## Add a Content Type The site will use two Content Types: **Ride** and **Landmark**. A Ride is a route of a bike trip. It can include one or more Landmarks - interesting places you can see along the way. More than one Ride can visit the same Landmark, so it is similar to an N-N relationship model in a database. -In this step you'll create the first Content Type, Ride. +In this step you'll add the first Content Type, Ride. Go to the admin interface (`<yourdomain>/admin`) and log in with the default username: `admin` and the default password: `publish`.  @@ -35,7 +39,7 @@ You will see a list of **Content Type Groups**. They are used to group Content Select **Content** and then click the plus icon.  -![Create a content type button](img/bike_tutorial_create_content_type.png) +![Add a Content Type button](img/bike_tutorial_create_content_type.png) Fill the form with this basic info:  @@ -57,6 +61,18 @@ Confirm the creation of the Content Type by selecting Save. ## Create Rides +!!! note + + If you are using [[= product_name_exp =]], the root Content item in your installation is + a Page called "Ibexa Digital Experience Platform". + + For this tutorial, swap it with its child, a Folder called "Ibexa Platform". + + To do this, go to "Ibexa Digital Experience Platform", select the **Locations** tab + and in the **Swap Locations** section navigate to "Ibexa Platform". + + You can learn how to work with Pages in [another tutorial](../enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md). + Go back to the Content structure page by selecting **Content** in the top bar. Then browse the Content tree and create a Folder named *All Rides* using the **Create** button in the Action bar on the right of the screen. Publish the Folder. diff --git a/docs/tutorials/platform_beginner/3_customize_the_front_page.md b/docs/tutorials/platform_beginner/3_customize_the_front_page.md index 677d05b94c..1b2f7234f6 100644 --- a/docs/tutorials/platform_beginner/3_customize_the_front_page.md +++ b/docs/tutorials/platform_beginner/3_customize_the_front_page.md @@ -1,3 +1,7 @@ +--- +description: Try customizing the front page by using custom templates and adding assets. +--- + # Step 3 — Customize the front page In this step you will create the global layout of your site, and display content using custom templates. @@ -9,6 +13,12 @@ You will customize this step by instructing Platform to use a custom template to To use a custom template when rendering the root content, create a `content_view` configuration block for `ezplatform`. +!!! note + + When pasting YAML code, pay attention to indentation and levels. + The code blocks shown here include the full structure of the YAML file to help you learn where to place new blocks. + Be careful not to duplicate existing keys, because YAML does not allow it. + Edit `config/packages/ezplatform.yaml`. Add the following block under `site` while paying attention to indentation - `content_view` should be one level below `site`: @@ -27,7 +37,7 @@ ezplatform: This tells [[= product_name =]] to use the `template` when rendering content with Location ID `2`. `2` is the default Location for the root Content item. -`Id\Location` is one of several [view matchers](../../guide/content_rendering.md#configuring-views-the-viewprovider) that you can use to customize rendering depending on different criteria. +`Id\Location` is one of several [view matchers](../../guide/content_rendering/templates/view_matcher_reference.md) that you can use to customize rendering depending on different criteria. !!! note "Clear the cache" @@ -57,6 +67,10 @@ Create a `home_page.html.twig` file in `templates/full/`: Refresh the page and you will see a simple, unstyled version of the message. +!!! note + + If you still see the welcome page, go to `config/packages/` and make sure you deleted the `ezplatform_welcome_page.yaml` file. + ### Add the site's main layout Most sites have a general layout which includes things like header with a logo or footer. @@ -72,9 +86,9 @@ To add a template like this to your site, create a `main_layout.html.twig` file <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <meta name="description" content="Ibexa Platform beginner tutorial"> + <meta name="description" content="Ibexa DXP beginner tutorial"> - <title>Ibexa Platform Beginner Tutorial + Ibexa DXP Beginner Tutorial @@ -146,7 +160,7 @@ To add a template like this to your site, create a `main_layout.html.twig` file
    -

    Website design & content © copyright {{ "now"|date("Y") }} Ibexa. This website was made with Ibexa Platform.

    +

    Website design & content © copyright {{ "now"|date("Y") }} Ibexa. This website was made with Ibexa DXP.

    @@ -159,7 +173,7 @@ To add a template like this to your site, create a `main_layout.html.twig` file ``` -Note that in the highlighted lines (12 and 89) the template takes advantage of [Symfony Webpack Encore](https://symfony.com/doc/5.0/frontend.html#webpack-encore). +Note that in the highlighted lines (12 and 89) the template takes advantage of [Symfony Webpack Encore]([[= symfony_doc =]]/frontend.html#webpack-encore). This tutorial will lead you through configuring Webpack, but first you need assets. ### Adding assets @@ -177,14 +191,14 @@ Before proceeding, ensure that the structure of the added files looks like this: ### Configuring Webpack -In [[= product_name =]], you can add assets by using [Symfony Webpack Encore](https://symfony.com/doc/5.0/frontend.html#webpack-encore) +In [[= product_name =]], you can add assets by using [Symfony Webpack Encore]([[= symfony_doc =]]/frontend.html#webpack-encore) — an integration of Webpack that enables you to build bundles of CSS stylesheets and JS scripts and add them to the project. For more details, see [importing assets from a bundle](../../guide/project_organization.md#importing-assets-from-a-bundle). To create bundles, first, indicate which files to include in them. Open the `webpack.config.js` file located in the root folder of your project. -Paste the following code right under `// Put your config here`: +Paste the following code right under `Encore.addEntry('app', './assets/app.js');`: ``` javascript hl_lines="2 8" Encore @@ -233,9 +247,16 @@ Clear the cache and regenerate the assets by running the following commands: ``` bash php bin/console cache:clear php bin/console assets:install -yarn encore prod +yarn encore ``` +!!! tip + + You should run the `yarn encore` command with the [environment](../../guide/environments.md) you are using. + + By default, [[= product_name =]] installs in the dev environment. + If you changed it to prod, use `yarn encore prod`. + Refresh the page and you should see the "Hello world" placed inside a styled layout. ![Homepage with a Hello world](img/bike_tutorial_hello_world.png) diff --git a/docs/tutorials/platform_beginner/4_display_single_content_item.md b/docs/tutorials/platform_beginner/4_display_single_content_item.md index 8c6751025c..afa7dcbc17 100644 --- a/docs/tutorials/platform_beginner/4_display_single_content_item.md +++ b/docs/tutorials/platform_beginner/4_display_single_content_item.md @@ -1,4 +1,8 @@ -# Step 4 — Display a single content item +--- +description: Learn how to render content details with a custom template. +--- + +# Step 4 — Display a single Content item You'll render a list of all Rides here in the next step. But before that, you can use the existing page layout to render the content of a single Ride. @@ -69,12 +73,6 @@ This template reuses `main_layout.html.twig` and again places the template in a Now you need to indicate when this template should be used. -!!! note - - When pasting YAML code, pay attention to indentation and levels. - The code blocks shown here include the full structure of the YAML file to help you learn where to place new blocks. - Be careful not to duplicate existing keys, because YAML does not allow it. - Go back to `config/packages/ezplatform.yaml` and add the following configuration (under the existing `content_view` and `full` keys:): ``` yaml diff --git a/docs/tutorials/platform_beginner/5_display_a_list_of_content_items.md b/docs/tutorials/platform_beginner/5_display_a_list_of_content_items.md index f0b8ed99c6..a6ddbedbfb 100644 --- a/docs/tutorials/platform_beginner/5_display_a_list_of_content_items.md +++ b/docs/tutorials/platform_beginner/5_display_a_list_of_content_items.md @@ -1,4 +1,8 @@ -# Step 5 — Display a list of content items +--- +description: Learn how to query for content and render it in a list. +--- + +# Step 5 — Display a list of Content items Now that you know how to display a single Content item, you can take care of rendering a list of Content items. @@ -44,8 +48,8 @@ To get this list, you will use a Query Type. ## Create a QueryType for the home page -QueryType objects are used to limit and sort results for Content Item queries. -For more information, see [Built-In Query Types](../../guide/controllers/#built-in-query-types) +QueryType objects are used to limit and sort results for Content Item queries. +For more information, see [Built-In Query Types](../../guide/content_rendering/queries_and_controllers/built-in_query_types.md). Here, you need to display `ride` objects that have been published (are visible). Create a `RideQueryType.php` file in `src/QueryType`: diff --git a/docs/tutorials/platform_beginner/6_improve_configuration.md b/docs/tutorials/platform_beginner/6_improve_configuration.md index c38820bb01..e541876e7a 100644 --- a/docs/tutorials/platform_beginner/6_improve_configuration.md +++ b/docs/tutorials/platform_beginner/6_improve_configuration.md @@ -1,3 +1,7 @@ +--- +description: See how you can manage Ibexa DXP configuration files. +--- + # Step 6 — Improve configuration ## Define image variations diff --git a/docs/tutorials/platform_beginner/7_embed_content.md b/docs/tutorials/platform_beginner/7_embed_content.md index 827c786a10..c6844e9b6c 100644 --- a/docs/tutorials/platform_beginner/7_embed_content.md +++ b/docs/tutorials/platform_beginner/7_embed_content.md @@ -1,3 +1,7 @@ +--- +description: Learn how to embed related content in another Content item's template. +--- + # Step 7 — Embed content Creating lists and detailed views of Content Types and their respective items often involves loading related resources. @@ -5,18 +9,18 @@ In this step, you add a related object, a Landmark, which will be displayed on R You can add as many or as little related resources as you like. -## Create the Landmark Content Type +## Add the Landmark Content Type -Now you need to create the second Content Type needed in the site, Landmark. +Now you need to add the second Content Type needed in the site, Landmark. -Go to **Admin** > **Content Types**, and under the **Content** group, create the Landmark Content Type. +Go to **Admin** > **Content Types**, and in the **Content** group, create the Landmark Content Type. A Landmark is an interesting place that Rides go through. Each Ride may be related to multiple Landmarks. - **Name**: Landmark - **Identifier**: landmark -Then create all Fields with the following information:  +Then add all Fields with the following information:  | Field Type | Name | Identifier | Required | Searchable | Translatable | | ------------ | ---------------- | ---------------- | --------- | ---------- | ------------ | @@ -112,7 +116,7 @@ landmark_list: ### Create the RideController -You must provide additional information when the Ride object is displayed. +You must provide additional information when the Ride object is displayed. This requires creating a custom controller. The controller uses `ContentService` to load related resources (Landmarks) for a particular Ride. diff --git a/docs/tutorials/platform_beginner/8_enable_account_registration.md b/docs/tutorials/platform_beginner/8_enable_account_registration.md index e778c80810..87a6f73066 100644 --- a/docs/tutorials/platform_beginner/8_enable_account_registration.md +++ b/docs/tutorials/platform_beginner/8_enable_account_registration.md @@ -1,3 +1,7 @@ +--- +description: See how you can enable external users to register and contribute to your site. +--- + # Step 8 — Enable account registration In this step you will enable other users to create accounts on your site, access the Back Office and create content. @@ -167,7 +171,7 @@ The User you have just created will have the Roles assigned to this group. !!! tip You can change the group in which new Users are placed (but you don't need to do it for this tutorial). - See [Registering new users](../../../guide/user_management/#registering-new-users) for more information. + See [Registering new users](../../guide/user_management/user_management.md#registering-new-users) for more information. At this point you don't want anyone who registers to be able to add content to the website. That's why you'll create a new User Group with additional permissions. @@ -201,7 +205,7 @@ Now add the following Policies to the Contributors Role. !!! tip The Limitations are a powerful tool for fine-tuning the permission management of the Users. - See [the documentation about Limitations for more technical details](../../guide/limitation_reference.md#content-type-group-limitation) + See [the documentation about Limitations for more technical details](../../guide/limitation_reference.md#content-type-group-limitation). Once the Policies are set, go to the "Assignments" tab and assign the Role to the User Group *Go Bike Members*. diff --git a/docs/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md b/docs/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md index 18897cba66..3332036367 100644 --- a/docs/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md +++ b/docs/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md @@ -1,7 +1,13 @@ -# Building a bicycle route tracker in [[= product_name =]] +--- +description: Go through a beginner tutorial which presents the Ibexa DXP content model and show how to configure and use templates to create a basic site. +--- + +# Beginner tutorial This tutorial is a step-by-step guide to building an [[= product_name =]] website. +You can use it with both [[= product_name_content =]] and [[= product_name_exp =]]. + ## Intended audience The tutorial is intended for users who have little or no previous experience with [[= product_name =]]. @@ -10,7 +16,7 @@ To follow it, you should: - Have basic knowledge of HTML and CSS. - Have basic knowledge of the database you've selected. -  + ## Learning outcomes After finishing this tutorial, you will: diff --git a/docs/tutorials/platform_beginner/img/bike_tutorial_all_rides_admin.png b/docs/tutorials/platform_beginner/img/bike_tutorial_all_rides_admin.png index 9fd9d4b6cc..28df427d48 100644 Binary files a/docs/tutorials/platform_beginner/img/bike_tutorial_all_rides_admin.png and b/docs/tutorials/platform_beginner/img/bike_tutorial_all_rides_admin.png differ diff --git a/docs/tutorials/platform_beginner/img/bike_tutorial_homepage_install_clean.png b/docs/tutorials/platform_beginner/img/bike_tutorial_homepage_install_clean.png index 9585913c13..a3db9e9b43 100644 Binary files a/docs/tutorials/platform_beginner/img/bike_tutorial_homepage_install_clean.png and b/docs/tutorials/platform_beginner/img/bike_tutorial_homepage_install_clean.png differ diff --git a/docs/tutorials/platform_beginner/img/bike_tutorial_listing_web_v3.png b/docs/tutorials/platform_beginner/img/bike_tutorial_listing_web_v3.png index 39aceda518..e27503c087 100644 Binary files a/docs/tutorials/platform_beginner/img/bike_tutorial_listing_web_v3.png and b/docs/tutorials/platform_beginner/img/bike_tutorial_listing_web_v3.png differ diff --git a/docs/tutorials/platform_beginner/img/bike_tutorial_nav_hub.png b/docs/tutorials/platform_beginner/img/bike_tutorial_nav_hub.png index 9cb0ecdec3..f126b7876f 100644 Binary files a/docs/tutorials/platform_beginner/img/bike_tutorial_nav_hub.png and b/docs/tutorials/platform_beginner/img/bike_tutorial_nav_hub.png differ diff --git a/docs/tutorials/platform_beginner/img/diagram_source/bike_tutorial_listing_web_v3.xml b/docs/tutorials/platform_beginner/img/diagram_source/bike_tutorial_listing_web_v3.xml index 139f4610b3..742a5cab3a 100644 --- a/docs/tutorials/platform_beginner/img/diagram_source/bike_tutorial_listing_web_v3.xml +++ b/docs/tutorials/platform_beginner/img/diagram_source/bike_tutorial_listing_web_v3.xml @@ -1,2 +1 @@ - -7ZnNcpswEICfxsd4hMTvMbaTtjNtkzYzTXtUjILVAHKFiKFPX2GEQeB0HGPipvHFFqtld9F+aLXDCE2j7B3Hy8Un5pNwBIGfjdBsBKFr2vK3EOSlwLTcUhBw6pcioxbc0N9ECYGSptQniaYoGAsFXerCOYtjMheaDHPOVrraPQt1r0sckI7gZo7DrvSW+mKhHssCtfw9ocGi8mwANRPhSlkJkgX22aohQhcjNOWMiXIUZVMSFmtXrUt53+UTs5vAOInFLjeIq4f467fUMa/y/AtPbz9+yN0zp7TyiMNUPbAKVuTVCpDYPy8WUl7FLJbCyUJEobwy5JBkVHyXY6DGP4rx2LHU5SxrzM3yxsU14TQignAlK90Sv5ON+vGMzaJJ2AiTd/NcqqzqtFRZWTQyUsk4CbGgj7p5rOgINuY2Hq4ZlY4hUCAjBMbAsB3LcxzbQjZCpQWFtWF5Y89F0ARS1bSRA3UHCUv5nCibzSS13QBLt2sD3ZDAPCCiY0hmCOcNtWWhkDQ15KCxbLVoTckziHF7EvMKEq1lAHrgQKk0+6ZyNz9VwDvHpevLQRnBQakx7C3Y2KFM3eSeraOr+bF/payaOEvWFeFcKhjmMqsn5Sgo/nGSkGJ1SlsyttJcOdkhU5BM6DhyIj3gu7VCsQ/hVLDS53oahzSI5Xgu4Sz2qskj4YLK8nCuJiLq+8XNE5Um6caajKxZET8NwykLGa/fg0Rw9kBaQs7S2Ce+CqCI/xJHNCzSM5WsUekXgs9kpSZv1OMosouASPbcd6liobpls40pRhvvmrPlXYMtlJuvlbbhPJuTbQXpEJzMkxMk+0Li2DokbhcS80Uh2VaDDgFJoXTCZG9MWnXIgB1M7BfFxBsIk59PM1KK7/iJmp2pcXVqTKtDDXpJaqD5vx9wXf2Aa3gtEzsfcM32QbJl6Oi9Cuzb3v71VfjnE+14Wn6QAcbWvqk2x+0uw+mZ7N2gqoMetD+BQx0pluldSOenenG4jgXqHFb141gdCxzqlHHqbPudK3ROENhsfsciBRlbSHkz1cg0Whs7cveuRp7ZqhHeMLWo7WcT8qC1CMGBdhQa4YCcepeD7TGmoZ+kUXXePNoO89Z6F1g9YN/epWPo6L0L6vuh5VVXi06iLXuM9m5e2t/U3GHKRZspy+5TLORl/XG4VK+/sKOLPw== \ No newline at end of file +7Zpfb5swEMA/TR4bGZu/j03SdpM2tVuldXt0wSVeAWfGtMk+/UywA4Z0SpvQpBIviTkf5+PyOx9HGKFpurzieDH/yiKSjCCIliM0G0Fo2dCVX6VkVUk8LYg5jZRSLbilf4kSAiUtaERyQ1Ewlgi6MIUhyzISCkOGOWfPptoDS8xVFzgmHcFtiJOu9I5GYl5JfQfU8k+ExnO9sgXUTIq1shLkcxyx54YIXYzQlDMmqlG6nJKkDJ6OS3Xe5QuzG8c4ycQuJ4jrx+z7j8Kzr1erb7y4+/J55Z95lZUnnBTqgpWzYqUjQLLovAykPMpYJoWTuUgTeWTJIVlS8VOOgRr/Ksdjz1GHs2VjbrZqHNwQTlMiCFeyB5aJS5zSpFR6ZvyxdAJnuZq6ZQUPS4fmQshfHjroXH7Iay0/SoV8HDMWJwQvaD4OWbqeCPO16uVDZVgOlWkHTprG1cWCbkhVlElkUKICfEWYvAQuDYPnmg2NxryBhZZxkmBBn0y2sEI03pjbrHDDqPQEApVOCIExsFzPCTzPdZCLUGVB5ZblBOPAR9AGUtV2kQfNBfJ1EJXNJintZYBj2nWBaUhgHhPRMSQxwauG2qJUyJsactAIWy1ao/oKbP09sR1o2402AwMYgAPxZO/L027raId39svUl4PKg4Oia7lb2HUToegwIHb/FExPnOXr2ihBBJa9WNaTchSX3zjPSRmdytaoAlVPdtJDkKUwc4ITuQK+V2xKe4Vg1ZrraZzQOJPjUNJa7tqTJ8IFlYXyXE2kNIrKkyfqZ5LLOJORMyv9p0kyZQnjdTLmgrNH0hJyVmQRiXYsCZs86jlpX8zTMgJkOfpfpmrS9F3PZqdWGdDIZHdLJsNWojST1thTX03htsJ/CArX8RwQPEEEvRaCfhdB+10R3FbED4HgOtADhCcJod+6pbBgB8JtdzT9QRj0BOHvlwmsxPd8YPI0mPR8k0nb6TCJ3pNJaA/dzTt0N62tyApaJnbubux2F9EydPRuGe77lGcAahegvMDgAFlg7LwVKXvcbmW9PaHaDd7a6V6bYNjXvd+iuE9oOJTeD1J69c6kKdel+FhtMezrdnB4OHPCFHomhQhsNu5jcYisLRwOFfvAFdu2WsUP+W+u2IHdqqNBP/W6vc7G5V7rNYI97Ys0xTEZWuUPslN2EkbX76Ptk0OrfIRWGeoo79sqdwwdvVVGwz/LxwDKccfozb1y+2UFv5/K22bXcfepu/KwfvWnUq9foEIX/wA= \ No newline at end of file diff --git a/docs/updating/1_check_out_version.md b/docs/updating/1_check_out_version.md deleted file mode 100644 index bf21ec9e20..0000000000 --- a/docs/updating/1_check_out_version.md +++ /dev/null @@ -1,114 +0,0 @@ -# 1. Check out a tagged version - -**1.1.** From the project's root, create a new branch from the project's *master*, or from the branch you're updating on: - -**From your master branch, create a branch for handling update changes** - -``` bash -git checkout -b -``` - -This creates a new project branch for the update based on your current project branch, typically `master`. An example `` would be `update-1.4`. -In the following steps it will be referred to as **update branch**. - -**1.2.** If it's not there, add `ezsystems/ezplatform` as an *upstream* remote -(on an Enterprise installation use `ezsystems/ezplatform-ee`, and on an eZ Commerce installation, `ezsystems/ezcommerce`): - -**From your update branch add upstream remote** - -``` bash -git remote add upstream http://github.com/ezsystems/ezplatform.git -or -git remote add upstream http://github.com/ezsystems/ezplatform-ee.git -or -git remote add upstream http://github.com/ezsystems/ezcommerce.git -``` - -**1.3.** Prepare for pulling changes - -??? note "Adding `sort-packages` option when updating from <=1.7.8, 1.13.4, 2.2.3, 2.3.2" - - To reduce the number of conflicts in the future, [EZP-29835](https://jira.ez.no/browse/EZP-29835) adds a setting to - Composer to make it sort packages listed in `composer.json`. If you don't already do this, you should prepare for - this update to make it clearer which changes you introduce. - - Assuming you have installed packages on your installation (`composer install`), do the following steps: - - 1\. Add [sort-packages](https://getcomposer.org/doc/06-config.md#sort-packages) to the `config` section in `composer.json` as shown in the highlighted line: - - ``` json hl_lines="3" - "config": { - "bin-dir": "bin", - "sort-packages": true, - "preferred-install": { - "ezsystems/*": "dist" - } - }, - ``` - - 2\. Use `composer require` to get Composer to sort your packages: - - With this new option you should ideally always use `composer require` to add or adjust packages to make sure they - are sorted. The following code example updates a few requirements with what you can also expect in the upcoming - change: - - ``` bash hl_lines="1 2 4" - composer require --no-scripts --no-update doctrine/doctrine-bundle:^1.9.1 - composer require --dev --no-scripts --no-update behat/behat:^3.5.0 - # The upcoming change also moves security-advisories to dev as advised by the package itself - composer require --dev --no-scripts --no-update roave/security-advisories:dev-master - ``` - - 3\. Check that you can install/update packages: - - ``` bash - composer update - ``` - - You can consider the result a success if Composer says there were no updates, or if it updated packages without stopping with conflicts. - - 4\. Now that packages are sorted, save your work. - - With packages sorted you are ready to pull in changes - As they will also be sorted, it will be easier to see which changes are relevant to your `composer.json`. - - ``` bash - git commit -am "Sort my existing composer packages in anticipation of update with sorted merge" - ``` - -**1.4.** Then pull the tag into your branch. - -If you are unsure which version to pull, run `git ls-remote --tags upstream` to list all possible tags. - -**Pull the tag into your update branch** - -``` bash -git pull upstream -``` - -!!! tip - - Don't forget the `v` parameter here. You want to pull the tag `` and not the branch `` (i.e.: `v1.11.0`, and NOT `1.11.0` or `1.10` which is dev branch). - -At this stage you may get conflicts, which are a normal part of the procedure and no reason to worry. -The most common ones will be on `composer.json` and `composer.lock`. - -The latter can be ignored, as it will be regenerated when we execute `composer update` later. -The easiest is to checkout the version from the tag and add it to the changes: - -If you get a **lot** of conflicts (on the `doc` folder for instance), and eZ Platform was installed from the [ezplatform.com](https://ezplatform.com) or [support.ez.no](https://support.ez.no) (for Enterprise and eZ Commerce) tarball, it might be because of incomplete history. -You will have to run `git fetch upstream --unshallow` to load the full history, and run the merge again. - -**From your update branch** - -``` bash -git checkout --theirs composer.lock && git add composer.lock -``` - -If you do not keep a copy in the branch, you may also run: - -**From your update branch** - -``` bash -git rm composer.lock -``` diff --git a/docs/updating/2_merge_composer.md b/docs/updating/2_merge_composer.md deleted file mode 100644 index 233206bff9..0000000000 --- a/docs/updating/2_merge_composer.md +++ /dev/null @@ -1,36 +0,0 @@ -# 2. Merge composer.json - -## Manual merging - -!!! note "Sorted packages since 2.4 changes" - - Because the 2.4 packages in `composer.json` are sorted, there will be more conflicts when updating to 2.4, but far fewer conflicts in the future. This is controlled by [sort-packages](https://getcomposer.org/doc/06-config.md#sort-packages) config in `composer.json`. - -Conflicts in `composer.json` need to be fixed manually. If you're not familiar with the diff output, you may checkout the tag's version and inspect the changes. It should be readable for most: - -**From your new update branch** - -``` bash -git checkout --theirs composer.json && git diff HEAD composer.json -``` - -You should see what was changed, as compared to your own version, in the diff output. The update changes the requirements for all of the `ezsystems/` packages. Those changes should be left untouched. All of the other changes will be removals of what you added for your own project. Use `git checkout -p` to selectively cancel those changes: - -``` bash -git checkout -p composer.json -``` - -Answer `no` (do not discard) to the requirement changes of `ezsystems` dependencies. Answer `yes` (discard) to removals of your changes. - -Once you are done, inspect the file, by either using an editor or running `git diff composer.json`. You may also test the file's sanity with `composer validate`, and test the dependencies by running `composer update --dry-run` (it will output what it would do to the dependencies, without applying the changes). - -Once finished, run `git add composer.json` and commit. - -## Fixing other conflicts - -Depending on the local changes you have done, you may get other conflicts on configuration files, kernel, etc. - -There shouldn't be many, and you should be able to figure out which value is the right one for all of them: - -- Edit the file, and identify the conflicting changes. If a setting you have modified has also been changed by us, you should be able to figure out which value is the right one. -- Run `git add conflicting-file` to add the changes diff --git a/docs/updating/3_update_app.md b/docs/updating/3_update_app.md deleted file mode 100644 index 40c17d5ef7..0000000000 --- a/docs/updating/3_update_app.md +++ /dev/null @@ -1,37 +0,0 @@ -# 3. Update the app - -At this point, you should have a `composer.json` file with the correct requirements. Run `composer update` to update the dependencies.  - -``` bash -composer update -``` - -If you want to first test how the update proceeds without actually updating any packages, you can try the command with the `--dry-run` switch: - -`composer update --dry-run` - -??? note "When updating from <2.2" - - ##### Adding EzSystemsPlatformEEAssetsBundle - - !!! dxp "EZ ENTERPRISE" - - When upgrading from releases between (and including) v1.10 and v2.1 to v2.2 and higher, you need to disable `EzSystemsPlatformEEAssetsBundle` by removing: - - `new EzSystems\PlatformEEAssetsBundle\EzSystemsPlatformEEAssetsBundle(),` - - in `app/AppKernel.php`. - -!!! note "Updating from <2.5" - - Since v2.5 eZ Platform uses [Webpack Encore](https://symfony.com/doc/5.0/frontend.html#webpack-encore) for asset management. - You need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/lang/en/docs/install) to update to this version. - - In v2.5 it is still possible to use Assetic, like in earlier versions. - However, if you are using the latest Bootstrap version, [`scssphp`](https://github.com/leafo/scssphp) - will not compile correctly with Assetic. - In this case, use Webpack Encore. See [Importing assets from a bundle](../guide/organization.md#importing-assets-from-a-bundle) for more information. - -!!! caution "Common errors" - - If you experienced issues during the update, please check [Common errors](../getting_started/troubleshooting.md#cloning-failed-using-an-ssh-key) section on the Composer about page. diff --git a/docs/updating/4_update_1.13.md b/docs/updating/4_update_1.13.md deleted file mode 100644 index 8ed624f979..0000000000 --- a/docs/updating/4_update_1.13.md +++ /dev/null @@ -1,90 +0,0 @@ -# Updating from <1.13 - -If you are updating from a version prior to 1.7, you have to implement all the changes from [Updating from <1.7](4_update_1.7.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Updating from <1.8 - -### Changes to permissions - -v1.8.0 introduced a new `content/publish` permission separated out of the `content/edit` permission. -`edit` now covers only editing content, without the right to publishing it. -For that you need the `publish` permission. -`edit` without `publish` can be used in conjunction with the content review workflow to ensure that a user cannot publish content themselves, but must pass it on for review. - -To make sure existing users will be able to both edit and publish content, those with the `content/edit` permission will be given the `content/publish` permission by the following database update script: - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-6.7.0-to-6.8.0.sql -``` - -### Changes to form-uploaded files - -To complete this step you have to [dump assets](6_dump_assets.md) first. - -Since v1.8.0 you can add a File field to the Form block on a Landing Page. -Files uploaded through such a form will be automatically placed in a specific folder in the repository. - -If you are upgrading to v1.8+ you need to create this folder and assign it to a new specific Section. -Then, add them in the config (for example, in `app/config/default_parameters.yml`, depending on how your configuration is set up): - -``` bash -#Location id of the root for form uploads -form_builder.upload_folder.location_id: - -#Section identifier for form uploads -form_builder.upload_folder.section_identifier:
    -``` - -## Updating from <1.11 - -### `ezsearch_return_count` table removal - -v1.11.0 removes the `ezsearch_return_count` table, which had been removed in eZ Publish legacy since 5.4/2014.11. -This avoids issues which would occur when you upgrade using legacy bridge. -Apply the following database update script if your installation has not had the table removed by an earlier eZ Publish upgrade: - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-6.10.0-to-6.11.0.sql -``` - -## Updating from <1.12 - -### Increased password hash length - -v1.12.0 improves password security by introducing support for PHP's `PASSWORD_BCRYPT` and `PASSWORD_DEFAULT` hashing algorithms. -By default `PASSWORD_DEFAULT` is used. -This currently uses bcrypt, but this may change in the future as PHP adds support for new and stronger algorithms. -Apply the following database update script to change the schema and enable the storage of longer passwords. -Note that the script is available for PostgreSQL as well. - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-6.11.0-to-6.12.0.sql -``` - -These algorithms produce longer hashes, and so the length of the `password_hash` column of the `ezuser` table must be increased, like this: - -**MySQL** - -​``` sql -ALTER TABLE ezuser CHANGE password_hash password_hash VARCHAR(255) default NULL; -​``` - -**PostgreSQL** - -​``` sql -ALTER TABLE ezuser ALTER COLUMN password_hash TYPE VARCHAR(255); -​``` - -## Run general database update script - -Apply the following database update script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-6.13.3-to-6.13.4.sql` - -You can now follow the steps from [Updating from <2.2](4_update_2.2.md). diff --git a/docs/updating/4_update_1.7.md b/docs/updating/4_update_1.7.md deleted file mode 100644 index cf15499691..0000000000 --- a/docs/updating/4_update_1.7.md +++ /dev/null @@ -1,49 +0,0 @@ -# Updating from <1.7 - -Apply the following database update script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-6.7.7-to-6.7.8.sql` - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Changes to Solr Bundle - -Solr Bundle v1.4 introduced among other things index time boosting feature, this involves a slight change to the Solr scheme that will need to be applied to your config. - -To make sure indexing continues to work, apply the following change, restart Solr and reindex your content: - -``` xml -diff --git a/lib/Resources/config/solr/schema.xml b/lib/Resources/config/solr/schema.xml -index 49a17a9..80c4cd7 100644 ---- a/lib/Resources/config/solr/schema.xml -+++ b/lib/Resources/config/solr/schema.xml -@@ -92,7 +92,7 @@ should not remove or drastically change the existing definitions. - - - -- -+ - - - -@@ -104,13 +104,6 @@ should not remove or drastically change the existing definitions. - - - -- -- -- -- -- - -``` - -You can now follow the steps from [Updating from <1.13](4_update_1.13.md). \ No newline at end of file diff --git a/docs/updating/4_update_2.2.md b/docs/updating/4_update_2.2.md deleted file mode 100644 index ba52f170e0..0000000000 --- a/docs/updating/4_update_2.2.md +++ /dev/null @@ -1,637 +0,0 @@ -# Updating from <2.2 - -If you are updating from a version prior to 1.13, you have to implement all the changes from [Updating from <1.13](4_update_1.13.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Change from UTF8 to UTF8MB4 - -Since v2.2 the character set for MySQL/MariaDB database tables changes from `utf8` to `utf8mb4` to support 4-byte characters. - -To apply this change, use the following database update script: - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.1.0-to-7.2.0.sql -``` - -If you use DFS Cluster, also execute the following database update script: - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.1.0-to-7.2.0-dfs.sql -``` - -Be aware that these upgrade statements may fail due to index collisions. -This is because the indexes have been shortened, so duplicates may occur. -If that happens, you must remove the duplicates manually, and then repeat the statements that failed. - -After successfully running those statements, change the character set and collation for each table, as described in [kernel upgrade documentation.](https://github.com/ezsystems/ezpublish-kernel/blob/7.5/doc/upgrade/7.2.md) - -You should also change the character set that is specified in the application config: - -In `app/config/config.yml`, set the following: - -``` yaml -doctrine: - dbal: - connections: - default: - charset: utf8mb4 -``` - -Also make the corresponding change in `app/config/dfs/dfs.yml`. - -## Migrate Landing Pages - -To update to v2.2 with existing Landing Pages, you need to use a dedicated script. -The script is contained in the `ezplatform-page-migration` bundle and **works since version v2.2.2**. -To use it: - -1. Run `composer require ezsystems/ezplatform-page-migration` -2. Add the bundle to `app/AppKernel.php`: `new EzSystems\EzPlatformPageMigrationBundle\EzPlatformPageMigrationBundle(),` -3. Run command `bin/console ezplatform:page:migrate` - -!!! tip - - This script will use the layout defined in your Landing Page. - To migrate successfully, you need to copy your zone configuration - from `ez_systems_landing_page_field_type` under `ezplatform_page_fieldtype` in the new config. - Otherwise the script will encounter errors. - - -You can remove the bundle after the migration is complete. - -The command will migrate Landing Pages created in eZ Platform 1.x, 2.0 and 2.1 to new Pages. -The operation is transactional and will roll back in case of errors. - -### Block migration - -In 2.2 Page Builder does not offer all blocks that Landing Page editor did. The removed blocks include Keyword, Schedule, and Form blocks. -The Places block has been removed from the clean installation and will only be available in the demo out of the box. If you had been using this block in your site, re-apply its configuration based on the [demo](https://github.com/ezsystems/ezplatform-ee-demo/blob/v2.2.2/app/config/blocks.yml). - -Later versions of Page Builder come with a Content Scheduler block and new Form Blocks, but migration of Schedule blocks to Content Scheduler blocks and of Form Blocks is not supported. - -If there are missing block definitions, such as Form Block or Schedule Block, -you have an option to continue, but migrated Landing Pages will come without those blocks. - -!!! tip - - If you use different repositories with different SiteAccesses, use the `--siteaccess` switch - to migrate them separately. - -!!! tip - - You can use the `--dry-run` switch to test the migration. - -After the migration is finished, you need to clear the cache. - -#### Migrate layouts - -The `ez_block::renderBlockAction` controller used in layout templates has been replaced by `EzPlatformPageFieldTypeBundle:Block:render`. This controller has two additional parameters, `locationId` and `languageCode`. Only `languageCode` is required. -Also, the HTML class `data-studio-zone` has been replaced with `data-ez-zone-id` -See [documentation](../guide/page_rendering.md#layout-template) for an example on usage of the new controller. - -#### Migrate custom blocks - -Landing Page blocks (from 2.1 and earlier) were defined using a class implementing `EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Model\AbstractBlockType`. -In Page Builder (from 2.2 onwards), this interface is no longer present. Instead the logic of your block must be implemented in a [Listener](../extending/extending_page.md#block-rendering-events). -Typically, what you previously would do in `getTemplateParameters()`, you'll now do in the `onBlockPreRender()` event handler. - -The definition of block parameters has to be moved from `createBlockDefinition()` to the [YAML configuration](../extending/extending_page.md#creating-page-blocks) for your custom blocks. - -For more information about how custom blocks are implemented in Pagebuilder, have a look at [Creating custom Page blocks](../extending/extending_page.md) - -For the migration of blocks from Landing Page to Page Builder, you'll need to provide a converter for attributes of custom blocks. For simple blocks you can use `\EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter`. -Custom converters must implement the `\EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\ConverterInterface` interface. -`convert()` will parse XML `\DOMNode $node` and return an array of `\EzSystems\EzPlatformPageFieldType\FieldType\LandingPage\Model\Attribute` objects. - -Below is an example of a simple converter for a custom block: - -``` yaml -app.block.foobar.converter: - class: EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter - tags: - - { name: ezplatform.fieldtype.ezlandingpage.migration.attribute.converter, block_type: foobar } -``` - -Notice service tag `ezplatform.fieldtype.ezlandingpage.migration.attribute.converter` that must be used for attribute converters. - -This converter is only needed when running the `ezplatform:page:migrate` script and can be removed once that has completed. - -#### Page migration example - -Below is an example how to migrate a Landing Page Layout and Block to new Page Builder. The code is based on the Random block -defined in the [Enterprise Beginner tutorial](../tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md) - -??? tip "Landing Page code" - - `app/Resources/views/layouts/sidebar.html.twig`: - - ```php -
    -
    - {% if zones[0].blocks %} - {% for block in zones[0].blocks %} -
    - {{ render_esi(controller('ez_block::renderBlockAction', { - 'contentId': contentInfo.id, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo - })) }} -
    - {% endfor %} - {% endif %} -
    - -
    - ``` - - `app/config/layouts.yml`: - - ``` yaml - ez_systems_landing_page_field_type: - layouts: - sidebar: - identifier: sidebar - name: Right sidebar - description: Main section with sidebar on the right - thumbnail: assets/images/layouts/sidebar.png - template: layouts/sidebar.html.twig - zones: - first: - name: First zone - second: - name: Second zone - ``` - - `src/AppBundle/Block/RandomBlock.php`: - - ``` php - locationService = $locationService; - $this->contentService = $contentService; - $this->searchService = $searchService; - } - - public function getTemplateParameters(BlockValue $blockValue) - { - $attributes = $blockValue->getAttributes(); - $contentInfo = $this->contentService->loadContentInfo($attributes['parentContentId']); - $randomContent = $this->getRandomContent( - $this->getQuery($contentInfo->mainLocationId) - ); - - return [ - 'content' => $randomContent, - ]; - } - - /** - * Returns random picked Content. - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - private function getRandomContent(LocationQuery $query) - { - $results = $this->searchService->findLocations($query); - $searchHits = $results->searchHits; - if (count($searchHits) > 0) { - shuffle($searchHits); - - return $this->contentService->loadContentByContentInfo( - $searchHits[0]->valueObject->contentInfo - ); - } - - return null; - } - - /** - * Returns LocationQuery object based on given arguments. - * - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\LocationQuery - */ - private function getQuery($parentLocationId) - { - $query = new LocationQuery(); - $query->query = new Criterion\LogicalAnd([ - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - new Criterion\ParentLocationId($parentLocationId), - ]); - - return $query; - } - - public function createBlockDefinition() - { - return new BlockDefinition( - 'random', - 'Random', - 'default', - 'assets/images/blocks/random_block.svg', - [], - [ - new BlockAttributeDefinition( - 'parentContentId', - 'Parent', - 'embed', - self::PATTERN_CONTENT_ID, - 'Choose a valid ContentID', - true, - false, - [], - [] - ), - ] - ); - } - - public function checkAttributesStructure(array $attributes) - { - if (!isset($attributes['parentContentId']) || preg_match(self::PATTERN_CONTENT_ID, $attributes['parentContentId']) !== 1) { - throw new InvalidBlockAttributeException('Parent container', 'parentContentId', 'Parent ContentID must be defined.'); - } - } - } - ``` - - `src/AppBundle/DependencyInjection/AppExtension.php`: - - ``` php - load('services.yml'); - } - - public function prepend(ContainerBuilder $container) - { - $configFile = __DIR__ . '/../Resources/config/blocks.yml'; - $config = Yaml::parse(file_get_contents($configFile)); - $container->prependExtensionConfig('ez_systems_landing_page_field_type', $config); - $container->addResource(new FileResource($configFile)); - } - } - ``` - - `src/AppBundle/Resources/config/blocks.yml`: - - ``` yaml - blocks: - random: - views: - random: - template: AppBundle:blocks:random.html.twig - name: Random Content Block View - ``` - - `src/AppBundle/Resources/config/services.yml`: - - ``` yaml - services: - app.block.random: - class: AppBundle\Block\RandomBlock - arguments: - - '@ezpublish.api.service.location' - - '@ezpublish.api.service.content' - - '@ezpublish.api.service.search' - tags: - - { name: landing_page_field_type.block_type, alias: random } - ``` - -??? tip "Corresponding Page Builder code" - - `app/Resources/views/layouts/sidebar.html.twig`: - - ```php -
    -
    - {% if zones[0].blocks %} - {% set locationId = parameters.location is not null ? parameters.location.id : contentInfo.mainLocationId %} - {% for block in zones[0].blocks %} -
    - {{ render_esi(controller('EzPlatformPageFieldTypeBundle:Block:render', { - 'locationId': locationId, - 'contentId': contentInfo.id, - 'blockId': block.id, - 'versionNo': versionInfo.versionNo, - 'languageCode': field.languageCode - })) }} -
    - {% endfor %} - {% endif %} -
    - -
    - ``` - - `app/config/layouts.yml`: - - ``` yaml - ezplatform_page_fieldtype: - layouts: - sidebar: - identifier: sidebar - name: Right sidebar - description: Main section with sidebar on the right - thumbnail: assets/images/layouts/sidebar.png - template: layouts/sidebar.html.twig - zones: - first: - name: First zone - second: - name: Second zone - ``` - - `src/AppBundle/Block/Event/Listener/RandomBlockListener.php` in place of `src/AppBundle/Block/RandomBlock.php`: - - ``` php - contentService = $contentService; - $this->locationService = $locationService; - $this->searchService = $searchService; - } - - /** - * @return array The event names to listen to - */ - public static function getSubscribedEvents() - { - return [ - BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender', - ]; - } - - /** - * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function onBlockPreRender(PreRenderEvent $event) - { - //BlockDefinitionFactory - $blockValue = $event->getBlockValue(); - $renderRequest = $event->getRenderRequest(); - $contentInfo = $this->contentService->loadContentInfo($blockValue->getAttribute('parentContentId')->getValue()); - - $randomContent = $this->getRandomContent( - $this->getQuery($contentInfo->mainLocationId) - ); - - $parameters = $renderRequest->getParameters(); - $parameters['content'] = $randomContent; - - $renderRequest->setParameters($parameters); - } - - /** - * Returns random picked Content. - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function getRandomContent(LocationQuery $query) - { - $results = $this->searchService->findLocations($query); - $searchHits = $results->searchHits; - if (count($searchHits) > 0) { - shuffle($searchHits); - - return $this->contentService->loadContentByContentInfo( - $searchHits[0]->valueObject->contentInfo - ); - } - - return null; - } - - /** - * Returns LocationQuery object based on given arguments. - * - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\LocationQuery - */ - private function getQuery($parentLocationId) - { - $query = new LocationQuery(); - $query->query = new Criterion\LogicalAnd([ - new Criterion\Visibility(Criterion\Visibility::VISIBLE), - new Criterion\ParentLocationId($parentLocationId), - ]); - - return $query; - } - } - ``` - - `src/AppBundle/DependencyInjection/AppExtension.php`: - - ``` php - load('services.yml'); - } - - public function prepend(ContainerBuilder $container) - { - $configFile = __DIR__ . '/../Resources/config/blocks.yml'; - $config = Yaml::parse(file_get_contents($configFile)); - $container->prependExtensionConfig('ezplatform_page_fieldtype', $config); - $container->addResource(new FileResource($configFile)); - } - } - ``` - - `src/AppBundle/Resources/config/blocks.yml`: - - ``` yaml - blocks: - random: - name: Random - category: default - thumbnail: assets/images/layouts/sidebar.png - # read https://doc.ezplatform.com/en/latest/guide/extending/extending_page/#block-modal-template - #configuration_template: blocks/random_config.html.twig - views: - random: - template: AppBundle:blocks:random.html.twig - name: Random Content Block View - attributes: - parentContentId: - type: embed - name: Parent Location ID - validators: - not_blank: - message: Please provide parent node - ``` - - `src/AppBundle/Resources/config/services.yml`: - - ``` yaml - services: - _defaults: - autowire: true - autoconfigure: true - public: false - - AppBundle\Block\Event\Listener\RandomBlockListener: ~ - - app.block.random.converter: - class: EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter - tags: - - { name: ezplatform.fieldtype.ezlandingpage.migration.attribute.converter, block_type: random } - ``` - -You can now follow the steps from [Updating from <2.3](4_update_2.3.md). diff --git a/docs/updating/4_update_2.3.md b/docs/updating/4_update_2.3.md deleted file mode 100644 index 3ba7ec8e34..0000000000 --- a/docs/updating/4_update_2.3.md +++ /dev/null @@ -1,51 +0,0 @@ -# Updating from <2.3 - -If you are updating from a version prior to 2.2, you have to implement all the changes from [Updating from <2.2](4_update_2.2.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Database update script - -Apply the following database update script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.2.0-to-7.3.0.sql` - -## Changes to timestamp - -A new timestamp column has been added in order to keep track of when items were trashed, this is exposed in the API but not yet in UI. - -To apply this change, use the following database update script: - -``` bash -mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.2.0-to-7.3.0.sql -``` - -## Form builder - -!!! dxp - - To create the *Forms* container under the content tree root use the following command: - - ``` bash - php bin/console ezplatform:form-builder:create-forms-container - ``` - - You can also specify Content Type, Field values and language code of the container, e.g.: - - ``` bash - php bin/console ezplatform:form-builder:create-forms-container --content-type custom --field title --value 'My Forms' --field description --value 'Custom container for the forms' --language-code eng-US - ``` - - You also need to run a script to add database tables for the Form Builder. - You can find it in https://github.com/ezsystems/ezplatform-ee-installer/blob/2.3/Resources/sql/schema.sql#L136 - - !!! caution "Form (ezform) Field Type" - - After the update, in order to create forms, you have to add a new Content Type (e.g. named "Form") that contains `Form` Field (this Content Type can contain other fields - as well). After that you can use forms inside Landing Pages via Embed block. - -You can now follow the steps from [Updating from <2.4](4_update_2.4.md). \ No newline at end of file diff --git a/docs/updating/4_update_2.4.md b/docs/updating/4_update_2.4.md deleted file mode 100644 index 81c49d7a6a..0000000000 --- a/docs/updating/4_update_2.4.md +++ /dev/null @@ -1,50 +0,0 @@ -# Updating from <2.4 - -If you are updating from a version prior to 2.3, you have implement all the changes from [Updating from <2.3](4_update_2.3.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Workflow - -!!! dxp - - When updating an Enterprise installation, you need to run a script to add database tables for the Editorial Workflow. - You can find it in https://github.com/ezsystems/ezplatform-ee-installer/blob/2.4/Resources/sql/schema.sql#L198 - -## Changes to the Forms folder - -!!! dxp - - The built-in Forms folder is located in the Form Section in versions 2.4+. - - If you are updating your installation, you need to add this Section manually and move the folder to it. - - To allow anonymous users to access Forms, you also need to add the `content/read` Policy - with the *Form* Section to the Anonymous User. - -## Changes to Custom tags - -v2.4 changed the way of configuring custom tags. They are no longer configured under the `ezpublish` key, -but one level higher in the YAML structure: - -``` yaml -ezpublish: - system: - : - fieldtypes: - ezrichtext: - custom_tags: [exampletag] - -ezrichtext: - custom_tags: - exampletag: - # ... -``` - -The old configuration is deprecated, so if you use custom tags, you need to modify your config accordingly. - -You can now follow the steps from [Updating to <2.5](4_update_2.5.md). diff --git a/docs/updating/4_update_2.5.md b/docs/updating/4_update_2.5.md deleted file mode 100644 index ade4dbdbd1..0000000000 --- a/docs/updating/4_update_2.5.md +++ /dev/null @@ -1,129 +0,0 @@ -# Updating from <2.5 - -If you are updating from a version prior to 2.4, you have to implement all the changes from [Updating from <2.4](4_update_2.4.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Database update script - -Apply the following database update script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.4.0-to-7.5.0.sql` - -### v2.5.3 - -To update to v2.5.3, additionally run the following script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.2-to-7.5.3.sql` - -### v2.5.6 - -To update to v2.5.6, additionally run the following script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.4-to-7.5.5.sql` - -or for PostgreSQL: - -`psql < vendor/ezsystems/ezpublish-kernel/data/update/postgres/dbupdate-7.5.4-to-7.5.5.sql` - -### v2.5.9 - -To update to v2.5.9, additionally run the following script: - -`mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.6-to-7.5.7.sql` - -or for PostgreSQL: - -`psql < vendor/ezsystems/ezpublish-kernel/data/update/postgres/dbupdate-7.5.6-to-7.5.7.sql` - -Additionally, reindex the content: - -``` bash -php bin/console ezplatform:reindex -``` - -## Changes to database schema - -The introduction of [support for PostgreSQL](../guide/databases.md#using-postgresql) includes a change in the way database schema is generated. - -It is now created based on [YAML configuration](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml), using the new [`DoctrineSchemaBundle`](https://github.com/ezsystems/doctrine-dbal-schema). - -If you are updating your application according to the usual procedure, no additional actions are required. -However, if you do not update your meta-repository, you need to take two additional steps: - -- enable `EzSystems\DoctrineSchemaBundle\DoctrineSchemaBundle()` in `AppKernel.php` -- add [`ez_doctrine_schema`](https://github.com/ezsystems/ezplatform/blob/2.5/app/config/config.yml#L33) configuration - -## Changes to Matrix Field Type - -To migrate your content from legacy XML format to a new `ezmatrix` value use the following command: - -```bash -bin/console ezplatform:migrate:legacy_matrix -``` - -## Required manual cache clearing if using Redis - -If you are using Redis as your persistence cache storage you should always clear it manually after an upgrade. -You can do it in two ways, by using `redis-cli` and executing the following command: - -```bash -FLUSHALL -``` - -or by executing the following command: - -```bash -bin/console cache:pool:clear cache.redis -``` - -## Updating to 2.5.3 - -### Page builder - -!!! dxp - - This step is only **required when updating from versions higher than 2.2 and lower than 2.5.3**. - In case of versions lower than 2.2, please skip this step or ignore the information that indexes from a script below already exist. - - When updating to v2.5.3, you need to run the following script to add missing indexes: - - ``` bash - CREATE INDEX ezpage_map_zones_pages_zone_id ON ezpage_map_zones_pages(zone_id); - CREATE INDEX ezpage_map_zones_pages_page_id ON ezpage_map_zones_pages(page_id); - CREATE INDEX ezpage_map_blocks_zones_block_id ON ezpage_map_blocks_zones(block_id); - CREATE INDEX ezpage_map_blocks_zones_zone_id ON ezpage_map_blocks_zones(zone_id); - CREATE INDEX ezpage_map_attributes_blocks_attribute_id ON ezpage_map_attributes_blocks(attribute_id); - CREATE INDEX ezpage_map_attributes_blocks_block_id ON ezpage_map_attributes_blocks(block_id); - CREATE INDEX ezpage_blocks_design_block_id ON ezpage_blocks_design(block_id); - CREATE INDEX ezpage_blocks_visibility_block_id ON ezpage_blocks_visibility(block_id); - CREATE INDEX ezpage_pages_content_id_version_no ON ezpage_pages(content_id, version_no); - ``` - -## Updating to 2.5.16 - -### Powered-By header - -In order to promote use of eZ Platform, `ezsystems/ez-support-tools` v1.0.10, as of eZ Platform v2.5.16, sets the Powered-By header. -It is enabled by default and generates a header like `Powered-By: eZ Platform Enterprise v2`. - -To omit the version number, use the following configuration: -``` yaml -ezplatform_support_tools: - system_info: - powered_by: - release: "none" -``` - -To opt out of the whole feature, disable it with the following configuration: - -``` yaml -ezplatform_support_tools: - system_info: - powered_by: - enabled: false -``` diff --git a/docs/updating/4_update_3.1.md b/docs/updating/4_update_3.1.md deleted file mode 100644 index 898e5e207d..0000000000 --- a/docs/updating/4_update_3.1.md +++ /dev/null @@ -1,42 +0,0 @@ -# Updating from <3.1 - -If you are updating from a version prior to 3.0, you have to implement all the changes from [Upgrading eZ Platform to v3](../upgrading/upgrading_to_v3.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - Only after applying all changes your database will work properly. - -## Check out and update the app - -1\. [Check out a tagged version](../updating/1_check_out_version.md) - -2\. [Merge composer.json](../updating/2_merge_composer.md) - -3\. [Update the app](../updating/3_update_app.md) - -## Site Factory - -To be able to create a Location for the Site skeletons, run `php ./bin/console ezplatform:site-factory:create-site-skeletons-container` during the update procedure. - -Additionally, you can specify: - -- `--section-name "Custom section"` - a name of the Section to which the Site skeleton container will be assigned. -- `--section-identifier custom_section_identifier` - an identifier of the Ssection to which the Site skeleton container will be assigned. - -If a section with the provided name or identifier does not exist, it is created in the update process. - -`bin/console ezplatform:site-factory:create-site-skeletons-container --section-identifier "Custom section" --section-name custom_section_identifier` - -If you do not provide a Section name or an identifier, the default values will be used: `Site skeleton` and `site_skeleton`. - -## Continue update procedure - -At this point you can continue with the standard update procedure: - -5\. [Platform.sh changes](../updating/5_platform_sh_changes.md) - -6\. [Dump assets](../updating/../updating/6_dump_assets.md) - -7\. [Commit, test and merge](../updating/7_commit_test_merge.md) diff --git a/docs/updating/4_update_3.2.md b/docs/updating/4_update_3.2.md deleted file mode 100644 index de915eead9..0000000000 --- a/docs/updating/4_update_3.2.md +++ /dev/null @@ -1,41 +0,0 @@ -# Updating from <3.2 - -If you are updating from a version prior to 3.0, you have to implement all the changes from [Upgrading eZ Platform to v3](../upgrading/upgrading_to_v3.md) before following the steps below. - -!!! note - - During database update, you have to go through all the changes between your current version and your final version - **for example, when you update from v2.2 to v2.5, you have to perform all the steps from: <2.3, <2.4 and <2.5**. - The database will work properly only if you apply all the required changes. - -## Check out and update the app - -1\. [Check out a tagged version](../updating/1_check_out_version.md) - -2\. [Merge composer.json](../updating/2_merge_composer.md) - -3\. [Update the app](../updating/3_update_app.md) - -## Run the database update script - -!!! dxp "Ibexa DXP or Ibexa Commerce" - - If you are using Ibexa DXP or Ibexa Commerce, apply one of the following database update scripts: - - - for MySQL: - - `mysql -u -p < upgrade/db/mysql/ezplatform-3.1.0-to-3.2.0.sql` - - - for PostgreSQL: - - `psql < upgrade/db/postgresql/ezplatform-3.1.0-to-3.2.0.sql` - -## Continue with the update procedure - -At this point you can continue with the standard update procedure: - -5\. [Platform.sh changes](../updating/5_platform_sh_changes.md) - -6\. [Dump assets](../updating/../updating/6_dump_assets.md) - -7\. [Commit, test and merge](../updating/7_commit_test_merge.md) diff --git a/docs/updating/4_update_database.md b/docs/updating/4_update_database.md deleted file mode 100644 index 1b4f88af1a..0000000000 --- a/docs/updating/4_update_database.md +++ /dev/null @@ -1,39 +0,0 @@ -# 4. Update database - -Some versions require updates to the database. Look through the list of database update scripts -[for MySQL](https://github.com/ezsystems/ezpublish-kernel/tree/master/data/update/mysql) -and [for PostgreSQL](https://github.com/ezsystems/ezpublish-kernel/tree/master/data/update/postgres) -and find a script for the database version you are updating to. - -!!! tip "Database version number" - - Database version number corresponds to the `ezpublish-kernel` version. - - To find out which `ezpublish-kernel` version you have, after running `composer update` or `composer install` - run `composer show ezsystems/ezpublish-kernel|grep versions`. - -During database update, you have to go through all the changes between your current version and your final version -**e.g. during update from v2.2 to v2.5 you have to perform all the steps from: <2.3, <2.4 and <2.5**. - -Start with the version closest to your current version: - -- [Updating from <1.7](4_update_1.7.md) -- [Updating from <1.13](4_update_1.13.md) -- [Updating from <2.2](4_update_2.2.md) -- [Updating from <2.3](4_update_2.3.md) -- [Updating from <2.4](4_update_2.4.md) -- [Updating from <2.5](4_update_2.5.md) -- [Updating from <3.0](../upgrading/upgrading_to_v3.md) -- [Updating from <3.1](4_update_3.1.md) - -Only after applying all changes your database will work properly. - -!!! caution - - Always back up your data before running any database update scripts. - - After updating the database, clear the cache. - - Do not use `--force` argument for `mysql` / `psql` commands when performing update queries. - If there is any problem during the update, it is best if the query fails immediately, so you can fix the underlying problem before you execute the update again. - If you leave this for later you risk ending up with an incompatible database, though the problems might not surface immediately. diff --git a/docs/updating/5_platform_sh_changes.md b/docs/updating/5_platform_sh_changes.md deleted file mode 100644 index 2dc2067c93..0000000000 --- a/docs/updating/5_platform_sh_changes.md +++ /dev/null @@ -1,4 +0,0 @@ -# 5. Platform.sh changes - -If you are hosting your site on Platform.sh be aware of the fact that Varnish is enabled by default as of eZ Platform 1.13.5, 2.4.3 and 2.5.0. -If you are using Fastly, please read about [how to disable Varnish](https://docs.platform.sh/frameworks/ibexa/fastly.html#remove-varnish-configuration). diff --git a/docs/updating/6_dump_assets.md b/docs/updating/6_dump_assets.md deleted file mode 100644 index 27e8786214..0000000000 --- a/docs/updating/6_dump_assets.md +++ /dev/null @@ -1,23 +0,0 @@ -# 6. Dump assets - -The web assets must be dumped again if you are using the `prod` environment. In `dev` this happens automatically: - -``` bash -# In v1 and v2: -php bin/console assetic:dump -e prod -# In v2 and v3: -yarn install -yarn encore prod -``` - -If you encounter problems, additionally clear the cache and install assets: - -``` bash -php bin/console cache:clear -e prod -php bin/console assets:install --symlink -e prod -# In v1 and v2: -php bin/console assetic:dump -e prod -# In v2 and v3: -yarn install -yarn encore prod -``` diff --git a/docs/updating/7_commit_test_merge.md b/docs/updating/7_commit_test_merge.md deleted file mode 100644 index 3855145132..0000000000 --- a/docs/updating/7_commit_test_merge.md +++ /dev/null @@ -1,15 +0,0 @@ -# 7. Commit, test and merge - -Once all the conflicts have been resolved, and `composer.lock` updated, the merge can be committed. -Note that you may or may not keep `composer.lock`, depending on your version management workflow. -If you do not wish to keep it, run `git reset HEAD ` to remove it from the changes. -Run `git commit`, and adapt the message if necessary. - -You can now verify the project and once the update has been approved, go back to `master`, and merge your update branch: - -``` bash -git checkout master -git merge -``` - -**Your eZ Platform / [[= product_name =]] should now be up-to-date with the chosen version!** diff --git a/docs/updating/from_1.x_2.x/update_app_to_2.5.md b/docs/updating/from_1.x_2.x/update_app_to_2.5.md new file mode 100644 index 0000000000..ebe5f7cc57 --- /dev/null +++ b/docs/updating/from_1.x_2.x/update_app_to_2.5.md @@ -0,0 +1,37 @@ +--- +target_version: '2.5' +latest_tag: '2.5.30' +--- + +# Update app to v2.5 + +## 1. Check out a version + +[[% include 'snippets/update/check_out_version.md' %]] + +## 2. Resolve conflicts + +[[% include 'snippets/update/merge_composer.md' %]] + +## 3. Update the app + +If `EzSystemsPlatformEEAssetsBundle` is present in `app/AppKernel.php`, +disable it by removing the `new EzSystems\PlatformEEAssetsBundle\EzSystemsPlatformEEAssetsBundle(),` entry. + +Since v2.5 eZ Platform uses [Webpack Encore]([[= symfony_doc =]]/frontend.html#webpack-encore) for asset management. +You need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/lang/en/docs/install) to update to this version. + +In v2.5 it is still possible to use Assetic, like in earlier versions. +However, if you are using the latest Bootstrap version, [`scssphp`](https://github.com/leafo/scssphp) +will not compile correctly with Assetic. +In this case, use Webpack Encore. See [Importing assets from a bundle](../../guide/project_organization.md#importing-assets-from-a-bundle) for more information. + +If you experience issues during the update, see [Troubleshooting](../../getting_started/troubleshooting.md#cloning-failed-using-an-ssh-key). + +### Run composer update + +[[% include 'snippets/update/update_app.md' %]] + +## Next steps + +Now, proceed to the next step, [updating the database to v2.5](update_db_to_2.5.md). diff --git a/docs/updating/from_1.x_2.x/update_db_to_2.5.md b/docs/updating/from_1.x_2.x/update_db_to_2.5.md new file mode 100644 index 0000000000..f9bbe3cb91 --- /dev/null +++ b/docs/updating/from_1.x_2.x/update_db_to_2.5.md @@ -0,0 +1,952 @@ +--- +target_version: '2.5' +latest_tag: '2.5.30' +--- + +# Update database to v2.5 + +## 4. Update the database + +Before you start this procedure, make sure you have completed the previous step, +[Updating the app to v2.5](update_app_to_2.5.md). + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +!!! note + + If you are starting from version v2.2 or later, skip to the relevant section. + +### A. Update to v2.2 + +#### Change from UTF8 to UTF8MB4 + +In v2.2 the character set for MySQL/MariaDB database tables changes from `utf8` to `utf8mb4` to support 4-byte characters. + +To apply this change, use the following database update script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.1.0-to-7.2.0.sql +``` + +If you use DFS Cluster, also execute the following database update script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.1.0-to-7.2.0-dfs.sql +``` + +Be aware that these upgrade statements may fail due to index collisions. +This is because the indexes have been shortened, so duplicates may occur. +If that happens, you must remove the duplicates manually, and then repeat the statements that failed. + +After successfully running those statements, change the character set and collation for each table, as described in [kernel upgrade documentation.](https://github.com/ezsystems/ezpublish-kernel/blob/7.5/doc/upgrade/7.2.md) + +You should also change the character set that is specified in the application config: + +In `app/config/config.yml`, set the following: + +``` yaml +doctrine: + dbal: + connections: + default: + charset: utf8mb4 +``` + +Also make the corresponding change in `app/config/dfs/dfs.yml`. + +#### Migrate Landing Pages + +To update to v2.2 with existing Landing Pages, you need to use a dedicated script. +The script is contained in the `ezplatform-page-migration` bundle and **works since version v2.2.2**. +To use the script: + +1. Run `composer require ezsystems/ezplatform-page-migration` +2. Add the bundle to `app/AppKernel.php`: `new EzSystems\EzPlatformPageMigrationBundle\EzPlatformPageMigrationBundle(),` +3. Run command `bin/console ezplatform:page:migrate` + +!!! tip + + This script uses the layout defined in your Landing Page. + To migrate successfully, you need to copy your zone configuration + from `ez_systems_landing_page_field_type` under `ezplatform_page_fieldtype` in the new config. + Otherwise the script will encounter errors. + +You can remove the bundle after the migration is complete. + +The `ezplatform:page:migrate` command migrates Landing Pages created in eZ Platform v1.x, v2.0 and v2.1 to new Pages. +The operation is transactional and rolls back in case of errors. + +!!! caution "Avoid exception when migrating from eZ Publish" + + If you [migrated to v1.13 from eZ Publish](../../migrating/migrating_from_ez_publish.md), and want to upgrade to v2.5, an exception will occur when you run the `bin/console ezplatform:page:migrate` command and the database contains internal drafts of Landing Pages. + + To avoid this exception, you must first [remove all internal drafts before you migrate](../../migrating/migrating_from_ez_publish.md#migration_exception). + +##### Block migration + +In v2.2 Page Builder does not offer all blocks that Landing Page editor did. The removed blocks include Keyword, Schedule, and Form blocks. +The Places block has been removed from the clean installation and will only be available in the demo out of the box. +If you use this block in your site, re-apply its configuration based on the [demo](https://github.com/ezsystems/ezplatform-ee-demo/blob/v2.2.2/app/config/blocks.yml). + +Later versions of Page Builder come with a Content Scheduler block and new Form Blocks, but migration of Schedule blocks to Content Scheduler blocks and of Form Blocks is not supported. + +If there are missing block definitions, such as Form Block or Schedule Block, +you have an option to continue, but migrated Landing Pages will come without those blocks. + +!!! tip + + If you use different repositories with different SiteAccesses, use the `--siteaccess` switch + to migrate them separately. + +!!! tip + + You can use the `--dry-run` switch to test the migration. + +After the migration is finished, you need to clear the cache. + +###### Migrate layouts + +The `ez_block::renderBlockAction` controller used in layout templates has been replaced by `EzPlatformPageFieldTypeBundle:Block:render`. This controller has two additional parameters, `locationId` and `languageCode`. Only `languageCode` is required. +Also, the HTML class `data-studio-zone` has been replaced with `data-ez-zone-id` +See [documentation](../../guide/content_rendering/render_content/render_page.md#render-a-layout) for an example on usage of the new controller. + +###### Migrate custom blocks + +Landing Page blocks (from v2.1 and earlier) were defined using a class implementing `EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Model\AbstractBlockType`. +In Page Builder (from v2.2 onwards), this interface is no longer present. Instead the logic of your block must be implemented in a [Listener](../../guide/page/page_blocks.md#block-events). +Typically, what you previously would do in `getTemplateParameters()`, you'll now do in the `onBlockPreRender()` event handler. + +The definition of block parameters has to be moved from `createBlockDefinition()` to the [YAML configuration](../../guide/page/create_custom_page_block.md) for your custom blocks. + +For more information about how custom blocks are implemented in Page Builder, have a look at [Creating custom Page blocks](../../guide/page/create_custom_page_block.md) for your custom blocks. + +For the migration of blocks from Landing Page to Page Builder, you'll need to provide a converter for attributes of custom blocks. For simple blocks you can use `\EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter`. +Custom converters must implement the `\EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\ConverterInterface` interface. +`convert()` will parse XML `\DOMNode $node` and return an array of `\EzSystems\EzPlatformPageFieldType\FieldType\LandingPage\Model\Attribute` objects. + +Below is an example of a simple converter for a custom block: + +``` yaml +app.block.foobar.converter: + class: EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter + tags: + - { name: ezplatform.fieldtype.ezlandingpage.migration.attribute.converter, block_type: foobar } +``` + +Notice service tag `ezplatform.fieldtype.ezlandingpage.migration.attribute.converter` that must be used for attribute converters. + +This converter is only needed when running the `ezplatform:page:migrate` script and can be removed once that has completed. + +###### Page migration example + +Below is an example how to migrate a Landing Page Layout and Block to new Page Builder. The code is based on the Random block +defined in the [Enterprise Beginner tutorial](../../tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md) + +??? tip "Landing Page code" + + `app/Resources/views/layouts/sidebar.html.twig`: + + ```php +
    +
    + {% if zones[0].blocks %} + {% for block in zones[0].blocks %} +
    + {{ render_esi(controller('ez_block::renderBlockAction', { + 'contentId': contentInfo.id, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo + })) }} +
    + {% endfor %} + {% endif %} +
    + +
    + ``` + + `app/config/layouts.yml`: + + ``` yaml + ez_systems_landing_page_field_type: + layouts: + sidebar: + identifier: sidebar + name: Right sidebar + description: Main section with sidebar on the right + thumbnail: assets/images/layouts/sidebar.png + template: layouts/sidebar.html.twig + zones: + first: + name: First zone + second: + name: Second zone + ``` + + `src/AppBundle/Block/RandomBlock.php`: + + ``` php + locationService = $locationService; + $this->contentService = $contentService; + $this->searchService = $searchService; + } + + public function getTemplateParameters(BlockValue $blockValue) + { + $attributes = $blockValue->getAttributes(); + $contentInfo = $this->contentService->loadContentInfo($attributes['parentContentId']); + $randomContent = $this->getRandomContent( + $this->getQuery($contentInfo->mainLocationId) + ); + + return [ + 'content' => $randomContent, + ]; + } + + /** + * Returns random picked Content. + * + * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query + * + * @return \eZ\Publish\API\Repository\Values\Content\Content + */ + private function getRandomContent(LocationQuery $query) + { + $results = $this->searchService->findLocations($query); + $searchHits = $results->searchHits; + if (count($searchHits) > 0) { + shuffle($searchHits); + + return $this->contentService->loadContentByContentInfo( + $searchHits[0]->valueObject->contentInfo + ); + } + + return null; + } + + /** + * Returns LocationQuery object based on given arguments. + * + * @param int $parentLocationId + * + * @return \eZ\Publish\API\Repository\Values\Content\LocationQuery + */ + private function getQuery($parentLocationId) + { + $query = new LocationQuery(); + $query->query = new Criterion\LogicalAnd([ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + new Criterion\ParentLocationId($parentLocationId), + ]); + + return $query; + } + + public function createBlockDefinition() + { + return new BlockDefinition( + 'random', + 'Random', + 'default', + 'assets/images/blocks/random_block.svg', + [], + [ + new BlockAttributeDefinition( + 'parentContentId', + 'Parent', + 'embed', + self::PATTERN_CONTENT_ID, + 'Choose a valid ContentID', + true, + false, + [], + [] + ), + ] + ); + } + + public function checkAttributesStructure(array $attributes) + { + if (!isset($attributes['parentContentId']) || preg_match(self::PATTERN_CONTENT_ID, $attributes['parentContentId']) !== 1) { + throw new InvalidBlockAttributeException('Parent container', 'parentContentId', 'Parent ContentID must be defined.'); + } + } + } + ``` + + `src/AppBundle/DependencyInjection/AppExtension.php`: + + ``` php + load('services.yml'); + } + + public function prepend(ContainerBuilder $container) + { + $configFile = __DIR__ . '/../Resources/config/blocks.yml'; + $config = Yaml::parse(file_get_contents($configFile)); + $container->prependExtensionConfig('ez_systems_landing_page_field_type', $config); + $container->addResource(new FileResource($configFile)); + } + } + ``` + + `src/AppBundle/Resources/config/blocks.yml`: + + ``` yaml + blocks: + random: + views: + random: + template: AppBundle:blocks:random.html.twig + name: Random Content Block View + ``` + + `src/AppBundle/Resources/config/services.yml`: + + ``` yaml + services: + app.block.random: + class: AppBundle\Block\RandomBlock + arguments: + - '@ezpublish.api.service.location' + - '@ezpublish.api.service.content' + - '@ezpublish.api.service.search' + tags: + - { name: landing_page_field_type.block_type, alias: random } + ``` + +??? tip "Corresponding Page Builder code" + + `app/Resources/views/layouts/sidebar.html.twig`: + + ```php +
    +
    + {% if zones[0].blocks %} + {% set locationId = parameters.location is not null ? parameters.location.id : contentInfo.mainLocationId %} + {% for block in zones[0].blocks %} +
    + {{ render_esi(controller('EzPlatformPageFieldTypeBundle:Block:render', { + 'locationId': locationId, + 'contentId': contentInfo.id, + 'blockId': block.id, + 'versionNo': versionInfo.versionNo, + 'languageCode': field.languageCode + })) }} +
    + {% endfor %} + {% endif %} +
    + +
    + ``` + + `app/config/layouts.yml`: + + ``` yaml + ezplatform_page_fieldtype: + layouts: + sidebar: + identifier: sidebar + name: Right sidebar + description: Main section with sidebar on the right + thumbnail: assets/images/layouts/sidebar.png + template: layouts/sidebar.html.twig + zones: + first: + name: First zone + second: + name: Second zone + ``` + + `src/AppBundle/Block/Event/Listener/RandomBlockListener.php` in place of `src/AppBundle/Block/RandomBlock.php`: + + ``` php + contentService = $contentService; + $this->locationService = $locationService; + $this->searchService = $searchService; + } + + /** + * @return array The event names to listen to + */ + public static function getSubscribedEvents() + { + return [ + BlockRenderEvents::getBlockPreRenderEventName('random') => 'onBlockPreRender', + ]; + } + + /** + * @param \EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent $event + * + * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + */ + public function onBlockPreRender(PreRenderEvent $event) + { + //BlockDefinitionFactory + $blockValue = $event->getBlockValue(); + $renderRequest = $event->getRenderRequest(); + $contentInfo = $this->contentService->loadContentInfo($blockValue->getAttribute('parentContentId')->getValue()); + + $randomContent = $this->getRandomContent( + $this->getQuery($contentInfo->mainLocationId) + ); + + $parameters = $renderRequest->getParameters(); + $parameters['content'] = $randomContent; + + $renderRequest->setParameters($parameters); + } + + /** + * Returns random picked Content. + * + * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query + * + * @return \eZ\Publish\API\Repository\Values\Content\Content + * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + */ + private function getRandomContent(LocationQuery $query) + { + $results = $this->searchService->findLocations($query); + $searchHits = $results->searchHits; + if (count($searchHits) > 0) { + shuffle($searchHits); + + return $this->contentService->loadContentByContentInfo( + $searchHits[0]->valueObject->contentInfo + ); + } + + return null; + } + + /** + * Returns LocationQuery object based on given arguments. + * + * @param int $parentLocationId + * + * @return \eZ\Publish\API\Repository\Values\Content\LocationQuery + */ + private function getQuery($parentLocationId) + { + $query = new LocationQuery(); + $query->query = new Criterion\LogicalAnd([ + new Criterion\Visibility(Criterion\Visibility::VISIBLE), + new Criterion\ParentLocationId($parentLocationId), + ]); + + return $query; + } + } + ``` + + `src/AppBundle/DependencyInjection/AppExtension.php`: + + ``` php + load('services.yml'); + } + + public function prepend(ContainerBuilder $container) + { + $configFile = __DIR__ . '/../Resources/config/blocks.yml'; + $config = Yaml::parse(file_get_contents($configFile)); + $container->prependExtensionConfig('ezplatform_page_fieldtype', $config); + $container->addResource(new FileResource($configFile)); + } + } + ``` + + `src/AppBundle/Resources/config/blocks.yml`: + + ``` yaml + blocks: + random: + name: Random + category: default + thumbnail: assets/images/layouts/sidebar.png + #configuration_template: blocks/random_config.html.twig + views: + random: + template: AppBundle:blocks:random.html.twig + name: Random Content Block View + attributes: + parentContentId: + type: embed + name: Parent Location ID + validators: + not_blank: + message: Please provide parent node + ``` + + `src/AppBundle/Resources/config/services.yml`: + + ``` yaml + services: + _defaults: + autowire: true + autoconfigure: true + public: false + + AppBundle\Block\Event\Listener\RandomBlockListener: ~ + + app.block.random.converter: + class: EzSystems\EzPlatformPageMigration\Converter\AttributeConverter\DefaultConverter + tags: + - { name: ezplatform.fieldtype.ezlandingpage.migration.attribute.converter, block_type: random } + ``` + + +### B. Update to v2.3 + +#### Database update script + +Apply the following database update script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.2.0-to-7.3.0.sql +``` + +#### Form Builder + +In an Enterprise installation, to create the *Forms* container under the content tree root use the following command: + +``` bash +php bin/console ezplatform:form-builder:create-forms-container +``` + +You can also specify Content Type, Field values and language code of the container, for example: + +``` bash +php bin/console ezplatform:form-builder:create-forms-container --content-type custom --field title --value 'My Forms' --field description --value 'Custom container for the forms' --language-code eng-US +``` + +You also need to run a script to add database tables for the Form Builder. +You can find it in https://github.com/ezsystems/ezplatform-ee-installer/blob/2.3/Resources/sql/schema.sql#L136 + +!!! caution "Form (ezform) Field Type" + + After the update, in order to create forms, you have to add a new Content Type (for example, named "Form") that contains `Form` Field (this Content Type can contain other fields + as well). After that you can use forms inside Landing Pages via Embed block. + +### C. Update to v2.4 + +#### Workflow + +When updating an Enterprise installation, you need to [run a script](https://github.com/ezsystems/ezplatform-ee-installer/blob/2.4/Resources/sql/schema.sql#L198) +to add database tables for the Editorial Workflow. + +#### Changes to the Forms folder + +The built-in Forms folder is located in the Form Section in versions 2.4+. + +If you are updating your Enterprise installation, you need to add this Section manually and move the folder to it. + +To allow anonymous users to access Forms, you also need to add the `content/read` Policy +with the *Form* Section to the Anonymous User. + +#### Changes to Custom tags + +v2.4 changed the way of configuring custom tags. They are no longer configured under the `ezpublish` key, +but one level higher in the YAML structure: + +``` yaml +ezpublish: + system: + : + fieldtypes: + ezrichtext: + custom_tags: [exampletag] + +ezrichtext: + custom_tags: + exampletag: + # ... +``` + +The old configuration is deprecated, so if you use custom tags, you need to modify your config accordingly. + + +### D. Update to v2.5 + +#### Database update script + +Apply the following database update script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.4.0-to-7.5.0.sql +``` + +##### v2.5.3 + +To update to v2.5.3, additionally run the following script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.2-to-7.5.3.sql +``` + +##### v2.5.6 + +To update to v2.5.6, additionally run the following script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.4-to-7.5.5.sql +``` + +or for PostgreSQL: + +``` bash +psql < vendor/ezsystems/ezpublish-kernel/data/update/postgres/dbupdate-7.5.4-to-7.5.5.sql +``` + +##### v2.5.9 + +To update to v2.5.9, additionally run the following script: + +``` bash +mysql -u -p < vendor/ezsystems/ezpublish-kernel/data/update/mysql/dbupdate-7.5.6-to-7.5.7.sql +``` + +or for PostgreSQL: + +``` bash +psql < vendor/ezsystems/ezpublish-kernel/data/update/postgres/dbupdate-7.5.6-to-7.5.7.sql +``` + +Additionally, reindex the content: + +``` bash +php bin/console ezplatform:reindex +``` + +#### Changes to database schema + +The introduction of [support for PostgreSQL](../../guide/databases.md#using-postgresql) includes a change in the way database schema is generated. + +It is now created based on [YAML configuration](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml), using the new [`DoctrineSchemaBundle`](https://github.com/ezsystems/doctrine-dbal-schema). + +If you are updating your application according to the usual procedure, no additional actions are required. +However, if you do not update your meta-repository, you need to take two additional steps: + +- enable `EzSystems\DoctrineSchemaBundle\DoctrineSchemaBundle()` in `AppKernel.php` +- add [`ez_doctrine_schema`](https://github.com/ezsystems/ezplatform/blob/2.5/app/config/config.yml#L33) configuration + +#### Changes to Matrix Field Type + +To migrate your content from legacy XML format to a new `ezmatrix` value use the following command: + +```bash +bin/console ezplatform:migrate:legacy_matrix +``` + +#### Required manual cache clearing if using Redis + +If you are using Redis as your persistence cache storage you should always clear it manually after an upgrade. +You can do it in two ways, by using `redis-cli` and executing the following command: + +```bash +FLUSHALL +``` + +or by executing the following command: + +```bash +bin/console cache:pool:clear cache.redis +``` + +#### Updating to 2.5.3 + +##### Page Builder + +This step is only required when updating an Enterprise installation from versions higher than v2.2 and lower than v2.5.3. +In case of versions lower than 2.2, skip this step or ignore the information that indexes from a script below already exist. + +When updating to v2.5.3, you need to run the following SQL commands to add missing indexes: + +``` bash +CREATE INDEX ezpage_map_zones_pages_zone_id ON ezpage_map_zones_pages(zone_id); +CREATE INDEX ezpage_map_zones_pages_page_id ON ezpage_map_zones_pages(page_id); +CREATE INDEX ezpage_map_blocks_zones_block_id ON ezpage_map_blocks_zones(block_id); +CREATE INDEX ezpage_map_blocks_zones_zone_id ON ezpage_map_blocks_zones(zone_id); +CREATE INDEX ezpage_map_attributes_blocks_attribute_id ON ezpage_map_attributes_blocks(attribute_id); +CREATE INDEX ezpage_map_attributes_blocks_block_id ON ezpage_map_attributes_blocks(block_id); +CREATE INDEX ezpage_blocks_design_block_id ON ezpage_blocks_design(block_id); +CREATE INDEX ezpage_blocks_visibility_block_id ON ezpage_blocks_visibility(block_id); +CREATE INDEX ezpage_pages_content_id_version_no ON ezpage_pages(content_id, version_no); +``` + +#### Updating to 2.5.16 + +##### Powered-By header + +In order to promote use of eZ Platform, `ezsystems/ez-support-tools` v1.0.10, as of eZ Platform v2.5.16, sets the Powered-By header. +It is enabled by default and generates a header like `Powered-By: eZ Platform Enterprise v2`. + +To omit the version number, use the following configuration: +``` yaml +ezplatform_support_tools: + system_info: + powered_by: + release: "none" +``` + +To opt out of the whole feature, disable it with the following configuration: + +``` yaml +ezplatform_support_tools: + system_info: + powered_by: + enabled: false +``` + +#### Updating to v2.5.18 + +To update to v2.5.18, if you are using MySQL, additionally run the following update SQL command: + +``` sql +ALTER TABLE ezpage_attributes MODIFY value LONGTEXT; +``` + +##### Update entity managers + +Version v2.5.18 introduces new entity managers. +To ensure that they work in multi-repository setups, you must update the GraphQL schema. +You do this manually by following this procedure: + +1. Update your project to v2.5.18 and run the `php bin/console cache:clear` command to generate the [service container](../../api/public_php_api.md#service-container). + +1. Run the following command to discover the names of the new entity managers. + Take note of the names that you discover: + + `php bin/console debug:container --parameter=doctrine.entity_managers --format=json | grep ibexa_` + +1. For every entity manager prefixed with `ibexa_`, run the following command: + + `php bin/console doctrine:schema:update --em= --dump-sql` + +1. Review the queries and ensure that there are no harmful changes that could affect your data. + +1. For every entity manager prefixed with `ibexa_`, run the following command to run queries on the database: + + `php bin/console doctrine:schema:update --em= --force` + +###### VCL configuration for Fastly + +[[% include 'snippets/update/vcl_configuration_for_fastly_v3.md' %]] + +##### Optimize workflow queries + +Run the following SQL queries to optimize workflow performance: + +``` sql +CREATE INDEX idx_workflow_co_id_ver ON ezeditorialworkflow_workflows(content_id, version_no); +CREATE INDEX idx_workflow_name ON ezeditorialworkflow_workflows(workflow_name); +``` + + +## 5. Finish the update + +[[% include 'snippets/update/finish_the_update.md' %]] + +[[% include 'snippets/update/notify_support.md' %]] + +??? tip "`defaultLayout` setting not available" + + If you migrated you installation from eZ Publish Platform, + in Page Builder you can encounter an issue where the **Default layout** dropdown is disabled + with a "Layout '' for setting 'defaultLayout' is not available" error message. + + If this happens, add the following temporary configuration to `app/config/ezplatform.yml`: + + ``` yaml + ezpublish: + system: + global: + ezpage: + layouts: + GlobalZoneLayout: + name: Global zone layout + template: globalzonelayout.tpl + 2ZonesLayout1: + name: 2 zones (layout 1) + template: 2zoneslayout1.tpl + 2ZonesLayout2: + name: 2 zones (layout 2) + template: 2zoneslayout2.tpl + 2ZonesLayout3: + name: 2 zones (layout 3) + template: 2zoneslayout3.tpl + 3ZonesLayout1: + name: 3 zones (layout 1) + template: 3zoneslayout1.tpl + 3ZonesLayout2: + name: 3 zones (layout 2) + template: 3zoneslayout2.tpl + CallForActionLayout: + name: Call For Action zone layout + template: callforactionlayout.tpl + ``` + + Clear the cache and refresh the page. The dropdown should now be active. + Select any option in the dropdown and save the Content Type. + + You should now be able to remove the Field definition from the Content Type. + + Afterwards, you can remove the configuration above from `ezplatform.yml`. + +## Update to v3.3 + +It is strongly recommended to also [update to the latest LTS, v3.3](../from_2.5/update_from_2.5.md). diff --git a/docs/updating/from_1.x_2.x/update_from_1.x_2.x.md b/docs/updating/from_1.x_2.x/update_from_1.x_2.x.md new file mode 100644 index 0000000000..849ce4498e --- /dev/null +++ b/docs/updating/from_1.x_2.x/update_from_1.x_2.x.md @@ -0,0 +1,21 @@ +--- +description: Update your installation to the latest v2.5 version from v1.13 or earlier v2 version. +target_version: '2.5' +latest_tag: '2.5.30' +--- + +# From 1.13 and 2.x + +This update procedure applies if you are using: + +- v1.13 +- v2.x +- v2.5 lower than the latest v2.5.x + +Go through the following steps to update to the latest v2.5 LTS (v[[= latest_tag =]]). + +1. [Check out a version](update_app_to_2.5.md#1-check-out-a-version) +1. [Resolve conflicts](update_app_to_2.5.md#2-resolve-conflicts) +1. [Update the app](update_app_to_2.5.md#3-update-the-app) +1. [Update the database](update_db_to_2.5.md#4-update-the-database) +1. [Finish the update](update_db_to_2.5.md#5-finish-the-update) diff --git a/docs/updating/from_2.5/adapt_code_to_v3.md b/docs/updating/from_2.5/adapt_code_to_v3.md new file mode 100644 index 0000000000..ee15a3a3ed --- /dev/null +++ b/docs/updating/from_2.5/adapt_code_to_v3.md @@ -0,0 +1,103 @@ +# Update code to v3 + +Before you start this procedure, make sure you have completed the previous step, +[Updating to v3.2](to_3.2.md). + +## 4. Update the code + +To adapt you installation to v3, you need to make a number of modifications to your code. + +### New project structure + +!!! tip + + If you run into issues, for details on all changes related to the switch to Symfony 5, + see [Symfony upgrade guide for 4.0](https://github.com/symfony/symfony/blob/4.4/UPGRADE-4.0.md) + and [for 5.0](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) + +The latest Symfony versions changed the organization of your project into folders and bundles. +When updating to eZ Platform v3 you need to move your files and modify file paths and namespace references. + +![Project structure changes in v3](../img/folder_structure_v3.png "Project folder structure changes between v2 and v3") + +#### Configuration + +Configuration files have been moved from `app/Resources/config` to `config`. +Package-specific configuration is placed in `config/packages` (e.g. `config/packages/ezplatform_admin_ui.yaml`). +This folder also contains `config/packages/ezplatform.yaml`, which contains all settings coming in from Kernel. + +#### PHP code and bundle organization + +Since Symfony 4 `src/` code is no longer organized in bundles, `AppBundle` has been removed from the default eZ Platform install. +In order to adapt, you'll need to move all your PHP code, such as controllers or event listeners, to the `src` folder and use the `App` namespace for your custom code instead. + +!!! tip "How to make AppBundle continue to work, for now" + + Refactoring bundles for `src/` folder can involve extensive changes, if you want to make your `src/AppBundle` continue to work, follow [an Autoloading src/AppBundle guide on Symfony Casts](https://symfonycasts.com/screencast/symfony4-upgrade/flex-composer.json#autoloading-src-amp-src-appbundle). + + You can also follow [Using a "path" Repository guide,](https://symfonycasts.com/screencast/symfony-bundle/extracting-bundle#using-a-path-repository) to create a [composer path repository.](https://getcomposer.org/doc/05-repositories.md#path) + If you have several bundles you can move them into a `packages/` directory and load them all with: + + ``` + "repositories": [ + { "type": "path", "url": "packages/*" }, + ], + ``` + + Once you are ready to refactor the code to `App` namespace, follow [Bye Bye AppBundle](https://symfonycasts.com/screencast/symfony4-upgrade/bye-appbundle) article. + +#### View templates + +Templates are no longer stored in `app/Resources/views`. +You need to move all your templates to the `templates` folder in your project's root. + +#### Translations + +Translation files have been moved out of `app/Resources/translations` into `translations` in your project's root. + +#### `web` and assets + +Content of the `web` folder is now placed in `public`. +Content of `app/Resources/assets` has been moved to `assets`. + +!!! note + + You also need to update paths that refer to the old location, + for example in [`webpack.config.js`](../../guide/project_organization.md#importing-configuration-from-a-bundle). + +!!! note "Full list of deprecations" + + If you encounter any issue during the upgrade, + see [eZ Platform v3.0 deprecations](../../releases/ez_platform_v3.0_deprecations.md#template-organization) + for details of all required changes to your code. + +### Third-party dependencies + +Because eZ Platform v3 is based on Symfony 5, you need to make sure all additional third-party dependencies +that your project uses have been adapted to Symfony 5. + +### Automatic code refactoring (optional) + +To simplify the process of adapting your code to Symfony 5, you can use [Rector, a reconstructor tool](https://github.com/rectorphp/rector) +that will automatically refactor your Symfony and PHPunit code. + +To properly refactor your code, you might need to run the Rector `process` command for each Symfony version from 4.0 to 5.0 in turn: + +`vendor/bin/rector process src --set symfony40` + +You can find all the available sets in [the Rector repository](https://github.com/rectorphp/rector/tree/v0.7.65/config/set). +Keep in mind that after automatic refactoring finishes there might be some code chunks that you need to fix manually. + +### Update code for specific parts of the system + +Now, go through the following steps and ensure all your code is up to date with v3: + +- [1. Update templates](update_code/1_update_templates.md) +- [2. Update configuration](update_code/2_update_configuration.md) +- [3. Update Field Types](update_code/3_update_field_types.md) +- [4. Update Signal Slots](update_code/4_update_signal_slots.md) +- [5. Update Online Editor](update_code/5_update_online_editor.md) +- [6. Update workflow](update_code/6_update_workflow.md) +- [7. Update extended code](update_code/7_update_extensions.md) +- [8. Update REST](update_code/8_update_rest.md) +- [9. Other code updates](update_code/9_update_other.md) diff --git a/docs/updating/from_2.5/to_3.2.md b/docs/updating/from_2.5/to_3.2.md new file mode 100644 index 0000000000..a1c6cb0465 --- /dev/null +++ b/docs/updating/from_2.5/to_3.2.md @@ -0,0 +1,29 @@ +--- +target_version: '3.2' +latest_tag: '3.2.8' +--- + +# Update the app to v3.2 + +!!! caution + + Before you start updating to v3.3, make sure that you are currently using the latest version of v2.5 (v[[= latest_tag_2_5 =]]). + If not, refer to the [update guide for v2.5](update_db_to_2.5.md#d-update-to-v25). + +To move from v2.5 to v3.3, first, you need to bring the app to version v3.2. + +## 1. Check out a version + +[[% include 'snippets/update/check_out_version.md' %]] + +## 2. Resolve conflicts + +[[% include 'snippets/update/merge_composer.md' %]] + +## 3. Update the app + +[[% include 'snippets/update/update_app.md' %]] + +## Next steps + +Now, proceed to the next step, [updating the code to v3.0](adapt_code_to_v3.md). diff --git a/docs/updating/from_2.5/to_3.3.latest.md b/docs/updating/from_2.5/to_3.3.latest.md new file mode 100644 index 0000000000..4570915c1c --- /dev/null +++ b/docs/updating/from_2.5/to_3.3.latest.md @@ -0,0 +1,177 @@ +--- +target_version: '3.3' +latest_tag: '3.3.24' +--- + +# Update to latest version of v3.3 + +Before you start this procedure, make sure you have completed the previous step, +[Updating the app to v3.3](to_3.3.md). + +Finally, bring your installation to the latest release of v3.3. + +## 6. Update the database + +[[% include 'snippets/update/db/update_db_2.5-3.3.md' %]] + +## 7. Update to the latest patch version + +### A. v3.3.2 + +#### Update entity managers + +Version v3.3.2 introduces new entity managers. +To ensure that they work in multi-repository setups, you must update the Doctrine schema. +You do this manually by following this procedure: + +1. Run the `php bin/console cache:clear` command to generate the service container. + +1. Run the following command to discover the names of the new entity managers. + Take note of the names that you discover: + + `php bin/console debug:container --parameter=doctrine.entity_managers --format=json | grep ibexa_` + +1. For every entity manager prefixed with `ibexa_`, run the following command: + + `php bin/console doctrine:schema:update --em= --dump-sql` + +1. Review the queries and ensure that there are no harmful changes that could affect your data. + +1. For every entity manager prefixed with `ibexa_`, run the following command to run queries on the database: + + `php bin/console doctrine:schema:update --em= --force` + +#### VCL configuration for Fastly + +[[% include 'snippets/update/vcl_configuration_for_fastly_v3.md' %]] + +#### Optimize workflow queries + +Run the following SQL queries to optimize workflow performance: + +``` sql +CREATE INDEX idx_workflow_co_id_ver ON ezeditorialworkflow_workflows(content_id, version_no); +CREATE INDEX idx_workflow_name ON ezeditorialworkflow_workflows(workflow_name); +``` + +#### Enable Commerce features + +Commerce features in Experience and Content editions are disabled by default. +If you use these features, after the update enable Commerce features by going to `config\packages\ecommerce.yaml` +and setting the following: + +``` yaml +ezplatform: + system: + default: + commerce: + enabled: true +``` + +Next, run the following command: + +``` bash +php bin/console ibexa:upgrade --force +``` + +#### Database update + +If you are using MySQL, run the following update script: + +``` sql +mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.1-to-3.3.2.sql +``` + +### B. v3.3.7 + +#### Commerce configuration + +If you are using Commerce, run the following migration action to update the way Commerce configuration is stored: + +``` bash +php bin/console ibexa:migrations:migrate --file=src/bundle/Resources/install/migrations/content/Components/move_configuration_to_settings.yaml +``` + +#### Database update + +Run the following SQL commands: + +=== "MySQL" + + ``` sql + CREATE TABLE IF NOT EXISTS `ibexa_workflow_version_lock` ( + `id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + `content_id` INT(11) NOT NULL, + `version` INT(11) NOT NULL, + `user_id` INT(11) NOT NULL, + `created` INT(11) NOT NULL DEFAULT 0, + `modified` INT(11) NOT NULL DEFAULT 0, + `is_locked` BOOLEAN NOT NULL DEFAULT true, + KEY `ibexa_workflow_version_lock_content_id_index` (`content_id`) USING BTREE, + KEY `ibexa_workflow_version_lock_user_id_index` (`user_id`) USING BTREE, + UNIQUE KEY `ibexa_workflow_version_lock_content_id_version_uindex` (`content_id`,`version`) USING BTREE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + ``` + +=== "PostgreSQL" + + ``` sql + CREATE TABLE IF NOT EXISTS ibexa_workflow_version_lock + ( + "id" SERIAL, + "content_id" INTEGER, + "version" INTEGER, + "user_id" INTEGER, + "created" INTEGER DEFAULT 0 NOT NULL, + "modified" INTEGER DEFAULT 0 NOT NULL, + "is_locked" boolean DEFAULT TRUE NOT NULL, + CONSTRAINT "ibexa_workflow_version_lock_pk" PRIMARY KEY ("id") + ); + + CREATE INDEX IF NOT EXISTS "ibexa_workflow_version_lock_content_id_index" + ON "ibexa_workflow_version_lock" ("content_id"); + + CREATE INDEX IF NOT EXISTS "ibexa_workflow_version_lock_user_id_index" + ON "ibexa_workflow_version_lock" ("user_id"); + + CREATE UNIQUE INDEX IF NOT EXISTS "ibexa_workflow_version_lock_content_id_version_uindex" + ON "ibexa_workflow_version_lock" ("content_id", "version"); + ``` + +### C. v3.3.9 + +#### Database update + +Run the following scripts: + +=== "MySQL" + + ``` sql + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.8-to-3.3.9.sql + ``` + +=== "PostgreSQL" + + ``` sql + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.8-to-3.3.9.sql + ``` + +### D. v3.3.24 + +#### VCL configuration for Fastly + +Ibexa DXP now supports Fastly shielding. If you are using Fastly and want to use shielding, you need to update your VCL files. + +!!! tip + + Even if you do not plan to use Fastly shielding, it is recommended to update the VCL files for future compatibility. + +1. Locate the `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_main.vcl` file and update your VCL file with the recent changes. +2. Do the same with `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_user_hash.vcl`. +3. Upload a new `snippet_re_enable_shielding.vcl` snippet file, based on `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl`. + +## 8. Finish the update + +[[% include 'snippets/update/finish_the_update.md' %]] + +[[% include 'snippets/update/notify_support.md' %]] diff --git a/docs/updating/from_2.5/to_3.3.md b/docs/updating/from_2.5/to_3.3.md new file mode 100644 index 0000000000..1d1b49b3ef --- /dev/null +++ b/docs/updating/from_2.5/to_3.3.md @@ -0,0 +1,120 @@ +--- +target_version: '3.3' +latest_tag: '3.3.24' +--- + +# Update the app to v3.3 + +Before you start this procedure, make sure you have completed the previous step, +[Updating code to v3](adapt_code_to_v3.md). + +## 5. Update to v3.3 + +Ibexa DXP v3.3 uses [Symfony Flex]([[= symfony_doc =]]/quick_tour/flex_recipes.html). +When updating from v3.2 to v3.3, you need to follow a special update procedure. + +!!! note + + Ibexa DXP v3.3 requires Composer 2.0.13 or higher. + +First, create an update branch `update-[[=target_version=]]` in git and commit your work. + +If you have not done it before, add the relevant meta-repository as an `upstream` remote: + +=== "ezplatform" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezplatform.git + ``` + +=== "ezplatform-ee" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezplatform-ee.git + ``` + +=== "ezcommerce" + + ``` bash + git remote add upstream http://github.com/ezsystems/ezcommerce.git + ``` + +!!! tip + + It is good practice to make git commits after every step of the update procedure. + +### A. Merge project skeleton + +Merge the current skeleton into your project: + +=== "Ibexa Content" + + ``` bash + git remote add content-skeleton https://github.com/ibexa/content-skeleton.git + git fetch content-skeleton --tags + git merge v[[= latest_tag =]] --allow-unrelated-histories + ``` + +=== "Ibexa Experience" + + ``` bash + git remote add experience-skeleton https://github.com/ibexa/experience-skeleton.git + git fetch experience-skeleton --tags + git merge v[[= latest_tag =]] --allow-unrelated-histories + ``` + +=== "Ibexa Commerce" + + ``` bash + git remote add commerce-skeleton https://github.com/ibexa/commerce-skeleton.git + git fetch commerce-skeleton --tags + git merge v[[= latest_tag =]] --allow-unrelated-histories + ``` + +This introduces changes from the relevant website skeleton and results in conflicts. + +Resolve the conflicts in the following way: + +- Make sure all automatically added `ezsystems/*` packages are removed. If you explicitly added any packages that are not part of the standard installation, retain them. +- Review the rest of the packages. If your project requires a package, keep it. +- If a package is only used as a dependency of an `ezsystems` package, remove it. You can check how the package is used with `composer why `. +- Keep the dependencies listed in the website skeleton. + +!!! tip + + You can also approach resolving conflicts differently: + run `git checkout --theirs composer.json` to get a clean `composer.json` from the skeleton + and then manually add any necessary changes from your project. + +!!! caution + + It is impossible to update an Enterprise edition (`ezsystems/ezplatform-ee`) + to an Ibexa Content edition. + +### B. Update the app + +Run `composer update` to update the dependencies: + +``` bash +composer update +``` + +### C. Configure the web server + +Add the following rewrite rule to your web server configuration: + +=== "Apache" + + ``` + RewriteRule ^/build/ - [L] + ``` + +=== "nginx" + + ``` + rewrite "^/build/(.*)" "/build/$1" break; + ``` + +## Next steps + +Now, proceed to the last step, [updating to the latest v3.3 patch version](to_3.3.latest.md). diff --git a/docs/updating/from_2.5/update_code/1_update_templates.md b/docs/updating/from_2.5/update_code/1_update_templates.md new file mode 100644 index 0000000000..7e5ea231bd --- /dev/null +++ b/docs/updating/from_2.5/update_code/1_update_templates.md @@ -0,0 +1,45 @@ +# 4.1. Update templates + +## Back-Office templates + +The naming and location of templates in the Back Office have been changed. +If you extend or modify these templates, you need to adapt your code. + +For the full list of template changes, see [the list of removals and deprecations](../../../releases/ez_platform_v3.0_deprecations.md#template-organization). + +## Twig functions and filters + +A number of [Twig functions, filters and helpers have been renamed](../../../releases/ez_platform_v3.0_deprecations.md#functions-renamed). +If your templates use them, you need to update them. + +## Templating component + +[The templating component integration is now deprecated.](https://symfony.com/blog/new-in-symfony-4-3-deprecated-the-templating-component-integration) +As a result, the way to indicate a template path has changed. + +For example: + +- **Use:** `"@@EzPlatformUser/user_settings/list.html.twig"` **instead of:** `"EzPlatformUserBundle:user_settings:list.html.twig"` +- **Use:** `{% extends "@EzPublishCore/content_fields.html.twig" %}` **instead of:** `{% extends "EzPublishCoreBundle::content_fields.html.twig" %}` + +## Form templates + +Content Type editing has been [moved from `repository-forms` to `ezplatform-admin-ui`](../../../releases/ez_platform_v3.0_deprecations.md#content-type-forms). + +Forms for content creation have been [moved from `repository-forms` to `ezplatform-content-forms`](../../../releases/ez_platform_v3.0_deprecations.md#repository-forms). + +If your templates extend any of those built-in templates, you need to update their paths. + +## Deprecated controller actions + +If your templates still use the deprecated `viewLocation` and `embedLocation` actions of `ViewController`, +you need to rewrite them to use `viewAction` and `embedAction` respectively. + +## Referencing controller actions + +To reference a controller, you now need to use `serviceOrFqcn::method` syntax instead of +`bundle:controller:action`: + +**Use:** `controller: My\ExampleBundle\Controller\DefaultController::articleViewAction` + +**Instead of:** `controller: AcmeExampleBundle:Default:articleView` diff --git a/docs/updating/from_2.5/update_code/2_update_configuration.md b/docs/updating/from_2.5/update_code/2_update_configuration.md new file mode 100644 index 0000000000..e91dd097a1 --- /dev/null +++ b/docs/updating/from_2.5/update_code/2_update_configuration.md @@ -0,0 +1,66 @@ +# 4.2. Update configuration + +## `ezpublish` configuration key + +The main YAML configuration key is now [`ezplatform` instead of `ezpublish`](../../../releases/ez_platform_v3.0_deprecations.md#configuration-through-ezplatform). +You need to change your configuration files to make use of the new key. For example: + +**Use:** + +``` yaml +ezplatform: + system: + default: + # ... +``` + +**instead of:** + +``` yaml +ezpublish: + system: + default: + # ... +``` + +## Resolving settings + +If you used dynamic settings (through `$setting$`), +or got settings from the [ConfigResolver](../../../guide/configuration/config_dynamic.md#configresolver) in a class constructor, +you now need to rewrite your code to inject the ConfigResolver and get the relevant setting: + +**Use:** + +``` php +use eZ\Publish\Core\MVC\ConfigResolverInterface; + +class MyService +{ + /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + private $configResolver; + + public function __construct(ConfigResolverInterface $configResolver) + { + $this->configResolver = $configResolver; + } + + public function myMethodWhichUsesSetting(): void + { + $setting = $this->configResolver->getParameter('setting'); + } +} +``` + +**instead of:** + +``` php +use eZ\Publish\Core\MVC\ConfigResolverInterface; + +class MyService +{ + public function __construct(ConfigResolverInterface $configResolver) + { + $this->setting = $configResolver->getParameter('setting'); + } +} +``` diff --git a/docs/updating/from_2.5/update_code/3_update_field_types.md b/docs/updating/from_2.5/update_code/3_update_field_types.md new file mode 100644 index 0000000000..83c6de1fd6 --- /dev/null +++ b/docs/updating/from_2.5/update_code/3_update_field_types.md @@ -0,0 +1,55 @@ +# 4.3. Update Field Types + +You need to adapt your custom Field Types to the new Field Type architecture. + +## `eZ\Publish\SPI\FieldType\FieldType` interface + +The `eZ\Publish\SPI\FieldType\FieldType` interface is now an abstract class. +You need to replace `implements FieldType` in your Field Type code with `extends FieldType`. + +## Deprecated `getName` method + +The deprecated method `getName` from the `eZ\Publish\SPI\FieldType\FieldType` interface has been changed. +Now it accepts two additional parameters: `FieldDefinition $fieldDefinition` and `string $languageCode`. + +In your code you need to change the `getName` signature +to `function getName(Value $value, FieldDefinition $fieldDefinition, string $languageCode): string;`. + +## `eZ\Publish\SPI\FieldType\Nameable` interface + +The `eZ\Publish\SPI\FieldType\Nameable` interface has been removed. +In your code you need to remove implementations of `Nameable` and replace them with +`eZ\Publish\SPI\FieldType\FieldType::getName`. + +## Deprecated tags + +You need to replace deprecated tags in service configuration: + +|Deprecated tag|Current tag| +|---|---| +|ezpublish.fieldType.parameterProvider|ezplatform.field_type.parameter_provider| +|ezpublish.fieldType.externalStorageHandler|ezplatform.field_type.external_storage_handler| +|ezpublish.fieldType.externalStorageHandler.gateway|ezplatform.field_type.external_storage_handler.gateway| +|ezpublish.fieldType|ezplatform.field_type| +|ezpublish.fieldType.indexable|ezplatform.field_type.indexable| +|ezpublish.storageEngine.legacy.converter|ezplatform.field_type.legacy_storage.converter| +|ez.fieldFormMapper.definition|ezplatform.field_type.form_mapper.definition| +|ez.fieldFormMapper.value|ezplatform.field_type.form_mapper.value| + +## Moved classes + +You need to replace importing the following classes: + +|Previous location|Current location| +|---|---| +|EzSystems\RepositoryForms\Data\Content\FieldData|EzSystems\EzPlatformContentForms\Data\Content\FieldData| +|EzSystems\RepositoryForms\Data\FieldDefinitionData|EzSystems\EzPlatformAdminUi\Form\Data\FieldDefinitionData| +|EzSystems\RepositoryForms\FieldType\FieldDefinitionFormMapperInterface|EzSystems\EzPlatformAdminUi\FieldType\FieldDefinitionFormMapperInterface| +|EzSystems\RepositoryForms\FieldType\FieldValueFormMapperInterface|EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface| + +## Extending Field Type templates + +If you extended templates for `ezobjectrelationlist_field`, `ezimageasset_field`, or `ezobjectrelation_field` Fields +using `{% extends "@EzPublishCore/content_fields.html.twig" %}`, +you now need to extend `EzSystemsPlatformHttpCache` instead, if you wish to make use of cache: +`{% extends "@EzSystemsPlatformHttpCache/content_fields.html.twig" %}`. diff --git a/docs/updating/from_2.5/update_code/4_update_signal_slots.md b/docs/updating/from_2.5/update_code/4_update_signal_slots.md new file mode 100644 index 0000000000..fddea918b9 --- /dev/null +++ b/docs/updating/from_2.5/update_code/4_update_signal_slots.md @@ -0,0 +1,39 @@ +# 4.4. Update Signal Slots + +If you used Signal Slots to listen for events in you custom code, +you need to rewrite them using Symfony Events and Listeners instead. + +The application now triggers two Events per operation: one before and one after the relevant thing happens +(see for example [Bookmark events](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Event/BookmarkService.php)). + +To use them, create [Event Listeners]([[= symfony_doc =]]/event_dispatcher.html) in your code, +for example: + +**Use:** + +``` php +public static function getSubscribedEvents(): array +{ + return [ + CreateBookmarkEvent::class => 'onCreateBookmark', + ] +} + +public function onCreateBookmark(CreateBookmarkEvent $event): void +{ + /// your code +} +``` + +**instead of:** + +``` php +public function receive(Signal $signal) +{ + if (!($signal instanceof CreateBookmarkSignal)) { + return; + } +} + +// your code +``` diff --git a/docs/updating/from_2.5/update_code/5_update_online_editor.md b/docs/updating/from_2.5/update_code/5_update_online_editor.md new file mode 100644 index 0000000000..80305e0542 --- /dev/null +++ b/docs/updating/from_2.5/update_code/5_update_online_editor.md @@ -0,0 +1,15 @@ +# 4.5. Update Online Editor + +## RichText + +Deprecated code related to the RichText Field Type has been removed from `ezpublish-kernel`. + +If your code still relies on the `eZ\Publish\Core\FieldType\RichText` namespace, you need to rewrite it +to use `EzSystems\EzPlatformRichText\eZ\RichText` instead. + +## Extra buttons + +Configuring custom Online Editor buttons with `ezrichtext.alloy_editor.extra_buttons` is deprecated. + +If you added custom buttons in this way, you need to rewrite your code to use +`ezplatform.system..fieldtypes.ezrichtext.toolbars..buttons` instead. diff --git a/docs/updating/from_2.5/update_code/6_update_workflow.md b/docs/updating/from_2.5/update_code/6_update_workflow.md new file mode 100644 index 0000000000..7e7e121de5 --- /dev/null +++ b/docs/updating/from_2.5/update_code/6_update_workflow.md @@ -0,0 +1,11 @@ +# 4.6. Update workflow + +[`flex-workflow` has been combined with `ezplatform-workflow`](../../../releases/ez_platform_v3.0_deprecations.md#flex-workflow) in the form of a Quick Review functionality. + +If you used custom subscribers for events in workflow, you can now rewrite this code +to use [custom actions](../../../guide/workflow/add_custom_workflow_action.md). + +To migrate your content which had been using Flex Workflow to the new Quick Review workflow, +run the following command: + +`php bin/console ezplatform:migrate:flex-workflow` diff --git a/docs/updating/from_2.5/update_code/7_update_extensions.md b/docs/updating/from_2.5/update_code/7_update_extensions.md new file mode 100644 index 0000000000..4534b0ef2e --- /dev/null +++ b/docs/updating/from_2.5/update_code/7_update_extensions.md @@ -0,0 +1,12 @@ +# 4.7. Update extended code + +## Universal Discovery Widget + +If you extended the Universal Discovery Widget +(e.g. added your own tabs or triggered opening the UDW for your own customizations), +you need to rewrite this extension using the [new YAML configuration](../../../extending/extending_udw.md). + +## Back Office extensibility + +If you added custom tab groups in the Back Office, +you now need to [make use of the `TabsComponent`](../../../extending/tabs/back_office_tabs.md#tab-groups). diff --git a/docs/updating/from_2.5/update_code/8_update_rest.md b/docs/updating/from_2.5/update_code/8_update_rest.md new file mode 100644 index 0000000000..a35a5d0e12 --- /dev/null +++ b/docs/updating/from_2.5/update_code/8_update_rest.md @@ -0,0 +1,57 @@ +# 4.8. Update REST + +If your code extends the REST API, you need to modify namespaces. +The `eZ\Publish\Core\REST` and `eZ\Publish\Core\REST\Common\` namespaces have been replaced by `EzSystems\EzPlatformRest`. +This is due to the fact that REST code has been moved from Kernel to a new `ezpublish-rest` package. + +## Custom installers + +eZ Platform provides extension point to create named custom installer which can be used instead of the native one. +To use it, execute the Symfony command: + +``` bash +php ./bin/console ezplatform:install +``` + +In eZ Platform v3.0, service definitions around that extension point have changed: + +1\. The deprecated Clean Installer has been dropped from `ezpublish-kernel` package. +If your project uses custom installer and has relied on Clean Installer service definition (`ezplatform.installer.clean_installer`) +you need to switch to Core Installer. + +**Use:** + +``` php +services: + Acme\App\Installer\MyCustomInstaller: + parent: EzSystems\PlatformInstallerBundle\Installer\CoreInstaller +``` + +**instead of**: + +``` php +services: + Acme\App\Installer\MyCustomInstaller: + parent: ezplatform.installer.clean_installer +``` + +`CoreInstaller` relies on [`DoctrineSchemaBundle`](https://github.com/ezsystems/doctrine-dbal-schema). +Custom schema can be installed defining Symfony Event Subscriber subscribing to `EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvents::BUILD_SCHEMA` event. + +2\. The deprecated Symfony Service definition `ezplatform.installer.db_based_installer` has been removed in favor of its FQCN-named definition. + +**Use:** + +``` php +services: + Acme\App\Installer\MyCustomInstaller: + parent: EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller +``` + +**instead of:** + +``` php +services: + Acme\App\Installer\MyCustomInstaller: + parent: ezplatform.installer.db_based_installer +``` diff --git a/docs/updating/from_2.5/update_code/9_update_other.md b/docs/updating/from_2.5/update_code/9_update_other.md new file mode 100644 index 0000000000..f996e8b955 --- /dev/null +++ b/docs/updating/from_2.5/update_code/9_update_other.md @@ -0,0 +1,68 @@ +# 4.9 Other code updates + +## HTTP cache + +HTTP cache bundle now uses FOS Cache Bundle v2. +If your code makes use of HTTP cache bundle, see [the list of changes and deprecations](../../../releases/ez_platform_v3.0_deprecations.md#ezplatform-http-cache). + +## User checker + +Add the user checker to firewall by adding the following line to `config/packages/security.yaml`: + +``` yaml hl_lines="5" +security: + firewalls: + ezpublish_front: + # ... + user_checker: eZ\Publish\Core\MVC\Symfony\Security\UserChecker + # ... +``` + +## Commands + +The `ContainerAwareCommand` class is not available in Symfony 5. Therefore, if your custom commands use `Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand` +as a base class, you must rewrite them to use `Symfony\Component\Console\Command\Command` instead. + +## Permissions + +Some [permission choice loaders](../../../releases/ez_platform_v3.0_deprecations.md#permission-choice-loaders) and [permission-related methods](../../../releases/ez_platform_v3.0_deprecations.md#code-cleanup-in-ez-platform-kernel) have been removed. +If your code uses them, you must rewrite it to use the permission resolver. + +## Service container parameters + +A number of Symfony [service container](../../../api/public_php_api.md#service-container) parameters [have been dropped](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/doc/bc/1.0/dropped-container-parameters.md). + +Check if your code uses such invalid parameters: search for them by using the `ezpublish\..*\.class` regular expression pattern. +When found, replace all the occurrences with fully-qualified class names. + +## QueryTypes + +If your code relies on automatically registering QueryTypes through the naming convention `\QueryType\*QueryType`, +you need to register your QueryTypes as services and tag them with `ezpublish.query`, or enable their automatic configuration (`autoconfigure: true`). + +## Symfony namespaces + +A number of Symfony namespaces have changed, and you must update your code if it uses them. +For example, the following namespaces are now different: + +|Use|Instead of| +|---|---| +|Symfony\Contracts\Translation\TranslatorInterface|Symfony\Component\Translation\TranslatorInterface| +|Symfony\Contracts\EventDispatcher\Event|Symfony\Component\EventDispatcher\Event| + +For more information, search for removed classes in Symfony [version 4.0](https://github.com/symfony/symfony/blob/4.4/UPGRADE-4.0.md) and [version 5.0](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) documentation. + +## Apache/Nginx configuration + +Make sure that your Apache/Nginx configuration is up to date with Symfony 5. +Refer to [the provided `vhost.template`](https://github.com/ezsystems/ezplatform/blob/master/doc/apache2/vhost.template) +for an example. + +## Deprecations + +Due to a number of compatibility breaks and deprecations introduced in eZ Platform v3.0, the changes that result from the above considerations might not be sufficient. +Make sure that you review your code and account for all changes listed in [Deprecations and backwards compatibility breaks](../../../releases/ez_platform_v3.0_deprecations.md). + +## Next steps + +Now, proceed to the next step, [updating to v3.3](../to_3.3.md). diff --git a/docs/updating/from_2.5/update_from_2.5.md b/docs/updating/from_2.5/update_from_2.5.md new file mode 100644 index 0000000000..96f046bdaa --- /dev/null +++ b/docs/updating/from_2.5/update_from_2.5.md @@ -0,0 +1,20 @@ +--- +description: Update your installation to the latest v3.3 version from v2.5. +target_version: '3.2' +latest_tag: '3.3.24' +--- + +# From 2.5 + +This update procedure applies if you are using v2.5. + +Go through the following steps to update to the latest v3.3 LTS (v[[= latest_tag =]]). + +1. [Check out a version](to_3.2.md) +1. [Resolve conflicts](to_3.2.md#2-resolve-conflicts) +1. [Update the app](to_3.2.md#3-update-the-app) +1. [Update code to v3](adapt_code_to_v3.md) +1. [Update to v3.3](to_3.3.md) +1. [Update the database](to_3.3.latest.md#6-update-the-database) +1. [Update to the latest patch version](to_3.3.latest.md#7-update-to-the-latest-patch-version) +1. [Finish the update](to_3.3.latest.md#8-finish-the-update) diff --git a/docs/updating/from_3.3/to_4.0.md b/docs/updating/from_3.3/to_4.0.md new file mode 100644 index 0000000000..14132dc181 --- /dev/null +++ b/docs/updating/from_3.3/to_4.0.md @@ -0,0 +1,196 @@ +# Update from v3.3.x to v4.0 + +This update procedure applies if you are using v3.3. + +Go through the following steps to update to v4.0. + +Besides updating the application and database, you need to account for changes related to code refactoring and numerous namespace changes. +See [a list of all changed namespaces, configuration key, service names, and other changes](../../releases/ibexa_dxp_v4.0_deprecations.md). + +An additional compatibility layer makes the process of updating your code easier. + +!!! note "Symfony 5.4" + + If you are using Symfony 5.3, you need to update your installation to Symfony 5.4. + To do this, update your composer.json to refer to `5.4.*` instead or `5.3.*`. + + Refer to the relevant website skeleton for an example: [content](https://github.com/ibexa/content-skeleton/blob/v4.0.1/composer.json), [experience](https://github.com/ibexa/experience-skeleton/blob/v4.0.1/composer.json), [commerce](https://github.com/ibexa/commerce-skeleton/blob/v4.0.1/composer.json). + +## Update the app to v4.0 + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + ``` + +### Update Flex server + +The `flex.ibexa.co` Flex server has been disabled. +If you are using v4.0.2 or earlier v4.0 version, you need to update your Flex server. + +To do it, in your `composer.json` check whether the `https://flex.ibexa.co` endpoint is still listed in `extra.symfony.endpoint`. +If so, replace it with the new [`https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main`](https://github.com/ibexa/website-skeleton/blob/v4.0.7/composer.json#L98) endpoint. + +If your `composer.json` still uses the `https://flex.ibexa.co` endpoint in `extra.symfony.endpoint`, +replace it with the new [`https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main`](https://github.com/ibexa/website-skeleton/blob/v4.0.7/composer.json#L96) endpoint. + +You can do it manually, or by running the following command: + +``` bash +composer config extra.symfony.endpoint "https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main" +``` + +Next, continue with updating the app: + +=== "[[= product_name_content =]]" + + ``` bash + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files, +which have been [renamed in this release](../../releases/ibexa_dxp_v4.0_deprecations.md#configuration-file-names). + +Look through the old YAML files and move your custom configuration to the relevant new files. + +In `bundles.php`, remove all entries starting with `eZ`, `EzSystems`, `Ibexa\Platform`, `Silversolutions` and `Siso`. +Leave only third-party entires and entries added by the `recipes:install` command, starting with `Ibexa\Bundle`. + +## Add compatibility layer package + +You can use the provided compatibility layer to speed up adaptation of your custom code to the new namespaces. + +Add the compatibility layer package using Composer: + +``` bash +composer require ibexa/compatibility-layer +composer recipes:install ibexa/compatibility-layer --force +``` + +Make sure that `Ibexa\Bundle\CompatibilityLayer\IbexaCompatibilityLayerBundle` is last in your bundle list in `config/bundles.php`. + +Next, clear the cache: + +``` bash +php bin/console cache:clear +``` + +## Update the database + +Apply the following database update script: + +``` bash +mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.latest-to-4.0.0.sql +``` + +``` bash +psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.latest-to-4.0.0.sql +``` + +### Prepare new database tables + +For every database connection you have configured, perform the following steps: + +1. Run `php bin/console doctrine:schema:update --dump-sql --em=ibexa_{connection}` +2. Check the queries and verify that they are safe and will not damage the data. +3. Run `php bin/console doctrine:schema:update --dump-sql --em=ibexa_{connection} --force` + +Next, run the following commands to import necessary data migration scripts: + +``` bash +php bin/console ibexa:migrations:import vendor/ibexa/taxonomy/src/bundle/Resources/install/migrations/content_types.yaml --name=000_taxonomy_content_types.yml +php bin/console ibexa:migrations:import vendor/ibexa/taxonomy/src/bundle/Resources/install/migrations/sections.yaml --name=001_taxonomy_sections.yml +php bin/console ibexa:migrations:import vendor/ibexa/taxonomy/src/bundle/Resources/install/migrations/content.yaml --name=002_taxonomy_content.yml +php bin/console ibexa:migrations:import vendor/ibexa/taxonomy/src/bundle/Resources/install/migrations/permissions.yaml --name=003_taxonomy_permissions.yml +php bin/console ibexa:migrations:import vendor/ibexa/product-catalog/src/bundle/Resources/migrations/product_catalog.yaml --name=001_product_catalog.yaml +php bin/console ibexa:migrations:import vendor/ibexa/product-catalog/src/bundle/Resources/migrations/currencies.yaml --name=001_currencies.yaml +``` + +Run `php bin/console ibexa:migrations:migrate -v --dry-run` to ensure that all migrations are ready to be performed. +If the dry run is successful, run: + +``` bash +php bin/console ibexa:migrations:migrate +``` + +## Update your custom code + +### Online editor + +#### Custom plugins and buttons + +If you added your own Online Editor plugins or buttons, you need to rewrite them +using [CKEditor 5's extensibility](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/plugins/creating-simple-plugin.html). + +#### Custom tags + +If you created a custom tag, you need to adapt it to the new configuration, for example: + +``` yaml +ibexa: + system: + admin_group: + fieldtypes: + ezrichtext: + custom_tags: [ezfactbox] + toolbar: + custom_tags_group: + buttons: + ezfactbox: + priority: 5 +``` + +### Personalization + +In Personalization, the `included_content_types` configuration key has changed to `included_item_types`. +Update your configuration, if it applies. + +## Finish update + +Adapt your `composer.json` file according to [`manifest.json`](https://github.com/ibexa/recipes/blob/master/ibexa/commerce/4.0.x-dev/manifest.json#L170-L171), by adding the following lines: + +``` json hl_lines="2-3" +"yarn install": "script", +"ibexa:encore:compile --config-name app": "symfony-cmd", +"bazinga:js-translation:dump %PUBLIC_DIR%/assets --merge-domains": "symfony-cmd", +"ibexa:encore:compile": "symfony-cmd" +``` + +Then, finish the update process: + +``` bash +composer run post-install-cmd +``` + +Finally, generate the new GraphQl schema: + +``` bash +php bin/console ibexa:graphql:generate-schema +``` diff --git a/docs/updating/from_3.3/update_from_3.3.md b/docs/updating/from_3.3/update_from_3.3.md new file mode 100644 index 0000000000..2488898120 --- /dev/null +++ b/docs/updating/from_3.3/update_from_3.3.md @@ -0,0 +1,414 @@ +--- +description: Update your installation to the latest v3.3 version from an earlier v3.3 version. +--- + +# Update from v3.3.x to v3.3.latest + +This update procedure applies if you are using a v3.3 installation without the latest maintenance release. + +Go through the following steps to update to the latest maintenance release of v3.3 (v[[= latest_tag_3_3 =]]). + +!!! note + + You can only update to the latest patch release of 3.3.x. + +## Update the application + +!!! note + + If you are using v3.3.15 or earlier v3.3 version, or encounter an error related to flex.ibexa.co, you need to [update your Flex server](#update-flex-server) first. + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_3_3 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_3_3 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_3_3 =]] --with-all-dependencies --no-scripts + ``` + +### Update Flex server + +The `flex.ibexa.co` Flex server has been disabled. +If you are using v3.3.15 or earlier v3.3 version, you need to update your Flex server. +In your `composer.json` check whether the `https://flex.ibexa.co` endpoint is still listed in `extra.symfony.endpoint`. +If that's the case, you need to perform the following update procedure. + +First, update the `symfony/flex` bundle to handle the new endpoint: + +```bash +composer update symfony/flex --no-plugins --no-scripts; +``` + +Then, replace the `https://flex.ibexa.co` endpoint with the new [`https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main`](https://github.com/ibexa/website-skeleton/blob/v3.3.20/composer.json#L98) endpoint in `composer.json` under `extra.symfony.endpoint`. + +You can do it manually, or by running the following command: + +```bash +composer config extra.symfony.endpoint "https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main" +``` + +Next, continue with updating the app: + +=== "[[= product_name_content =]]" + + ``` bash + composer recipes:install ibexa/content --force -v + composer run post-install-cmd + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer recipes:install ibexa/experience --force -v + composer run post-install-cmd + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer recipes:install ibexa/commerce --force -v + composer run post-install-cmd + ``` + +Review the changes to make sure your custom configuration wasn't affected. + +Remove the `vendor` folder to prevent issues related to the [new Flex server](#update-flex-server). + +Then, perform a database upgrade and other steps relevant to the version you are updating to. + +!!! caution "Clear Redis cache" + + If you are using Redis as your persistence cache storage you should always clear it manually after an upgrade. + You can do it by executing the following command: + + ```bash + php bin/console cache:pool:clear cache.redis + ``` + +### v3.3.2 + +#### Update entity managers + +Version v3.3.2 introduces new entity managers. +To ensure that they work in multi-repository setups, you must update the Doctrine schema. +You do this manually by following this procedure: + +1. Update your project to v3.3.2 and run the `php bin/console cache:clear` command to generate the service container. + +1. Run the following command to discover the names of the new entity managers. + Take note of the names that you discover: + + `php bin/console debug:container --parameter=doctrine.entity_managers --format=json | grep ibexa_` + +1. For every entity manager prefixed with `ibexa_`, run the following command: + + `php bin/console doctrine:schema:update --em= --dump-sql` + +1. Review the queries and ensure that there are no harmful changes that could affect your data. + +1. For every entity manager prefixed with `ibexa_`, run the following command to run queries on the database: + + `php bin/console doctrine:schema:update --em= --force` + +#### VCL configuration for Fastly + +[[% include 'snippets/update/vcl_configuration_for_fastly_v3.md' %]] + +#### Optimize workflow queries + +Run the following SQL queries to optimize workflow performance: + +``` sql +CREATE INDEX idx_workflow_co_id_ver ON ezeditorialworkflow_workflows(content_id, version_no); +CREATE INDEX idx_workflow_name ON ezeditorialworkflow_workflows(workflow_name); +``` + +#### Enable Commerce features + +Commerce features in Experience and Content editions are disabled by default. +If you use these features, after the update enable Commerce features by going to `config\packages\ecommerce.yaml` +and setting the following: + +``` yaml +ezplatform: + system: + default: + commerce: + enabled: true +``` + +Next, run the following command: + +``` bash +php bin/console ibexa:upgrade --force +``` + +#### Database update + +If you are using MySQL, run the following update script: + +``` sql +mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.1-to-3.3.2.sql +``` + +### v3.3.4 + +#### Migration Bundle + +Remove `Kaliop\eZMigrationBundle\eZMigrationBundle::class => ['all' => true],` +from `config/bundles.php` before running `composer require`. + +Then, in `composer.json`, set minimum stability to `stable`: + +``` json +"minimum-stability": "stable", +``` + +### v3.3.6 + +#### Symfony 5.3 + +To update to Symfony 5.3, update the following package versions in your `composer.json`, +including the Symfony version (line 9): + +``` json hl_lines="9" +"symfony/flex": "^1.3.1" +"sensio/framework-extra-bundle": "^6.1", +"symfony/runtime": "*", +"doctrine/doctrine-bundle": "^2.4" +"symfony/maker-bundle": "^1.0", + +"symfony": { + "allow-contrib": true, + "require": "5.3.*", + "endpoint": "https://flex.ibexa.co" +}, +``` + +See https://github.com/ibexa/website-skeleton/pull/5/files for details of the package version change. + +### v3.3.7 + +#### Commerce configuration + +If you are using Commerce, run the following migration action to update the way Commerce configuration is stored: + +``` bash +mkdir --parent src/Migrations/Ibexa/migrations +cp vendor/ibexa/installer/src/bundle/Resources/install/migrations/content/Components/move_configuration_to_settings.yaml src/Migrations/Ibexa/migrations/ +php bin/console ibexa:migrations:migrate --file=move_configuration_to_settings.yaml +``` + +#### Database update + +Run the following scripts: + +=== "MySQL" + + ``` shell + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.6-to-3.3.7.sql + ``` + +=== "PostgreSQL" + + ``` shell + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.6-to-3.3.7.sql + ``` + +### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, apply the following database upgrade script: + +=== "MySQL" + + ``` sql + DROP TABLE IF EXISTS `ibexa_setting`; + CREATE TABLE `ibexa_setting` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `group` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL, + `identifier` varchar(128) COLLATE utf8mb4_unicode_520_ci NOT NULL, + `value` text COLLATE utf8mb4_unicode_520_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ibexa_setting_group_identifier` (`group`, `identifier`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + ``` + +=== "PostgreSQL" + + ``` sql + DROP TABLE IF EXISTS ibexa_setting; + CREATE TABLE ibexa_setting ( + id SERIAL NOT NULL, + "group" varchar(128) NOT NULL, + identifier varchar(128) NOT NULL, + value json NOT NULL, + PRIMARY KEY (id), + CONSTRAINT ibexa_setting_group_identifier UNIQUE ("group", identifier) + ); + ``` + +### v3.3.9 + +#### Database update + +Run the following scripts: + +=== "MySQL" + + ``` shell + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.8-to-3.3.9.sql + ``` + +=== "PostgreSQL" + + ``` shell + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.8-to-3.3.9.sql + ``` + +### v3.3.13 + +!!! note "Symfony 5.4" + + Prior to v3.3.13, Symfony 5.3 was used by default. + + If you are still using Symfony 5.3, you need to update your installation to Symfony 5.4. + To do this, update your `composer.json` to refer to `5.4.*` instead or `5.3.*`. + + Refer to the relevant website skeleton: [content](https://github.com/ibexa/content-skeleton/blob/v3.3.13/composer.json), [experience](https://github.com/ibexa/experience-skeleton/blob/v3.3.13/composer.json), [commerce](https://github.com/ibexa/commerce-skeleton/blob/v3.3.13/composer.json). + + The following `sed` commands should update the relevant lines. + Use them with caution and properly check the result: + + ```shell + sed -i -E 's/"symfony\/(.+)": "5.3.*"/"symfony\/\1": "5.4.*"/' composer.json; + sed -i -E 's/"require": "5.3.*"/"require": "5.4.*"/' composer.json; + ``` + + After this `composer.json` update, run `composer update "symfony/*"`. + + You may need to adapt configuration to fit the new minor version of Symfony. + For example, you might have to remove `timeout` related config from `nelmio_solarium` bundle config: + + ```shell + sed -i -E '/ *timeout: [0-9]+/d' ./config/packages/nelmio_solarium.yaml ./config/packages/ezcommerce/ezcommerce_advanced.yaml + composer update "symfony/*" + ``` + +### v3.3.14 + +#### VCL configuration + +Update your Varnish VCL file to align with [`docs/varnish/vcl/varnish5.vcl`](https://github.com/ezsystems/ezplatform-http-cache/blob/2.3/docs/varnish/vcl/varnish5.vcl). +Make sure it contains the highlighted additions. + +``` vcl hl_lines="4-7 16" +// Compressing the content +// ... + +// Modify xkey header to add translation suffix +if (beresp.http.xkey && beresp.http.x-lang) { + set beresp.http.xkey = beresp.http.xkey + " " + regsuball(beresp.http.xkey, "(\S+)", "\1" + beresp.http.x-lang); +} + +// ... + +if (client.ip ~ debuggers) { +/// ... +} else { + // Remove tag headers when delivering to non debug client + unset resp.http.xkey; + unset resp.http.x-lang; + // Sanity check to prevent ever exposing the hash to a non debug client. + unset resp.http.x-user-context-hash; +} +``` + +### v3.3.15 + +Adapt your `composer.json` file according to [`manifest.json`](https://github.com/ibexa/recipes/blob/master/ibexa/commerce/3.3.x-dev/manifest.json#L167-L168), by adding and moving the following lines: + +``` diff + "composer-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd", +- "bazinga:js-translation:dump %PUBLIC_DIR%/assets --merge-domains": "symfony-cmd", + "yarn install": "script", ++ "ibexa:encore:compile --config-name app": "symfony-cmd", ++ "bazinga:js-translation:dump %PUBLIC_DIR%/assets --merge-domains": "symfony-cmd", + "ibexa:encore:compile": "symfony-cmd" + } +``` + +### v3.3.16 + +See [Update Flex server](#update-flex-server). + +### v3.3.24 + +#### VCL configuration for Fastly + +Ibexa DXP now supports Fastly shielding. If you are using Fastly and want to use shielding, you need to update your VCL files. + +!!! tip + + Even if you do not plan to use Fastly shielding, it is recommended to update the VCL files for future compatibility. + +1. Locate the `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_main.vcl` file and update your VCL file with the recent changes. +2. Do the same with `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/ez_user_hash.vcl`. +3. Upload a new `snippet_re_enable_shielding.vcl` snippet file, based on `vendor/ezsystems/ezplatform-http-cache-fastly/fastly/snippet_re_enable_shielding.vcl`. + +### v3.3.25 + +#### Database update + +On Experience or Commerce edition, run the following scripts: + +=== "MySQL" + + ``` shell + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.24-to-3.3.25.sql + ``` + +=== "PostgreSQL" + + ``` shell + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.24-to-3.3.25.sql + ``` + +### v3.3.28 + +#### Ensure password safety + +Following [Security advisory: IBEXA-SA-2022-009](https://developers.ibexa.co/security-advisories/ibexa-sa-2022-009-critical-vulnerabilities-in-graphql-role-assignment-ct-editing-and-drafts-tooltips), +unless you can verify based on your log files that the vulnerability hasn't been exploited, +you should [revoke passwords](https://doc.ibexa.co/en/latest/users/user_management/#revoking-passwords) for all affected users. + +### v3.3.34 + +#### Database update + +Run the following scripts: + +=== "MySQL" + + ``` sql + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-3.3.33-to-3.3.34.sql + ``` + +=== "PostgreSQL" + + ``` sql + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-3.3.33-to-3.3.34.sql + ``` \ No newline at end of file diff --git a/docs/updating/from_4.0/to_4.1.md b/docs/updating/from_4.0/to_4.1.md new file mode 100644 index 0000000000..1c7080c8c2 --- /dev/null +++ b/docs/updating/from_4.0/to_4.1.md @@ -0,0 +1,301 @@ +--- +description: Update your installation to the latest v4.1 version from v4.0. +--- + +# Update from v4.0.x to v4.1 + +This update procedure applies if you are using v4.0.0. + +Go through the following steps to update to v4.1. + +!!! note + + During the update process you can encounter the following error: + + `Failed to create closure from callable: class 'Ibexa\Bundle\Commerce\Eshop\Twig\SilvercommonExtension' does not have a method 'getNavigation'` + + You can ignore this error, it does not require any action on your part. + +## Update the app to latest version of v4.0 + +First, update your application to the latest version of v4.0: v4.0.8. + +### Update Flex server + +The `flex.ibexa.co` Flex server has been disabled. +If you are using earlier v4.x versions, and you have not done it before, +you have to update your Flex server. + +To do it, in your `composer.json`, check whether the `https://flex.ibexa.co` endpoint is still listed in `extra.symfony.endpoint`. +If so, replace it with the new [`https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main`](https://github.com/ibexa/website-skeleton/blob/v4.1.5/composer.json#L96) endpoint. + +You can do it manually, or by running the following command: + +``` bash +composer config extra.symfony.endpoint "https://api.github.com/repos/ibexa/recipes/contents/index.json?ref=flex/main" +``` + +Perform the update: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_0 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +Next, run: + +``` bash +composer run post-install-cmd +mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.0.3-to-4.0.4.sql +``` + +## Update the app to v4.1.0 + +When you have the v4.0 version, you can update to v4.1.0: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:4.1.0 --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:4.1.0 --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:4.1.0 --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. Look through the old YAML files and move your custom configuration to the relevant new files. + +Next, run: + +``` bash +composer run post-install-cmd +``` + +### Update the database + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.0.0-to-4.1.0.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.0.0-to-4.1.0.sql + ``` + +#### Ibexa Open Source + +If you are using Ibexa OSS and have no access to Ibexa DXP's `ibexa/installer` package, database upgrade is not necessary. + +## Update the app to latest version of v4.1 + +Now, update the application to the latest version of v4.1: [[= latest_tag_4_1 =]]. + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +Next, run: + +``` bash +composer run post-install-cmd +``` + +### Update the database + +Apply the following database update scripts: + +=== "MySQL" + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.1.0-to-4.1.1.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.1.0-to-4.1.1.sql + ``` + +## Configure the product catalog + +!!! caution + + Always back up your data before you perform any actions on the product catalog. + +Regardless of whether your application already uses the product catalog or you want +to start using this functionality, you can choose to use the old features, +present in v4.0.x, or upgrade to the all new product catalog that v4.1.x brings. + +To use the legacy solution, in the `config/packages` folder, +in YAML files with shop configuration, under the `parameters` key, +make sure that the `ibexa.commerce.site_access.config.eshop.default.catalog_data_provider` parameter is set to `ez5`. + +To use the new product catalog, since the new solution does not support the old +price engine out of the box, in your price engine configuration, +you must update the following parameters by providing the +`Ibexa\\ProductCatalog\\Bridge\\PriceProvider` value in the `ibexa_setting` table, +`commerce` group, `config` identifier: + +```yaml +ibexa.commerce.site_access.config.price.default.price_service_chain.basket +ibexa.commerce.site_access.config.price.default.price_service_chain.wish_list +ibexa.commerce.site_access.config.price.default.price_service_chain.comparison +ibexa.commerce.site_access.config.price.default.price_service_chain.wish_list +ibexa.commerce.site_access.config.price.default.price_service_chain.comparison +ibexa.commerce.site_access.config.price.default.price_service_chain.quick_order +ibexa.commerce.site_access.config.price.default.price_service_chain.search_list +ibexa.commerce.site_access.config.price.default.price_service_chain.product_list +ibexa.commerce.site_access.config.price.default.price_service_chain.stored_basket +ibexa.commerce.site_access.config.price.default.price_service_chain.basket_variant +ibexa.commerce.site_access.config.price.default.price_service_chain.product_detail +ibexa.commerce.site_access.config.price.default.price_service_chain.bestseller_list +ibexa.commerce.site_access.config.price.default.price_service_chain.slider_product_list +ibexa.commerce.site_access.config.price.default.price_service_chain.quick_order_line_preview +``` + +You can do it by using the `UPDATE ibexa_setting` command. + +??? note "Example of price engine configuration" + + ``` bash + UPDATE ibexa_setting SET value = + '{"ibexa.commerce.site_access.config.basket.default.validHours": 120, + "ibexa.commerce.site_access.config.core.default.category_view": "product_list", + "ibexa.commerce.site_access.config.core.default.currency_list": {"CAD": "1.55686", "EUR": "1", "GBP": "0.86466", "USD": "1.23625"}, + "ibexa.commerce.site_access.config.basket.default.stock_in_column": true, + "ibexa.commerce.site_access.config.core.default.shipping_vat_code": "19", + "ibexa.commerce.site_access.config.basket.default.description_limit": 50, + "ibexa.commerce.site_access.config.core.default.bestseller_threshold": 1, + "ibexa.commerce.site_access.config.checkout.de.payment_method.invoice": true, + "ibexa.commerce.site_access.config.checkout.en.payment_method.invoice": true, + "ibexa.commerce.site_access.config.eshop.default.erp.variant_handling": "SKU_ONLY", + "ibexa.commerce.site_access.config.wishlist.default.description_limit": 50, + "ibexa.commerce.site_access.config.eshop.default.webconnector.password": "passwo", + "ibexa.commerce.site_access.config.eshop.default.webconnector.username": "admin", + "ibexa.commerce.site_access.config.checkout.de.shipping_method.standard": true, + "ibexa.commerce.site_access.config.checkout.en.shipping_method.standard": true, + "ibexa.commerce.site_access.config.core.default.marketing.olark_chat.id": "6295-386-10-7457", "ibexa.commerce.site_access.config.newsletter.default.newsletter_active": false, + "ibexa.commerce.site_access.config.basket.default.recalculatePricesAfter": "3 hours", + "ibexa.commerce.site_access.config.basket.stored.default.stock_in_column": true, + "ibexa.commerce.site_access.config.core.default.currency_rate_changed_at": "01.01.2018", + "ibexa.commerce.site_access.config.core.default.template_debitor_country": "DE", + "ibexa.commerce.site_access.config.eshop.default.webconnector.erpTimeout": 5, + "ibexa.commerce.site_access.config.eshop.default.webconnector.soapTimeout": 5, + "ibexa.commerce.site_access.config.basket.stored.default.description_limit": 50, + "ibexa.commerce.site_access.config.checkout.default.payment_method.invoice": true, + "ibexa.commerce.site_access.config.eshop.default.catalog_description_limit": 50, + "ibexa.commerce.site_access.config.newsletter.default.unsubscribe_globally": true, + "ibexa.commerce.site_access.config.price.default.price_service_chain.basket": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.basket.default.refreshCatalogElementAfter": "1 hours", + "ibexa.commerce.site_access.config.checkout.default.shipping_method.standard": true, + "ibexa.commerce.site_access.config.core.default.enable_customer_number_login": false, + "ibexa.commerce.site_access.config.newsletter.default.newsletter2go_auth_key": "", + "ibexa.commerce.site_access.config.newsletter.default.newsletter2go_password": "", + "ibexa.commerce.site_access.config.newsletter.default.newsletter2go_username": "", + "ibexa.commerce.site_access.config.core.default.automatic_currency_conversion": true, + "ibexa.commerce.site_access.config.erp.default.web_connector.service_location": "http://webconnproxy.silver-eshop.de?config=harmony_wc3_noop_mapping", + "ibexa.commerce.site_access.config.core.default.marketing.olark_chat.activated": false, + "ibexa.commerce.site_access.config.price.default.price_service_chain.wish_list": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.checkout.de.shipping_method.express_delivery": true, + "ibexa.commerce.site_access.config.checkout.en.shipping_method.express_delivery": true, + "ibexa.commerce.site_access.config.order.management.local.default.shipping_cost": "", + "ibexa.commerce.site_access.config.order.management.local.default.shipping_free": "", + "ibexa.commerce.site_access.config.price.default.price_service_chain.comparison": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.core.default.bestseller_limit_on_catalog_page": 6, + "ibexa.commerce.site_access.config.price.default.price_service_chain.quick_order": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.price.default.price_service_chain.search_list": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.basket.default.additional_text_for_basket_line": false, + "ibexa.commerce.site_access.config.core.default.bestseller_limit_in_silver_module": 6, + "ibexa.commerce.site_access.config.price.default.price_service_chain.product_list": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.price.default.price_service_chain.stored_basket": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.core.de.standard_price_factory.fallback_currency": "EUR", + "ibexa.commerce.site_access.config.core.default.bestseller_limit_on_bestseller_page": 6, + "ibexa.commerce.site_access.config.core.default.use_template_debitor_contact_number": false, + "ibexa.commerce.site_access.config.core.en.standard_price_factory.fallback_currency": "EUR", + "ibexa.commerce.site_access.config.price.default.price_service_chain.basket_variant": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.price.default.price_service_chain.product_detail": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.checkout.default.shipping_method.express_delivery": false, + "ibexa.commerce.site_access.config.core.default.standard_price_factory.base_currency": "EUR", + "ibexa.commerce.site_access.config.core.default.use_template_debitor_customer_number": true, + "ibexa.commerce.site_access.config.price.default.price_service_chain.bestseller_list": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.checkout.de.payment_method.paypal_express_checkout": true, + "ibexa.commerce.site_access.config.checkout.en.payment_method.paypal_express_checkout": true, + "ibexa.commerce.site_access.config.core.default.price_requests_without_customer_number": true, + "ibexa.commerce.site_access.config.eshop.default.last_viewed_products_in_session_limit": 10, + "ibexa.commerce.site_access.config.basket.default.discontinued_products_listener_active": true, + "ibexa.commerce.site_access.config.core.default.standard_price_factory.fallback_currency": "EUR", + "ibexa.commerce.site_access.config.price.default.price_service_chain.slider_product_list": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.checkout.default.order_confirmation.sales_email_address": "", + "ibexa.commerce.site_access.config.checkout.default.payment_method.paypal_express_checkout": true, + "ibexa.commerce.site_access.config.basket.default.additional_text_for_basket_line_input_limit": 30, + "ibexa.commerce.site_access.config.price.default.price_service_chain.quick_order_line_preview": ["Ibexa\\\\ProductCatalog\\\\Bridge\\\\PriceProvider"], + "ibexa.commerce.site_access.config.newsletter.default.display_newsletter_box_for_logged_in_users": true, + "ibexa.commerce.site_access.config.basket.default.discontinued_products_listener_consider_packaging_unit": true}' WHERE `group` = 'commerce' AND identifier = 'config'; + ``` + +After you update the settings, you can proceed to working with your products. + +## Finish update + +Finish the update process: + +``` bash +composer run post-install-cmd +``` + +Finally, generate the new GraphQL schema: + +``` bash +php bin/console ibexa:graphql:generate-schema +``` + +YAML files with the schema are located in `config/graphql/types/ibexa`. diff --git a/docs/updating/from_4.1/update_from_4.1.md b/docs/updating/from_4.1/update_from_4.1.md new file mode 100644 index 0000000000..d1cfe5d7ed --- /dev/null +++ b/docs/updating/from_4.1/update_from_4.1.md @@ -0,0 +1,146 @@ +--- +description: Update your installation to the v4.2.latest version from an v4.1 version. +--- + +# Update from v4.1.x to v4.2 + +This update procedure applies if you are using a v4.1 installation. + +## Update from v4.1.x to v4.1.latest + +Before you update to v4.2, you need to go through the following steps to update to the latest maintenance release of v4.1 (v[[= latest_tag_4_1 =]]). + +### Update the application + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_1 =]] --with-all-dependencies --no-scripts + ``` + +### VCL configuration for Fastly + +The Fastly `.vcl` configuration files have changed. +Follow the upgrade steps below to update them: + +1. Locate the `vendor/ibexa/fastly/fastly/ez_main.vcl` file and update your VCL file with the recent changes. +2. Do the same with `vendor/ibexa/fastly/fastly/ez_user_hash.vcl`. +3. Upload a new `snippet_re_enable_shielding.vcl` snippet file, based on `vendor/ibexa/fastly/fastly/snippet_re_enable_shielding.vcl`. + +Once the VCL configuration has been updated, +you may enable [Fastly Shielding](https://docs.fastly.com/en/guides/shielding) if you prefer. + +## Update from v4.1.latest to v4.2 + +When you have the latest version of v4.1, you can update to v4.2. + +### Update the application + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. +Review the old YAML files and move your custom configuration to the relevant new files. + +#### Run data migration + +Next, run data migration required by Product Categories: + +``` bash +php bin/console ibexa:migrations:import vendor/ibexa/product-catalog/src/bundle/Resources/migrations/2022_06_23_09_39_product_categories.yaml --name=013_product_categories.yaml +``` + +If you are using [[= product_name_exp =]] or [[= product_name_com =]], run data migration required by the Customer portal feature: + +``` bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/corporate_account.yaml --name=001_corporate_account.yaml +``` + +If you are using [[= product_name_com =]], additionally run: + +``` bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/corporate_account_commerce.yaml --name=002_corporate_account_commerce.yaml +``` + +Run `php bin/console ibexa:migrations:migrate -v --dry-run` to ensure that all migrations are ready to be performed. +If the dry run is successful, run: + +``` bash +php bin/console ibexa:migrations:migrate +``` + +### Update the database + +Next, update the database. + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.1.latest-to-4.2.0.sql + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.2.2-to-4.2.3.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.1.latest-to-4.2.0.sql + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.2.2-to-4.2.3.sql + ``` + +#### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, database upgrade is not necessary. + +## Ensure password safety + +Following [Security advisory: IBEXA-SA-2022-009](https://developers.ibexa.co/security-advisories/ibexa-sa-2022-009-critical-vulnerabilities-in-graphql-role-assignment-ct-editing-and-drafts-tooltips), +unless you can verify based on your log files that the vulnerability has not been exploited, +you should [revoke passwords](https://doc.ibexa.co/en/latest/users/user_management/#revoking-passwords) for all affected users. + +### Remove `node_modules` and `yarn.lock` + +Next, remove `node_modules` and `yarn.lock` before running `composer run post-update-cmd`, +otherwise you can encounter errors during compiling. + +``` bash +rm -Rf node_modules +rm -Rf yarn.lock +``` \ No newline at end of file diff --git a/docs/updating/from_4.2/update_from_4.2.md b/docs/updating/from_4.2/update_from_4.2.md new file mode 100644 index 0000000000..45b80bf0b9 --- /dev/null +++ b/docs/updating/from_4.2/update_from_4.2.md @@ -0,0 +1,159 @@ +--- +description: Update your installation to the latest v4.3 version from v4.2.x. +--- + +# Update from v4.2.x to v4.3 + +This update procedure applies if you are using a v4.2 installation. + +## Update from v4.2.x to v4.2.latest + +Before you update to v4.3, you need to go through the following steps to update to the latest maintenance release of v4.2 (v[[= latest_tag_4_2 =]]). + +### Update the application + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_2 =]] --with-all-dependencies --no-scripts + ``` + +## Update from v4.2.latest to v4.3 + +When you have the latest version of v4.2, you can update to v4.3. + +### Update the application + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. +Review the old YAML files and move your custom configuration to the relevant new files. + +### Run data migration + +#### Customer Portal self-registration + +If you are using [[= product_name_exp =]] or [[= product_name_com =]], +run data migration required by the Customer Portal self-registration feature: + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/corporate_account_registration.yaml --name=012_corporate_account_registration.yaml +``` + +#### Migration to `customer` Content Type + +This step is required if you have users in your installation that need to be transferred to a new User Content Type: `customer`. +This Content Type is dedicated to registered frontend customers. +This migration is intended for all product versions. +If there are no users that are customers in your platform, you can skip this step and move on to [executing migrations](#execute-migrations). + +##### Basic migration + +Use this option to define a user group that should be migrated to a new Content Type. + +```bash +php bin/console ibexa:migrate:customers --input-user-group=3a3beb3d09ae0dacebf1d324f61bbc34 --create-content-type +``` + +- `--input-user-group` - represents the remote ID of a User Group you want to migrate to a new Content Type. +After migration, this will also be the ID of a new Private Customer User Group. +- `--create-content-type` - if you add this parameter, the system creates the new Content Type based on the one defined in `--input-user-content-type` + +##### Additional parameters + +Use the parameters below if you need to change a Content Type name during migration, for example because you already have a `customer` Content Type, +or you want to define different source Content Type. +If you don't have custom User Content Types, use the basic migration. + +- `--input-user-content-type` - defines input Content Type +- `--output-user-content-type` - defines output Content Type +- `--user` - defines the user that this command should be executed as, default is Admin +- `--batch-limit` - defines data limit for migration of one batch, default value is 25 + +!!! caution + + This improvement will prevent logged in backend users from making purchases in the frontend store. + +#### Execute migrations + +Run `php bin/console ibexa:migrations:migrate -v --dry-run` to ensure that all migrations are ready to be performed. +If the dry run is successful, run the following command to execute the above migrations: + +``` bash +php bin/console ibexa:migrations:migrate +``` + +### Update the database + +Next, update the database. + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.2.2-to-4.2.3.sql + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.2.latest-to-4.3.0.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.2.2-to-4.2.3.sql + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.2.latest-to-4.3.0.sql + ``` + +#### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, database upgrade is not necessary. + +## Ensure password safety + +Following [Security advisory: IBEXA-SA-2022-009](https://developers.ibexa.co/security-advisories/ibexa-sa-2022-009-critical-vulnerabilities-in-graphql-role-assignment-ct-editing-and-drafts-tooltips), +unless you can verify based on your log files that the vulnerability has not been exploited, +you should [revoke passwords](https://doc.ibexa.co/en/latest/users/user_management/#revoking-passwords) for all affected users. + +## Finish update + +Finish the update process: + +``` bash +composer run post-install-cmd +``` diff --git a/docs/updating/from_4.3/update_from_4.3.md b/docs/updating/from_4.3/update_from_4.3.md new file mode 100644 index 0000000000..fbea7670e9 --- /dev/null +++ b/docs/updating/from_4.3/update_from_4.3.md @@ -0,0 +1,22 @@ +--- +description: Update your installation to the latest v4.4 version from v4.3.x. +--- + +# Update from v4.3.x to v4.4 + +This update procedure applies if you are using the newest v4.3 installation. + +This release deprecates all Commerce packages in Ibexa DXP. They will be removed in v5. +Until that time, they will be maintained by Ibexa with fixes, including security fixes, but they won't be further developed. +Old packages are replaced by [the all-new Ibexa Commerce packages](ibexa_dxp_v4.4.md#all-new-ibexa-commerce-packages). + +For that reason, there are two update routes you can take. + +A. If you do not use Commerce functionalities, you can proceed with removing them. + +B. If you use Commerce functionalities based on the deprecated packages, you can continue to use them for the time being. + +[[= cards([ +"updating/from_4.3/update_from_4.3_new_commerce", +"updating/from_4.3/update_from_4.3_old_commerce", +], force_version="3.3") =]] diff --git a/docs/updating/from_4.3/update_from_4.3_new_commerce.md b/docs/updating/from_4.3/update_from_4.3_new_commerce.md new file mode 100644 index 0000000000..a643da14c5 --- /dev/null +++ b/docs/updating/from_4.3/update_from_4.3_new_commerce.md @@ -0,0 +1,346 @@ +--- +description: Update procedure to v4.4 for people who don't use Commerce packages and can remove them. +--- +# Update with new Commerce packages + +This update procedure applies if you have a v4.3 installation, and you do not use Commerce packages. + +## Update from v4.3.x to v4.3.latest + +Before you update to v4.4, you need to go through the following steps to update to the latest maintenance release of v4.3 (v[[= latest_tag_4_3 =]]). + +### Update the application to v4.3.latest + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` + +## Remove deprecated Field Types + +By default, every v4.3 installation has a set of built-in Content Types. +Some of them use Field Types deprecated in v4.4, which need to be removed manually. +Make sure to remove all occurrences of `sesspecificationstype`, `uivarvarianttype`, `sesselection`, `sesprofiledata` Field Types from your Content Types. + +This step should be performed on the working installation, omitting it will result in an error during update: + +``` + [Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound (404)] + Could not find 'Persistence Field Value Converter' with identifier 'sesspecificationstype' +``` + +In that case, you can use [Null Field Type](nullfield.md) to define a replacement for deprecated Field Types in `config/services.yaml`: + +```yaml +services: + ibexa.field_type.sesspecificationstype: + class: Ibexa\Core\FieldType\Null\Type + arguments: [sesspecificationstype] + tags: [{name: ibexa.field_type, alias: sesspecificationstype}] + ibexa.field_type.sesspecificationstype.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: [{name: ibexa.field_type.storage.legacy.converter, alias: sesspecificationstype}] + ibexa.field_type.sesspecificationstype.indexable: + class: Ibexa\Core\FieldType\Unindexed + tags: [{name: ibexa.field_type.indexable, alias: sesspecificationstype}] + + ibexa.field_type.uivarvarianttype: + class: Ibexa\Core\FieldType\Null\Type + arguments: [uivarvarianttype] + tags: [{name: ibexa.field_type, alias: uivarvarianttype}] + ibexa.field_type.uivarvarianttype.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: [{name: ibexa.field_type.storage.legacy.converter, alias: uivarvarianttype}] + ibexa.field_type.uivarvarianttype.indexable: + class: Ibexa\Core\FieldType\Unindexed + tags: [{name: ibexa.field_type.indexable, alias: uivarvarianttype}] + + ibexa.field_type.sesselection: + class: Ibexa\Core\FieldType\Null\Type + arguments: [sesselection] + tags: [{name: ibexa.field_type, alias: sesselection}] + ibexa.field_type.sesselection.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: [{name: ibexa.field_type.storage.legacy.converter, alias: sesselection}] + ibexa.field_type.sesselection.indexable: + class: Ibexa\Core\FieldType\Unindexed + tags: [{name: ibexa.field_type.indexable, alias: sesselection}] + + ibexa.field_type.sesprofiledata: + class: Ibexa\Core\FieldType\Null\Type + arguments: [sesprofiledata] + tags: [{name: ibexa.field_type, alias: sesprofiledata}] + ibexa.field_type.sesprofiledata.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: [{name: ibexa.field_type.storage.legacy.converter, alias: sesprofiledata}] + ibexa.field_type.sesprofiledata.indexable: + class: Ibexa\Core\FieldType\Unindexed + tags: [{name: ibexa.field_type.indexable, alias: sesprofiledata}] +``` + +## Update from v4.3.latest to v4.4 + +When you have the latest version of v4.3, you can update to v4.4. + +### Update the application to v4.4 + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. +Review the old YAML files and move your custom configuration to the relevant new files + +### Flysystem v2 + +Local adapters' `directory` key changed to `location`. +It is defined in `config/packages/oneup_flysystem.yaml`: + +```yaml +oneup_flysystem: + adapters: + default_adapter: + local: + location: '%kernel.cache_dir%/flysystem' +``` +If you haven't applied custom changes to that file, +you can reset the third-party `oneup/flysystem-bundle` recipe by executing: + +```bash +composer recipe:install --force --reset -- oneup/flysystem-bundle +``` + +### Remove `ibexa/commerce-*` packages with dependencies + +Remove the following bundles from `config/bundles.php`. +You do not have to remove third-party bundles (`FOS\` to `JMS\`) if they are used by your installation. + +=== "[[= product_name_content =]]" + + ``` php + FOS\CommentBundle\FOSCommentBundle + Tedivm\StashBundle\TedivmStashBundle + WhiteOctober\BreadcrumbsBundle\WhiteOctoberBreadcrumbsBundle + Nelmio\SolariumBundle\NelmioSolariumBundle + JMS\Payment\CoreBundle\JMSPaymentCoreBundle + Joli\ApacheTikaBundle\ApacheTikaBundle + JMS\JobQueueBundle\JMSJobQueueBundle + FOS\RestBundle\FOSRestBundle + JMS\SerializerBundle\JMSSerializerBundle + Ibexa\Bundle\Commerce\Eshop\IbexaCommerceEshopBundle + Ibexa\Bundle\Commerce\ShopTools\IbexaCommerceShopToolsBundle + Ibexa\Bundle\Commerce\Translation\IbexaCommerceTranslationBundle + Ibexa\Bundle\Commerce\Payment\IbexaCommercePaymentBundle + Ibexa\Bundle\Commerce\Price\IbexaCommercePriceBundle + Ibexa\Bundle\Commerce\Tools\IbexaCommerceToolsBundle + Ibexa\Bundle\Commerce\Search\IbexaCommerceSearchBundle + Ibexa\Bundle\Commerce\PriceEngine\IbexaCommercePriceEngineBundle + Ibexa\Bundle\Commerce\SpecificationsType\IbexaCommerceSpecificationsTypeBundle + Ibexa\Bundle\Commerce\BaseDesign\IbexaCommerceBaseDesignBundle + Ibexa\Bundle\Commerce\FieldTypes\IbexaCommerceFieldTypesBundle + Ibexa\Bundle\Commerce\Checkout\IbexaCommerceCheckoutBundle + Ibexa\Bundle\Commerce\ShopUi\IbexaCommerceShopUiBundle + ``` + +=== "[[= product_name_exp =]]" + + ``` php + FOS\CommentBundle\FOSCommentBundle + Tedivm\StashBundle\TedivmStashBundle + WhiteOctober\BreadcrumbsBundle\WhiteOctoberBreadcrumbsBundle + Nelmio\SolariumBundle\NelmioSolariumBundle + JMS\Payment\CoreBundle\JMSPaymentCoreBundle + Joli\ApacheTikaBundle\ApacheTikaBundle + JMS\JobQueueBundle\JMSJobQueueBundle + FOS\RestBundle\FOSRestBundle + JMS\SerializerBundle\JMSSerializerBundle + Ibexa\Bundle\Commerce\Eshop\IbexaCommerceEshopBundle + Ibexa\Bundle\Commerce\ShopTools\IbexaCommerceShopToolsBundle + Ibexa\Bundle\Commerce\Translation\IbexaCommerceTranslationBundle + Ibexa\Bundle\Commerce\Payment\IbexaCommercePaymentBundle + Ibexa\Bundle\Commerce\Price\IbexaCommercePriceBundle + Ibexa\Bundle\Commerce\Tools\IbexaCommerceToolsBundle + Ibexa\Bundle\Commerce\Search\IbexaCommerceSearchBundle + Ibexa\Bundle\Commerce\PriceEngine\IbexaCommercePriceEngineBundle + Ibexa\Bundle\Commerce\SpecificationsType\IbexaCommerceSpecificationsTypeBundle + Ibexa\Bundle\Commerce\BaseDesign\IbexaCommerceBaseDesignBundle + Ibexa\Bundle\Commerce\FieldTypes\IbexaCommerceFieldTypesBundle + Ibexa\Bundle\Commerce\Checkout\IbexaCommerceCheckoutBundle + Ibexa\Bundle\Commerce\ShopUi\IbexaCommerceShopUiBundle + ``` + +=== "[[= product_name_com =]]" + + ``` php + FOS\CommentBundle\FOSCommentBundle + Tedivm\StashBundle\TedivmStashBundle + WhiteOctober\BreadcrumbsBundle\WhiteOctoberBreadcrumbsBundle + Nelmio\SolariumBundle\NelmioSolariumBundle + JMS\Payment\CoreBundle\JMSPaymentCoreBundle + Joli\ApacheTikaBundle\ApacheTikaBundle + JMS\JobQueueBundle\JMSJobQueueBundle + FOS\RestBundle\FOSRestBundle + JMS\SerializerBundle\JMSSerializerBundle + Ibexa\Bundle\Commerce\Eshop\IbexaCommerceEshopBundle + Ibexa\Bundle\Commerce\ShopTools\IbexaCommerceShopToolsBundle + Ibexa\Bundle\Commerce\Translation\IbexaCommerceTranslationBundle + Ibexa\Bundle\Commerce\Payment\IbexaCommercePaymentBundle + Ibexa\Bundle\Commerce\Price\IbexaCommercePriceBundle + Ibexa\Bundle\Commerce\Tools\IbexaCommerceToolsBundle + Ibexa\Bundle\Commerce\Search\IbexaCommerceSearchBundle + Ibexa\Bundle\Commerce\PriceEngine\IbexaCommercePriceEngineBundle + Ibexa\Bundle\Commerce\SpecificationsType\IbexaCommerceSpecificationsTypeBundle + Ibexa\Bundle\Commerce\BaseDesign\IbexaCommerceBaseDesignBundle + Ibexa\Bundle\Commerce\FieldTypes\IbexaCommerceFieldTypesBundle + Ibexa\Bundle\Commerce\Checkout\IbexaCommerceCheckoutBundle + Ibexa\Bundle\Commerce\ShopUi\IbexaCommerceShopUiBundle + # ... + Ibexa\Bundle\Commerce\OneSky\IbexaCommerceOneSkyBundle + Ibexa\Bundle\Commerce\EzStudio\IbexaCommerceEzStudioBundle + Ibexa\Bundle\Commerce\Comparison\IbexaCommerceComparisonBundle + Ibexa\Bundle\Commerce\QuickOrder\IbexaCommerceQuickOrderBundle + Ibexa\Bundle\Commerce\TestTools\IbexaCommerceTestToolsBundle + Ibexa\Bundle\Commerce\Voucher\IbexaCommerceVoucherBundle + Ibexa\Bundle\Commerce\LocalOrderManagement\IbexaCommerceLocalOrderManagementBundle + Ibexa\Bundle\Commerce\Newsletter\IbexaCommerceNewsletterBundle + Ibexa\Bundle\Commerce\OrderHistory\IbexaCommerceOrderHistoryBundle + Ibexa\Bundle\Commerce\ErpAdmin\IbexaCommerceErpAdminBundle + Ibexa\Bundle\Commerce\ShopFrontend\IbexaCommerceShopFrontendBundle + Ibexa\Bundle\Commerce\Basket\IbexaCommerceBasketBundle::class + Ibexa\Bundle\Commerce\Rest\IbexaCommerceRestBundle::class + Ibexa\Bundle\Commerce\AdminUi\IbexaCommerceAdminUiBundle::class + Ibexa\Bundle\Commerce\PageBuilder\IbexaCommercePageBuilderBundle::class + EWZ\Bundle\RecaptchaBundle\EWZRecaptchaBundle::class + ``` + +Next, remove related extensions' configuration. +You do not have to remove third-party bundles (for example `config/packages/fos_rest.yaml`) if they are used by your installation. + +=== "[[= product_name_content =]]" + + ``` + config/packages/commerce.yaml + config/packages/commerce/autogenerated/.gitkeep + config/packages/commerce/commerce.yaml + config/packages/commerce/commerce_advanced.yaml + config/packages/commerce/commerce_common.yaml + config/packages/commerce/commerce_demo.yaml + config/packages/commerce/commerce_parameters.yaml + config/packages/nelmio_solarium.yaml + ``` + +=== "[[= product_name_exp =]]" + + ``` + config/packages/commerce.yaml + config/packages/commerce/autogenerated/.gitkeep + config/packages/commerce/commerce.yaml + config/packages/commerce/commerce_advanced.yaml + config/packages/commerce/commerce_common.yaml + config/packages/commerce/commerce_demo.yaml + config/packages/commerce/commerce_parameters.yaml + config/packages/nelmio_solarium.yaml + ``` + +=== "[[= product_name_com =]]" + + ``` + config/packages/commerce.yaml + config/packages/commerce/autogenerated/.gitkeep + config/packages/commerce/commerce.yaml + config/packages/commerce/commerce_advanced.yaml + config/packages/commerce/commerce_common.yaml + config/packages/commerce/commerce_demo.yaml + config/packages/commerce/commerce_parameters.yaml + config/packages/dev/ewz_recaptcha.yaml + config/packages/dev/jms_serializer.yaml + config/packages/ewz_recaptcha.yaml + config/packages/ezcommerce/autogenerated/commerce_repository_parameters.yaml + config/packages/fos_rest.yaml + config/packages/google_recaptcha.yaml + config/packages/jms_serializer.yaml + config/packages/nelmio_solarium.yaml + config/packages/prod/jms_serializer.yaml + ``` + +Finally, remove related routes by deleting `config/routes/ibexa_commerce.yaml` file. + +### Update the database + +Next, update the database if you are using Ibexa Commerce. +Ibexa Content and Ibexa Experience do not require the database update. + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/commerce/ibexa-4.3.latest-to-4.4.0.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/commerce/ibexa-4.3.latest-to-4.4.0.sql + ``` + +#### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, database upgrade is not necessary. + +## Ensure password safety + +Following [Security advisory: IBEXA-SA-2022-009](https://developers.ibexa.co/security-advisories/ibexa-sa-2022-009-critical-vulnerabilities-in-graphql-role-assignment-ct-editing-and-drafts-tooltips), +unless you can verify based on your log files that the vulnerability has not been exploited, +you should [revoke passwords](https://doc.ibexa.co/en/latest/users/user_management/#revoking-passwords) for all affected users. + +## Finish code update + +Finish the code update by running: + +```bash +composer run post-install-cmd +``` +## Run data migration + +### Customer Portal self-registration + +If you are using Ibexa Experience or Ibexa Commerce, +you can now run data migration required by the Customer Portal applications feature to finish the update process: + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/application_internal_fields.yaml --name=2022_11_07_22_46_application_internal_fields.yaml +php bin/console ibexa:migrations:migrate --file=2022_11_07_22_46_application_internal_fields.yaml +``` diff --git a/docs/updating/from_4.3/update_from_4.3_old_commerce.md b/docs/updating/from_4.3/update_from_4.3_old_commerce.md new file mode 100644 index 0000000000..64d93b0326 --- /dev/null +++ b/docs/updating/from_4.3/update_from_4.3_old_commerce.md @@ -0,0 +1,222 @@ +--- +description: Update procedure to v4.4 for people who use deprecated Commerce packages and want to keep them. +--- + +# Update with old Commerce packages + +This update procedure applies if you have a v4.3 installation, you use Commerce packages and would like to continue to use them. + +Note that all commerce packages as of v4.4 are deprecated and will be removed in v5. +Until that time, they will be maintained by Ibexa with fixes, including security fixes, but they won't be further developed. +Old packages are replaced by [the all-new Ibexa Commerce packages](ibexa_dxp_v4.4.md#all-new-ibexa-commerce-packages). + +## Update from v4.3.x to v4.3.latest + +Before you update to v4.4, you need to go through the following steps to update to the latest maintenance release of v4.3 (v[[= latest_tag_4_3 =]]). + +### Update the application to v4.3.latest + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_3 =]] --with-all-dependencies --no-scripts + ``` + +## Update from v4.3.latest to v4.4 + +When you have the latest version of v4.3, you can update to v4.4. + +### Update the application to v4.4 + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` + +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` + +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. +Review the old YAML files and move your custom configuration to the relevant new files. + +#### Flysystem v2 + +Local adapters' `directory` key changed to `location`. +It is defined in `config/packages/oneup_flysystem.yaml`: + +```yaml +oneup_flysystem: + adapters: + default_adapter: + local: + location: '%kernel.cache_dir%/flysystem' +``` + +If you haven't applied custom changes to that file, +you can reset third-party `oneup/flysystem-bundle` recipe by executing: + +```bash +composer recipe:install --force --reset -- oneup/flysystem-bundle +``` + +### Add `ibexa/commerce-*` packages dependencies + +Add the following dependencies in the `require` section in `composer.json`: + +=== "[[= product_name_content =]]" + + ``` json + "require":{ + "ibexa/commerce-base-design": "4.4.0", + "ibexa/commerce-checkout": "4.4.0", + "ibexa/commerce-fieldtypes": "4.4.0", + "ibexa/commerce-price-engine": "4.4.0", + "ibexa/commerce-shop": "4.4.0", + "ibexa/commerce-shop-ui": "4.4.0", + "ezsystems/apache-tika-bundle": "^2.0", + "ezsystems/comment-bundle": "^3.1", + "ezsystems/job-queue-bundle": "^4.0", + "ezsystems/payment-core-bundle": "^3.0", + "ezsystems/stash-bundle": "^0.9", + } + ``` + +=== "[[= product_name_exp =]]" + + ``` json + "require":{ + "ibexa/commerce-base-design": "4.4.0", + "ibexa/commerce-checkout": "4.4.0", + "ibexa/commerce-fieldtypes": "4.4.0", + "ibexa/commerce-price-engine": "4.4.0", + "ibexa/commerce-shop": "4.4.0", + "ibexa/commerce-shop-ui": "4.4.0", + "ezsystems/apache-tika-bundle": "^2.0", + "ezsystems/comment-bundle": "^3.1", + "ezsystems/job-queue-bundle": "^4.0", + "ezsystems/payment-core-bundle": "^3.0", + "ezsystems/stash-bundle": "^0.9", + } + ``` + +=== "[[= product_name_com =]]" + + ``` json + "require":{ + "ibexa/commerce-base-design": "4.4.0", + "ibexa/commerce-checkout": "4.4.0", + "ibexa/commerce-fieldtypes": "4.4.0", + "ibexa/commerce-price-engine": "4.4.0", + "ibexa/commerce-shop": "4.4.0", + "ibexa/commerce-shop-ui": "4.4.0", + "ezsystems/apache-tika-bundle": "^2.0", + "ezsystems/comment-bundle": "^3.1", + "ezsystems/job-queue-bundle": "^4.0", + "ezsystems/payment-core-bundle": "^3.0", + "ezsystems/stash-bundle": "^0.9", + "ibexa/commerce-admin-ui": "4.4.0", + "ibexa/commerce-erp-admin": "4.4.0", + "ibexa/commerce-order-history": "4.4.0", + "ibexa/commerce-page-builder": "4.4.0", + "ibexa/commerce-rest": "4.4.0", + "ibexa/commerce-transaction": "4.4.0" + } + ``` + +Next, remove the entries with new packages alongside with routing and configuration in `config/routes/ibexa_cart.yaml`, `config/routes/ibexa_checkout.yaml` and `config/routes/ibexa_storefront.yaml`: + +```php + Ibexa\Bundle\Cart\IbexaCartBundle::class => ['all' => true], + Ibexa\Bundle\Checkout\IbexaCheckoutBundle::class => ['all' => true], + Ibexa\Bundle\Storefront\IbexaStorefrontBundle::class => ['all' => true], +``` + +Finally, remove the new `storefront_group` SiteAccess from `config/packages/ibexa.yaml`: + +```yaml +ibexa: + siteaccess: + groups: + site_group: [import, site] + storefront_group: [site] +``` + +### Update the database + +Next, update the database if you are using Ibexa Commerce. +Ibexa Content and Ibexa Experience do not require the database update. + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/commerce/ibexa-4.3.latest-to-4.4.0.sql + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/commerce/ibexa-4.3.latest-to-4.4.0.sql + ``` + +#### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, database upgrade is not necessary. + +## Ensure password safety + +Following [Security advisory: IBEXA-SA-2022-009](https://developers.ibexa.co/security-advisories/ibexa-sa-2022-009-critical-vulnerabilities-in-graphql-role-assignment-ct-editing-and-drafts-tooltips), +unless you can verify based on your log files that the vulnerability has not been exploited, +you should [revoke passwords](https://doc.ibexa.co/en/latest/users/user_management/#revoking-passwords) for all affected users. + +## Finish code update + +Finish the code update by running: + +``` bash +composer run post-install-cmd +``` + +## Run data migration + +### Customer Portal self-registration + +If you are using Ibexa Experience or Ibexa Commerce, +run data migration required by the Customer Portal applications feature: + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/application_internal_fields.yaml --name=2022_11_07_22_46_application_internal_fields.yaml +php bin/console ibexa:migrations:migrate --file=2022_11_07_22_46_application_internal_fields.yaml +``` \ No newline at end of file diff --git a/docs/updating/from_4.4/update_from_4.4.md b/docs/updating/from_4.4/update_from_4.4.md new file mode 100644 index 0000000000..6e7d181f35 --- /dev/null +++ b/docs/updating/from_4.4/update_from_4.4.md @@ -0,0 +1,234 @@ +--- +description: Update your installation to the latest v4.5 version from v4.4.x. +--- + +# Update from v4.4.x to v4.5 + +This update procedure applies if you are using a v4.4 installation. + +## Update from v4.4.x to v4.4.latest + +Before you update to v4.5, you need to go through the following steps to update to the latest maintenance release of v4.4 (v[[= latest_tag_4_4 =]]). + +### Update the application to v4.4.latest + +Run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + ``` +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + ``` +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_4 =]] --with-all-dependencies --no-scripts + ``` + +## Update from v4.4.latest to v4.5 + +When you have the latest version of v4.4, you can update to v4.5. + +### Update the application + +First, run: + +=== "[[= product_name_content =]]" + + ``` bash + composer require ibexa/content:[[= latest_tag_4_5 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/content --force -v + ``` +=== "[[= product_name_exp =]]" + + ``` bash + composer require ibexa/experience:[[= latest_tag_4_5 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/experience --force -v + ``` +=== "[[= product_name_com =]]" + + ``` bash + composer require ibexa/commerce:[[= latest_tag_4_5 =]] --with-all-dependencies --no-scripts + composer recipes:install ibexa/commerce --force -v + ``` + +The `recipes:install` command installs new YAML configuration files. +Review the old YAML files and move your custom configuration to the relevant new files. + +### Define measurement base unit in configuration + +If your installation has defined measurement units in the configuration, +you need to specify one of them as base unit in the `config/packages/ibexa_measurement.yaml` file: + +```yaml +ibexa_measurement: + types: + my_type: + my_unit: { symbol: my, is_base_unit: true } +``` + +Next, add unit conversion to `src/bundle/Resources/config/services/conversion.yaml`. +For more information, see [Modify and add Measurement types and units](https://doc.ibexa.co/en/4.5/content_management/field_types/field_type_reference/measurementfield/#modify-and-add-measurement-types-and-units). + +### Update the database + +Next, update the database: + +[[% include 'snippets/update/db/db_backup_warning.md' %]] + +Apply the following database update scripts: + +=== "MySQL" + + ``` bash + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.4.latest-to-4.5.0.sql + + ``` + +=== "PostgreSQL" + + ``` bash + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.4.latest-to-4.5.0.sql + ``` + +#### Migrate richtext namespaces + +If you earlier upgraded from v3.3 to v4.x and haven't run the migrate script yet, do it now, run: + +```bash +bash bin/console ibexa:migrate:richtext-namespaces +``` + +#### Ibexa Open Source + +If you have no access to Ibexa DXP's `ibexa/installer` package, apply the following database update: + +=== "MySQL" + + ``` sql + CREATE TABLE ibexa_token_type + ( + id int(11) NOT NULL AUTO_INCREMENT, + identifier varchar(64) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY ibexa_token_type_unique (identifier) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + + CREATE TABLE ibexa_token + ( + id int(11) NOT NULL AUTO_INCREMENT, + type_id int(11) NOT NULL, + token varchar(255) NOT NULL, + identifier varchar(128) DEFAULT NULL, + created int(11) NOT NULL DEFAULT 0, + expires int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (id), + UNIQUE KEY ibexa_token_unique (token,identifier,type_id), + CONSTRAINT ibexa_token_type_id_fk + FOREIGN KEY (type_id) REFERENCES ibexa_token_type (id) + ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + ``` + +=== "PostgreSQL" + + ``` sql + CREATE TABLE ibexa_token_type + ( + id serial PRIMARY KEY, + identifier varchar(64) NOT NULL + ); + + CREATE TABLE ibexa_token + ( + id serial PRIMARY KEY, + type_id int NOT NULL + CONSTRAINT ibexa_token_type_id_fk + REFERENCES ibexa_token_type (id) + ON DELETE CASCADE, + token varchar(255) NOT NULL, + identifier varchar(128) DEFAULT NULL, + created int NOT NULL DEFAULT 0, + expires int NOT NULL DEFAULT 0 + ); + ``` + +## Finish code update + +Finish the code update by running: + +```bash +composer run post-install-cmd +``` + +## Run data migration + +If you are using Ibexa Experience or Ibexa Commerce, +you can now run data migration required by the Customer Portal and Commerce features to finish the update process: + +- Customer Portal [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/customer_portal.yaml --name=2023_03_06_13_00_customer_portal.yaml +php bin/console ibexa:migrations:migrate --file=2023_03_06_13_00_customer_portal.yaml +``` + +- Corporate access role update [[% include 'snippets/experience_badge.md' %]] [[% include 'snippets/commerce_badge.md' %]] + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/corporate-account/src/bundle/Resources/migrations/2023_05_09_12_40_corporate_access_role_update.yaml --name=2023_05_09_12_40_corporate_access_role_update.yaml +php bin/console ibexa:migrations:migrate --file=2023_05_09_12_40_corporate_access_role_update.yaml +``` + +- Corporate account [[% include 'snippets/commerce_badge.md' %]] + +This migration allows all company members to shop in the frontend shop. If you have implemented business logic that depends on keeping company members out of the frontend shop, you can skip it: + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/storefront/src/bundle/Resources/migrations/2023_04_27_10_30_corporate_account.yaml --name=2023_04_27_10_30_corporate_account.yaml +php bin/console ibexa:migrations:migrate --file=2023_04_27_10_30_corporate_account.yaml +``` + +- Storefront user update [[% include 'snippets/commerce_badge.md' %]] + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/storefront/src/bundle/Resources/migrations/2023_04_27_11_20_storefront_user_role_update.yaml --name=2023_04_27_11_20_storefront_user_role_update.yaml +php bin/console ibexa:migrations:migrate --file=2023_04_27_11_20_storefront_user_role_update.yaml +``` + +- Shipment permissions [[% include 'snippets/commerce_badge.md' %]] + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/shipping/src/bundle/Resources/install/migrations/shipment_permissions.yaml --name=shipment_permissions.yaml +php bin/console ibexa:migrations:migrate --file=shipment_permissions.yaml +``` + +- Order permissions [[% include 'snippets/commerce_badge.md' %]] + +```bash +php bin/console ibexa:migrations:import vendor/ibexa/order-management/src/bundle/Resources/install/migrations/order_permissions.yaml --name=order_permissions.yaml +php bin/console ibexa:migrations:migrate --file=order_permissions.yaml +``` + +### v4.5.2 + +#### Database update + +Run the following scripts: + +=== "MySQL" + + ``` sql + mysql -u -p < vendor/ibexa/installer/upgrade/db/mysql/ibexa-4.5.1-to-4.5.2.sql + ``` + +=== "PostgreSQL" + + ``` sql + psql < vendor/ibexa/installer/upgrade/db/postgresql/ibexa-4.5.1-to-4.5.2.sql + ``` \ No newline at end of file diff --git a/docs/updating/img/diagram_source/folder_structure_v3.xml b/docs/updating/img/diagram_source/folder_structure_v3.xml index de9e914a09..e240dc6fa8 100644 --- a/docs/updating/img/diagram_source/folder_structure_v3.xml +++ b/docs/updating/img/diagram_source/folder_structure_v3.xml @@ -1 +1 @@ -7Zxbk6I4FIB/jY9tEZJwebSvO1Nz2+mp2d152UJMK9MIDmLbzq/fRIKQELoViYrbdlWXnEAScj5OzjkJ9uDV9Pku8WaTj/GIhD3TGD334HXPNC0X0P9MsMoEEDuZYJwEo0wECsF98JtkQsyFi2BE5sJ5aRyHaTAThX4cRcRPBZmXJPFSPO0hDsVGZ96YVAT3vhdWpX8Fo3SSSR1sFPI/SDCe5C0Dg5cMPf9xnMSLiLcXxRHJSqZeXg0/dT7xRvGyJII3PXiVxHGafZs+X5GQDWo+YNl1tzWlmy4nJEq3uWAaBX968fzJiL6+f7z7Zn+erm4v8jt88sIFHwve23SVD8769girBfTg5XISpOR+5vmsdElhoLJJOg158Tj05kwZBv3ux9PA59/naRI/kqs4jJN1rRA67G9Tkg87q+MhCMPSmbfrD5PHUaqSe4nPicKsqerA8LF6IklKnksiPlB3JJ6SNFnRU3gptqy+zdnkRAPMR2ZZAAJy3U5KcGDEueRMjje1F8qhX7h+dtAVct50pdCVic3T0xW0FbqyQtrs5ZB+Gafre88EbKgELVq/FnFecDFfj9aAngCs2XNRmNdCftCiL6GXPsTJlLW4aYh2PKtabI6KS12Q+KFjnoqQiDCs7ZuodS7ywmAc0UOfapJQ+SXTYEBN7IAXTIPRiDWjpLLgtjUq7L4EhW1UoQBGflYZixyV9rFQPcKHwQK+gsUw+X+Cgm1wXFBGn3//evf5bvrvh4+3jz9/XbxbDD5dAAPXkrI1GEgFhjeb1ZLwqo4TQqv3husT2Ph7izTOGlwXb6/aWRxE6XrQ8GUPX6tJURIlAsD6f+tNg5Dp7ipeJAFt1zQ+kSUvvOe3o3CXdrcpudfHQTEByqEooYIU84xGTixNnHwlczqcPvXKXzIQb/zswo8pGhpoVOlxD0oP2ML9J9FowEKsYghLCn1x/OhIJau/2Uj3qUedC/5hl/bN/PD6masiO1qVj76QJKB3ylB42dynXjIm6Tb2lIyESLCqq5IusMpj5LKE0Lk1eBLjR5WCeAtfGLAlFKBkSiwsVpE9fPyqclQnV2S6UkWWWFE2NJWKqEq9Vek0/kDVdxioO1zgl9VYwLgZ0334zJ+xpnzWEnMqHDii+iCWnvWtOchDmbqKduZAsCytKNM8kLGB5pFtjXVajLnio4vgxpfdmTJLwtWVKmrL2kg0F13WbG/QudsbQ3RBqEabkQAkqDYVt0xCpR3eYc0cqNI3Z8WBI00XbnObIM881aqOP/eYZ+9ISA82tho6EpUHDuzrSGzXTt5hvQ92rvbWw+V54r+FxQ3DYsPoG+UPFOd+7G7y+6+lWSS7016gbJ79hCDZD2RLVTS1H2jvQGTLdniH27Ifyvy9Ktl2ThRgOS3h4qZuAQaSp2mjvd2CLVvadForC+6eLJDnIGXx6gW1fsjmAhawGn0LQn5cRKzsYFU62DpezRT2wo3kcfmJMoibpsYqXGgKUeR2MMI79QsbB6D17DNpMjYIor7sDzQlBwHYN5FbfA7CUekO9KKhWmo8ZzRMVJmKGtsUE/bLYByGjOIG9IKx7xTXNTCg0zAbJisIWqjvHhyLvPtaocgZ6PqS4YkgKC8EYmA0nrZMdq2UOHfgJnjWvCJY6rleAOEbgG3aQEsyItBqPDkiLPMHNWUPcx+72mu97J37+lDFi3YsITfXMClUcW2PMj+ilufHmtXf+q2/e+7cm89J2sXtWNUd5LXJ51fts5h8ruwnr9t2Xvvc7ZCmlpfMkKHY/Wer0tK6thMrl6vbR+0NLK1gVcJTqNpWqgusmgVwXduP08SL5myKiqOOmjLDcEzsayDuIQxm30sXHuj1GdmsOSqzpnx7RiN+unY1PwVk2VHuRq45QuYZcSduJUMAK7izDjqdaoJuSYbdRA6DS2uAdCB3OMxe3lyAMehjUMFO1zsc6qBSE3Z+HD0E4y5wZlgID64PwJnN/rRwVnHiHOfIsYHq1Y6Wdzp1y5o9AOyB6vvNXQ4VkF2l7KC2S7mnvw3KZothGHQUtDOYNiubMCDo29aRLZoq8d8Ka57/6I07+abjmUye8hqEWY0EnIOipiv+VKU/ThoubcmOo5kyaJsVuLSlN9R01f8KxJ50kemMwkU6gZa+fMbR0EIKu6XrhW51KKkrhTGYzS6pQuilnZwjz8D1p26l6JEZQPVahBK3Fl6L+B6Fw5+DH8sP+Obr1Y/37kf3240yYSZpvdOr4ZWX3uXXXXfYmSNWhCQLULPi3WBRWqmofXd4vvhMnLwWZQcAwMbvOr4GhKa9VUWX29q7oMREl9crZiK7NXWcQXhV+bERrHGBmR4Wv2mZgVn8Yii8+Q8= \ No newline at end of file +7Z3dcps4FICfxpdhACEwl86Ps+2022zT6XZ7s4NBtmkwUMCx06evZCQDQk6JjWwSY88kcARHQvp0JB1JeACuFuvbxInnHyMPBQNd9dYDcD3Qdc3QTfyPSJ5yiWVauWCW+B69qBDc+79QLoRUuPQ9lFauy6IoyPy4KnSjMERuVpE5SRKtqpdNo6AaaezMUE1w7zpBXfqv72XzXDqEaiH/C/mzOYtZU2nIxHEfZkm0DGl8YRSiPGThMDX00nTueNGqJAI3A3CVRFGWHy3WVyggucoyLL9vvCN0m+QEhVmTGxah/48TpY9q+Pn9w+0X69PiaXzBnvDRCZY0L2hqsyeWOZvHQ0SLNgCXq7mfofvYcUnoCtOAZfNsEdDgWeCkpDBUfOxGC9+lx2mWRA/oKgqiZKMVgCH5bkNYthMdUz8ISleONx8ij8JMJHcSlxIFVXrZ2Fn4AWFxFSUP5HGcMKVB99Ey2SR+nmUYLx2CEf6Ds438IRekyiyKZgFyYj9V8DNsAtx0c+l4mivGh1Q11C/Lymm2qfXSoQX2iJIMrUsiWlq3KFqgLMGqVRoKTVOxaAWh1UqDtHhWBaUaA2xeIhQatHLQijHbai8IwQcUkhcAYwx7YLoKDFbdPWCAJQDGDDJSkLET4uMZOWYykicVmsyfy4gFXKSbUsPZr2pmvC4CmRb0CwfdBU42jZIFiXQb1yAvKj46LK6mgkMZ53xW5bXK5cbeVwGkIifwZyE+dXF5Iiy/JOXo4yZnRAMWvueRaIQVpKhCbwtQS+H4tNQ6n5rKrioTyqhtn1CRSZNF6LsJWjs49PrbHYkS/IHPSdITe9I22NJOS6z36dfPd59uF/9/+Dh++PHz4t1y9PeFpsKdyDbG0xDh6cTxTiL/CFqCsHpnQjMfK1tmUR7hJrg5X3HkYwRINPByAK/FuAqxfhmFW1AkU9mG6WQjDoohbsUZciUQDUHLzsZYEig0JVH4GaWbokifNYM9nd2hUx9W6AQqqLE5FFlIaWxqDQa2KPRGxHlQFFAJl2dKB+dT8vSNFI2CB2lM8B+5UdHZ6fWall1+9lQ+u0OJj5+TYCa/eJs1iZmTzFDWpM1BXsWXUmeiVOZQNNKgsgThfrr/WPXAiECgMdyRaldCDnAG0eRYyk0IvavsF+EV6TanyKwqyrOmpgij4zyVLqNmYXeCNXGCC8xzjQX02zw9pB6wuiyhHnQA267AOKwyBCDX9WsMIxuH71L0YhgrZrQVovSjWFagn41hNbvFsl21UwbYDm5eTLPJVQubU9SWaeVqTZFkycbV6I3rEYBUqyNwXeca6KY4ahzZW8Ut41iLhyZYMowiR2sPY+stPddA2/tbR76tr6s6fWuv9/3HE5g4aO7Zf6yZHu3Q/mOzeFiC5Zo4xl7rDqc0cXvHUicdSypQ1PIHVDt60N7OeJZqKjiqq0nvG94TmEjD4lTsayKNg4fYDeOhCW7LRArnMkUe+R7FllGEvOvRhvv2AaHGjW0s4+A+YMOYtomWCqR9IJBo7WfEIXShKqphUQHxCKmKCQA9L1xC5OSpdCJyCHWX75RFvys3me+toxUB7uuDr8EpaWTOxwMN+KJ0QfUIVaZ32Z+CXQMYCt9H3BdfQwOKbtjF5ygwl55ALp+ixSc9n7I7GUatZ7C3ddWBUqbzOHgWDyCXzkN7HD2de9AJhnv65HlKgGko9tHZZMmXSiYDsV+S8qpQ5xeaQE3du6Ogk3u5ucoh2LqwJK84KaVcLuigB/0Vgr4dvTGjCMy9exwG5DkHkmYi2BCunmq5jPdT/6cYpA3NynTAni7g2sjpJJ0Oo+VOx47VRbv3Xx24VSBNUfYaV2jX9xLunE1rUlFLs2mCfYW7tiV2etLNrs6y4ewS7DWAkibZxKM6kbltn+JOMfv22ORqoQY0VzfbYRYCbhU3EO2PkcXsjhVZsnZpZQnOT9KwRuFrMcDtwjwN/Phr6baGm7yPBfb4anRj6G0ZY6tqjC2RMbaPC7asjV+PPlr1RHeQ6OtLVQWtEW1W+8EaEJlqSVsZxd0LSTyv0OQ8ae4ewYY1Qd6kJYJV9blVadCwFajViJa1OVfsn5BEtBuFU392hghb5HtahFXV8KDXVn+5umNXZxM4JxvhiTbstry296xscAcHfOOxYWmgLYC5pcBWHWBZ64CfXZvVOsDxchL458nwG+9H8EYY6rYiwPi4dlg0edcKxo774MxezTs9zqE3cb150jZA5jf6singEsXWUSmW5aEQud7OiNsOGOCp66C2nGvQ4GY6BC/3kuVaE3O7+110B3KLFjHGFvXQngRab9Ke/wyCqkfYEBjb4VF9DbLcZ6M4vsQM4FvPsc/QwQHcdNreAE7nZuygqon2csp6bdjXMJj8GH1ffYA3n6++v7c/2l9uhH5gDqh+Uc/BCyr5F2Hxb4V5wWrKqiKj2RKzPdbWCGnp90GcYPktKGaLDuZF1uuItF1JbmuplxBHWUOeqsv/rJrgNz5s1/lJAChx0Qw+LX7sIme++C0RcPMb \ No newline at end of file diff --git a/docs/updating/img/folder_structure_v3.png b/docs/updating/img/folder_structure_v3.png index 08a45c00b9..87a62bb0cc 100644 Binary files a/docs/updating/img/folder_structure_v3.png and b/docs/updating/img/folder_structure_v3.png differ diff --git a/docs/updating/update_ibexa_dxp.md b/docs/updating/update_ibexa_dxp.md new file mode 100644 index 0000000000..6fdec8361d --- /dev/null +++ b/docs/updating/update_ibexa_dxp.md @@ -0,0 +1,28 @@ +--- +description: Update your installation to the latest supported version. +--- + +# Updating Ibexa DXP + +To update [[= product_name =]] to a newer version, select the version you are currently using: + +=== "I am using v1.13 or v2.x" + + If you have a v1.13 installation, or a v2.x installation lower than the latest v2.5, + [update to the v2.5 LTS](from_1.x_2.x/update_from_1.x_2.x.md). + + Afterwards, it is strongly recommended to also [update to the latest LTS, v3.3](from_2.5/update_from_2.5.md). + +=== "I am using v2.5" + + If you have a v2.5 installation, [update to the v3.3 LTS](from_2.5/update_from_2.5.md). + +=== "I am using v3.3" + + If you already have a v3.3 installation, [update to the latest v3.3 version](from_3.3/update_from_3.3.md). + + You can also [update to v4](from_3.3/to_4.0.md). + +=== "I am using v4.0" + + If you already have a v4.0 installation, [update to the latest v4.5 version](from_4.4/update_from_4.4.md). diff --git a/docs/updating/updating_ez_platform.md b/docs/updating/updating_ez_platform.md deleted file mode 100644 index 550e558443..0000000000 --- a/docs/updating/updating_ez_platform.md +++ /dev/null @@ -1,24 +0,0 @@ -# Updating [[= product_name =]] - -This section explains how to update eZ Platform or [[= product_name =]] to a new version. - -In the following instructions, replace `` with the version of eZ Platform or [[= product_name =]] you are updating to (for example: `v2.5.0`). -If you are testing a release candidate, use the latest `-rc` tag (for example: `v2.5.0-rc1`). - -You may upgrade from any eZ Platform version to the latest 2.5 version. For example, if you are using v2.0. you can then -upgrade your distribution directly to the latest 2.5 version, but follow all the database updates steps from 2.0 up to 2.5. It is -recommended that you first run all the database schema updates up to 2.5 before you run other update scripts or procedures. - -## Update procedure - -To update your eZ Platform installation follow the steps below: - -1. [Check out a tagged version](1_check_out_version.md) -1. [Merge composer.json](2_merge_composer.md) -1. [Update the app](3_update_app.md) -1. [Update database](4_update_database.md) -1. [Platform.sh changes](5_platform_sh_changes.md) -1. [Dump assets](6_dump_assets.md) -1. [Commit, test and merge](7_commit_test_merge.md) - -**Your eZ Platform should now be up-to-date with the chosen version!** diff --git a/docs/upgrading/1_check_out_version.md b/docs/upgrading/1_check_out_version.md deleted file mode 100644 index 4745a2f5de..0000000000 --- a/docs/upgrading/1_check_out_version.md +++ /dev/null @@ -1,62 +0,0 @@ -# 1. Check out a tagged version - -**1.1.** From the project's root, create a new branch from the project's *master*, or from the branch you're updating on: - -**From your master branch, create a branch for handling update changes** - -``` bash -git checkout -b -``` - -This creates a new project branch for the update based on your current project branch, typically `master`. An example `` would be `update-1.4`. -In the following steps it will be referred to as **update branch**. - -**1.2.** If it's not there, add `ezsystems/ezplatform` as an *upstream* remote -(on an Enterprise installation use `ezsystems/ezplatform-ee`, and on an eZ Commerce installation, `ezsystems/ezcommerce`): - -**From your update branch add upstream remote** - -``` bash -git remote add upstream http://github.com/ezsystems/ezplatform.git -or -git remote add upstream http://github.com/ezsystems/ezplatform-ee.git -or -git remote add upstream http://github.com/ezsystems/ezcommerce.git -``` - -**1.3.** Pull the tag into your branch. - -If you are unsure which version to pull, run `git ls-remote --tags upstream` to list all possible tags. - -**Pull the tag into your update branch** - -``` bash -git pull upstream -``` - -!!! tip - - Don't forget the `v` prefix here. You want to pull the tag `` and not the branch `` (i.e.: `v3.0.0`, and NOT `3.0.0` which is dev branch). - -At this stage you may get conflicts, which are a normal part of the procedure and no reason to worry. -The most common ones will be on `composer.json` and `composer.lock`. - -The latter can be ignored, as it will be regenerated when we execute `composer update` later. -The easiest is to checkout the version from the tag and add it to the changes: - -If you get a **lot** of conflicts (on the `doc` folder for instance), and eZ Platform was installed from the [ezplatform.com](https://ezplatform.com) or [support.ez.no](https://support.ez.no) (for Enterprise and eZ Commerce) tarball, it might be because of incomplete history. -You will have to run `git fetch upstream --unshallow` to load the full history, and run the merge again. - -**From your update branch** - -``` bash -git checkout --theirs composer.lock && git add composer.lock -``` - -If you do not keep a copy in the branch, you may also run: - -**From your update branch** - -``` bash -git rm composer.lock -``` diff --git a/docs/upgrading/2_merge_composer.md b/docs/upgrading/2_merge_composer.md deleted file mode 100644 index 52a9dea9d5..0000000000 --- a/docs/upgrading/2_merge_composer.md +++ /dev/null @@ -1,32 +0,0 @@ -# 2. Merge composer.json - -## Manual merging - -Conflicts in `composer.json` need to be fixed manually. If you're not familiar with the diff output, you may checkout the tag's version and inspect the changes. It should be readable for most: - -**From your new update branch** - -``` bash -git checkout --theirs composer.json && git diff HEAD composer.json -``` - -You should see what was changed, as compared to your own version, in the diff output. The update changes the requirements for all of the `ezsystems/` packages. Those changes should be left untouched. All of the other changes will be removals of what you added for your own project. Use `git checkout -p` to selectively cancel those changes: - -``` bash -git checkout -p composer.json -``` - -Answer `no` (do not discard) to the requirement changes of `ezsystems` dependencies. Answer `yes` (discard) to removals of your changes. - -Once you are done, inspect the file, by either using an editor or running `git diff composer.json`. You may also test the file's sanity with `composer validate`, and test the dependencies by running `composer update --dry-run` (it will output what it would do to the dependencies, without applying the changes). - -Once finished, run `git add composer.json` and commit. - -## Fixing other conflicts - -Depending on the local changes you have done, you may get other conflicts on configuration files, kernel, etc. - -There shouldn't be many, and you should be able to figure out which value is the right one for all of them: - -- Edit the file, and identify the conflicting changes. If a setting you have modified has also been changed by us, you should be able to figure out which value is the right one. -- Run `git add conflicting-file` to add the changes diff --git a/docs/upgrading/3_upgrade_app.md b/docs/upgrading/3_upgrade_app.md deleted file mode 100644 index d11369deb8..0000000000 --- a/docs/upgrading/3_upgrade_app.md +++ /dev/null @@ -1,20 +0,0 @@ -# 3. Upgrade the app - -At this point, you should have a `composer.json` file with the correct requirements. Run `composer update` to update the dependencies.  - -``` bash -composer update -``` - -If you want to first test how the update proceeds without actually updating any packages, you can try the command with the `--dry-run` switch: - -`composer update --dry-run` - -!!! caution "Upgrading packages" - - If your application consists of several packages that are placed in locations other than the `src/` folder, - apply the suggestions from the upgrade documentation to all the packages before you run `composer update`. - -!!! tip "Common errors" - - If you experience issues during the update, such as a [cloning failure](../getting_started/troubleshooting.md#cloning-failed-using-an-ssh-key), see the Common errors section on the Composer about page. diff --git a/docs/upgrading/4_1_upgrade_templates.md b/docs/upgrading/4_1_upgrade_templates.md deleted file mode 100644 index 7ef4f23f27..0000000000 --- a/docs/upgrading/4_1_upgrade_templates.md +++ /dev/null @@ -1,45 +0,0 @@ -# 4.1. Upgrade templates - -## Back-Office templates - -The naming and location of templates in the Back Office have been changed. -If you extend or modify these templates, you need to adapt your code. - -For the full list of template changes, see [the list of removals and deprecations](../releases/ez_platform_v3.0_deprecations.md#template-organization). - -## Twig functions and filters - -A number of [Twig functions, filters and helpers have been renamed](../releases/ez_platform_v3.0_deprecations.md#functions-renamed). -If your templates use them, you need to update them. - -## Templating component - -[The templating component integration is now deprecated.](https://symfony.com/blog/new-in-symfony-4-3-deprecated-the-templating-component-integration) -As a result, the way to indicate a template path has changed. - -For example: - -- **Use:** `"@@EzPlatformUser/user_settings/list.html.twig"` **instead of:** `"EzPlatformUserBundle:user_settings:list.html.twig"` -- **Use:** `{% extends "@EzPublishCore/content_fields.html.twig" %}` **instead of:** `{% extends "EzPublishCoreBundle::content_fields.html.twig" %}` - -## Form templates - -Content Type editing has been [moved from `repository-forms` to `ezplatform-admin-ui`](../releases/ez_platform_v3.0_deprecations.md#content-type-forms). - -Forms for content creation have been [moved from `repository-forms` to `ezplatform-content-forms`](../releases/ez_platform_v3.0_deprecations.md#repository-forms). - -If your templates extend any of those built-in templates, you need to update their paths. - -## Deprecated controller actions - -If your templates still use the deprecated `viewLocation` and `embedLocation` actions of `ViewController`, -you need to rewrite them to use `viewAction` and `embedAction` respectively. - -## Referencing controller actions - -To reference a controller, you now need to use `serviceOrFqcn::method` syntax instead of -`bundle:controller:action`: - -**Use:** `controller: My\ExampleBundle\Controller\DefaultController::articleViewAction` - -**Instead of:** `controller: AcmeExampleBundle:Default:articleView` diff --git a/docs/upgrading/4_2_upgrade_configuration.md b/docs/upgrading/4_2_upgrade_configuration.md deleted file mode 100644 index a1153e924e..0000000000 --- a/docs/upgrading/4_2_upgrade_configuration.md +++ /dev/null @@ -1,66 +0,0 @@ -# 4.2. Upgrade configuration - -## `ezpublish` configuration key - -The main YAML configuration key is now [`ezplatform` instead of `ezpublish`](../releases/ez_platform_v3.0_deprecations.md#configuration-through-ezplatform). -You need to change your configuration files to make use of the new key. For example: - -**Use:** - -``` yaml -ezplatform: - system: - default: - # ... -``` - -**instead of:** - -``` yaml -ezpublish: - system: - default: - # ... -``` - -## Resolving settings - -If you used dynamic settings (through `$setting$`), -or got settings from the ConfigResolver in a class constructor, -you now need to rewrite your code to inject the ConfigResolver and get the relevant setting: - -**Use:** - -``` php -use eZ\Publish\Core\MVC\ConfigResolverInterface; - -class MyService -{ - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - public function __construct(ConfigResolverInterface $configResolver) - { - $this->configResolver = $configResolver; - } - - public function myMethodWhichUsesSetting(): void - { - $setting = $this->configResolver->getParameter('setting'); - } -} -``` - -**instead of:** - -``` php -use eZ\Publish\Core\MVC\ConfigResolverInterface; - -class MyService -{ - public function __construct(ConfigResolverInterface $configResolver) - { - $this->setting = $configResolver->getParameter('setting'); - } -} -``` \ No newline at end of file diff --git a/docs/upgrading/4_3_upgrade_field_types.md b/docs/upgrading/4_3_upgrade_field_types.md deleted file mode 100644 index 2b4d42edff..0000000000 --- a/docs/upgrading/4_3_upgrade_field_types.md +++ /dev/null @@ -1,55 +0,0 @@ -# 4.3. Upgrade Field Types - -You need to adapt your custom Field Types to the new Field Type architecture. - -## `eZ\Publish\SPI\FieldType\FieldType` interface - -The `eZ\Publish\SPI\FieldType\FieldType` interface is now an abstract class. -You need to replace `implements FieldType` in your Field Type code with `extends FieldType`. - -## Deprecated `getName` method - -The deprecated method `getName` from the `eZ\Publish\SPI\FieldType\FieldType` interface has been changed. -Now it accepts two additional parameters: `FieldDefinition $fieldDefinition` and `string $languageCode`. - -In your code you need to change the `getName` signature -to `function getName(Value $value, FieldDefinition $fieldDefinition, string $languageCode): string;`. - -## `eZ\Publish\SPI\FieldType\Nameable` interface - -The `eZ\Publish\SPI\FieldType\Nameable` interface has been removed. -In your code you need to remove implementations of `Nameable` and replace them with -`eZ\Publish\SPI\FieldType\FieldType::getName`. - -## Deprecated tags - -You need to replace deprecated tags in service configuration: - -|Deprecated tag|Current tag| -|---|---| -|ezpublish.fieldType.parameterProvider|ezplatform.field_type.parameter_provider| -|ezpublish.fieldType.externalStorageHandler|ezplatform.field_type.external_storage_handler| -|ezpublish.fieldType.externalStorageHandler.gateway|ezplatform.field_type.external_storage_handler.gateway| -|ezpublish.fieldType|ezplatform.field_type| -|ezpublish.fieldType.indexable|ezplatform.field_type.indexable| -|ezpublish.storageEngine.legacy.converter|ezplatform.field_type.legacy_storage.converter| -|ez.fieldFormMapper.definition|ezplatform.field_type.form_mapper.definition| -|ez.fieldFormMapper.value|ezplatform.field_type.form_mapper.value| - -## Moved classes - -You need to replace importing the following classes: - -|Previous location|Current location| -|---|---| -|EzSystems\RepositoryForms\Data\Content\FieldData|EzSystems\EzPlatformContentForms\Data\Content\FieldData| -|EzSystems\RepositoryForms\Data\FieldDefinitionData|EzSystems\EzPlatformAdminUi\Form\Data\FieldDefinitionData| -|EzSystems\RepositoryForms\FieldType\FieldDefinitionFormMapperInterface|EzSystems\EzPlatformAdminUi\FieldType\FieldDefinitionFormMapperInterface| -|EzSystems\RepositoryForms\FieldType\FieldValueFormMapperInterface|EzSystems\EzPlatformContentForms\FieldType\FieldValueFormMapperInterface| - -## Extending Field Type templates - -If you extended templates for `ezobjectrelationlist_field`, `ezimageasset_field`, or `ezobjectrelation_field` Fields -using `{% extends "@EzPublishCore/content_fields.html.twig" %}`, -you now need to extend `EzSystemsPlatformHttpCache` instead, if you wish to make use of cache: -`{% extends "@EzSystemsPlatformHttpCache/content_fields.html.twig" %}`. diff --git a/docs/upgrading/4_4_upgrade_signal_slots.md b/docs/upgrading/4_4_upgrade_signal_slots.md deleted file mode 100644 index aa5e1b06b8..0000000000 --- a/docs/upgrading/4_4_upgrade_signal_slots.md +++ /dev/null @@ -1,39 +0,0 @@ -# 4.4. Upgrade Signal Slots - -If you used Signal Slots to listen for events in you custom code, -you need to rewrite them using Symfony Events and Listeners instead. - -The application now triggers two Events per operation: one before and one after the relevant thing happens -(see for example [Bookmark events](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/eZ/Publish/Core/Event/BookmarkService.php)). - -To use them, create [Event Listeners](https://symfony.com/doc/5.0/event_dispatcher.html) in your code, -for example: - -**Use:** - -``` php -public static function getSubscribedEvents(): array -{ - return [ - CreateBookmarkEvent::class => 'onCreateBookmark', - ] -} - -public function onCreateBookmark(CreateBookmarkEvent $event): void -{ - /// your code -} -``` - -**instead of:** - -``` php -public function receive(Signal $signal) -{ - if (!($signal instanceof CreateBookmarkSignal)) { - return; - } -} - -// your code -``` \ No newline at end of file diff --git a/docs/upgrading/4_5_upgrade_online_editor.md b/docs/upgrading/4_5_upgrade_online_editor.md deleted file mode 100644 index 77adebe4f6..0000000000 --- a/docs/upgrading/4_5_upgrade_online_editor.md +++ /dev/null @@ -1,15 +0,0 @@ -# 4.5. Upgrade Online Editor - -## RichText - -Deprecated code related to the RichText Field Type has been removed from `ezpublish-kernel`. - -If your code still relies on the `eZ\Publish\Core\FieldType\RichText` namespace, you need to rewrite it -to use `EzSystems\EzPlatformRichText\eZ\RichText` instead. - -## Extra buttons - -Configuring custom Online Editor buttons with `ezrichtext.alloy_editor.extra_buttons` is deprecated. - -If you added custom buttons in this way, you need to rewrite your code to use -`ezplatform.system..fieldtypes.ezrichtext.toolbars..buttons` instead. diff --git a/docs/upgrading/4_6_upgrade_workflow.md b/docs/upgrading/4_6_upgrade_workflow.md deleted file mode 100644 index fd2704805d..0000000000 --- a/docs/upgrading/4_6_upgrade_workflow.md +++ /dev/null @@ -1,11 +0,0 @@ -# 4.6. Upgrade workflow - -[`flex-workflow` has been combined with `ezplatform-workflow`](../releases/ez_platform_v3.0_deprecations.md#flex-workflow) in the form of a Quick Review functionality. - -If you used custom subscribers for events in workflow, you can now rewrite this code -to use [custom actions](../extending/extending_workflow.md#adding-custom-actions). - -To migrate your content which had been using Flex Workflow to the new Quick Review workflow, -run the following command: - -`php bin/console ezplatform:migrate:flex-workflow` \ No newline at end of file diff --git a/docs/upgrading/4_7_upgrade_extensions.md b/docs/upgrading/4_7_upgrade_extensions.md deleted file mode 100644 index dce749f3b1..0000000000 --- a/docs/upgrading/4_7_upgrade_extensions.md +++ /dev/null @@ -1,12 +0,0 @@ -# 4.7. Upgrade extended code - -## Universal Discovery Widget - -If you extended the Universal Discovery Widget -(e.g. added your own tabs or triggered opening the UDW for your own customizations), -you need to rewrite this extension using the [new YAML configuration](../extending/extending_udw.md). - -## Back Office extensibility - -If you added custom tab groups in the Back Office, -you now need to [make use of the `TabsComponent`](../extending/extending_tabs.md#adding-a-new-tab-group). diff --git a/docs/upgrading/4_8_upgrade_rest.md b/docs/upgrading/4_8_upgrade_rest.md deleted file mode 100644 index 26f8440ddb..0000000000 --- a/docs/upgrading/4_8_upgrade_rest.md +++ /dev/null @@ -1,57 +0,0 @@ -# 4.8. Upgrade REST - -If your code extends the REST API, you need to modify namespaces. -The `eZ\Publish\Core\REST` and `eZ\Publish\Core\REST\Common\` namespaces have been replaced by `EzSystems\EzPlatformRest`. -This is due to the fact that REST code has been moved from Kernel to a new `ezpublish-rest` package. - -## Custom installers - -eZ Platform provides extension point to create named custom installer which can be used instead of the native one. -To use it, execute the Symfony command: - -``` bash -php ./bin/console ezplatform:install -``` - -In eZ Platform v3.0, service definitions around that extension point have changed: - -1\. The deprecated Clean Installer has been dropped from `ezpublish-kernel` package. -If your project uses custom installer and has relied on Clean Installer service definition (`ezplatform.installer.clean_installer`) -you need to switch to Core Installer. - -**Use:** - -``` php -services: - Acme\App\Installer\MyCustomInstaller: - parent: EzSystems\PlatformInstallerBundle\Installer\CoreInstaller -``` - -**instead of**: - -``` php -services: - Acme\App\Installer\MyCustomInstaller: - parent: ezplatform.installer.clean_installer -``` - -`CoreInstaller` relies on [`DoctrineSchemaBundle`](https://github.com/ezsystems/doctrine-dbal-schema). -Custom schema can be installed defining Symfony Event Subscriber subscribing to `EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvents::BUILD_SCHEMA` event. - -2\. The deprecated Symfony Service definition `ezplatform.installer.db_based_installer` has been removed in favor of its FQCN-named definition. - -**Use:** - -``` php -services: - Acme\App\Installer\MyCustomInstaller: - parent: EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller -``` - -**instead of:** - -``` php -services: - Acme\App\Installer\MyCustomInstaller: - parent: ezplatform.installer.db_based_installer -``` \ No newline at end of file diff --git a/docs/upgrading/4_9_upgrade_other.md b/docs/upgrading/4_9_upgrade_other.md deleted file mode 100644 index afbc15195c..0000000000 --- a/docs/upgrading/4_9_upgrade_other.md +++ /dev/null @@ -1,64 +0,0 @@ -# 4.9 Other code upgrades - -## HTTP cache - -HTTP cache bundle now uses FOS Cache Bundle v2. -If your code makes use of HTTP cache bundle, see [the list of changes and deprecations](../releases/ez_platform_v3.0_deprecations.md#ezplatform-http-cache). - -## User checker - -Add the user checker to firewall by adding the following line to `config/packages/security.yaml`: - -``` yaml hl_lines="5" -security: - firewalls: - ezpublish_front: - # ... - user_checker: eZ\Publish\Core\MVC\Symfony\Security\UserChecker - # ... -``` - -## Commands - -The `ContainerAwareCommand` class is not available in Symfony 5. Therefore, if your custom commands use `Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand` -as a base class, you must rewrite them to use `Symfony\Component\Console\Command\Command` instead. - -## Permissions - -Some [permission choice loaders](../releases/ez_platform_v3.0_deprecations.md#permission-choice-loaders) and [permission-related methods](../releases/ez_platform_v3.0_deprecations.md#code-cleanup-in-ez-platform-kernel) have been removed. -If your code uses them, you must rewrite it to use the permission resolver. - -## Container parameters - -A number of Symfony Dependency Injection Container parameters [have been dropped](https://github.com/ezsystems/ezplatform-kernel/blob/v1.0.0/doc/bc/1.0/dropped-container-parameters.md). - -Check if your code uses such invalid parameters: search for them by using the `ezpublish\..*\.class` regular expression pattern. -When found, replace all the occurrences with fully-qualified class names. - -## QueryTypes - -If your code relies on automatically registering QueryTypes through the naming convention `\QueryType\*QueryType`, -you need to register your QueryTypes as services and tag them with `ezpublish.query`, or enable their automatic configuration (`autoconfigure: true`). - -## Symfony namespaces - -A number of Symfony namespaces have changed, and you must update your code if it uses them. -For example, the following namespaces are now different: - -|Use|Instead of| -|---|---| -|Symfony\Contracts\Translation\TranslatorInterface|Symfony\Component\Translation\TranslatorInterface| -|Symfony\Contracts\EventDispatcher\Event|Symfony\Component\EventDispatcher\Event| - -For more information, search for removed classes in Symfony [version 4.0](https://github.com/symfony/symfony/blob/4.4/UPGRADE-4.0.md) and [version 5.0](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) documentation. - -## Apache/Nginx configuration - -Make sure that your Apache/Nginx configuration is up to date with Symfony 5. -Refer to [the provided `vhost.template`](https://github.com/ezsystems/ezplatform/blob/master/doc/apache2/vhost.template) -for an example. - -## Deprecations - -Due to a number of compatibility breaks and deprecations introduced in eZ Platform v3.0, the changes that result from the above considerations might not be sufficient. -Make sure that you review your code and account for all changes listed in [Deprecations and backwards compatibility breaks](../releases/ez_platform_v3.0_deprecations.md). diff --git a/docs/upgrading/4_upgrade_the_code.md b/docs/upgrading/4_upgrade_the_code.md deleted file mode 100644 index 1eaf560ca0..0000000000 --- a/docs/upgrading/4_upgrade_the_code.md +++ /dev/null @@ -1,24 +0,0 @@ -# 4. Upgrade your code - -!!! note "Full list of deprecations" - - If you encounter any issue during the upgrade, - see [eZ Platform v3.0 deprecations](../releases/ez_platform_v3.0_deprecations.md#template-organization) - for details of all required changes to your code. - -## Third-party dependencies - -Because eZ Platform v3 is based on Symfony 5, you need to make sure all additional third-party dependencies -that your project uses have been adapted to Symfony 5. - -## Automatic code refactoring - non-essential step - -To simplify the process of adapting your code to Symfony 5, you can use [Rector, a reconstructor tool](https://github.com/rectorphp/rector) -that will automatically refactor your Symfony and PHPunit code. - -To properly refactor your code, you might need to run the Rector `process` command for each Symfony version from 4.0 to 5.0 in turn: - -`vendor/bin/rector process src --set symfony40` - -You can find all the available sets in [the Rector repository](https://github.com/rectorphp/rector/tree/v0.7.65/config/set). -Keep in mind that after automatic refactoring finishes there might be some code chunks that you need to fix manually. diff --git a/docs/upgrading/5_upgrade_database.md b/docs/upgrading/5_upgrade_database.md deleted file mode 100644 index 9b6af49a2a..0000000000 --- a/docs/upgrading/5_upgrade_database.md +++ /dev/null @@ -1,83 +0,0 @@ -# 5. Upgrade the database - -Apply the following database update script: - -`mysql -u -p < upgrade/db/mysql/ezplatform-2.5.latest-to-3.0.0.sql` - -or for PostgreSQL: - -`psql < upgrade/db/postgresql/ezplatform-2.5.latest-to-3.0.0.sql` - -### Solr configuration - -If you use Solr as the search engine, make sure that Solr configuration is set to commit Solr index changes directly during Repository updates. -For more information, see [Solr configuration](../guide/search/solr/#further-configuration). - -!!! dxp - - ### Site Factory upgrade - - For production, create the `ezsite` and `ezsite_public_access` tables and manually import their schema definitions. - - For MySQL: - - ```sql - DROP TABLE IF EXISTS `ezsite`; - - CREATE TABLE `ezsite` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(255) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '', - `created` int(11) NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - - -- - -- - - DROP TABLE IF EXISTS `ezsite_public_access`; - - CREATE TABLE `ezsite_public_access` ( - `public_access_identifier` varchar(255) COLLATE utf8mb4_unicode_520_ci NOT NULL, - `site_id` int(11) NOT NULL, - `site_access_group` varchar(255) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT '', - `status` int(11) NOT NULL, - `config` text COLLATE utf8mb4_unicode_520_ci NOT NULL, - `site_matcher_host` varchar(255) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL, - PRIMARY KEY (`public_access_identifier`), - KEY `ezsite_public_access_site_id` (`site_id`), - CONSTRAINT `fk_ezsite_public_access_site_id` FOREIGN KEY (`site_id`) REFERENCES `ezsite` (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - ``` - - For PostgreSQL: - - ```sql - DROP TABLE IF EXISTS ezsite; - - CREATE SEQUENCE ezsite_seq; - - CREATE TABLE ezsite ( - id int NOT NULL DEFAULT NEXTVAL ('ezsite_seq'), - name varchar(255) NOT NULL DEFAULT '', - created int NOT NULL, - PRIMARY KEY (id) - ) ; - - -- - -- - - DROP TABLE IF EXISTS ezsite_public_access; - - CREATE TABLE ezsite_public_access ( - public_access_identifier varchar(255) NOT NULL, - site_id int NOT NULL, - site_access_group varchar(255) NOT NULL DEFAULT '', - status int NOT NULL, - config text NOT NULL, - site_matcher_host varchar(255) DEFAULT NULL, - PRIMARY KEY (public_access_identifier), - CONSTRAINT fk_ezsite_public_access_site_id FOREIGN KEY (site_id) REFERENCES ezsite (id) - ) ; - - CREATE INDEX ezsite_public_access_site_id ON ezsite_public_access (site_id); - ``` diff --git a/docs/upgrading/6_platform_sh_changes.md b/docs/upgrading/6_platform_sh_changes.md deleted file mode 100644 index 48d6328e61..0000000000 --- a/docs/upgrading/6_platform_sh_changes.md +++ /dev/null @@ -1,4 +0,0 @@ -# 5. Platform.sh changes - -If you are hosting your site on Platform.sh be aware of the fact that Varnish is enabled by default as of eZ Platform 2.5.0. -If you are using Fastly, please read about [how to disable Varnish](https://docs.platform.sh/frameworks/ibexa/fastly.html#remove-varnish-configuration). diff --git a/docs/upgrading/7_dump_assets.md b/docs/upgrading/7_dump_assets.md deleted file mode 100644 index 6052b358b9..0000000000 --- a/docs/upgrading/7_dump_assets.md +++ /dev/null @@ -1,16 +0,0 @@ -# 6. Dump assets - -The web assets must be dumped again if you are using the `prod` environment. In `dev` this happens automatically: - -``` bash -yarn install -yarn encore prod -``` - -If you encounter problems, additionally clear the cache and install assets: - -``` bash -php bin/console cache:clear -e prod -yarn install -yarn encore prod -``` diff --git a/docs/upgrading/8_commit_test_merge.md b/docs/upgrading/8_commit_test_merge.md deleted file mode 100644 index 6c7defe69b..0000000000 --- a/docs/upgrading/8_commit_test_merge.md +++ /dev/null @@ -1,25 +0,0 @@ -# 7. Commit, test and merge - -Once all the conflicts have been resolved, and `composer.lock` updated, the merge can be committed. -Note that you may or may not keep `composer.lock`, depending on your version management workflow. -If you do not wish to keep it, run `git reset HEAD ` to remove it from the changes. -Run `git commit`, and adapt the message if necessary. - -You can now verify the project and once the update has been approved, go back to `master`, and merge your update branch: - -``` bash -git checkout master -git merge -``` - -!!! note "Insecure password hashes" - - To ensure that no users have unsupported, insecure password hashes, run the following command: - - ``` bash - php bin/console ezplatform:user:validate-password-hashes - ``` - - This command checks if all user hashes are up-to-date and informs you if any of them need to be updated. - -**Your eZ Platform should now be up-to-date with the chosen version!** diff --git a/docs/upgrading/upgrading_to_v3.md b/docs/upgrading/upgrading_to_v3.md deleted file mode 100644 index b290ebb70b..0000000000 --- a/docs/upgrading/upgrading_to_v3.md +++ /dev/null @@ -1,64 +0,0 @@ -# Upgrading eZ Platform to v3.0 - -The following upgrade documentation describes how to upgrade eZ Platform from v2.5 to v3.0. - -If you are upgrading from a version lower than v2.5, follow the [standard update procedure](../updating/updating_ez_platform.md) first. -Do not proceed with an upgrade to v3.0 before you complete an update to v2.5. - -## Familiarize yourself with a new project structure - -!!! tip - - If you are running into issues, for details on all changes related to the switch to Symfony 5, - see [Symfony upgrade guide for 4.0](https://github.com/symfony/symfony/blob/4.4/UPGRADE-4.0.md) - and [for 5.0](https://github.com/symfony/symfony/blob/5.0/UPGRADE-5.0.md) - -The latest Symfony versions changed the organization of your project into folders and bundles. -When updating to eZ Platform v3 you need to move your files and modify file paths and namespace references. - -![Project structure changes in v3](../updating/img/folder_structure_v3.png "Project folder structure changes between v2 and v3") - -### Configuration - -Configuration files have been moved from `app/Resources/config` to `config`. -Package-specific configuration is placed in `config/packages` (e.g. `config/packages/ezplatform_admin_ui.yaml`). -This folder also contains `config/packages/ezplatform.yaml`, which contains all settings coming in from Kernel. - -### PHP code and bundle organization - -Since Symfony 4 `src/` code is no longer organized in bundles, `AppBundle` has been removed from the default eZ Platform install. -In order to adapt, you'll need to move all your PHP code, such as controllers or event listeners, to the `src` folder and use the `App` namespace for your custom code instead. - -!!! tip "How to make AppBundle continue to work, for now" - - Refactoring bundles for `src/` folder can involve extensive changes, if you want to make your `src/AppBundle` continue to work, follow [an Autoloading src/AppBundle guide on Symfony Casts](https://symfonycasts.com/screencast/symfony4-upgrade/flex-composer.json#autoloading-src-amp-src-appbundle). - - You can also follow [Using a "path" Repository guide,](https://symfonycasts.com/screencast/symfony-bundle/extracting-bundle#using-a-path-repository) to create a [composer path repository.](https://getcomposer.org/doc/05-repositories.md#path) - If you have several bundles you can move them into a `packages/` directory and load them all with: - - ``` - "repositories": [ - { "type": "path", "url": "packages/*" }, - ], - ``` - - Once you are ready to refactor the code to `App` namespace, follow [Bye Bye AppBundle](https://symfonycasts.com/screencast/symfony4-upgrade/bye-appbundle) article. - -### View templates - -Templates are no longer stored in `app/Resources/views`. -You need to move all your templates to the `templates` folder in your project's root. - -### Translations - -Translation files have been moved out of `app/Resources/translations` into `translations` in your project's root. - -### `web` and assets - -Content of the `web` folder is now placed in `public`. -Content of `app/Resources/assets` has been moved to `assets`. - -!!! note - - You also need to update paths that refer to the old location, - for example in [`webpack.config.js`](../guide/project_organization.md#importing-configuration-from-a-bundle). diff --git a/main.py b/main.py new file mode 100644 index 0000000000..213f1897ed --- /dev/null +++ b/main.py @@ -0,0 +1,89 @@ +import os +import pprint +import re +from mkdocs.structure.pages import Page +from mkdocs.utils import meta + +CARDS_TEMPLATE = """ + +""" + + +def define_env(env): + """ + This is the hook for defining variables, macros and filters + + - variables: the dictionary that contains the environment variables + - macro: a decorator function, to declare a macro. + """ + + + @env.macro + def include_file(filename, start_line=0, end_line=None, glue=''): + """ + Include a file, + optionally indicating start_line and end_line (start counting from 0) + optionally set a glue string to lead every string except the first one (can be used for indent) + The path is relative to the top directory of the documentation + project. + """ + full_filename = os.path.join(env.project_dir, filename) + with open(full_filename, 'r') as f: + lines = f.readlines() + line_range = lines[start_line:end_line] + return glue.join(line_range) + + @env.macro + def cards(pages, columns=1, style="cards", force_version=False): + current_page = env.variables.page + absolute_url = current_page.abs_url + canonical = current_page.canonical_url + url_parts = re.search("//([^/]+)/([^/]+)/([^/]+)/", canonical) + (site, language, version) = url_parts.groups() + version = force_version or version + + if isinstance(pages, str): + pages = [pages] + cards = [] + for page in pages: + with open("docs/%s.md" % page, "r") as doc_file: + doc = doc_file.read() + match = re.search("^# (.*)", doc, re.MULTILINE) + if match: + header = match.groups()[0] + else: + header = "" + default_meta = { + "title": header, + "short": "", + "description": "" + } + doc_meta = { + **default_meta, + **meta.get_data(doc)[1] + } + cards.append( + CARDS_TEMPLATE % ( + '/'.join(( + '', + site, + language, + version, + page + )), + doc_meta['short'] or doc_meta['title'], + doc_meta['description'] or " " + # site_url + ) + ) + + return """
    %s
    """ % (style, columns, "\n".join(cards)) diff --git a/material/404.html b/material/404.html deleted file mode 100644 index 52beb3b8b3..0000000000 --- a/material/404.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends "base.html" %} -{% block content %} -

    404 - Not found

    -{% endblock %} diff --git a/material/assets/images/favicon.png b/material/assets/images/favicon.png deleted file mode 100644 index 76d17f57ad..0000000000 Binary files a/material/assets/images/favicon.png and /dev/null differ diff --git a/material/assets/images/icons/bitbucket.4ebea66e.svg b/material/assets/images/icons/bitbucket.4ebea66e.svg deleted file mode 100644 index ffc79b1ecf..0000000000 --- a/material/assets/images/icons/bitbucket.4ebea66e.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/material/assets/images/icons/github.a4034fb1.svg b/material/assets/images/icons/github.a4034fb1.svg deleted file mode 100644 index f8944b0154..0000000000 --- a/material/assets/images/icons/github.a4034fb1.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/material/assets/images/icons/gitlab.348cdb3a.svg b/material/assets/images/icons/gitlab.348cdb3a.svg deleted file mode 100644 index f4df57c665..0000000000 --- a/material/assets/images/icons/gitlab.348cdb3a.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/material/assets/javascripts/application.0cf9b500.js b/material/assets/javascripts/application.0cf9b500.js deleted file mode 100644 index 62700fa21b..0000000000 --- a/material/assets/javascripts/application.0cf9b500.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=6)}([function(e,t,n){"use strict";t.__esModule=!0,t.default={createElement:function(e,t){var n=document.createElement(e);t&&Array.prototype.forEach.call(Object.keys(t),function(e){n.setAttribute(e,t[e])});for(var r=arguments.length,i=Array(r>2?r-2:0),o=2;o pre, pre > code");Array.prototype.forEach.call(n,function(t,n){var r="__code_"+n,i=e.createElement("button",{class:"md-clipboard",title:h("clipboard.copy"),"data-clipboard-target":"#"+r+" pre, #"+r+" code"},e.createElement("span",{class:"md-clipboard__message"})),o=t.parentNode;o.id=r,o.insertBefore(i,t)});new c.default(".md-clipboard").on("success",function(e){var t=e.trigger.querySelector(".md-clipboard__message");if(!(t instanceof HTMLElement))throw new ReferenceError;e.clearSelection(),t.dataset.mdTimer&&clearTimeout(parseInt(t.dataset.mdTimer,10)),t.classList.add("md-clipboard__message--active"),t.innerHTML=h("clipboard.copied"),t.dataset.mdTimer=setTimeout(function(){t.classList.remove("md-clipboard__message--active"),t.dataset.mdTimer=""},2e3).toString()})}if(!Modernizr.details){var r=document.querySelectorAll("details > summary");Array.prototype.forEach.call(r,function(e){e.addEventListener("click",function(e){var t=e.target.parentNode;t.hasAttribute("open")?t.removeAttribute("open"):t.setAttribute("open","")})})}var i=function(){if(document.location.hash){var e=document.getElementById(document.location.hash.substring(1));if(!e)return;for(var t=e.parentNode;t&&!(t instanceof HTMLDetailsElement);)t=t.parentNode;if(t&&!t.open){t.open=!0;var n=location.hash;location.hash=" ",location.hash=n}}};if(window.addEventListener("hashchange",i),i(),Modernizr.ios){var o=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(o,function(e){e.addEventListener("touchstart",function(){var t=e.scrollTop;0===t?e.scrollTop=1:t+e.offsetHeight===e.scrollHeight&&(e.scrollTop=t-1)})})}}).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Shadow("[data-md-component=container]","[data-md-component=header]")).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Title("[data-md-component=title]",".md-typeset h1")).listen(),document.querySelector("[data-md-component=hero]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=hero]")).listen(),document.querySelector("[data-md-component=tabs]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=tabs]")).listen(),new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=navigation]","[data-md-component=header]"))),document.querySelector("[data-md-component=toc]")&&new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=toc]","[data-md-component=header]"))),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,"scroll",new f.default.Nav.Blur("[data-md-component=toc] [href]")));var n=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(n,function(e){new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(e.previousElementSibling,"click",new f.default.Nav.Collapse(e)))}),new f.default.Event.MatchMedia("(max-width: 1219px)",new f.default.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new f.default.Nav.Scrolling("[data-md-component=navigation] nav"))),document.querySelector("[data-md-component=search]")&&(new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-toggle=search]","change",new f.default.Search.Lock("[data-md-toggle=search]"))),new f.default.Event.Listener("[data-md-component=query]",["focus","keyup","change"],new f.default.Search.Result("[data-md-component=result]",function(){return fetch(t.url.base+"/"+(t.version<"0.17"?"mkdocs":"search")+"/search_index.json",{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return e.docs.map(function(e){return e.location=t.url.base+e.location,e})})})).listen(),new f.default.Event.Listener("[data-md-component=reset]","click",function(){setTimeout(function(){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()},10)}).listen(),new f.default.Event.Listener("[data-md-toggle=search]","change",function(e){setTimeout(function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.focus()}},400,e.target)}).listen(),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener("[data-md-component=query]","focus",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked||(e.checked=!0,e.dispatchEvent(new CustomEvent("change")))})),new f.default.Event.Listener(window,"keydown",function(e){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;var n=document.querySelector("[data-md-component=query]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(!e.metaKey&&!e.ctrlKey)if(t.checked){if(13===e.keyCode){if(n===document.activeElement){e.preventDefault();var r=document.querySelector("[data-md-component=search] [href][data-md-state=active]");r instanceof HTMLLinkElement&&(window.location=r.getAttribute("href"),t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur())}}else if(9===e.keyCode||27===e.keyCode)t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur();else if(-1!==[8,37,39].indexOf(e.keyCode))n!==document.activeElement&&n.focus();else if(-1!==[38,40].indexOf(e.keyCode)){var i=e.keyCode,o=Array.prototype.slice.call(document.querySelectorAll("[data-md-component=query], [data-md-component=search] [href]")),a=o.find(function(e){if(!(e instanceof HTMLElement))throw new ReferenceError;return"active"===e.dataset.mdState});a&&(a.dataset.mdState="");var s=Math.max(0,(o.indexOf(a)+o.length+(38===i?-1:1))%o.length);return o[s]&&(o[s].dataset.mdState="active",o[s].focus()),e.preventDefault(),e.stopPropagation(),!1}}else document.activeElement&&!document.activeElement.form&&(70!==e.keyCode&&83!==e.keyCode||(n.focus(),e.preventDefault()))}).listen(),new f.default.Event.Listener(window,"keypress",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t!==document.activeElement&&t.focus()}}).listen()),new f.default.Event.Listener(document.body,"keydown",function(e){if(9===e.keyCode){var t=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[for]:not([tabindex])");Array.prototype.forEach.call(t,function(e){e.offsetHeight&&(e.tabIndex=0)})}}).listen(),new f.default.Event.Listener(document.body,"mousedown",function(){var e=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[tabindex]");Array.prototype.forEach.call(e,function(e){e.removeAttribute("tabIndex")})}).listen(),document.body.addEventListener("click",function(){"tabbing"===document.body.dataset.mdState&&(document.body.dataset.mdState="")}),new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var e=document.querySelector("[data-md-toggle=drawer]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked&&(e.checked=!1,e.dispatchEvent(new CustomEvent("change")))})),function(){var e=document.querySelector("[data-md-source]");if(!e)return a.default.resolve([]);if(!(e instanceof HTMLAnchorElement))throw new ReferenceError;switch(e.dataset.mdSource){case"github":return new f.default.Source.Adapter.GitHub(e).fetch();default:return a.default.resolve([])}}().then(function(e){var t=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(t,function(t){new f.default.Source.Repository(t).initialize(e)})})}t.__esModule=!0,t.app=void 0,n(7),n(8),n(9),n(10),n(11),n(12),n(13);var o=n(14),a=r(o),s=n(18),c=r(s),u=n(19),l=r(u),d=n(20),f=r(d);window.Promise=window.Promise||a.default;var h=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},p={initialize:i};t.app=p}).call(t,n(0))},function(e,t,n){e.exports=n.p+"assets/images/icons/bitbucket.4ebea66e.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/github.a4034fb1.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/gitlab.348cdb3a.svg"},function(e,t){},function(e,t){},function(e,t){try{var n=new window.CustomEvent("test");if(n.preventDefault(),!0!==n.defaultPrevented)throw new Error("Could not prevent default")}catch(e){var r=function(e,t){var n,r;return t=t||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),r=n.preventDefault,n.preventDefault=function(){r.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch(e){this.defaultPrevented=!0}},n};r.prototype=window.Event.prototype,window.CustomEvent=r}},function(e,t,n){window.fetch||(window.fetch=n(2).default||n(2))},function(e,t,n){"use strict";(function(t){function n(){}function r(e,t){return function(){e.apply(t,arguments)}}function i(e){if(!(this instanceof i))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],l(e,this)}function o(e,t){for(;3===e._state;)e=e._value;if(0===e._state)return void e._deferreds.push(t);e._handled=!0,i._immediateFn(function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null===n)return void(1===e._state?a:s)(t.promise,e._value);var r;try{r=n(e._value)}catch(e){return void s(t.promise,e)}a(t.promise,r)})}function a(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof i)return e._state=3,e._value=t,void c(e);if("function"==typeof n)return void l(r(n,t),e)}e._state=1,e._value=t,c(e)}catch(t){s(e,t)}}function s(e,t){e._state=2,e._value=t,c(e)}function c(e){2===e._state&&0===e._deferreds.length&&i._immediateFn(function(){e._handled||i._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(16),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";function r(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=e.action,this.container=e.container,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var e=this,t="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return e.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[t?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(e){this.emitter.emit(e?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(e){if(void 0!==e){if(!e||"object"!==(void 0===e?"undefined":i(e))||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=e}},get:function(){return this._target}}]),e}();e.exports=a})},function(e,t,n){function r(e,t,n){if(!e&&!t&&!n)throw new Error("Missing required arguments");if(!s.string(t))throw new TypeError("Second argument must be a String");if(!s.fn(n))throw new TypeError("Third argument must be a Function");if(s.node(e))return i(e,t,n);if(s.nodeList(e))return o(e,t,n);if(s.string(e))return a(e,t,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function i(e,t,n){return e.addEventListener(t,n),{destroy:function(){e.removeEventListener(t,n)}}}function o(e,t,n){return Array.prototype.forEach.call(e,function(e){e.addEventListener(t,n)}),{destroy:function(){Array.prototype.forEach.call(e,function(e){e.removeEventListener(t,n)})}}}function a(e,t,n){return c(document.body,e,t,n)}var s=n(6),c=n(5);e.exports=r},function(e,t){function n(){}n.prototype={on:function(e,t,n){var r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){function r(){i.off(e,r),t.apply(n,arguments)}var i=this;return r._=t,this.on(e,r,n)},emit:function(e){var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),r=0,i=n.length;for(r;r0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===f(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=(0,d.default)(e,"click",function(e){return t.onClick(e)})}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new u.default({action:this.action(t),target:this.target(t),text:this.text(t),container:this.container,trigger:t,emitter:this})}},{key:"defaultAction",value:function(e){return c("action",e)}},{key:"defaultTarget",value:function(e){var t=c("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return c("text",e)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach(function(e){n=n&&!!document.queryCommandSupported(e)}),n}}]),t}(l.default);e.exports=p})},function(e,t){function n(e,t){for(;e&&e.nodeType!==r;){if("function"==typeof e.matches&&e.matches(t))return e;e=e.parentNode}}var r=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}e.exports=n},function(e,t,n){function r(e,t,n,r,i){var a=o.apply(this,arguments);return e.addEventListener(n,a,i),{destroy:function(){e.removeEventListener(n,a,i)}}}function i(e,t,n,i,o){return"function"==typeof e.addEventListener?r.apply(null,arguments):"function"==typeof n?r.bind(null,document).apply(null,arguments):("string"==typeof e&&(e=document.querySelectorAll(e)),Array.prototype.map.call(e,function(e){return r(e,t,n,i,o)}))}function o(e,t,n,r){return function(n){n.delegateTarget=a(n.target,t),n.delegateTarget&&r.call(e,n)}}var a=n(4);e.exports=i},function(e,t){t.node=function(e){return void 0!==e&&e instanceof HTMLElement&&1===e.nodeType},t.nodeList=function(e){var n=Object.prototype.toString.call(e);return void 0!==e&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in e&&(0===e.length||t.node(e[0]))},t.string=function(e){return"string"==typeof e||e instanceof String},t.fn=function(e){return"[object Function]"===Object.prototype.toString.call(e)}},function(e,t){function n(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var n=e.hasAttribute("readonly");n||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(e),r.removeAllRanges(),r.addRange(i),t=r.toString()}return t}e.exports=n}])})},function(e,t,n){var r;!function(){"use strict";function i(e,t){var n;if(t=t||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=t.touchBoundary||10,this.layer=e,this.tapDelay=t.tapDelay||200,this.tapTimeout=t.tapTimeout||700,!i.notNeeded(e)){for(var r=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],o=this,s=0,c=r.length;s=0,a=navigator.userAgent.indexOf("Android")>0&&!o,s=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,c=s&&/OS 4_\d(_\d)?/.test(navigator.userAgent),u=s&&/OS [6-7]_\d/.test(navigator.userAgent),l=navigator.userAgent.indexOf("BB10")>0;i.prototype.needsClick=function(e){switch(e.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(e.disabled)return!0;break;case"input":if(s&&"file"===e.type||e.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(e.className)},i.prototype.needsFocus=function(e){switch(e.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!a;case"input":switch(e.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!e.disabled&&!e.readOnly;default:return/\bneedsfocus\b/.test(e.className)}},i.prototype.sendClick=function(e,t){var n,r;document.activeElement&&document.activeElement!==e&&document.activeElement.blur(),r=t.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(e),!0,!0,window,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,e.dispatchEvent(n)},i.prototype.determineEventType=function(e){return a&&"select"===e.tagName.toLowerCase()?"mousedown":"click"},i.prototype.focus=function(e){var t;s&&e.setSelectionRange&&0!==e.type.indexOf("date")&&"time"!==e.type&&"month"!==e.type?(t=e.value.length,e.setSelectionRange(t,t)):e.focus()},i.prototype.updateScrollParent=function(e){var t,n;if(!(t=e.fastClickScrollParent)||!t.contains(e)){n=e;do{if(n.scrollHeight>n.offsetHeight){t=n,e.fastClickScrollParent=n;break}n=n.parentElement}while(n)}t&&(t.fastClickLastScrollTop=t.scrollTop)},i.prototype.getTargetElementFromEventTarget=function(e){return e.nodeType===Node.TEXT_NODE?e.parentNode:e},i.prototype.onTouchStart=function(e){var t,n,r;if(e.targetTouches.length>1)return!0;if(t=this.getTargetElementFromEventTarget(e.target),n=e.targetTouches[0],s){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!c){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return e.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(t)}}return this.trackingClick=!0,this.trackingClickStart=e.timeStamp,this.targetElement=t,this.touchStartX=n.pageX,this.touchStartY=n.pageY,e.timeStamp-this.lastClickTimen||Math.abs(t.pageY-this.touchStartY)>n},i.prototype.onTouchMove=function(e){return!this.trackingClick||((this.targetElement!==this.getTargetElementFromEventTarget(e.target)||this.touchHasMoved(e))&&(this.trackingClick=!1,this.targetElement=null),!0)},i.prototype.findControl=function(e){return void 0!==e.control?e.control:e.htmlFor?document.getElementById(e.htmlFor):e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},i.prototype.onTouchEnd=function(e){var t,n,r,i,o,l=this.targetElement;if(!this.trackingClick)return!0;if(e.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=e.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,u&&(o=e.changedTouches[0],l=document.elementFromPoint(o.pageX-window.pageXOffset,o.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),"label"===(r=l.tagName.toLowerCase())){if(t=this.findControl(l)){if(this.focus(l),a)return!1;l=t}}else if(this.needsFocus(l))return e.timeStamp-n>100||s&&window.top!==window&&"input"===r?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,e),s&&"select"===r||(this.targetElement=null,e.preventDefault()),!1);return!(!s||c||!(i=l.fastClickScrollParent)||i.fastClickLastScrollTop===i.scrollTop)||(this.needsClick(l)||(e.preventDefault(),this.sendClick(l,e)),!1)},i.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},i.prototype.onMouse=function(e){return!this.targetElement||(!!e.forwardedTouchEvent||(!e.cancelable||(!(!this.needsClick(this.targetElement)||this.cancelNextClick)||(e.stopImmediatePropagation?e.stopImmediatePropagation():e.propagationStopped=!0,e.stopPropagation(),e.preventDefault(),!1))))},i.prototype.onClick=function(e){var t;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===e.target.type&&0===e.detail||(t=this.onMouse(e),t||(this.targetElement=null),t)},i.prototype.destroy=function(){var e=this.layer;a&&(e.removeEventListener("mouseover",this.onMouse,!0),e.removeEventListener("mousedown",this.onMouse,!0),e.removeEventListener("mouseup",this.onMouse,!0)),e.removeEventListener("click",this.onClick,!0),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1),e.removeEventListener("touchcancel",this.onTouchCancel,!1)},i.notNeeded=function(e){var t,n,r;if(void 0===window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!a)return!0;if(t=document.querySelector("meta[name=viewport]")){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(l&&(r=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),r[1]>=10&&r[2]>=3&&(t=document.querySelector("meta[name=viewport]")))){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===e.style.msTouchAction||"manipulation"===e.style.touchAction||(!!(+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]>=27&&(t=document.querySelector("meta[name=viewport]"))&&(-1!==t.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))||("none"===e.style.touchAction||"manipulation"===e.style.touchAction))},i.attach=function(e,t){return new i(e,t)},void 0!==(r=function(){return i}.call(t,n,t,e))&&(e.exports=r)}()},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(21),o=r(i),a=n(23),s=r(a),c=n(26),u=r(c),l=n(30),d=r(l),f=n(36),h=r(f),p=n(38),m=r(p),v=n(44),y=r(v);t.default={Event:o.default,Header:s.default,Nav:u.default,Search:d.default,Sidebar:h.default,Source:m.default,Tabs:y.default}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(3),o=r(i),a=n(22),s=r(a);t.default={Listener:o.default,MatchMedia:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(3),o=(function(e){e&&e.__esModule}(i),function e(t,n){r(this,e),this.handler_=function(e){e.matches?n.listen():n.unlisten()};var i=window.matchMedia(t);i.addListener(this.handler_),this.handler_(i)});t.default=o},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(24),o=r(i),a=n(25),s=r(a);t.default={Shadow:o.default,Title:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.active_=!1}return e.prototype.setup=function(){for(var e=this.el_;e=e.previousElementSibling;){if(!(e instanceof HTMLElement))throw new ReferenceError;this.height_+=e.offsetHeight}this.update()},e.prototype.update=function(e){if(!e||"resize"!==e.type&&"orientationchange"!==e.type){var t=window.pageYOffset>=this.height_;t!==this.active_&&(this.header_.dataset.mdState=(this.active_=t)?"shadow":"")}else this.height_=0,this.setup()},e.prototype.reset=function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLHeadingElement))throw new ReferenceError;this.header_=i,this.active_=!1}return e.prototype.setup=function(){var e=this;Array.prototype.forEach.call(this.el_.children,function(t){t.style.width=e.el_.offsetWidth-20+"px"})},e.prototype.update=function(e){var t=this,n=window.pageYOffset>=this.header_.offsetTop;n!==this.active_&&(this.el_.dataset.mdState=(this.active_=n)?"active":""),"resize"!==e.type&&"orientationchange"!==e.type||Array.prototype.forEach.call(this.el_.children,function(e){e.style.width=t.el_.offsetWidth-20+"px"})},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.width="",this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(27),o=r(i),a=n(28),s=r(a),c=n(29),u=r(c);t.default={Blur:o.default,Collapse:s.default,Scrolling:u.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e),this.els_="string"==typeof t?document.querySelectorAll(t):t,this.index_=0,this.offset_=window.pageYOffset,this.dir_=!1,this.anchors_=[].reduce.call(this.els_,function(e,t){return e.concat(document.getElementById(t.hash.substring(1))||[])},[])}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=window.pageYOffset,t=this.offset_-e<0;if(this.dir_!==t&&(this.index_=this.index_=t?0:this.els_.length-1),0!==this.anchors_.length){if(this.offset_<=e)for(var n=this.index_+1;n0&&(this.els_[n-1].dataset.mdState="blur"),this.index_=n;else for(var r=this.index_;r>=0;r--){if(!(this.anchors_[r].offsetTop-80>e)){this.index_=r;break}r>0&&(this.els_[r-1].dataset.mdState="")}this.offset_=e,this.dir_=t}},e.prototype.reset=function(){Array.prototype.forEach.call(this.els_,function(e){e.dataset.mdState=""}),this.index_=0,this.offset_=window.pageYOffset},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){var e=this.el_.getBoundingClientRect().height;this.el_.style.display=e?"block":"none",this.el_.style.overflow=e?"visible":"hidden"},e.prototype.update=function(){var e=this,t=this.el_.getBoundingClientRect().height;if(this.el_.style.display="block",this.el_.style.overflow="",t)this.el_.style.maxHeight=t+"px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight="0px"});else{this.el_.setAttribute("data-md-state","expand"),this.el_.style.maxHeight="";var n=this.el_.getBoundingClientRect().height;this.el_.removeAttribute("data-md-state"),this.el_.style.maxHeight="0px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight=n+"px"})}var r=function e(n){var r=n.target;if(!(r instanceof HTMLElement))throw new ReferenceError;r.removeAttribute("data-md-state"),r.style.maxHeight="",r.style.display=t?"none":"block",r.style.overflow=t?"hidden":"visible",r.removeEventListener("transitionend",e)};this.el_.addEventListener("transitionend",r,!1)},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.maxHeight="",this.el_.style.display="",this.el_.style.overflow=""},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){this.el_.children[this.el_.children.length-1].style.webkitOverflowScrolling="touch";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling="touch"}})},e.prototype.update=function(e){var t=e.target;if(!(t instanceof HTMLElement))throw new ReferenceError;var n=t.nextElementSibling;if(!(n instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==n.tagName&&n.nextElementSibling;)n=n.nextElementSibling;if(!(t.parentNode instanceof HTMLElement&&t.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var r=t.parentNode.parentNode,i=n.children[n.children.length-1];if(r.style.webkitOverflowScrolling="",i.style.webkitOverflowScrolling="",!t.checked){var o=function e(){n instanceof HTMLElement&&(r.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",o,!1)}if(t.checked){var a=function e(){n instanceof HTMLElement&&(i.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",a,!1)}},e.prototype.reset=function(){this.el_.children[1].style.webkitOverflowScrolling="";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling=""}})},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(31),o=r(i),a=n(32),s=r(a);t.default={Lock:o.default,Result:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(this.el_=n,!document.body)throw new ReferenceError;this.lock_=document.body}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=this;this.el_.checked?(this.offset_=window.pageYOffset,setTimeout(function(){window.scrollTo(0,0),e.el_.checked&&(e.lock_.dataset.mdState="lock")},400)):(this.lock_.dataset.mdState="",setTimeout(function(){void 0!==e.offset_&&window.scrollTo(0,e.offset_)},100))},e.prototype.reset=function(){"lock"===this.lock_.dataset.mdState&&window.scrollTo(0,this.offset_),this.lock_.dataset.mdState=""},e}();t.default=i},function(e,t,n){"use strict";(function(e){function r(e){return e&&e.__esModule?e:{default:e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=n(33),a=r(o),s=n(34),c=r(s),u=function(e,t){var n=t;if(e.length>n){for(;" "!==e[n]&&--n>0;);return e.substring(0,n)+"..."}return e},l=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},d=function(){function t(e,n){i(this,t);var r="string"==typeof e?document.querySelector(e):e;if(!(r instanceof HTMLElement))throw new ReferenceError;this.el_=r;var o=Array.prototype.slice.call(this.el_.children),a=o[0],s=o[1];this.data_=n,this.meta_=a,this.list_=s,this.message_={placeholder:this.meta_.textContent,none:l("search.result.none"),one:l("search.result.one"),other:l("search.result.other")};var u=l("search.tokenizer");u.length&&(c.default.tokenizer.separator=u),this.lang_=l("search.language").split(",").filter(Boolean).map(function(e){return e.trim()})}return t.prototype.update=function(t){var n=this;if("focus"!==t.type||this.index_){if("focus"===t.type||"keyup"===t.type){var r=t.target;if(!(r instanceof HTMLInputElement))throw new ReferenceError;if(!this.index_||r.value===this.value_)return;for(;this.list_.firstChild;)this.list_.removeChild(this.list_.firstChild);if(this.value_=r.value,0===this.value_.length)return void(this.meta_.textContent=this.message_.placeholder);var i=this.index_.query(function(e){n.value_.toLowerCase().split(" ").filter(Boolean).forEach(function(t){e.term(t,{wildcard:c.default.Query.wildcard.TRAILING})})}).reduce(function(e,t){var r=n.docs_.get(t.ref);if(r.parent){var i=r.parent.location;e.set(i,(e.get(i)||[]).concat(t))}else{var o=r.location;e.set(o,e.get(o)||[])}return e},new Map),o=(0,a.default)(this.value_.trim()).replace(new RegExp(c.default.tokenizer.separator,"img"),"|"),s=new RegExp("(^|"+c.default.tokenizer.separator+")("+o+")","img"),d=function(e,t,n){return t+""+n+""};this.stack_=[],i.forEach(function(t,r){var i,o=n.docs_.get(r),a=e.createElement("li",{class:"md-search-result__item"},e.createElement("a",{href:o.location,title:o.title,class:"md-search-result__link",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article md-search-result__article--document"},e.createElement("h1",{class:"md-search-result__title"},{__html:o.title.replace(s,d)}),o.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:o.text.replace(s,d)}):{}))),c=t.map(function(t){return function(){var r=n.docs_.get(t.ref);a.appendChild(e.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link","data-md-rel":"anchor",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article"},e.createElement("h1",{class:"md-search-result__title"},{__html:r.title.replace(s,d)}),r.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:u(r.text.replace(s,d),400)}):{})))}});(i=n.stack_).push.apply(i,[function(){return n.list_.appendChild(a)}].concat(c))});var f=this.el_.parentNode;if(!(f instanceof HTMLElement))throw new ReferenceError;for(;this.stack_.length&&f.offsetHeight>=f.scrollHeight-16;)this.stack_.shift()();var h=this.list_.querySelectorAll("[data-md-rel=anchor]");switch(Array.prototype.forEach.call(h,function(e){["click","keydown"].forEach(function(t){e.addEventListener(t,function(n){if("keydown"!==t||13===n.keyCode){var r=document.querySelector("[data-md-toggle=search]");if(!(r instanceof HTMLInputElement))throw new ReferenceError;r.checked&&(r.checked=!1,r.dispatchEvent(new CustomEvent("change"))),n.preventDefault(),setTimeout(function(){document.location.href=e.href},100)}})})}),i.size){case 0:this.meta_.textContent=this.message_.none;break;case 1:this.meta_.textContent=this.message_.one;break;default:this.meta_.textContent=this.message_.other.replace("#",i.size)}}}else{var p=function(e){n.docs_=e.reduce(function(e,t){var n=t.location.split("#"),r=n[0];return n[1]&&(t.parent=e.get(r),t.parent&&!t.parent.done&&(t.parent.title=t.title,t.parent.text=t.text,t.parent.done=!0)),t.text=t.text.replace(/\n/g," ").replace(/\s+/g," ").replace(/\s+([,.:;!?])/g,function(e,t){return t}),t.parent&&t.parent.title===t.title||e.set(t.location,t),e},new Map);var t=n.docs_,r=n.lang_;n.stack_=[],n.index_=(0,c.default)(function(){var e,n=this,i={"search.pipeline.trimmer":c.default.trimmer,"search.pipeline.stopwords":c.default.stopWordFilter},o=Object.keys(i).reduce(function(e,t){return l(t).match(/^false$/i)||e.push(i[t]),e},[]);this.pipeline.reset(),o&&(e=this.pipeline).add.apply(e,o),1===r.length&&"en"!==r[0]&&c.default[r[0]]?this.use(c.default[r[0]]):r.length>1&&this.use(c.default.multiLanguage.apply(c.default,r)),this.field("title",{boost:10}),this.field("text"),this.ref("location"),t.forEach(function(e){return n.add(e)})});var i=n.el_.parentNode;if(!(i instanceof HTMLElement))throw new ReferenceError;i.addEventListener("scroll",function(){for(;n.stack_.length&&i.scrollTop+i.offsetHeight>=i.scrollHeight-16;)n.stack_.splice(0,10).forEach(function(e){return e()})})};setTimeout(function(){return"function"==typeof n.data_?n.data_().then(p):p(n.data_)},250)}},t}();t.default=d}).call(t,n(0))},function(e,t,n){"use strict";var r=/[|\\{}()[\]^$+*?.]/g;e.exports=function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(r,"\\$&")}},function(e,t,n){(function(t){e.exports=t.lunr=n(35)}).call(t,n(1))},function(e,t,n){var r,i;!function(){var o=function(e){var t=new o.Builder;return t.pipeline.add(o.trimmer,o.stopWordFilter,o.stemmer),t.searchPipeline.add(o.stemmer),e.call(t,t),t.build()};o.version="2.1.5",o.utils={},o.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),o.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},o.FieldRef=function(e,t,n){this.docRef=e,this.fieldName=t,this._stringValue=n},o.FieldRef.joiner="/",o.FieldRef.fromString=function(e){var t=e.indexOf(o.FieldRef.joiner);if(-1===t)throw"malformed field ref string";var n=e.slice(0,t),r=e.slice(t+1);return new o.FieldRef(r,n,e)},o.FieldRef.prototype.toString=function(){return void 0==this._stringValue&&(this._stringValue=this.fieldName+o.FieldRef.joiner+this.docRef),this._stringValue},o.idf=function(e,t){var n=0;for(var r in e)"_index"!=r&&(n+=Object.keys(e[r]).length);var i=(t-n+.5)/(n+.5);return Math.log(1+Math.abs(i))},o.Token=function(e,t){this.str=e||"",this.metadata=t||{}},o.Token.prototype.toString=function(){return this.str},o.Token.prototype.update=function(e){return this.str=e(this.str,this.metadata),this},o.Token.prototype.clone=function(e){return e=e||function(e){return e},new o.Token(e(this.str,this.metadata),this.metadata)},o.tokenizer=function(e){if(null==e||void 0==e)return[];if(Array.isArray(e))return e.map(function(e){return new o.Token(o.utils.asString(e).toLowerCase())});for(var t=e.toString().trim().toLowerCase(),n=t.length,r=[],i=0,a=0;i<=n;i++){var s=t.charAt(i),c=i-a;(s.match(o.tokenizer.separator)||i==n)&&(c>0&&r.push(new o.Token(t.slice(a,i),{position:[a,c],index:r.length})),a=i+1)}return r},o.tokenizer.separator=/[\s\-]+/,o.Pipeline=function(){this._stack=[]},o.Pipeline.registeredFunctions=Object.create(null),o.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&o.utils.warn("Overwriting existing registered function: "+t),e.label=t,o.Pipeline.registeredFunctions[e.label]=e},o.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||o.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},o.Pipeline.load=function(e){var t=new o.Pipeline;return e.forEach(function(e){var n=o.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)}),t},o.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach(function(e){o.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},o.Pipeline.prototype.after=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},o.Pipeline.prototype.before=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},o.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},o.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n1&&(oe&&(n=i),o!=e);)r=n-t,i=t+Math.floor(r/2),o=this.elements[2*i];return o==e?2*i:o>e?2*i:os?u+=2:a==s&&(t+=n[c+1]*r[u+1],c+=2,u+=2);return t},o.Vector.prototype.similarity=function(e){return this.dot(e)/(this.magnitude()*e.magnitude())},o.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var a,s=i.str.charAt(0);s in i.node.edges?a=i.node.edges[s]:(a=new o.TokenSet,i.node.edges[s]=a),1==i.str.length?a.final=!0:r.push({node:a,editsRemaining:i.editsRemaining,str:i.str.slice(1)})}if(i.editsRemaining>0&&i.str.length>1){var c,s=i.str.charAt(1);s in i.node.edges?c=i.node.edges[s]:(c=new o.TokenSet,i.node.edges[s]=c),i.str.length<=2?c.final=!0:r.push({node:c,editsRemaining:i.editsRemaining-1,str:i.str.slice(2)})}if(i.editsRemaining>0&&1==i.str.length&&(i.node.final=!0),i.editsRemaining>0&&i.str.length>=1){if("*"in i.node.edges)var u=i.node.edges["*"];else{var u=new o.TokenSet;i.node.edges["*"]=u}1==i.str.length?u.final=!0:r.push({node:u,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)})}if(i.editsRemaining>0){if("*"in i.node.edges)var l=i.node.edges["*"];else{var l=new o.TokenSet;i.node.edges["*"]=l}0==i.str.length?l.final=!0:r.push({node:l,editsRemaining:i.editsRemaining-1,str:i.str})}if(i.editsRemaining>0&&i.str.length>1){var d,f=i.str.charAt(0),h=i.str.charAt(1);h in i.node.edges?d=i.node.edges[h]:(d=new o.TokenSet,i.node.edges[h]=d),1==i.str.length?d.final=!0:r.push({node:d,editsRemaining:i.editsRemaining-1,str:f+i.str.slice(2)})}}return n},o.TokenSet.fromString=function(e){for(var t=new o.TokenSet,n=t,r=!1,i=0,a=e.length;i=e;t--){var n=this.uncheckedNodes[t],r=n.child.toString();r in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[r]:(n.child._str=r,this.minimizedNodes[r]=n.child),this.uncheckedNodes.pop()}},o.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},o.Index.prototype.search=function(e){return this.query(function(t){new o.QueryParser(e,t).parse()})},o.Index.prototype.query=function(e){var t=new o.Query(this.fields),n=Object.create(null),r=Object.create(null),i=Object.create(null);e.call(t,t);for(var a=0;a1?1:e},o.Builder.prototype.k1=function(e){this._k1=e},o.Builder.prototype.add=function(e){var t=e[this._ref];this.documentCount+=1;for(var n=0;n=this.length)return o.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},o.QueryLexer.prototype.width=function(){return this.pos-this.start},o.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},o.QueryLexer.prototype.backup=function(){this.pos-=1},o.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{e=this.next(),t=e.charCodeAt(0)}while(t>47&&t<58);e!=o.QueryLexer.EOS&&this.backup()},o.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(o.QueryLexer.TERM)),e.ignore(),e.more())return o.QueryLexer.lexText},o.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.EDIT_DISTANCE),o.QueryLexer.lexText},o.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.BOOST),o.QueryLexer.lexText},o.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(o.QueryLexer.TERM)},o.QueryLexer.termSeparator=o.tokenizer.separator,o.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==o.QueryLexer.EOS)return o.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return o.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexBoost;if(t.match(o.QueryLexer.termSeparator))return o.QueryLexer.lexTerm}else e.escapeCharacter()}},o.QueryParser=function(e,t){this.lexer=new o.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},o.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=o.QueryParser.parseFieldOrTerm;e;)e=e(this);return this.query},o.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},o.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},o.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},o.QueryParser.parseFieldOrTerm=function(e){var t=e.peekLexeme();if(void 0!=t)switch(t.type){case o.QueryLexer.FIELD:return o.QueryParser.parseField;case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(n+=" with value '"+t.str+"'"),new o.QueryParseError(n,t.start,t.end)}},o.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(void 0!=t){if(-1==e.query.allFields.indexOf(t.str)){var n=e.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),r="unrecognised field '"+t.str+"', possible fields: "+n;throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.fields=[t.str];var i=e.peekLexeme();if(void 0==i){var r="expecting term, found nothing";throw new o.QueryParseError(r,t.start,t.end)}switch(i.type){case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var r="expecting term, found '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(void 0!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(void 0==n)return void e.nextClause();switch(n.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+n.type+"'";throw new o.QueryParseError(r,n.start,n.end)}}},o.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="edit distance must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.editDistance=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="boost must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.boost=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},function(o,a){r=a,void 0!==(i="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=i)}(0,function(){return o})}()},function(e,t,n){"use strict";t.__esModule=!0;var r=n(37),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={Position:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,this.parent_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.pad_="fixed"===window.getComputedStyle(this.header_).position}return e.prototype.setup=function(){var e=Array.prototype.reduce.call(this.parent_.children,function(e,t){return Math.max(e,t.offsetTop)},0);this.offset_=e-(this.pad_?this.header_.offsetHeight:0),this.update()},e.prototype.update=function(e){var t=window.pageYOffset,n=window.innerHeight;e&&"resize"===e.type&&this.setup();var r={top:this.pad_?this.header_.offsetHeight:0,bottom:this.parent_.offsetTop+this.parent_.offsetHeight},i=n-r.top-Math.max(0,this.offset_-t)-Math.max(0,t+n-r.bottom);i!==this.height_&&(this.el_.style.height=(this.height_=i)+"px"),t>=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(39),o=r(i),a=n(43),s=r(a);t.default={Adapter:o.default,Repository:s.default}},function(e,t,n){"use strict";t.__esModule=!0;var r=n(40),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={GitHub:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}t.__esModule=!0;var a=n(41),s=function(e){return e&&e.__esModule?e:{default:e}}(a),c=function(e){function t(n){r(this,t);var o=i(this,e.call(this,n)),a=/^.+github\.com\/([^\/]+)\/?([^\/]+)?.*$/.exec(o.base_);if(a&&3===a.length){var s=a[1],c=a[2];o.base_="https://api.github.com/users/"+s+"/repos",o.name_=c}return o}return o(t,e),t.prototype.fetch_=function(){var e=this;return function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;return fetch(e.base_+"?per_page=30&page="+n).then(function(e){return e.json()}).then(function(r){if(!(r instanceof Array))throw new TypeError;if(e.name_){var i=r.find(function(t){return t.name===e.name_});return i||30!==r.length?i?[e.format_(i.stargazers_count)+" Stars",e.format_(i.forks_count)+" Forks"]:[]:t(n+1)}return[r.length+" Repositories"]})}()},t}(s.default);t.default=c},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(42),o=function(e){return e&&e.__esModule?e:{default:e}}(i),a=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLAnchorElement))throw new ReferenceError;this.el_=n,this.base_=this.el_.href,this.salt_=this.hash_(this.base_)}return e.prototype.fetch=function(){var e=this;return new Promise(function(t){var n=o.default.getJSON(e.salt_+".cache-source");void 0!==n?t(n):e.fetch_().then(function(n){o.default.set(e.salt_+".cache-source",n,{expires:1/96}),t(n)})})},e.prototype.fetch_=function(){throw new Error("fetch_(): Not implemented")},e.prototype.format_=function(e){return e>1e4?(e/1e3).toFixed(0)+"k":e>1e3?(e/1e3).toFixed(1)+"k":""+e},e.prototype.hash_=function(e){var t=0;if(0===e.length)return t;for(var n=0,r=e.length;n1){if(o=e({path:"/"},r.defaults,o),"number"==typeof o.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*o.expires),o.expires=s}o.expires=o.expires?o.expires.toUTCString():"";try{a=JSON.stringify(i),/^[\{\[]/.test(a)&&(i=a)}catch(e){}i=n.write?n.write(i,t):encodeURIComponent(String(i)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var c="";for(var u in o)o[u]&&(c+="; "+u,!0!==o[u]&&(c+="="+o[u]));return document.cookie=t+"="+i+c}t||(a={});for(var l=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f=this.el_.children[0].offsetTop+-43;e!==this.active_&&(this.el_.dataset.mdState=(this.active_=e)?"hidden":"")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.active_=!1},e}();t.default=i}])); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.da.js b/material/assets/javascripts/lunr/lunr.da.js deleted file mode 100644 index 3b07b2c19a..0000000000 --- a/material/assets/javascripts/lunr/lunr.da.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,s=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],o=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],u=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],c=new i;function l(){var e,r=c.limit-c.cursor;c.cursor>=n&&(e=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.find_among_b(o,4)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e)}this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var r,i=c.cursor;return function(){var r,i=c.cursor+3;if(n=c.limit,0<=i&&i<=c.limit){for(e=i;;){if(r=c.cursor,c.in_grouping(d,97,248)){c.cursor=r;break}if(c.cursor=r,r>=c.limit)return;c.cursor++}for(;!c.out_grouping(d,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(n=c.cursor)=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(s,32),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:c.in_grouping_b(u,97,229)&&c.slice_del()}}(),c.cursor=c.limit,l(),c.cursor=c.limit,function(){var e,r,i,t=c.limit-c.cursor;if(c.ket=c.cursor,c.eq_s_b(2,"st")&&(c.bra=c.cursor,c.eq_s_b(2,"ig")&&c.slice_del()),c.cursor=c.limit-t,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(a,5),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del(),i=c.limit-c.cursor,l(),c.cursor=c.limit-i;break;case 2:c.slice_from("løs")}}(),c.cursor=c.limit,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.out_grouping_b(d,97,248)?(c.bra=c.cursor,t=c.slice_to(t),c.limit_backward=r,c.eq_v_b(t)&&c.slice_del()):c.limit_backward=r),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.de.js b/material/assets/javascripts/lunr/lunr.de.js deleted file mode 100644 index ebd78f2819..0000000000 --- a/material/assets/javascripts/lunr/lunr.de.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,i;e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,s,t=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],o=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],c=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],u=[new r("ig",-1,1),new r("lich",-1,1)],a=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],l=[117,30,5],m=[117,30,4],h=new n;function w(e,r,n){return!(!h.eq_s(1,e)||(h.ket=h.cursor,!h.in_grouping(d,97,252)))&&(h.slice_from(r),h.cursor=n,!0)}function f(){for(;!h.in_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}for(;!h.out_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}return!1}function b(){return s<=h.cursor}function _(){return i<=h.cursor}this.setCurrent=function(e){h.setCurrent(e)},this.getCurrent=function(){return h.getCurrent()},this.stem=function(){var r=h.cursor;return function(){for(var e,r,n,i,s=h.cursor;;)if(e=h.cursor,h.bra=e,h.eq_s(1,"ß"))h.ket=h.cursor,h.slice_from("ss");else{if(e>=h.limit)break;h.cursor=e+1}for(h.cursor=s;;)for(r=h.cursor;;){if(n=h.cursor,h.in_grouping(d,97,252)){if(i=h.cursor,h.bra=i,w("u","U",n))break;if(h.cursor=i,w("y","Y",n))break}if(n>=h.limit)return void(h.cursor=r);h.cursor=n+1}}(),h.cursor=r,function(){s=h.limit,i=s;var r=h.cursor+3;0<=r&&r<=h.limit&&(e=r,f()||((s=h.cursor)=h.limit)return;h.cursor++}}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.du.js b/material/assets/javascripts/lunr/lunr.du.js deleted file mode 100644 index 375c0e763d..0000000000 --- a/material/assets/javascripts/lunr/lunr.du.js +++ /dev/null @@ -1 +0,0 @@ -!function(r,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,i,n;r.du=function(){this.pipeline.reset(),this.pipeline.add(r.du.trimmer,r.du.stopWordFilter,r.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.du.stemmer))},r.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.du.trimmer=r.trimmerSupport.generateTrimmer(r.du.wordCharacters),r.Pipeline.registerFunction(r.du.trimmer,"trimmer-du"),r.du.stemmer=(e=r.stemmerSupport.Among,i=r.stemmerSupport.SnowballProgram,n=new function(){var r,n,o,t=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],s=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],u=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],c=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],a=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],l=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],m=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],d=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],f=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],_=new i;function w(r){return _.cursor=r,r>=_.limit||(_.cursor++,!1)}function b(){for(;!_.in_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}for(;!_.out_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}function p(){return n<=_.cursor}function g(){return r<=_.cursor}function h(){var r=_.limit-_.cursor;_.find_among_b(u,3)&&(_.cursor=_.limit-r,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del()))}function k(){var r;o=!1,_.ket=_.cursor,_.eq_s_b(1,"e")&&(_.bra=_.cursor,p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.slice_del(),o=!0,h())))}function v(){var r;p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.eq_s_b(3,"gem")||(_.cursor=_.limit-r,_.slice_del(),h())))}this.setCurrent=function(r){_.setCurrent(r)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var e=_.cursor;return function(){for(var r,e,i,n=_.cursor;;){if(_.bra=_.cursor,r=_.find_among(t,11))switch(_.ket=_.cursor,r){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}for(_.cursor=n,_.bra=n,_.eq_s(1,"y")?(_.ket=_.cursor,_.slice_from("Y")):_.cursor=n;;)if(e=_.cursor,_.in_grouping(m,97,232)){if(i=_.cursor,_.bra=i,_.eq_s(1,"i"))_.ket=_.cursor,_.in_grouping(m,97,232)&&(_.slice_from("I"),_.cursor=e);else if(_.cursor=i,_.eq_s(1,"y"))_.ket=_.cursor,_.slice_from("Y"),_.cursor=e;else if(w(e))break}else if(w(e))break}(),_.cursor=e,n=_.limit,r=n,b()||((n=_.cursor)<3&&(n=3),b()||(r=_.cursor)),_.limit_backward=e,_.cursor=_.limit,function(){var r,e,i,n,t,s,u=_.limit-_.cursor;if(_.ket=_.cursor,r=_.find_among_b(c,5))switch(_.bra=_.cursor,r){case 1:p()&&_.slice_from("heid");break;case 2:v();break;case 3:p()&&_.out_grouping_b(f,97,232)&&_.slice_del()}if(_.cursor=_.limit-u,k(),_.cursor=_.limit-u,_.ket=_.cursor,_.eq_s_b(4,"heid")&&(_.bra=_.cursor,g()&&(e=_.limit-_.cursor,_.eq_s_b(1,"c")||(_.cursor=_.limit-e,_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,"en")&&(_.bra=_.cursor,v())))),_.cursor=_.limit-u,_.ket=_.cursor,r=_.find_among_b(a,6))switch(_.bra=_.cursor,r){case 1:if(g()){if(_.slice_del(),i=_.limit-_.cursor,_.ket=_.cursor,_.eq_s_b(2,"ig")&&(_.bra=_.cursor,g()&&(n=_.limit-_.cursor,!_.eq_s_b(1,"e")))){_.cursor=_.limit-n,_.slice_del();break}_.cursor=_.limit-i,h()}break;case 2:g()&&(t=_.limit-_.cursor,_.eq_s_b(1,"e")||(_.cursor=_.limit-t,_.slice_del()));break;case 3:g()&&(_.slice_del(),k());break;case 4:g()&&_.slice_del();break;case 5:g()&&o&&_.slice_del()}_.cursor=_.limit-u,_.out_grouping_b(d,73,232)&&(s=_.limit-_.cursor,_.find_among_b(l,4)&&_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-s,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del())))}(),_.cursor=_.limit_backward,function(){for(var r;;)if(_.bra=_.cursor,r=_.find_among(s,3))switch(_.ket=_.cursor,r){case 1:_.slice_from("y");break;case 2:_.slice_from("i");break;case 3:if(_.cursor>=_.limit)return;_.cursor++}}(),!0}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.du.stemmer,"stemmer-du"),r.du.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.es.js b/material/assets/javascripts/lunr/lunr.es.js deleted file mode 100644 index 4cb634f0ac..0000000000 --- a/material/assets/javascripts/lunr/lunr.es.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,s){"function"==typeof define&&define.amd?define(s):"object"==typeof exports?module.exports=s():s()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var s,r,n;e.es=function(){this.pipeline.reset(),this.pipeline.add(e.es.trimmer,e.es.stopWordFilter,e.es.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.es.stemmer))},e.es.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.es.trimmer=e.trimmerSupport.generateTrimmer(e.es.wordCharacters),e.Pipeline.registerFunction(e.es.trimmer,"trimmer-es"),e.es.stemmer=(s=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,a=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],t=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],o=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],u=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],w=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],c=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],m=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],l=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],d=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],b=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],_=new r;function h(){if(_.out_grouping(f,97,252)){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}return!0}function v(){var e,s=_.cursor;if(function(){if(_.in_grouping(f,97,252)){var e=_.cursor;if(h()){if(_.cursor=e,!_.in_grouping(f,97,252))return!0;for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}}return!1}return!0}()){if(_.cursor=s,!_.out_grouping(f,97,252))return;if(e=_.cursor,h()){if(_.cursor=e,!_.in_grouping(f,97,252)||_.cursor>=_.limit)return;_.cursor++}}i=_.cursor}function p(){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}return!0}function g(){return i<=_.cursor}function k(){return e<=_.cursor}function y(e,s){if(!k())return!0;_.slice_del(),_.ket=_.cursor;var r=_.find_among_b(e,s);return r&&(_.bra=_.cursor,1==r&&k()&&_.slice_del()),!1}function q(e){return!k()||(_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,e)&&(_.bra=_.cursor,k()&&_.slice_del()),!1)}function C(){var e;if(_.ket=_.cursor,e=_.find_among_b(m,46)){switch(_.bra=_.cursor,e){case 1:if(!k())return!1;_.slice_del();break;case 2:if(q("ic"))return!1;break;case 3:if(!k())return!1;_.slice_from("log");break;case 4:if(!k())return!1;_.slice_from("u");break;case 5:if(!k())return!1;_.slice_from("ente");break;case 6:if(!(n<=_.cursor))return!1;_.slice_del(),_.ket=_.cursor,(e=_.find_among_b(u,4))&&(_.bra=_.cursor,k()&&(_.slice_del(),1==e&&(_.ket=_.cursor,_.eq_s_b(2,"at")&&(_.bra=_.cursor,k()&&_.slice_del()))));break;case 7:if(y(w,3))return!1;break;case 8:if(y(c,3))return!1;break;case 9:if(q("at"))return!1}return!0}return!1}this.setCurrent=function(e){_.setCurrent(e)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var s,r=_.cursor;return s=_.cursor,i=_.limit,n=i,e=i,v(),_.cursor=s,p()&&(n=_.cursor,p()&&(e=_.cursor)),_.limit_backward=r,_.cursor=_.limit,function(){var e;if(_.ket=_.cursor,_.find_among_b(t,13)&&(_.bra=_.cursor,(e=_.find_among_b(o,11))&&g()))switch(e){case 1:_.bra=_.cursor,_.slice_from("iendo");break;case 2:_.bra=_.cursor,_.slice_from("ando");break;case 3:_.bra=_.cursor,_.slice_from("ar");break;case 4:_.bra=_.cursor,_.slice_from("er");break;case 5:_.bra=_.cursor,_.slice_from("ir");break;case 6:_.slice_del();break;case 7:_.eq_s_b(1,"u")&&_.slice_del()}}(),_.cursor=_.limit,C()||(_.cursor=_.limit,function(){var e,s;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(l,12),_.limit_backward=s,e)){if(_.bra=_.cursor,1==e){if(!_.eq_s_b(1,"u"))return!1;_.slice_del()}return!0}return!1}()||(_.cursor=_.limit,function(){var e,s,r,n;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(d,96),_.limit_backward=s,e))switch(_.bra=_.cursor,e){case 1:r=_.limit-_.cursor,_.eq_s_b(1,"u")?(n=_.limit-_.cursor,_.eq_s_b(1,"g")?_.cursor=_.limit-n:_.cursor=_.limit-r):_.cursor=_.limit-r,_.bra=_.cursor;case 2:_.slice_del()}}())),_.cursor=_.limit,function(){var e,s;if(_.ket=_.cursor,e=_.find_among_b(b,8))switch(_.bra=_.cursor,e){case 1:g()&&_.slice_del();break;case 2:g()&&(_.slice_del(),_.ket=_.cursor,_.eq_s_b(1,"u")&&(_.bra=_.cursor,s=_.limit-_.cursor,_.eq_s_b(1,"g")&&(_.cursor=_.limit-s,g()&&_.slice_del())))}}(),_.cursor=_.limit_backward,function(){for(var e;;){if(_.bra=_.cursor,e=_.find_among(a,6))switch(_.ket=_.cursor,e){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.fi.js b/material/assets/javascripts/lunr/lunr.fi.js deleted file mode 100644 index 0200b1fcb4..0000000000 --- a/material/assets/javascripts/lunr/lunr.fi.js +++ /dev/null @@ -1 +0,0 @@ -!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,r,n;i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=(e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){var i,n,t,s,l=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],o=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],a=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],u=[new e("lle",-1,-1),new e("ine",-1,-1)],c=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],m=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],w=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,C),new e("seen",11,-1,v),new e("hen",11,2),new e("tten",11,-1,C),new e("hin",11,3),new e("siin",11,-1,C),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],_=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],k=[new e("i",-1,-1),new e("j",-1,-1)],b=[new e("mma",-1,1),new e("imma",0,-1)],d=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],f=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],h=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],p=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],g=new r;function j(){for(var i;i=g.cursor,!g.in_grouping(f,97,246);){if(g.cursor=i,i>=g.limit)return!0;g.cursor++}for(g.cursor=i;!g.out_grouping(f,97,246);){if(g.cursor>=g.limit)return!0;g.cursor++}return!1}function q(){var i,e;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(l,10)){switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:if(!g.in_grouping_b(p,97,246))return;break;case 2:if(!(t<=g.cursor))return}g.slice_del()}else g.limit_backward=e}function v(){return g.find_among_b(m,7)}function C(){return g.eq_s_b(1,"i")&&g.in_grouping_b(h,97,246)}this.setCurrent=function(i){g.setCurrent(i)},this.getCurrent=function(){return g.getCurrent()},this.stem=function(){var e,r=g.cursor;return s=g.limit,t=s,j()||(s=g.cursor,j()||(t=g.cursor)),i=!1,g.limit_backward=r,g.cursor=g.limit,q(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(c,9))switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:r=g.limit-g.cursor,g.eq_s_b(1,"k")||(g.cursor=g.limit-r,g.slice_del());break;case 2:g.slice_del(),g.ket=g.cursor,g.eq_s_b(3,"kse")&&(g.bra=g.cursor,g.slice_from("ksi"));break;case 3:g.slice_del();break;case 4:g.find_among_b(o,6)&&g.slice_del();break;case 5:g.find_among_b(a,6)&&g.slice_del();break;case 6:g.find_among_b(u,2)&&g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,function(){var e,r,n;if(g.cursor>=s)if(r=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,e=g.find_among_b(w,30)){switch(g.bra=g.cursor,g.limit_backward=r,e){case 1:if(!g.eq_s_b(1,"a"))return;break;case 2:case 9:if(!g.eq_s_b(1,"e"))return;break;case 3:if(!g.eq_s_b(1,"i"))return;break;case 4:if(!g.eq_s_b(1,"o"))return;break;case 5:if(!g.eq_s_b(1,"ä"))return;break;case 6:if(!g.eq_s_b(1,"ö"))return;break;case 7:if(n=g.limit-g.cursor,!v()&&(g.cursor=g.limit-n,!g.eq_s_b(2,"ie"))){g.cursor=g.limit-n;break}if(g.cursor=g.limit-n,g.cursor<=g.limit_backward){g.cursor=g.limit-n;break}g.cursor--,g.bra=g.cursor;break;case 8:if(!g.in_grouping_b(f,97,246)||!g.out_grouping_b(f,97,246))return}g.slice_del(),i=!0}else g.limit_backward=r}(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=t)if(e=g.limit_backward,g.limit_backward=t,g.ket=g.cursor,i=g.find_among_b(_,14)){if(g.bra=g.cursor,g.limit_backward=e,1==i){if(r=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-r}g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,i?(g.cursor>=s&&(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.find_among_b(k,2)?(g.bra=g.cursor,g.limit_backward=e,g.slice_del()):g.limit_backward=e),g.cursor=g.limit):(g.cursor=g.limit,function(){var i,e,r,n,l,o;if(g.cursor>=s){if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.eq_s_b(1,"t")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.in_grouping_b(f,97,246)&&(g.cursor=g.limit-r,g.slice_del(),g.limit_backward=e,n=g.limit-g.cursor,g.cursor>=t&&(g.cursor=t,l=g.limit_backward,g.limit_backward=g.cursor,g.cursor=g.limit-n,g.ket=g.cursor,i=g.find_among_b(b,2))))){if(g.bra=g.cursor,g.limit_backward=l,1==i){if(o=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-o}return void g.slice_del()}g.limit_backward=e}}(),g.cursor=g.limit),function(){var i,e,r,t;if(g.cursor>=s){for(i=g.limit_backward,g.limit_backward=s,e=g.limit-g.cursor,v()&&(g.cursor=g.limit-e,g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.in_grouping_b(d,97,228)&&(g.bra=g.cursor,g.out_grouping_b(f,97,246)&&g.slice_del()),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"j")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.eq_s_b(1,"o")?g.slice_del():(g.cursor=g.limit-r,g.eq_s_b(1,"u")&&g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"o")&&(g.bra=g.cursor,g.eq_s_b(1,"j")&&g.slice_del()),g.cursor=g.limit-e,g.limit_backward=i;;){if(t=g.limit-g.cursor,g.out_grouping_b(f,97,246)){g.cursor=g.limit-t;break}if(g.cursor=g.limit-t,g.cursor<=g.limit_backward)return;g.cursor--}g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,n=g.slice_to(),g.eq_v_b(n)&&g.slice_del())}}(),!0}},function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.fr.js b/material/assets/javascripts/lunr/lunr.fr.js deleted file mode 100644 index 3a9b9b1775..0000000000 --- a/material/assets/javascripts/lunr/lunr.fr.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,i;e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,n,t=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],u=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],o=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],c=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],a=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],l=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],w=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],f=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],m=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],_=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],b=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],d=new s;function k(e,r,s){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(_,97,251)))&&(d.slice_from(r),d.cursor=s,!0)}function p(e,r,s){return!!d.eq_s(1,e)&&(d.ket=d.cursor,d.slice_from(r),d.cursor=s,!0)}function g(){for(;!d.in_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}for(;!d.out_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}return!1}function q(){return n<=d.cursor}function v(){return i<=d.cursor}function h(){return e<=d.cursor}function z(){if(!function(){var e,r;if(d.ket=d.cursor,e=d.find_among_b(a,43)){switch(d.bra=d.cursor,e){case 1:if(!h())return!1;d.slice_del();break;case 2:if(!h())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU"));break;case 3:if(!h())return!1;d.slice_from("log");break;case 4:if(!h())return!1;d.slice_from("u");break;case 5:if(!h())return!1;d.slice_from("ent");break;case 6:if(!q())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(o,6))switch(d.bra=d.cursor,e){case 1:h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&d.slice_del()));break;case 2:h()?d.slice_del():v()&&d.slice_from("eux");break;case 3:h()&&d.slice_del();break;case 4:q()&&d.slice_from("i")}break;case 7:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(c,3))switch(d.bra=d.cursor,e){case 1:h()?d.slice_del():d.slice_from("abl");break;case 2:h()?d.slice_del():d.slice_from("iqU");break;case 3:h()&&d.slice_del()}break;case 8:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")))){d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU");break}break;case 9:d.slice_from("eau");break;case 10:if(!v())return!1;d.slice_from("al");break;case 11:if(h())d.slice_del();else{if(!v())return!1;d.slice_from("eux")}break;case 12:if(!v()||!d.out_grouping_b(_,97,251))return!1;d.slice_del();break;case 13:return q()&&d.slice_from("ant"),!1;case 14:return q()&&d.slice_from("ent"),!1;case 15:return r=d.limit-d.cursor,d.in_grouping_b(_,97,251)&&q()&&(d.cursor=d.limit-r,d.slice_del()),!1}return!0}return!1}()&&(d.cursor=d.limit,!function(){var e,r;if(d.cursor=n){if(s=d.limit_backward,d.limit_backward=n,d.ket=d.cursor,e=d.find_among_b(f,7))switch(d.bra=d.cursor,e){case 1:if(h()){if(i=d.limit-d.cursor,!d.eq_s_b(1,"s")&&(d.cursor=d.limit-i,!d.eq_s_b(1,"t")))break;d.slice_del()}break;case 2:d.slice_from("i");break;case 3:d.slice_del();break;case 4:d.eq_s_b(2,"gu")&&d.slice_del()}d.limit_backward=s}}();d.cursor=d.limit,d.ket=d.cursor,d.eq_s_b(1,"Y")?(d.bra=d.cursor,d.slice_from("i")):(d.cursor=d.limit,d.eq_s_b(1,"ç")&&(d.bra=d.cursor,d.slice_from("c")))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,s=d.cursor;return function(){for(var e,r;;){if(e=d.cursor,d.in_grouping(_,97,251)){if(d.bra=d.cursor,r=d.cursor,k("u","U",e))continue;if(d.cursor=r,k("i","I",e))continue;if(d.cursor=r,p("y","Y",e))continue}if(d.cursor=e,d.bra=e,!k("y","Y",e)){if(d.cursor=e,d.eq_s(1,"q")&&(d.bra=d.cursor,p("u","U",e)))continue;if(d.cursor=e,e>=d.limit)return;d.cursor++}}}(),d.cursor=s,function(){var r=d.cursor;if(n=d.limit,i=n,e=n,d.in_grouping(_,97,251)&&d.in_grouping(_,97,251)&&d.cursor=d.limit){d.cursor=n;break}d.cursor++}while(!d.in_grouping(_,97,251))}n=d.cursor,d.cursor=r,g()||(i=d.cursor,g()||(e=d.cursor))}(),d.limit_backward=s,d.cursor=d.limit,z(),d.cursor=d.limit,r=d.limit-d.cursor,d.find_among_b(m,5)&&(d.cursor=d.limit-r,d.ket=d.cursor,d.cursor>d.limit_backward&&(d.cursor--,d.bra=d.cursor,d.slice_del())),d.cursor=d.limit,function(){for(var e,r=1;d.out_grouping_b(_,97,251);)r--;if(r<=0){if(d.ket=d.cursor,e=d.limit-d.cursor,!d.eq_s_b(1,"é")&&(d.cursor=d.limit-e,!d.eq_s_b(1,"è")))return;d.bra=d.cursor,d.slice_from("e")}}(),d.cursor=d.limit_backward,function(){for(var e,r;r=d.cursor,d.bra=r,e=d.find_among(u,4);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:d.slice_from("y");break;case 4:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.hu.js b/material/assets/javascripts/lunr/lunr.hu.js deleted file mode 100644 index fa704a69cb..0000000000 --- a/material/assets/javascripts/lunr/lunr.hu.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,i;e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){var e,i=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],a=[new n("á",-1,1),new n("é",-1,2)],t=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],s=[new n("al",-1,1),new n("el",-1,2)],c=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],w=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],o=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],l=[new n("á",-1,1),new n("é",-1,2)],u=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],m=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],k=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],f=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],b=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],d=new r;function g(){return e<=d.cursor}function h(){var e=d.limit-d.cursor;return!!d.find_among_b(t,23)&&(d.cursor=d.limit-e,!0)}function p(){if(d.cursor>d.limit_backward){d.cursor--,d.ket=d.cursor;var e=d.cursor-1;d.limit_backward<=e&&e<=d.limit&&(d.cursor=e,d.bra=e,d.slice_del())}}function _(){d.ket=d.cursor,d.find_among_b(c,44)&&(d.bra=d.cursor,g()&&(d.slice_del(),function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(a,2))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e")}}()))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var n=d.cursor;return function(){var n,r=d.cursor;if(e=d.limit,d.in_grouping(b,97,252))for(;;){if(n=d.cursor,d.out_grouping(b,97,252))return d.cursor=n,d.find_among(i,8)||(d.cursor=n,n=d.limit)return void(e=n);d.cursor++}if(d.cursor=r,d.out_grouping(b,97,252)){for(;!d.in_grouping(b,97,252);){if(d.cursor>=d.limit)return;d.cursor++}e=d.cursor}}(),d.limit_backward=n,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(s,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,_(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("e");break;case 2:case 3:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(o,6))&&(d.bra=d.cursor,g()))switch(e){case 1:case 2:d.slice_del();break;case 3:d.slice_from("a");break;case 4:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(l,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(m,12))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 9:d.slice_del();break;case 2:case 5:case 8:d.slice_from("e");break;case 3:case 6:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(k,31))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:d.slice_del();break;case 2:case 5:case 10:case 14:case 19:d.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(f,42))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:d.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:d.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(u,7))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:d.slice_del()}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.it.js b/material/assets/javascripts/lunr/lunr.it.js deleted file mode 100644 index 2930733892..0000000000 --- a/material/assets/javascripts/lunr/lunr.it.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,o,t=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],s=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],a=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],u=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],c=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],l=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],m=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],v=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],b=[17],d=new i;function _(e,r,i){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(f,97,249)))&&(d.slice_from(r),d.cursor=i,!0)}function g(e){if(d.cursor=e,!d.in_grouping(f,97,249))return!1;for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function p(){var e,r=d.cursor;if(!function(){if(d.in_grouping(f,97,249)){var e=d.cursor;if(d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return g(e);d.cursor++}return!0}return g(e)}return!1}()){if(d.cursor=r,!d.out_grouping(f,97,249))return;if(e=d.cursor,d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return d.cursor=e,void(d.in_grouping(f,97,249)&&d.cursor=d.limit)return;d.cursor++}o=d.cursor}function k(){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function h(){return o<=d.cursor}function q(){return e<=d.cursor}function C(){var e;if(d.ket=d.cursor,!(e=d.find_among_b(l,51)))return!1;switch(d.bra=d.cursor,e){case 1:if(!q())return!1;d.slice_del();break;case 2:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del());break;case 3:if(!q())return!1;d.slice_from("log");break;case 4:if(!q())return!1;d.slice_from("u");break;case 5:if(!q())return!1;d.slice_from("ente");break;case 6:if(!h())return!1;d.slice_del();break;case 7:if(!(n<=d.cursor))return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(c,4))&&(d.bra=d.cursor,q()&&(d.slice_del(),1==e&&(d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&d.slice_del()))));break;case 8:if(!q())return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,1==e&&q()&&d.slice_del());break;case 9:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del())))}return!0}function z(){var e;e=d.limit-d.cursor,d.ket=d.cursor,d.in_grouping_b(v,97,242)&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(1,"i")&&(d.bra=d.cursor,h())))?d.slice_del():d.cursor=d.limit-e,d.ket=d.cursor,d.eq_s_b(1,"h")&&(d.bra=d.cursor,d.in_grouping_b(b,99,103)&&h()&&d.slice_del())}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,i,c,w=d.cursor;return function(){for(var e,r,i,n,o=d.cursor;;){if(d.bra=d.cursor,e=d.find_among(t,7))switch(d.ket=d.cursor,e){case 1:d.slice_from("à");continue;case 2:d.slice_from("è");continue;case 3:d.slice_from("ì");continue;case 4:d.slice_from("ò");continue;case 5:d.slice_from("ù");continue;case 6:d.slice_from("qU");continue;case 7:if(d.cursor>=d.limit)break;d.cursor++;continue}break}for(d.cursor=o;;)for(r=d.cursor;;){if(i=d.cursor,d.in_grouping(f,97,249)){if(d.bra=d.cursor,n=d.cursor,_("u","U",i))break;if(d.cursor=n,_("i","I",i))break}if(d.cursor=i,d.cursor>=d.limit)return void(d.cursor=r);d.cursor++}}(),d.cursor=w,r=d.cursor,o=d.limit,n=o,e=o,p(),d.cursor=r,k()&&(n=d.cursor,k()&&(e=d.cursor)),d.limit_backward=w,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,d.find_among_b(a,37)&&(d.bra=d.cursor,(e=d.find_among_b(u,5))&&h()))switch(e){case 1:d.slice_del();break;case 2:d.slice_from("e")}}(),d.cursor=d.limit,C()||(d.cursor=d.limit,d.cursor>=o&&(c=d.limit_backward,d.limit_backward=o,d.ket=d.cursor,(i=d.find_among_b(m,87))&&(d.bra=d.cursor,1==i&&d.slice_del()),d.limit_backward=c)),d.cursor=d.limit,z(),d.cursor=d.limit_backward,function(){for(var e;d.bra=d.cursor,e=d.find_among(s,3);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.jp.js b/material/assets/javascripts/lunr/lunr.jp.js deleted file mode 100644 index a33c3c71c3..0000000000 --- a/material/assets/javascripts/lunr/lunr.jp.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.jp=function(){this.pipeline.reset(),this.pipeline.add(e.jp.stopWordFilter,e.jp.stemmer),r?this.tokenizer=e.jp.tokenizer:(e.tokenizer&&(e.tokenizer=e.jp.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.jp.tokenizer))};var t=new e.TinySegmenter;e.jp.tokenizer=function(n){if(!arguments.length||null==n||null==n)return[];if(Array.isArray(n))return n.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(var i=n.toString().toLowerCase().replace(/^\s+/,""),o=i.length-1;o>=0;o--)if(/\S/.test(i.charAt(o))){i=i.substring(0,o+1);break}return t.segment(i).filter(function(e){return!!e}).map(function(t){return r?new e.Token(t):t})},e.jp.stemmer=function(e){return e},e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.jp.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.jp.stopWordFilter=function(t){if(-1===e.jp.stopWordFilter.stopWords.indexOf(r?t.toString():t))return t},e.jp.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.multi.js b/material/assets/javascripts/lunr/lunr.multi.js deleted file mode 100644 index d3dbc860c4..0000000000 --- a/material/assets/javascripts/lunr/lunr.multi.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var i=Array.prototype.slice.call(arguments),t=i.join("-"),r="",n=[],s=[],p=0;p=l.limit)return;l.cursor=r+1}for(;!l.out_grouping(a,97,248);){if(l.cursor>=l.limit)return;l.cursor++}(i=l.cursor)=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,e=l.find_among_b(t,29),l.limit_backward=r,e))switch(l.bra=l.cursor,e){case 1:l.slice_del();break;case 2:n=l.limit-l.cursor,l.in_grouping_b(m,98,122)?l.slice_del():(l.cursor=l.limit-n,l.eq_s_b(1,"k")&&l.out_grouping_b(a,97,248)&&l.slice_del());break;case 3:l.slice_from("er")}}(),l.cursor=l.limit,n=l.limit-l.cursor,l.cursor>=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,l.find_among_b(o,2)?(l.bra=l.cursor,l.limit_backward=r,l.cursor=l.limit-n,l.cursor>l.limit_backward&&(l.cursor--,l.bra=l.cursor,l.slice_del())):l.limit_backward=r),l.cursor=l.limit,l.cursor>=i&&(d=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,(u=l.find_among_b(s,11))?(l.bra=l.cursor,l.limit_backward=d,1==u&&l.slice_del()):l.limit_backward=d),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.pt.js b/material/assets/javascripts/lunr/lunr.pt.js deleted file mode 100644 index 51035c969d..0000000000 --- a/material/assets/javascripts/lunr/lunr.pt.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,n;e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,o=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],a=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],t=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],u=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],m=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],c=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],l=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],f=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],d=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],v=new s;function p(){if(v.out_grouping(d,97,250)){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}return!0}function _(){var e,r,s=v.cursor;if(v.in_grouping(d,97,250))if(e=v.cursor,p()){if(v.cursor=e,function(){if(v.in_grouping(d,97,250))for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return i=v.cursor,!0}())return}else i=v.cursor;if(v.cursor=s,v.out_grouping(d,97,250)){if(r=v.cursor,p()){if(v.cursor=r,!v.in_grouping(d,97,250)||v.cursor>=v.limit)return;v.cursor++}i=v.cursor}}function h(){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return!0}function b(){return i<=v.cursor}function g(){return e<=v.cursor}function k(){var e;if(v.ket=v.cursor,!(e=v.find_among_b(m,45)))return!1;switch(v.bra=v.cursor,e){case 1:if(!g())return!1;v.slice_del();break;case 2:if(!g())return!1;v.slice_from("log");break;case 3:if(!g())return!1;v.slice_from("u");break;case 4:if(!g())return!1;v.slice_from("ente");break;case 5:if(!(n<=v.cursor))return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(t,4))&&(v.bra=v.cursor,g()&&(v.slice_del(),1==e&&(v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del()))));break;case 6:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(u,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 7:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(w,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 8:if(!g())return!1;v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del());break;case 9:if(!b()||!v.eq_s_b(1,"e"))return!1;v.slice_from("ir")}return!0}function q(e,r){if(v.eq_s_b(1,e)){v.bra=v.cursor;var s=v.limit-v.cursor;if(v.eq_s_b(1,r))return v.cursor=v.limit-s,b()&&v.slice_del(),!1}return!0}function j(){if(!k()&&(v.cursor=v.limit,!function(){var e,r;if(v.cursor>=i){if(r=v.limit_backward,v.limit_backward=i,v.ket=v.cursor,e=v.find_among_b(c,120))return v.bra=v.cursor,1==e&&v.slice_del(),v.limit_backward=r,!0;v.limit_backward=r}return!1}()))return v.cursor=v.limit,v.ket=v.cursor,void((e=v.find_among_b(l,7))&&(v.bra=v.cursor,1==e&&b()&&v.slice_del()));var e;v.cursor=v.limit,v.ket=v.cursor,v.eq_s_b(1,"i")&&(v.bra=v.cursor,v.eq_s_b(1,"c")&&(v.cursor=v.limit,b()&&v.slice_del()))}this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var r,s=v.cursor;return function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(o,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("a~");continue;case 2:v.slice_from("o~");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),v.cursor=s,r=v.cursor,i=v.limit,n=i,e=i,_(),v.cursor=r,h()&&(n=v.cursor,h()&&(e=v.cursor)),v.limit_backward=s,v.cursor=v.limit,j(),v.cursor=v.limit,function(){var e;if(v.ket=v.cursor,e=v.find_among_b(f,4))switch(v.bra=v.cursor,e){case 1:b()&&(v.slice_del(),v.ket=v.cursor,v.limit,v.cursor,q("u","g")&&q("i","c"));break;case 2:v.slice_from("c")}}(),v.cursor=v.limit_backward,function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(a,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("ã");continue;case 2:v.slice_from("õ");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.ro.js b/material/assets/javascripts/lunr/lunr.ro.js deleted file mode 100644 index 155cb5621c..0000000000 --- a/material/assets/javascripts/lunr/lunr.ro.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,r,n;e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=(i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,a,o=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],s=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],c=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],u=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],w=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],m=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],l=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],f=new r;function p(e,i){f.eq_s(1,e)&&(f.ket=f.cursor,f.in_grouping(l,97,259)&&f.slice_from(i))}function d(){if(f.out_grouping(l,97,259)){for(;!f.in_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}return!0}function b(){var e,i,r=f.cursor;if(f.in_grouping(l,97,259)){if(e=f.cursor,!d())return void(a=f.cursor);if(f.cursor=e,!function(){if(f.in_grouping(l,97,259))for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}())return void(a=f.cursor)}f.cursor=r,f.out_grouping(l,97,259)&&(i=f.cursor,d()&&(f.cursor=i,f.in_grouping(l,97,259)&&f.cursor=f.limit)return!1;f.cursor++}for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!1;f.cursor++}return!0}function _(){return t<=f.cursor}function g(){var i,r=f.limit-f.cursor;if(f.ket=f.cursor,(i=f.find_among_b(c,46))&&(f.bra=f.cursor,_())){switch(i){case 1:f.slice_from("abil");break;case 2:f.slice_from("ibil");break;case 3:f.slice_from("iv");break;case 4:f.slice_from("ic");break;case 5:f.slice_from("at");break;case 6:f.slice_from("it")}return e=!0,f.cursor=f.limit-r,!0}return!1}function k(){var i,r;for(e=!1;;)if(r=f.limit-f.cursor,!g()){f.cursor=f.limit-r;break}if(f.ket=f.cursor,(i=f.find_among_b(u,62))&&(f.bra=f.cursor,n<=f.cursor)){switch(i){case 1:f.slice_del();break;case 2:f.eq_s_b(1,"ţ")&&(f.bra=f.cursor,f.slice_from("t"));break;case 3:f.slice_from("ist")}e=!0}}function h(){var e;f.ket=f.cursor,(e=f.find_among_b(m,5))&&(f.bra=f.cursor,a<=f.cursor&&1==e&&f.slice_del())}this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var i,r=f.cursor;return function(){for(var e,i;e=f.cursor,f.in_grouping(l,97,259)&&(i=f.cursor,f.bra=i,p("u","U"),f.cursor=i,p("i","I")),f.cursor=e,!(f.cursor>=f.limit);)f.cursor++}(),f.cursor=r,i=f.cursor,a=f.limit,t=a,n=a,b(),f.cursor=i,v()&&(t=f.cursor,v()&&(n=f.cursor)),f.limit_backward=r,f.cursor=f.limit,function(){var e,i;if(f.ket=f.cursor,(e=f.find_among_b(s,16))&&(f.bra=f.cursor,_()))switch(e){case 1:f.slice_del();break;case 2:f.slice_from("a");break;case 3:f.slice_from("e");break;case 4:f.slice_from("i");break;case 5:i=f.limit-f.cursor,f.eq_s_b(2,"ab")||(f.cursor=f.limit-i,f.slice_from("i"));break;case 6:f.slice_from("at");break;case 7:f.slice_from("aţi")}}(),f.cursor=f.limit,k(),f.cursor=f.limit,e||(f.cursor=f.limit,function(){var e,i,r;if(f.cursor>=a){if(i=f.limit_backward,f.limit_backward=a,f.ket=f.cursor,e=f.find_among_b(w,94))switch(f.bra=f.cursor,e){case 1:if(r=f.limit-f.cursor,!f.out_grouping_b(l,97,259)&&(f.cursor=f.limit-r,!f.eq_s_b(1,"u")))break;case 2:f.slice_del()}f.limit_backward=i}}(),f.cursor=f.limit),h(),f.cursor=f.limit_backward,function(){for(var e;;){if(f.bra=f.cursor,e=f.find_among(o,3))switch(f.ket=f.cursor,e){case 1:f.slice_from("i");continue;case 2:f.slice_from("u");continue;case 3:if(f.cursor>=f.limit)break;f.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.ru.js b/material/assets/javascripts/lunr/lunr.ru.js deleted file mode 100644 index 078609ad8d..0000000000 --- a/material/assets/javascripts/lunr/lunr.ru.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,t;e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,w=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],i=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],u=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],s=[new n("сь",-1,1),new n("ся",-1,1)],o=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],c=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],m=[new n("ост",-1,1),new n("ость",-1,1)],l=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],f=[33,65,8,232],a=new r;function p(){for(;!a.in_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function d(){for(;!a.out_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function _(e,n){var r,t;if(a.ket=a.cursor,r=a.find_among_b(e,n)){switch(a.bra=a.cursor,r){case 1:if(t=a.limit-a.cursor,!a.eq_s_b(1,"а")&&(a.cursor=a.limit-t,!a.eq_s_b(1,"я")))return!1;case 2:a.slice_del()}return!0}return!1}function b(e,n){var r;return a.ket=a.cursor,!!(r=a.find_among_b(e,n))&&(a.bra=a.cursor,1==r&&a.slice_del(),!0)}function h(){return!!b(i,26)&&(_(u,8),!0)}function g(){var n;a.ket=a.cursor,(n=a.find_among_b(m,2))&&(a.bra=a.cursor,e<=a.cursor&&1==n&&a.slice_del())}this.setCurrent=function(e){a.setCurrent(e)},this.getCurrent=function(){return a.getCurrent()},this.stem=function(){return t=a.limit,e=t,p()&&(t=a.cursor,d()&&p()&&d()&&(e=a.cursor)),a.cursor=a.limit,!(a.cursor=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){if(o>=(_=t[s]).s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;_--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-m.s[_])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var m;if(o>=(m=t[s]).s_size){if(this.cursor=n-m.s_size,!m.method)return m.result;var b=m.method();if(this.cursor=n-m.s_size,b)return m.result}if((s=m.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.sv.js b/material/assets/javascripts/lunr/lunr.sv.js deleted file mode 100644 index 4bb0f9f92e..0000000000 --- a/material/assets/javascripts/lunr/lunr.sv.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,t;e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,i=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],s=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],o=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],u=[119,127,149],m=new n;this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var r,n=m.cursor;return function(){var r,n=m.cursor+3;if(t=m.limit,0<=n||n<=m.limit){for(e=n;;){if(r=m.cursor,m.in_grouping(o,97,246)){m.cursor=r;break}if(m.cursor=r,m.cursor>=m.limit)return;m.cursor++}for(;!m.out_grouping(o,97,246);){if(m.cursor>=m.limit)return;m.cursor++}(t=m.cursor)=t&&(m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(i,37),m.limit_backward=r,e))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.in_grouping_b(u,98,121)&&m.slice_del()}}(),m.cursor=m.limit,r=m.limit_backward,m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.find_among_b(s,7)&&(m.cursor=m.limit,m.ket=m.cursor,m.cursor>m.limit_backward&&(m.bra=--m.cursor,m.slice_del())),m.limit_backward=r),m.cursor=m.limit,function(){var e,r;if(m.cursor>=t){if(r=m.limit_backward,m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(a,5))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.slice_from("lös");break;case 3:m.slice_from("full")}m.limit_backward=r}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/lunr.tr.js b/material/assets/javascripts/lunr/lunr.tr.js deleted file mode 100644 index c42b349e81..0000000000 --- a/material/assets/javascripts/lunr/lunr.tr.js +++ /dev/null @@ -1 +0,0 @@ -!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,e,n;r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=(i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){var r,n=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],t=[new i("leri",-1,-1),new i("ları",-1,-1)],u=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],o=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],s=[new i("a",-1,-1),new i("e",-1,-1)],c=[new i("na",-1,-1),new i("ne",-1,-1)],l=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],a=[new i("nda",-1,-1),new i("nde",-1,-1)],m=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],d=[new i("ndan",-1,-1),new i("nden",-1,-1)],f=[new i("la",-1,-1),new i("le",-1,-1)],b=[new i("ca",-1,-1),new i("ce",-1,-1)],w=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],_=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],k=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],p=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],g=[new i("lar",-1,-1),new i("ler",-1,-1)],y=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],z=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],h=[new i("casına",-1,-1),new i("cesine",-1,-1)],v=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],q=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],C=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],P=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],F=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],S=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],W=[65],L=[65],x=[["a",[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["e",[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],101,252],["ı",[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["i",[17],101,105],["o",W,111,117],["ö",L,246,252],["u",W,111,117]],A=new e;function E(r,i,e){for(;;){var n=A.limit-A.cursor;if(A.in_grouping_b(r,i,e)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}function j(){var r,i;r=A.limit-A.cursor,E(F,97,305);for(var e=0;eA.limit_backward&&(A.cursor--,e=A.limit-A.cursor,i()))?(A.cursor=A.limit-e,!0):(A.cursor=A.limit-n,r()?(A.cursor=A.limit-n,!1):(A.cursor=A.limit-n,!(A.cursor<=A.limit_backward)&&(A.cursor--,!!i()&&(A.cursor=A.limit-n,!0))))}function Z(r){return T(r,function(){return A.in_grouping_b(F,97,305)})}function B(){return Z(function(){return A.eq_s_b(1,"n")})}function D(){return Z(function(){return A.eq_s_b(1,"y")})}function G(){return A.find_among_b(n,10)&&T(function(){return A.in_grouping_b(S,105,305)},function(){return A.out_grouping_b(F,97,305)})}function H(){return j()&&A.in_grouping_b(S,105,305)&&Z(function(){return A.eq_s_b(1,"s")})}function I(){return A.find_among_b(t,2)}function J(){return j()&&A.find_among_b(o,4)&&B()}function K(){return j()&&A.find_among_b(l,4)}function M(){return j()&&A.find_among_b(a,2)}function N(){return j()&&A.find_among_b(w,4)&&D()}function O(){return j()&&A.find_among_b(_,4)}function Q(){return j()&&A.find_among_b(k,4)&&D()}function R(){return A.find_among_b(p,4)}function U(){return j()&&A.find_among_b(g,2)}function V(){return j()&&A.find_among_b(z,8)}function X(){return j()&&A.find_among_b(v,32)&&D()}function Y(){return A.find_among_b(q,8)&&D()}function $(){return j()&&A.find_among_b(C,4)&&D()}function rr(){var r=A.limit-A.cursor;return!($()||(A.cursor=A.limit-r,X()||(A.cursor=A.limit-r,Y()||(A.cursor=A.limit-r,A.eq_s_b(3,"ken")&&D()))))}function ir(){if(A.find_among_b(h,2)){var r=A.limit-A.cursor;if(R()||(A.cursor=A.limit-r,U()||(A.cursor=A.limit-r,N()||(A.cursor=A.limit-r,O()||(A.cursor=A.limit-r,Q()||(A.cursor=A.limit-r))))),$())return!1}return!0}function er(){if(!j()||!A.find_among_b(y,4))return!0;var r=A.limit-A.cursor;return!X()&&(A.cursor=A.limit-r,!Y())}function nr(){var i,e,n,t=A.limit-A.cursor;if(A.ket=A.cursor,r=!0,rr()&&(A.cursor=A.limit-t,ir()&&(A.cursor=A.limit-t,function(){if(U()){A.bra=A.cursor,A.slice_del();var i=A.limit-A.cursor;return A.ket=A.cursor,V()||(A.cursor=A.limit-i,X()||(A.cursor=A.limit-i,Y()||(A.cursor=A.limit-i,$()||(A.cursor=A.limit-i)))),r=!1,!1}return!0}()&&(A.cursor=A.limit-t,er()&&(A.cursor=A.limit-t,n=A.limit-A.cursor,!(R()||(A.cursor=A.limit-n,Q()||(A.cursor=A.limit-n,O()||(A.cursor=A.limit-n,N()))))||(A.bra=A.cursor,A.slice_del(),e=A.limit-A.cursor,A.ket=A.cursor,$()||(A.cursor=A.limit-e),0)))))){if(A.cursor=A.limit-t,!V())return;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,R()||(A.cursor=A.limit-i,U()||(A.cursor=A.limit-i,N()||(A.cursor=A.limit-i,O()||(A.cursor=A.limit-i,Q()||(A.cursor=A.limit-i))))),$()||(A.cursor=A.limit-i)}A.bra=A.cursor,A.slice_del()}function tr(){var r,i,e,n;if(A.ket=A.cursor,A.eq_s_b(2,"ki")){if(r=A.limit-A.cursor,K())return A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,G()&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))),!0;if(A.cursor=A.limit-r,J()){if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,e=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-e,A.ket=A.cursor,!G()&&(A.cursor=A.limit-e,!H()&&(A.cursor=A.limit-e,!tr())))return!0;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}return!0}if(A.cursor=A.limit-r,M()){if(n=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-n,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-n,!tr())return!1;return!0}}return!1}function ur(r){if(A.ket=A.cursor,!M()&&(A.cursor=A.limit-r,!j()||!A.find_among_b(c,2)))return!1;var i=A.limit-A.cursor;if(I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-i,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-i,!tr())return!1;return!0}function or(r){if(A.ket=A.cursor,!(j()&&A.find_among_b(d,2)||(A.cursor=A.limit-r,j()&&A.find_among_b(u,4))))return!1;var i=A.limit-A.cursor;return!(!H()&&(A.cursor=A.limit-i,!I()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)}function sr(){var r,i=A.limit-A.cursor;return A.ket=A.cursor,!!(J()||(A.cursor=A.limit-i,j()&&A.find_among_b(f,2)&&D()))&&(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,!(!U()||(A.bra=A.cursor,A.slice_del(),!tr()))||(A.cursor=A.limit-r,A.ket=A.cursor,!(G()||(A.cursor=A.limit-r,H()||(A.cursor=A.limit-r,tr())))||(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)))}function cr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,!(K()||(A.cursor=A.limit-e,j()&&A.in_grouping_b(S,105,305)&&D()||(A.cursor=A.limit-e,j()&&A.find_among_b(s,2)&&D()))))return!1;if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,r=A.limit-A.cursor,G())A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()||(A.cursor=A.limit-i);else if(A.cursor=A.limit-r,!U())return!0;return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,tr(),!0}function lr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,U())return A.bra=A.cursor,A.slice_del(),void tr();if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(b,2)&&B())if(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-r,A.ket=A.cursor,!G()&&(A.cursor=A.limit-r,!H())){if(A.cursor=A.limit-r,A.ket=A.cursor,!U())return;if(A.bra=A.cursor,A.slice_del(),!tr())return}A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}else if(A.cursor=A.limit-e,!ur(e)&&(A.cursor=A.limit-e,!or(e))){if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(m,4))return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,void(G()?(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())):(A.cursor=A.limit-i,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,tr())));if(A.cursor=A.limit-e,!sr()){if(A.cursor=A.limit-e,I())return A.bra=A.cursor,void A.slice_del();A.cursor=A.limit-e,tr()||(A.cursor=A.limit-e,cr()||(A.cursor=A.limit-e,A.ket=A.cursor,(G()||(A.cursor=A.limit-e,H()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))))}}}function ar(r,i,e){if(A.cursor=A.limit-r,function(){for(;;){var r=A.limit-A.cursor;if(A.in_grouping_b(F,97,305)){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}()){var n=A.limit-A.cursor;if(!A.eq_s_b(1,i)&&(A.cursor=A.limit-n,!A.eq_s_b(1,e)))return!0;A.cursor=A.limit-r;var t=A.cursor;return A.insert(A.cursor,A.cursor,e),A.cursor=t,!1}return!0}function mr(r,i,e){for(;!A.eq_s(i,e);){if(A.cursor>=A.limit)return!0;A.cursor++}return i!=A.limit||(A.cursor=r,!1)}function dr(){var r,i,e=A.cursor;return!(!mr(r=A.cursor,2,"ad")||(A.cursor=r,!mr(r,5,"soyad")))&&(A.limit_backward=e,A.cursor=A.limit,i=A.limit-A.cursor,(A.eq_s_b(1,"d")||(A.cursor=A.limit-i,A.eq_s_b(1,"g")))&&ar(i,"a","ı")&&ar(i,"e","i")&&ar(i,"o","u")&&ar(i,"ö","ü"),A.cursor=A.limit,function(){var r;if(A.ket=A.cursor,r=A.find_among_b(P,4))switch(A.bra=A.cursor,r){case 1:A.slice_from("p");break;case 2:A.slice_from("ç");break;case 3:A.slice_from("t");break;case 4:A.slice_from("k")}}(),!0)}this.setCurrent=function(r){A.setCurrent(r)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){return!!(function(){for(var r,i=A.cursor,e=2;;){for(r=A.cursor;!A.in_grouping(F,97,305);){if(A.cursor>=A.limit)return A.cursor=r,!(e>0||(A.cursor=i,0));A.cursor++}e--}}()&&(A.limit_backward=A.cursor,A.cursor=A.limit,nr(),A.cursor=A.limit,r&&(lr(),A.cursor=A.limit_backward,dr())))}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/material/assets/javascripts/lunr/tinyseg.js b/material/assets/javascripts/lunr/tinyseg.js deleted file mode 100644 index f7ec603265..0000000000 --- a/material/assets/javascripts/lunr/tinyseg.js +++ /dev/null @@ -1 +0,0 @@ -!function(_,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(_.lunr)}(this,function(){return function(_){function t(){var _={"[一二三四五六七八九十百千万億兆]":"M","[一-龠々〆ヵヶ]":"H","[ぁ-ん]":"I","[ァ-ヴーア-ン゙ー]":"K","[a-zA-Za-zA-Z]":"A","[0-90-9]":"N"};for(var t in this.chartype_=[],_){var H=new RegExp;H.compile(t),this.chartype_.push([H,_[t]])}return this.BIAS__=-332,this.BC1__={HH:6,II:2461,KH:406,OH:-1378},this.BC2__={AA:-3267,AI:2744,AN:-878,HH:-4070,HM:-1711,HN:4012,HO:3761,IA:1327,IH:-1184,II:-1332,IK:1721,IO:5492,KI:3831,KK:-8741,MH:-3132,MK:3334,OO:-2920},this.BC3__={HH:996,HI:626,HK:-721,HN:-1307,HO:-836,IH:-301,KK:2762,MK:1079,MM:4034,OA:-1652,OH:266},this.BP1__={BB:295,OB:304,OO:-125,UB:352},this.BP2__={BO:60,OO:-1762},this.BQ1__={BHH:1150,BHM:1521,BII:-1158,BIM:886,BMH:1208,BNH:449,BOH:-91,BOO:-2597,OHI:451,OIH:-296,OKA:1851,OKH:-1020,OKK:904,OOO:2965},this.BQ2__={BHH:118,BHI:-1159,BHM:466,BIH:-919,BKK:-1720,BKO:864,OHH:-1139,OHM:-181,OIH:153,UHI:-1146},this.BQ3__={BHH:-792,BHI:2664,BII:-299,BKI:419,BMH:937,BMM:8335,BNN:998,BOH:775,OHH:2174,OHM:439,OII:280,OKH:1798,OKI:-793,OKO:-2242,OMH:-2402,OOO:11699},this.BQ4__={BHH:-3895,BIH:3761,BII:-4654,BIK:1348,BKK:-1806,BMI:-3385,BOO:-12396,OAH:926,OHH:266,OHK:-2036,ONN:-973},this.BW1__={",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682},this.BW2__={"..":-11822,11:-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669},this.BW3__={"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1e3,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990},this.TC1__={AAA:1093,HHH:1029,HHM:580,HII:998,HOH:-390,HOM:-331,IHI:1169,IOH:-142,IOI:-1015,IOM:467,MMH:187,OOI:-1832},this.TC2__={HHO:2088,HII:-1023,HMM:-1154,IHI:-1965,KKH:703,OII:-2649},this.TC3__={AAA:-294,HHH:346,HHI:-341,HII:-1088,HIK:731,HOH:-1486,IHH:128,IHI:-3041,IHO:-1935,IIH:-825,IIM:-1035,IOI:-542,KHH:-1216,KKA:491,KKH:-1217,KOK:-1009,MHH:-2694,MHM:-457,MHO:123,MMH:-471,NNH:-1689,NNO:662,OHO:-3393},this.TC4__={HHH:-203,HHI:1344,HHK:365,HHM:-122,HHN:182,HHO:669,HIH:804,HII:679,HOH:446,IHH:695,IHO:-2324,IIH:321,III:1497,IIO:656,IOO:54,KAK:4845,KKA:3386,KKK:3065,MHH:-405,MHI:201,MMH:-241,MMM:661,MOM:841},this.TQ1__={BHHH:-227,BHHI:316,BHIH:-132,BIHH:60,BIII:1595,BNHH:-744,BOHH:225,BOOO:-908,OAKK:482,OHHH:281,OHIH:249,OIHI:200,OIIH:-68},this.TQ2__={BIHH:-1401,BIII:-1033,BKAK:-543,BOOO:-5591},this.TQ3__={BHHH:478,BHHM:-1073,BHIH:222,BHII:-504,BIIH:-116,BIII:-105,BMHI:-863,BMHM:-464,BOMH:620,OHHH:346,OHHI:1729,OHII:997,OHMH:481,OIHH:623,OIIH:1344,OKAK:2792,OKHH:587,OKKA:679,OOHH:110,OOII:-685},this.TQ4__={BHHH:-721,BHHM:-3604,BHII:-966,BIIH:-607,BIII:-2181,OAAA:-2763,OAKK:180,OHHH:-294,OHHI:2446,OHHO:480,OHIH:-1573,OIHH:1935,OIHI:-493,OIIH:626,OIII:-4007,OKAK:-8156},this.TW1__={"につい":-4681,"東京都":2026},this.TW2__={"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216},this.TW3__={"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287},this.TW4__={"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865},this.UC1__={A:484,K:93,M:645,O:-505},this.UC2__={A:819,H:1059,I:409,M:3987,N:5775,O:646},this.UC3__={A:-1370,I:2311},this.UC4__={A:-2643,H:1809,I:-1032,K:-3450,M:3565,N:3876,O:6646},this.UC5__={H:313,I:-1238,K:-799,M:539,O:-831},this.UC6__={H:-506,I:-253,K:87,M:247,O:-387},this.UP1__={O:-214},this.UP2__={B:69,O:935},this.UP3__={B:189},this.UQ1__={BH:21,BI:-12,BK:-99,BN:142,BO:-56,OH:-95,OI:477,OK:410,OO:-2422},this.UQ2__={BH:216,BI:113,OK:1759},this.UQ3__={BA:-479,BH:42,BI:1913,BK:-7198,BM:3160,BN:6427,BO:14761,OI:-827,ON:-3212},this.UW1__={",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135},this.UW2__={",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568},this.UW3__={",":4889,1:-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278},this.UW4__={",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1e3,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637},this.UW5__={",":465,".":-299,1:-514,E2:-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343},this.UW6__={",":227,".":808,1:-270,E1:306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496},this}t.prototype.ctype_=function(_){for(var t in this.chartype_)if(_.match(this.chartype_[t][0]))return this.chartype_[t][1];return"O"},t.prototype.ts_=function(_){return _||0},t.prototype.segment=function(_){if(null==_||null==_||""==_)return[];var t=[],H=["B3","B2","B1"],s=["O","O","O"],h=_.split("");for(K=0;K0&&(t.push(i),i="",N="B"),I=O,O=B,B=N,i+=H[K]}return t.push(i),t},_.TinySegmenter=t}}); \ No newline at end of file diff --git a/material/assets/javascripts/modernizr.1aa3b519.js b/material/assets/javascripts/modernizr.1aa3b519.js deleted file mode 100644 index 14e111fc33..0000000000 --- a/material/assets/javascripts/modernizr.1aa3b519.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=4)}({4:function(e,t,n){"use strict";n(5)},5:function(e,t){!function(t){!function(e,t,n){function r(e,t){return typeof e===t}function o(e){var t=_.className,n=C._config.classPrefix||"";if(T&&(t=t.baseVal),C._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}C._config.enableClasses&&(t+=" "+n+e.join(" "+n),T?_.className.baseVal=t:_.className=t)}function i(e,t){if("object"==typeof e)for(var n in e)b(e,n)&&i(n,e[n]);else{e=e.toLowerCase();var r=e.split("."),s=C[r[0]];if(2==r.length&&(s=s[r[1]]),void 0!==s)return C;t="function"==typeof t?t():t,1==r.length?C[r[0]]=t:(!C[r[0]]||C[r[0]]instanceof Boolean||(C[r[0]]=new Boolean(C[r[0]])),C[r[0]][r[1]]=t),o([(t&&0!=t?"":"no-")+r.join("-")]),C._trigger(e,t)}return C}function s(){return"function"!=typeof t.createElement?t.createElement(arguments[0]):T?t.createElementNS.call(t,"http://www.w3.org/2000/svg",arguments[0]):t.createElement.apply(t,arguments)}function a(){var e=t.body;return e||(e=s(T?"svg":"body"),e.fake=!0),e}function u(e,n,r,o){var i,u,l,f,c="modernizr",d=s("div"),p=a();if(parseInt(r,10))for(;r--;)l=s("div"),l.id=o?o[r]:c+(r+1),d.appendChild(l);return i=s("style"),i.type="text/css",i.id="s"+c,(p.fake?p:d).appendChild(i),p.appendChild(d),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(t.createTextNode(e)),d.id=c,p.fake&&(p.style.background="",p.style.overflow="hidden",f=_.style.overflow,_.style.overflow="hidden",_.appendChild(p)),u=n(d,e),p.fake?(p.parentNode.removeChild(p),_.style.overflow=f,_.offsetHeight):d.parentNode.removeChild(d),!!u}function l(e,t){return!!~(""+e).indexOf(t)}function f(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function c(t,n,r){var o;if("getComputedStyle"in e){o=getComputedStyle.call(e,t,n);var i=e.console;if(null!==o)r&&(o=o.getPropertyValue(r));else if(i){var s=i.error?"error":"log";i[s].call(i,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}}else o=!n&&t.currentStyle&&t.currentStyle[r];return o}function d(t,r){var o=t.length;if("CSS"in e&&"supports"in e.CSS){for(;o--;)if(e.CSS.supports(f(t[o]),r))return!0;return!1}if("CSSSupportsRule"in e){for(var i=[];o--;)i.push("("+f(t[o])+":"+r+")");return i=i.join(" or "),u("@supports ("+i+") { #modernizr { position: absolute; } }",function(e){return"absolute"==c(e,null,"position")})}return n}function p(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function h(e,t,o,i){function a(){f&&(delete j.style,delete j.modElem)}if(i=!r(i,"undefined")&&i,!r(o,"undefined")){var u=d(e,o);if(!r(u,"undefined"))return u}for(var f,c,h,m,v,g=["modernizr","tspan","samp"];!j.style&&g.length;)f=!0,j.modElem=s(g.shift()),j.style=j.modElem.style;for(h=e.length,c=0;h>c;c++)if(m=e[c],v=j.style[m],l(m,"-")&&(m=p(m)),j.style[m]!==n){if(i||r(o,"undefined"))return a(),"pfx"!=t||m;try{j.style[m]=o}catch(e){}if(j.style[m]!=v)return a(),"pfx"!=t||m}return a(),!1}function m(e,t){return function(){return e.apply(t,arguments)}}function v(e,t,n){var o;for(var i in e)if(e[i]in t)return!1===n?e[i]:(o=t[e[i]],r(o,"function")?m(o,n||t):o);return!1}function g(e,t,n,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+k.join(s+" ")+s).split(" ");return r(t,"string")||r(t,"undefined")?h(a,t,o,i):(a=(e+" "+A.join(s+" ")+s).split(" "),v(a,t,n))}function y(e,t,r){return g(e,n,n,t,r)}var w=[],S={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},C=function(){};C.prototype=S,C=new C;var b,x=[],_=t.documentElement,T="svg"===_.nodeName.toLowerCase();!function(){var e={}.hasOwnProperty;b=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),C.hasOwnProperty(e)&&setTimeout(function(){C._trigger(e,C[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e;for(e=0;e.md-nav__link{color:inherit}button[data-md-color-primary=pink]{background-color:#e91e63}[data-md-color-primary=pink] .md-typeset a{color:#e91e63}[data-md-color-primary=pink] .md-header,[data-md-color-primary=pink] .md-hero{background-color:#e91e63}[data-md-color-primary=pink] .md-nav__link--active,[data-md-color-primary=pink] .md-nav__link:active{color:#e91e63}[data-md-color-primary=pink] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=purple]{background-color:#ab47bc}[data-md-color-primary=purple] .md-typeset a{color:#ab47bc}[data-md-color-primary=purple] .md-header,[data-md-color-primary=purple] .md-hero{background-color:#ab47bc}[data-md-color-primary=purple] .md-nav__link--active,[data-md-color-primary=purple] .md-nav__link:active{color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-purple]{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-typeset a{color:#7e57c2}[data-md-color-primary=deep-purple] .md-header,[data-md-color-primary=deep-purple] .md-hero{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__link--active,[data-md-color-primary=deep-purple] .md-nav__link:active{color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=indigo]{background-color:#3f51b5}[data-md-color-primary=indigo] .md-typeset a{color:#3f51b5}[data-md-color-primary=indigo] .md-header,[data-md-color-primary=indigo] .md-hero{background-color:#3f51b5}[data-md-color-primary=indigo] .md-nav__link--active,[data-md-color-primary=indigo] .md-nav__link:active{color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue]{background-color:#2196f3}[data-md-color-primary=blue] .md-typeset a{color:#2196f3}[data-md-color-primary=blue] .md-header,[data-md-color-primary=blue] .md-hero{background-color:#2196f3}[data-md-color-primary=blue] .md-nav__link--active,[data-md-color-primary=blue] .md-nav__link:active{color:#2196f3}[data-md-color-primary=blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-blue]{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-typeset a{color:#03a9f4}[data-md-color-primary=light-blue] .md-header,[data-md-color-primary=light-blue] .md-hero{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__link--active,[data-md-color-primary=light-blue] .md-nav__link:active{color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=cyan]{background-color:#00bcd4}[data-md-color-primary=cyan] .md-typeset a{color:#00bcd4}[data-md-color-primary=cyan] .md-header,[data-md-color-primary=cyan] .md-hero{background-color:#00bcd4}[data-md-color-primary=cyan] .md-nav__link--active,[data-md-color-primary=cyan] .md-nav__link:active{color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=teal]{background-color:#009688}[data-md-color-primary=teal] .md-typeset a{color:#009688}[data-md-color-primary=teal] .md-header,[data-md-color-primary=teal] .md-hero{background-color:#009688}[data-md-color-primary=teal] .md-nav__link--active,[data-md-color-primary=teal] .md-nav__link:active{color:#009688}[data-md-color-primary=teal] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=green]{background-color:#4caf50}[data-md-color-primary=green] .md-typeset a{color:#4caf50}[data-md-color-primary=green] .md-header,[data-md-color-primary=green] .md-hero{background-color:#4caf50}[data-md-color-primary=green] .md-nav__link--active,[data-md-color-primary=green] .md-nav__link:active{color:#4caf50}[data-md-color-primary=green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-green]{background-color:#7cb342}[data-md-color-primary=light-green] .md-typeset a{color:#7cb342}[data-md-color-primary=light-green] .md-header,[data-md-color-primary=light-green] .md-hero{background-color:#7cb342}[data-md-color-primary=light-green] .md-nav__link--active,[data-md-color-primary=light-green] .md-nav__link:active{color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=lime]{background-color:#c0ca33}[data-md-color-primary=lime] .md-typeset a{color:#c0ca33}[data-md-color-primary=lime] .md-header,[data-md-color-primary=lime] .md-hero{background-color:#c0ca33}[data-md-color-primary=lime] .md-nav__link--active,[data-md-color-primary=lime] .md-nav__link:active{color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=yellow]{background-color:#f9a825}[data-md-color-primary=yellow] .md-typeset a{color:#f9a825}[data-md-color-primary=yellow] .md-header,[data-md-color-primary=yellow] .md-hero{background-color:#f9a825}[data-md-color-primary=yellow] .md-nav__link--active,[data-md-color-primary=yellow] .md-nav__link:active{color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=amber]{background-color:#ffa000}[data-md-color-primary=amber] .md-typeset a{color:#ffa000}[data-md-color-primary=amber] .md-header,[data-md-color-primary=amber] .md-hero{background-color:#ffa000}[data-md-color-primary=amber] .md-nav__link--active,[data-md-color-primary=amber] .md-nav__link:active{color:#ffa000}[data-md-color-primary=amber] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=orange]{background-color:#fb8c00}[data-md-color-primary=orange] .md-typeset a{color:#fb8c00}[data-md-color-primary=orange] .md-header,[data-md-color-primary=orange] .md-hero{background-color:#fb8c00}[data-md-color-primary=orange] .md-nav__link--active,[data-md-color-primary=orange] .md-nav__link:active{color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-orange]{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-typeset a{color:#ff7043}[data-md-color-primary=deep-orange] .md-header,[data-md-color-primary=deep-orange] .md-hero{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__link--active,[data-md-color-primary=deep-orange] .md-nav__link:active{color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=brown]{background-color:#795548}[data-md-color-primary=brown] .md-typeset a{color:#795548}[data-md-color-primary=brown] .md-header,[data-md-color-primary=brown] .md-hero{background-color:#795548}[data-md-color-primary=brown] .md-nav__link--active,[data-md-color-primary=brown] .md-nav__link:active{color:#795548}[data-md-color-primary=brown] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=grey]{background-color:#757575}[data-md-color-primary=grey] .md-typeset a{color:#757575}[data-md-color-primary=grey] .md-header,[data-md-color-primary=grey] .md-hero{background-color:#757575}[data-md-color-primary=grey] .md-nav__link--active,[data-md-color-primary=grey] .md-nav__link:active{color:#757575}[data-md-color-primary=grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue-grey]{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-typeset a{color:#546e7a}[data-md-color-primary=blue-grey] .md-header,[data-md-color-primary=blue-grey] .md-hero{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__link--active,[data-md-color-primary=blue-grey] .md-nav__link:active{color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=white]{box-shadow:inset 0 0 .1rem rgba(0,0,0,.54)}[data-md-color-primary=white] .md-header,[data-md-color-primary=white] .md-hero,button[data-md-color-primary=white]{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero--expand{border-bottom:.1rem solid rgba(0,0,0,.07)}button[data-md-color-accent=red]{background-color:#ff1744}[data-md-color-accent=red] .md-typeset a:active,[data-md-color-accent=red] .md-typeset a:hover{color:#ff1744}[data-md-color-accent=red] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=red] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-nav__link:focus,[data-md-color-accent=red] .md-nav__link:hover,[data-md-color-accent=red] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=red] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=red] .md-typeset .md-clipboard:active:before,[data-md-color-accent=red] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=red] .md-typeset [id] .headerlink:focus,[data-md-color-accent=red] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=red] .md-typeset [id]:target .headerlink{color:#ff1744}[data-md-color-accent=red] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-search-result__link:hover,[data-md-color-accent=red] .md-search-result__link[data-md-state=active]{background-color:rgba(255,23,68,.1)}[data-md-color-accent=red] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-source-file:hover:before{background-color:#ff1744}button[data-md-color-accent=pink]{background-color:#f50057}[data-md-color-accent=pink] .md-typeset a:active,[data-md-color-accent=pink] .md-typeset a:hover{color:#f50057}[data-md-color-accent=pink] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=pink] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-nav__link:focus,[data-md-color-accent=pink] .md-nav__link:hover,[data-md-color-accent=pink] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=pink] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=pink] .md-typeset .md-clipboard:active:before,[data-md-color-accent=pink] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=pink] .md-typeset [id] .headerlink:focus,[data-md-color-accent=pink] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=pink] .md-typeset [id]:target .headerlink{color:#f50057}[data-md-color-accent=pink] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-search-result__link:hover,[data-md-color-accent=pink] .md-search-result__link[data-md-state=active]{background-color:rgba(245,0,87,.1)}[data-md-color-accent=pink] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-source-file:hover:before{background-color:#f50057}button[data-md-color-accent=purple]{background-color:#e040fb}[data-md-color-accent=purple] .md-typeset a:active,[data-md-color-accent=purple] .md-typeset a:hover{color:#e040fb}[data-md-color-accent=purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-nav__link:focus,[data-md-color-accent=purple] .md-nav__link:hover,[data-md-color-accent=purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=purple] .md-typeset [id]:target .headerlink{color:#e040fb}[data-md-color-accent=purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-search-result__link:hover,[data-md-color-accent=purple] .md-search-result__link[data-md-state=active]{background-color:rgba(224,64,251,.1)}[data-md-color-accent=purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-source-file:hover:before{background-color:#e040fb}button[data-md-color-accent=deep-purple]{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset a:active,[data-md-color-accent=deep-purple] .md-typeset a:hover{color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-purple] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-nav__link:focus,[data-md-color-accent=deep-purple] .md-nav__link:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-purple] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-purple] .md-typeset [id]:target .headerlink{color:#7c4dff}[data-md-color-accent=deep-purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-search-result__link:hover,[data-md-color-accent=deep-purple] .md-search-result__link[data-md-state=active]{background-color:rgba(124,77,255,.1)}[data-md-color-accent=deep-purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-source-file:hover:before{background-color:#7c4dff}button[data-md-color-accent=indigo]{background-color:#536dfe}[data-md-color-accent=indigo] .md-typeset a:active,[data-md-color-accent=indigo] .md-typeset a:hover{color:#536dfe}[data-md-color-accent=indigo] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=indigo] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-nav__link:focus,[data-md-color-accent=indigo] .md-nav__link:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=indigo] .md-typeset .md-clipboard:active:before,[data-md-color-accent=indigo] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=indigo] .md-typeset [id] .headerlink:focus,[data-md-color-accent=indigo] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=indigo] .md-typeset [id]:target .headerlink{color:#536dfe}[data-md-color-accent=indigo] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-search-result__link:hover,[data-md-color-accent=indigo] .md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}[data-md-color-accent=indigo] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-source-file:hover:before{background-color:#536dfe}button[data-md-color-accent=blue]{background-color:#448aff}[data-md-color-accent=blue] .md-typeset a:active,[data-md-color-accent=blue] .md-typeset a:hover{color:#448aff}[data-md-color-accent=blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-nav__link:focus,[data-md-color-accent=blue] .md-nav__link:hover,[data-md-color-accent=blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=blue] .md-typeset [id]:target .headerlink{color:#448aff}[data-md-color-accent=blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-search-result__link:hover,[data-md-color-accent=blue] .md-search-result__link[data-md-state=active]{background-color:rgba(68,138,255,.1)}[data-md-color-accent=blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-source-file:hover:before{background-color:#448aff}button[data-md-color-accent=light-blue]{background-color:#0091ea}[data-md-color-accent=light-blue] .md-typeset a:active,[data-md-color-accent=light-blue] .md-typeset a:hover{color:#0091ea}[data-md-color-accent=light-blue] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-blue] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-nav__link:focus,[data-md-color-accent=light-blue] .md-nav__link:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-blue] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-blue] .md-typeset [id]:target .headerlink{color:#0091ea}[data-md-color-accent=light-blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-search-result__link:hover,[data-md-color-accent=light-blue] .md-search-result__link[data-md-state=active]{background-color:rgba(0,145,234,.1)}[data-md-color-accent=light-blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-source-file:hover:before{background-color:#0091ea}button[data-md-color-accent=cyan]{background-color:#00b8d4}[data-md-color-accent=cyan] .md-typeset a:active,[data-md-color-accent=cyan] .md-typeset a:hover{color:#00b8d4}[data-md-color-accent=cyan] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=cyan] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-nav__link:focus,[data-md-color-accent=cyan] .md-nav__link:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=cyan] .md-typeset .md-clipboard:active:before,[data-md-color-accent=cyan] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=cyan] .md-typeset [id] .headerlink:focus,[data-md-color-accent=cyan] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=cyan] .md-typeset [id]:target .headerlink{color:#00b8d4}[data-md-color-accent=cyan] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-search-result__link:hover,[data-md-color-accent=cyan] .md-search-result__link[data-md-state=active]{background-color:rgba(0,184,212,.1)}[data-md-color-accent=cyan] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-source-file:hover:before{background-color:#00b8d4}button[data-md-color-accent=teal]{background-color:#00bfa5}[data-md-color-accent=teal] .md-typeset a:active,[data-md-color-accent=teal] .md-typeset a:hover{color:#00bfa5}[data-md-color-accent=teal] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=teal] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-nav__link:focus,[data-md-color-accent=teal] .md-nav__link:hover,[data-md-color-accent=teal] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=teal] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=teal] .md-typeset .md-clipboard:active:before,[data-md-color-accent=teal] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=teal] .md-typeset [id] .headerlink:focus,[data-md-color-accent=teal] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=teal] .md-typeset [id]:target .headerlink{color:#00bfa5}[data-md-color-accent=teal] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-search-result__link:hover,[data-md-color-accent=teal] .md-search-result__link[data-md-state=active]{background-color:rgba(0,191,165,.1)}[data-md-color-accent=teal] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-source-file:hover:before{background-color:#00bfa5}button[data-md-color-accent=green]{background-color:#00c853}[data-md-color-accent=green] .md-typeset a:active,[data-md-color-accent=green] .md-typeset a:hover{color:#00c853}[data-md-color-accent=green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-nav__link:focus,[data-md-color-accent=green] .md-nav__link:hover,[data-md-color-accent=green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=green] .md-typeset [id]:target .headerlink{color:#00c853}[data-md-color-accent=green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-search-result__link:hover,[data-md-color-accent=green] .md-search-result__link[data-md-state=active]{background-color:rgba(0,200,83,.1)}[data-md-color-accent=green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-source-file:hover:before{background-color:#00c853}button[data-md-color-accent=light-green]{background-color:#64dd17}[data-md-color-accent=light-green] .md-typeset a:active,[data-md-color-accent=light-green] .md-typeset a:hover{color:#64dd17}[data-md-color-accent=light-green] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-green] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-nav__link:focus,[data-md-color-accent=light-green] .md-nav__link:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-green] .md-typeset .md-clipboard:active:before,[data-md-color-accent=light-green] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=light-green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-green] .md-typeset [id]:target .headerlink{color:#64dd17}[data-md-color-accent=light-green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-search-result__link:hover,[data-md-color-accent=light-green] .md-search-result__link[data-md-state=active]{background-color:rgba(100,221,23,.1)}[data-md-color-accent=light-green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-source-file:hover:before{background-color:#64dd17}button[data-md-color-accent=lime]{background-color:#aeea00}[data-md-color-accent=lime] .md-typeset a:active,[data-md-color-accent=lime] .md-typeset a:hover{color:#aeea00}[data-md-color-accent=lime] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=lime] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-nav__link:focus,[data-md-color-accent=lime] .md-nav__link:hover,[data-md-color-accent=lime] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=lime] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=lime] .md-typeset .md-clipboard:active:before,[data-md-color-accent=lime] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=lime] .md-typeset [id] .headerlink:focus,[data-md-color-accent=lime] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=lime] .md-typeset [id]:target .headerlink{color:#aeea00}[data-md-color-accent=lime] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-search-result__link:hover,[data-md-color-accent=lime] .md-search-result__link[data-md-state=active]{background-color:rgba(174,234,0,.1)}[data-md-color-accent=lime] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-source-file:hover:before{background-color:#aeea00}button[data-md-color-accent=yellow]{background-color:#ffd600}[data-md-color-accent=yellow] .md-typeset a:active,[data-md-color-accent=yellow] .md-typeset a:hover{color:#ffd600}[data-md-color-accent=yellow] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=yellow] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-nav__link:focus,[data-md-color-accent=yellow] .md-nav__link:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=yellow] .md-typeset .md-clipboard:active:before,[data-md-color-accent=yellow] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=yellow] .md-typeset [id] .headerlink:focus,[data-md-color-accent=yellow] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=yellow] .md-typeset [id]:target .headerlink{color:#ffd600}[data-md-color-accent=yellow] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-search-result__link:hover,[data-md-color-accent=yellow] .md-search-result__link[data-md-state=active]{background-color:rgba(255,214,0,.1)}[data-md-color-accent=yellow] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-source-file:hover:before{background-color:#ffd600}button[data-md-color-accent=amber]{background-color:#ffab00}[data-md-color-accent=amber] .md-typeset a:active,[data-md-color-accent=amber] .md-typeset a:hover{color:#ffab00}[data-md-color-accent=amber] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=amber] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-nav__link:focus,[data-md-color-accent=amber] .md-nav__link:hover,[data-md-color-accent=amber] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=amber] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=amber] .md-typeset .md-clipboard:active:before,[data-md-color-accent=amber] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=amber] .md-typeset [id] .headerlink:focus,[data-md-color-accent=amber] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=amber] .md-typeset [id]:target .headerlink{color:#ffab00}[data-md-color-accent=amber] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-search-result__link:hover,[data-md-color-accent=amber] .md-search-result__link[data-md-state=active]{background-color:rgba(255,171,0,.1)}[data-md-color-accent=amber] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-source-file:hover:before{background-color:#ffab00}button[data-md-color-accent=orange]{background-color:#ff9100}[data-md-color-accent=orange] .md-typeset a:active,[data-md-color-accent=orange] .md-typeset a:hover{color:#ff9100}[data-md-color-accent=orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-nav__link:focus,[data-md-color-accent=orange] .md-nav__link:hover,[data-md-color-accent=orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=orange] .md-typeset [id]:target .headerlink{color:#ff9100}[data-md-color-accent=orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-search-result__link:hover,[data-md-color-accent=orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,145,0,.1)}[data-md-color-accent=orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-source-file:hover:before{background-color:#ff9100}button[data-md-color-accent=deep-orange]{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset a:active,[data-md-color-accent=deep-orange] .md-typeset a:hover{color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-orange] .md-typeset pre code::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-nav__link:focus,[data-md-color-accent=deep-orange] .md-nav__link:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:active:before,[data-md-color-accent=deep-orange] .md-typeset .md-clipboard:hover:before,[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink{color:#ff6e40}[data-md-color-accent=deep-orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-search-result__link:hover,[data-md-color-accent=deep-orange] .md-search-result__link[data-md-state=active]{background-color:rgba(255,110,64,.1)}[data-md-color-accent=deep-orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-source-file:hover:before{background-color:#ff6e40}@media only screen and (max-width:59.9375em){[data-md-color-primary=red] .md-nav__source{background-color:rgba(190,66,64,.9675)}[data-md-color-primary=pink] .md-nav__source{background-color:rgba(185,24,79,.9675)}[data-md-color-primary=purple] .md-nav__source{background-color:rgba(136,57,150,.9675)}[data-md-color-primary=deep-purple] .md-nav__source{background-color:rgba(100,69,154,.9675)}[data-md-color-primary=indigo] .md-nav__source{background-color:rgba(50,64,144,.9675)}[data-md-color-primary=blue] .md-nav__source{background-color:rgba(26,119,193,.9675)}[data-md-color-primary=light-blue] .md-nav__source{background-color:rgba(2,134,194,.9675)}[data-md-color-primary=cyan] .md-nav__source{background-color:rgba(0,150,169,.9675)}[data-md-color-primary=teal] .md-nav__source{background-color:rgba(0,119,108,.9675)}[data-md-color-primary=green] .md-nav__source{background-color:rgba(60,139,64,.9675)}[data-md-color-primary=light-green] .md-nav__source{background-color:rgba(99,142,53,.9675)}[data-md-color-primary=lime] .md-nav__source{background-color:rgba(153,161,41,.9675)}[data-md-color-primary=yellow] .md-nav__source{background-color:rgba(198,134,29,.9675)}[data-md-color-primary=amber] .md-nav__source{background-color:rgba(203,127,0,.9675)}[data-md-color-primary=orange] .md-nav__source{background-color:rgba(200,111,0,.9675)}[data-md-color-primary=deep-orange] .md-nav__source{background-color:rgba(203,89,53,.9675)}[data-md-color-primary=brown] .md-nav__source{background-color:rgba(96,68,57,.9675)}[data-md-color-primary=grey] .md-nav__source{background-color:rgba(93,93,93,.9675)}[data-md-color-primary=blue-grey] .md-nav__source{background-color:rgba(67,88,97,.9675)}[data-md-color-primary=white] .md-nav__source{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.87)}}@media only screen and (max-width:76.1875em){html [data-md-color-primary=red] .md-nav--primary .md-nav__title--site{background-color:#ef5350}html [data-md-color-primary=pink] .md-nav--primary .md-nav__title--site{background-color:#e91e63}html [data-md-color-primary=purple] .md-nav--primary .md-nav__title--site{background-color:#ab47bc}html [data-md-color-primary=deep-purple] .md-nav--primary .md-nav__title--site{background-color:#7e57c2}html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site{background-color:#3f51b5}html [data-md-color-primary=blue] .md-nav--primary .md-nav__title--site{background-color:#2196f3}html [data-md-color-primary=light-blue] .md-nav--primary .md-nav__title--site{background-color:#03a9f4}html [data-md-color-primary=cyan] .md-nav--primary .md-nav__title--site{background-color:#00bcd4}html [data-md-color-primary=teal] .md-nav--primary .md-nav__title--site{background-color:#009688}html [data-md-color-primary=green] .md-nav--primary .md-nav__title--site{background-color:#4caf50}html [data-md-color-primary=light-green] .md-nav--primary .md-nav__title--site{background-color:#7cb342}html [data-md-color-primary=lime] .md-nav--primary .md-nav__title--site{background-color:#c0ca33}html [data-md-color-primary=yellow] .md-nav--primary .md-nav__title--site{background-color:#f9a825}html [data-md-color-primary=amber] .md-nav--primary .md-nav__title--site{background-color:#ffa000}html [data-md-color-primary=orange] .md-nav--primary .md-nav__title--site{background-color:#fb8c00}html [data-md-color-primary=deep-orange] .md-nav--primary .md-nav__title--site{background-color:#ff7043}html [data-md-color-primary=brown] .md-nav--primary .md-nav__title--site{background-color:#795548}html [data-md-color-primary=grey] .md-nav--primary .md-nav__title--site{background-color:#757575}html [data-md-color-primary=blue-grey] .md-nav--primary .md-nav__title--site{background-color:#546e7a}html [data-md-color-primary=white] .md-nav--primary .md-nav__title--site{background-color:#fff;color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-hero{border-bottom:.1rem solid rgba(0,0,0,.07)}}@media only screen and (min-width:76.25em){[data-md-color-primary=red] .md-tabs{background-color:#ef5350}[data-md-color-primary=pink] .md-tabs{background-color:#e91e63}[data-md-color-primary=purple] .md-tabs{background-color:#ab47bc}[data-md-color-primary=deep-purple] .md-tabs{background-color:#7e57c2}[data-md-color-primary=indigo] .md-tabs{background-color:#3f51b5}[data-md-color-primary=blue] .md-tabs{background-color:#2196f3}[data-md-color-primary=light-blue] .md-tabs{background-color:#03a9f4}[data-md-color-primary=cyan] .md-tabs{background-color:#00bcd4}[data-md-color-primary=teal] .md-tabs{background-color:#009688}[data-md-color-primary=green] .md-tabs{background-color:#4caf50}[data-md-color-primary=light-green] .md-tabs{background-color:#7cb342}[data-md-color-primary=lime] .md-tabs{background-color:#c0ca33}[data-md-color-primary=yellow] .md-tabs{background-color:#f9a825}[data-md-color-primary=amber] .md-tabs{background-color:#ffa000}[data-md-color-primary=orange] .md-tabs{background-color:#fb8c00}[data-md-color-primary=deep-orange] .md-tabs{background-color:#ff7043}[data-md-color-primary=brown] .md-tabs{background-color:#795548}[data-md-color-primary=grey] .md-tabs{background-color:#757575}[data-md-color-primary=blue-grey] .md-tabs{background-color:#546e7a}[data-md-color-primary=white] .md-tabs{border-bottom:.1rem solid rgba(0,0,0,.07);background-color:#fff;color:rgba(0,0,0,.87)}}@media only screen and (min-width:60em){[data-md-color-primary=white] .md-search__input{background-color:rgba(0,0,0,.07)}[data-md-color-primary=white] .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input:-ms-input-placeholder,[data-md-color-primary=white] .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::placeholder{color:rgba(0,0,0,.54)}} -/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24tcGFsZXR0ZS42MDc5NDc2Yy5jc3MiLCJzb3VyY2VSb290IjoiIn0=*/ \ No newline at end of file diff --git a/material/assets/stylesheets/application.8d40d89b.css b/material/assets/stylesheets/application.8d40d89b.css deleted file mode 100644 index 194447e7b1..0000000000 --- a/material/assets/stylesheets/application.8d40d89b.css +++ /dev/null @@ -1,2 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}body{margin:0}hr{overflow:visible;box-sizing:content-box}a{-webkit-text-decoration-skip:objects}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small,sub,sup{font-size:80%}sub,sup{position:relative;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{margin:0;padding:0;border:0;outline-style:none;background:transparent;font-size:inherit}input{border:0;outline:0}.md-clipboard:before,.md-icon,.md-nav__button,.md-nav__link:after,.md-nav__title:before,.md-search-result__article--document:before,.md-source-file:before,.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset .critic.comment:before,.md-typeset .footnote-backref,.md-typeset .task-list-control .task-list-indicator:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before,.md-typeset summary:after{font-family:Material Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;white-space:nowrap;speak:none;word-wrap:normal;direction:ltr}.md-content__icon,.md-footer-nav__button,.md-header-nav__button,.md-nav__button,.md-nav__title:before,.md-search-result__article--document:before{display:inline-block;margin:.4rem;padding:.8rem;font-size:2.4rem;cursor:pointer}.md-icon--arrow-back:before{content:"\E5C4"}.md-icon--arrow-forward:before{content:"\E5C8"}.md-icon--menu:before{content:"\E5D2"}.md-icon--search:before{content:"\E8B6"}[dir=rtl] .md-icon--arrow-back:before{content:"\E5C8"}[dir=rtl] .md-icon--arrow-forward:before{content:"\E5C4"}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern","liga";font-feature-settings:"kern","liga";font-family:Helvetica Neue,Helvetica,Arial,sans-serif}code,kbd,pre{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern";font-feature-settings:"kern";font-family:Courier New,Courier,monospace}.md-typeset{font-size:1.6rem;line-height:1.6;-webkit-print-color-adjust:exact}.md-typeset blockquote,.md-typeset ol,.md-typeset p,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 4rem;color:rgba(0,0,0,.54);font-size:3.125rem;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:4rem 0 1.6rem;font-size:2.5rem;line-height:1.4}.md-typeset h3{margin:3.2rem 0 1.6rem;font-size:2rem;font-weight:400;letter-spacing:-.01em;line-height:1.5}.md-typeset h2+h3{margin-top:1.6rem}.md-typeset h4{font-size:1.6rem}.md-typeset h4,.md-typeset h5,.md-typeset h6{margin:1.6rem 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.1rem dotted rgba(0,0,0,.26)}.md-typeset a{color:#3f51b5;word-break:break-word}.md-typeset a,.md-typeset a:before{transition:color .125s}.md-typeset a:active,.md-typeset a:hover{color:#536dfe}.md-typeset code,.md-typeset pre{background-color:hsla(0,0%,93%,.5);color:#37474f;font-size:85%;direction:ltr}.md-typeset code{margin:0 .29412em;padding:.07353em 0;border-radius:.2rem;box-shadow:.29412em 0 0 hsla(0,0%,93%,.5),-.29412em 0 0 hsla(0,0%,93%,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:0;background-color:transparent;box-shadow:none}.md-typeset a>code{margin:inherit;padding:inherit;border-radius:none;background-color:inherit;color:inherit;box-shadow:none}.md-typeset pre{position:relative;margin:1em 0;border-radius:.2rem;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset pre>code{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;font-size:inherit;box-shadow:none;-webkit-box-decoration-break:none;box-decoration-break:none;overflow:auto}.md-typeset pre>code::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset kbd{padding:0 .29412em;border:.1rem solid #c9c9c9;border-radius:.3rem;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;box-shadow:0 .1rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;background-color:rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.1rem dotted rgba(0,0,0,.54);text-decoration:none;cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}[dir=rtl] .md-typeset sub,[dir=rtl] .md-typeset sup{margin-right:.07812em;margin-left:0}.md-typeset blockquote{padding-left:1.2rem;border-left:.4rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}[dir=rtl] .md-typeset blockquote{padding-right:1.2rem;padding-left:0;border-right:.4rem solid rgba(0,0,0,.26);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ol,[dir=rtl] .md-typeset ul{margin-right:.625em;margin-left:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ol li,[dir=rtl] .md-typeset ul li{margin-right:1.25em;margin-left:0}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ol li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ul li ul{margin-right:.625em;margin-left:0}.md-typeset dd{margin:1em 0 1em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:0}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.2rem;font-size:1.28rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) td:not([align]),[dir=rtl] .md-typeset table:not([class]) th:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:10rem;padding:1.2rem 1.6rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:1.2rem 1.6rem;border-top:.1rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -1.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 1.6rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:62.5%;overflow-x:hidden}body,html{height:100%}body{position:relative}hr{display:block;height:.1rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:122rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:4.8rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{height:100%;padding-top:3rem;padding-bottom:.1rem}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:3}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-skip{position:fixed;width:.1rem;height:.1rem;margin:1rem;padding:.6rem 1rem;clip:rect(.1rem);-webkit-transform:translateY(.8rem);transform:translateY(.8rem);border-radius:.2rem;background-color:rgba(0,0,0,.87);color:#fff;font-size:1.28rem;opacity:0;overflow:hidden}.md-skip:focus{width:auto;height:auto;clip:auto;-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;z-index:10}@page{margin:25mm}.md-clipboard{position:absolute;top:.6rem;right:.6rem;width:2.8rem;height:2.8rem;border-radius:.2rem;font-size:1.6rem;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-clipboard:before{transition:color .25s,opacity .25s;color:rgba(0,0,0,.07);content:"\E14D"}.codehilite:hover .md-clipboard:before,.md-typeset .highlight:hover .md-clipboard:before,pre:hover .md-clipboard:before{color:rgba(0,0,0,.54)}.md-clipboard:focus:before,.md-clipboard:hover:before{color:#536dfe}.md-clipboard__message{display:block;position:absolute;top:0;right:3.4rem;padding:.6rem 1rem;-webkit-transform:translateX(.8rem);transform:translateX(.8rem);transition:opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s;transition:transform .25s cubic-bezier(.9,.1,.9,0),opacity .175s,-webkit-transform .25s cubic-bezier(.9,.1,.9,0);border-radius:.2rem;background-color:rgba(0,0,0,.54);color:#fff;font-size:1.28rem;white-space:nowrap;opacity:0;pointer-events:none}.md-clipboard__message--active{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .175s 75ms,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1;pointer-events:auto}.md-clipboard__message:before{content:attr(aria-label)}.md-clipboard__message:after{display:block;position:absolute;top:50%;right:-.4rem;width:0;margin-top:-.4rem;border-width:.4rem 0 .4rem .4rem;border-style:solid;border-color:transparent rgba(0,0,0,.54);content:""}.md-content__inner{margin:0 1.6rem 2.4rem;padding-top:1.2rem}.md-content__inner:before{display:block;height:.8rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__icon{position:relative;margin:.8rem 0;padding:0;float:right}.md-typeset .md-content__icon{color:rgba(0,0,0,.26)}.md-header{position:fixed;top:0;right:0;left:0;height:4.8rem;transition:background-color .25s,color .25s;background-color:#3f51b5;color:#fff;box-shadow:none;z-index:2;-webkit-backface-visibility:hidden;backface-visibility:hidden}.no-js .md-header{transition:none;box-shadow:none}.md-header[data-md-state=shadow]{transition:background-color .25s,color .25s,box-shadow .25s;box-shadow:0 0 .4rem rgba(0,0,0,.1),0 .4rem .8rem rgba(0,0,0,.2)}.md-header-nav{padding:0 .4rem}.md-header-nav__button{position:relative;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo *{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__topic{display:block;position:absolute;transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem)}.no-js .md-header-nav__topic{position:static}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(-2.5rem);transform:translateX(-2.5rem);transition:opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s;transition:transform .4s cubic-bezier(1,.7,.1,.1),opacity .15s,-webkit-transform .4s cubic-bezier(1,.7,.1,.1);opacity:0;z-index:-1;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{-webkit-transform:translateX(2.5rem);transform:translateX(2.5rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);opacity:1;z-index:0;pointer-events:auto}.md-header-nav__source{display:none}.md-hero{transition:background .25s;background-color:#3f51b5;color:#fff;font-size:2rem;overflow:hidden}.md-hero__inner{margin-top:2rem;padding:1.6rem 1.6rem .8rem;transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition-delay:.1s}[data-md-state=hidden] .md-hero__inner{pointer-events:none;-webkit-transform:translateY(1.25rem);transform:translateY(1.25rem);transition:opacity .1s 0s,-webkit-transform 0s .4s;transition:transform 0s .4s,opacity .1s 0s;transition:transform 0s .4s,opacity .1s 0s,-webkit-transform 0s .4s;opacity:0}.md-hero--expand .md-hero__inner{margin-bottom:2.4rem}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.4rem;overflow:auto}.md-footer-nav__link{padding-top:2.8rem;padding-bottom:.8rem;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}.md-footer-nav__link--next{width:75%;float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}.md-footer-nav__button{transition:background .25s}.md-footer-nav__title{position:relative;padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-2rem;padding:0 2rem;color:hsla(0,0%,100%,.7);font-size:1.5rem}.md-footer-meta{background-color:rgba(0,0,0,.895)}.md-footer-meta__inner{padding:.4rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:#fff}.md-footer-copyright{margin:0 1.2rem;padding:.8rem 0;color:hsla(0,0%,100%,.3);font-size:1.28rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .8rem;padding:.4rem 0 1.2rem}.md-footer-social__link{display:inline-block;width:3.2rem;height:3.2rem;font-size:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:1.4rem;line-height:1.3}.md-nav__title{display:block;padding:0 1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"\E5C4"}[dir=rtl] .md-nav__title:before{content:"\E5C8"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 1.2rem}.md-nav__item:last-child{padding-bottom:1.2rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:1.2rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"\E313"}html .md-nav__link[for=toc],html .md-nav__link[for=toc]+.md-nav__link:after,html .md-nav__link[for=toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__link--active,.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{opacity:0;z-index:1}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 4.4rem 0 7.2rem;text-overflow:ellipsis;z-index:2}[dir=rtl] .md-search__input{padding:0 7.2rem 0 4.4rem}.md-search__input::-webkit-input-placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::placeholder{transition:color .25s cubic-bezier(.1,.7,.1,1)}.md-search__input::-webkit-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::placeholder,.md-search__input~.md-search__icon{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;transition:color .25s cubic-bezier(.1,.7,.1,1),opacity .25s;font-size:2.4rem;cursor:pointer;z-index:2}.md-search__icon:hover{opacity:.7}.md-search__icon[for=search]{top:.6rem;left:1rem}[dir=rtl] .md-search__icon[for=search]{right:1rem;left:auto}.md-search__icon[for=search]:before{content:"\E8B6"}.md-search__icon[type=reset]{top:.6rem;right:1rem;-webkit-transform:scale(.125);transform:scale(.125);transition:opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s;transition:transform .15s cubic-bezier(.1,.7,.1,1),opacity .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1);opacity:0}[dir=rtl] .md-search__icon[type=reset]{right:auto;left:1rem}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]{-webkit-transform:scale(1);transform:scale(1);opacity:1}[data-md-toggle=search]:checked~.md-header .md-search__input:valid~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;width:100%;border-radius:0 0 .2rem .2rem;overflow:hidden;z-index:1}.md-search__scrollwrap{height:100%;background-color:#fff;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result{color:rgba(0,0,0,.87);word-break:break-word}.md-search-result__meta{padding:0 1.6rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:1.28rem;line-height:3.6rem}.md-search-result__list{margin:0;padding:0;border-top:.1rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{box-shadow:0 -.1rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;transition:background .25s;outline:0;overflow:hidden}.md-search-result__link:hover,.md-search-result__link[data-md-state=active]{background-color:rgba(83,109,254,.1)}.md-search-result__link:hover .md-search-result__article:before,.md-search-result__link[data-md-state=active] .md-search-result__article:before{opacity:.7}.md-search-result__link:last-child .md-search-result__teaser{margin-bottom:1.2rem}.md-search-result__article{position:relative;padding:0 1.6rem;overflow:auto}.md-search-result__article--document:before{position:absolute;left:0;margin:.2rem;transition:opacity .25s;color:rgba(0,0,0,.54);content:"\E880"}[dir=rtl] .md-search-result__article--document:before{right:0;left:auto}.md-search-result__article--document .md-search-result__title{margin:1.1rem 0;font-size:1.6rem;font-weight:400;line-height:1.4}.md-search-result__title{margin:.5em 0;font-size:1.28rem;font-weight:700;line-height:1.4}.md-search-result__teaser{display:-webkit-box;max-height:3.3rem;margin:.5em 0;color:rgba(0,0,0,.54);font-size:1.28rem;line-height:1.4;text-overflow:ellipsis;overflow:hidden;-webkit-line-clamp:2}.md-search-result em{font-style:normal;font-weight:700;text-decoration:underline}.md-sidebar{position:absolute;width:24.2rem;padding:2.4rem 0;overflow:hidden}.md-sidebar[data-md-state=lock]{position:fixed;top:4.8rem}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .4rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@keyframes md-source__facts--done{0%{height:0}to{height:1.3rem}}@-webkit-keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes md-source__fact--done{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;padding-right:1.2rem;transition:opacity .25s;font-size:1.3rem;line-height:1.2;white-space:nowrap}[dir=rtl] .md-source{padding-right:0;padding-left:1.2rem}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:4.8rem;content:"";vertical-align:middle}.md-source__icon{width:4.8rem}.md-source__icon svg{width:2.4rem;height:2.4rem;margin-top:1.2rem;margin-left:1.2rem}[dir=rtl] .md-source__icon svg{margin-right:1.2rem;margin-left:0}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-4.4rem;margin-left:0;padding-right:4rem;padding-left:0}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done .25s ease-in;animation:md-source__facts--done .25s ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done .4s ease-out;animation:md-source__fact--done .4s ease-out}.md-source__fact:before{margin:0 .2rem;content:"\B7"}.md-source__fact:first-child:before{display:none}.md-source-file{display:inline-block;margin:1em .5em 1em 0;padding-right:.5rem;border-radius:.2rem;background-color:rgba(0,0,0,.07);font-size:1.28rem;list-style-type:none;cursor:pointer;overflow:hidden}.md-source-file:before{display:inline-block;margin-right:.5rem;padding:.5rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:1.6rem;content:"\E86F";vertical-align:middle}html .md-source-file{transition:background .4s,color .4s,box-shadow .4s cubic-bezier(.4,0,.2,1)}html .md-source-file:before{transition:inherit}html body .md-typeset .md-source-file{color:rgba(0,0,0,.54)}.md-source-file:hover{box-shadow:0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36)}.md-source-file:hover:before{background-color:#536dfe}.md-tabs{width:100%;transition:background .25s;background-color:#3f51b5;color:#fff;overflow:auto}.md-tabs__list{margin:0;margin-left:.4rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:4.8rem;padding-right:1.2rem;padding-left:1.2rem}.md-tabs__link{display:block;margin-top:1.6rem;transition:opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s;transition:transform .4s cubic-bezier(.1,.7,.1,1),opacity .25s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);font-size:1.4rem;opacity:.7}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:.3s}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.md-typeset .admonition,.md-typeset details{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:relative;margin:1.5625em 0;padding:0 1.2rem;border-left:.4rem solid #448aff;border-radius:.2rem;font-size:1.28rem;overflow:auto}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.4rem solid #448aff;border-left:none}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:1.2rem}.md-typeset .admonition .admonition,.md-typeset .admonition details,.md-typeset details .admonition,.md-typeset details details{margin:1em 0}.md-typeset .admonition>.admonition-title,.md-typeset .admonition>summary,.md-typeset details>.admonition-title,.md-typeset details>summary{margin:0 -1.2rem;padding:.8rem 1.2rem .8rem 4rem;border-bottom:.1rem solid rgba(68,138,255,.1);background-color:rgba(68,138,255,.1);font-weight:700}[dir=rtl] .md-typeset .admonition>.admonition-title,[dir=rtl] .md-typeset .admonition>summary,[dir=rtl] .md-typeset details>.admonition-title,[dir=rtl] .md-typeset details>summary{padding:.8rem 4rem .8rem 1.2rem}.md-typeset .admonition>.admonition-title:last-child,.md-typeset .admonition>summary:last-child,.md-typeset details>.admonition-title:last-child,.md-typeset details>summary:last-child{margin-bottom:0}.md-typeset .admonition>.admonition-title:before,.md-typeset .admonition>summary:before,.md-typeset details>.admonition-title:before,.md-typeset details>summary:before{position:absolute;left:1.2rem;color:#448aff;font-size:2rem;content:"\E3C9"}[dir=rtl] .md-typeset .admonition>.admonition-title:before,[dir=rtl] .md-typeset .admonition>summary:before,[dir=rtl] .md-typeset details>.admonition-title:before,[dir=rtl] .md-typeset details>summary:before{right:1.2rem;left:auto}.md-typeset .admonition.abstract,.md-typeset .admonition.summary,.md-typeset .admonition.tldr,.md-typeset details.abstract,.md-typeset details.summary,.md-typeset details.tldr{border-left-color:#00b0ff}[dir=rtl] .md-typeset .admonition.abstract,[dir=rtl] .md-typeset .admonition.summary,[dir=rtl] .md-typeset .admonition.tldr,[dir=rtl] .md-typeset details.abstract,[dir=rtl] .md-typeset details.summary,[dir=rtl] .md-typeset details.tldr{border-right-color:#00b0ff}.md-typeset .admonition.abstract>.admonition-title,.md-typeset .admonition.abstract>summary,.md-typeset .admonition.summary>.admonition-title,.md-typeset .admonition.summary>summary,.md-typeset .admonition.tldr>.admonition-title,.md-typeset .admonition.tldr>summary,.md-typeset details.abstract>.admonition-title,.md-typeset details.abstract>summary,.md-typeset details.summary>.admonition-title,.md-typeset details.summary>summary,.md-typeset details.tldr>.admonition-title,.md-typeset details.tldr>summary{border-bottom-color:.1rem solid rgba(0,176,255,.1);background-color:rgba(0,176,255,.1)}.md-typeset .admonition.abstract>.admonition-title:before,.md-typeset .admonition.abstract>summary:before,.md-typeset .admonition.summary>.admonition-title:before,.md-typeset .admonition.summary>summary:before,.md-typeset .admonition.tldr>.admonition-title:before,.md-typeset .admonition.tldr>summary:before,.md-typeset details.abstract>.admonition-title:before,.md-typeset details.abstract>summary:before,.md-typeset details.summary>.admonition-title:before,.md-typeset details.summary>summary:before,.md-typeset details.tldr>.admonition-title:before,.md-typeset details.tldr>summary:before{color:#00b0ff;content:"\E8D2"}.md-typeset .admonition.info,.md-typeset .admonition.todo,.md-typeset details.info,.md-typeset details.todo{border-left-color:#00b8d4}[dir=rtl] .md-typeset .admonition.info,[dir=rtl] .md-typeset .admonition.todo,[dir=rtl] .md-typeset details.info,[dir=rtl] .md-typeset details.todo{border-right-color:#00b8d4}.md-typeset .admonition.info>.admonition-title,.md-typeset .admonition.info>summary,.md-typeset .admonition.todo>.admonition-title,.md-typeset .admonition.todo>summary,.md-typeset details.info>.admonition-title,.md-typeset details.info>summary,.md-typeset details.todo>.admonition-title,.md-typeset details.todo>summary{border-bottom-color:.1rem solid rgba(0,184,212,.1);background-color:rgba(0,184,212,.1)}.md-typeset .admonition.info>.admonition-title:before,.md-typeset .admonition.info>summary:before,.md-typeset .admonition.todo>.admonition-title:before,.md-typeset .admonition.todo>summary:before,.md-typeset details.info>.admonition-title:before,.md-typeset details.info>summary:before,.md-typeset details.todo>.admonition-title:before,.md-typeset details.todo>summary:before{color:#00b8d4;content:"\E88E"}.md-typeset .admonition.hint,.md-typeset .admonition.important,.md-typeset .admonition.tip,.md-typeset details.hint,.md-typeset details.important,.md-typeset details.tip{border-left-color:#00bfa5}[dir=rtl] .md-typeset .admonition.hint,[dir=rtl] .md-typeset .admonition.important,[dir=rtl] .md-typeset .admonition.tip,[dir=rtl] .md-typeset details.hint,[dir=rtl] .md-typeset details.important,[dir=rtl] .md-typeset details.tip{border-right-color:#00bfa5}.md-typeset .admonition.hint>.admonition-title,.md-typeset .admonition.hint>summary,.md-typeset .admonition.important>.admonition-title,.md-typeset .admonition.important>summary,.md-typeset .admonition.tip>.admonition-title,.md-typeset .admonition.tip>summary,.md-typeset details.hint>.admonition-title,.md-typeset details.hint>summary,.md-typeset details.important>.admonition-title,.md-typeset details.important>summary,.md-typeset details.tip>.admonition-title,.md-typeset details.tip>summary{border-bottom-color:.1rem solid rgba(0,191,165,.1);background-color:rgba(0,191,165,.1)}.md-typeset .admonition.hint>.admonition-title:before,.md-typeset .admonition.hint>summary:before,.md-typeset .admonition.important>.admonition-title:before,.md-typeset .admonition.important>summary:before,.md-typeset .admonition.tip>.admonition-title:before,.md-typeset .admonition.tip>summary:before,.md-typeset details.hint>.admonition-title:before,.md-typeset details.hint>summary:before,.md-typeset details.important>.admonition-title:before,.md-typeset details.important>summary:before,.md-typeset details.tip>.admonition-title:before,.md-typeset details.tip>summary:before{color:#00bfa5;content:"\E80E"}.md-typeset .admonition.check,.md-typeset .admonition.done,.md-typeset .admonition.success,.md-typeset details.check,.md-typeset details.done,.md-typeset details.success{border-left-color:#00c853}[dir=rtl] .md-typeset .admonition.check,[dir=rtl] .md-typeset .admonition.done,[dir=rtl] .md-typeset .admonition.success,[dir=rtl] .md-typeset details.check,[dir=rtl] .md-typeset details.done,[dir=rtl] .md-typeset details.success{border-right-color:#00c853}.md-typeset .admonition.check>.admonition-title,.md-typeset .admonition.check>summary,.md-typeset .admonition.done>.admonition-title,.md-typeset .admonition.done>summary,.md-typeset .admonition.success>.admonition-title,.md-typeset .admonition.success>summary,.md-typeset details.check>.admonition-title,.md-typeset details.check>summary,.md-typeset details.done>.admonition-title,.md-typeset details.done>summary,.md-typeset details.success>.admonition-title,.md-typeset details.success>summary{border-bottom-color:.1rem solid rgba(0,200,83,.1);background-color:rgba(0,200,83,.1)}.md-typeset .admonition.check>.admonition-title:before,.md-typeset .admonition.check>summary:before,.md-typeset .admonition.done>.admonition-title:before,.md-typeset .admonition.done>summary:before,.md-typeset .admonition.success>.admonition-title:before,.md-typeset .admonition.success>summary:before,.md-typeset details.check>.admonition-title:before,.md-typeset details.check>summary:before,.md-typeset details.done>.admonition-title:before,.md-typeset details.done>summary:before,.md-typeset details.success>.admonition-title:before,.md-typeset details.success>summary:before{color:#00c853;content:"\E876"}.md-typeset .admonition.faq,.md-typeset .admonition.help,.md-typeset .admonition.question,.md-typeset details.faq,.md-typeset details.help,.md-typeset details.question{border-left-color:#64dd17}[dir=rtl] .md-typeset .admonition.faq,[dir=rtl] .md-typeset .admonition.help,[dir=rtl] .md-typeset .admonition.question,[dir=rtl] .md-typeset details.faq,[dir=rtl] .md-typeset details.help,[dir=rtl] .md-typeset details.question{border-right-color:#64dd17}.md-typeset .admonition.faq>.admonition-title,.md-typeset .admonition.faq>summary,.md-typeset .admonition.help>.admonition-title,.md-typeset .admonition.help>summary,.md-typeset .admonition.question>.admonition-title,.md-typeset .admonition.question>summary,.md-typeset details.faq>.admonition-title,.md-typeset details.faq>summary,.md-typeset details.help>.admonition-title,.md-typeset details.help>summary,.md-typeset details.question>.admonition-title,.md-typeset details.question>summary{border-bottom-color:.1rem solid rgba(100,221,23,.1);background-color:rgba(100,221,23,.1)}.md-typeset .admonition.faq>.admonition-title:before,.md-typeset .admonition.faq>summary:before,.md-typeset .admonition.help>.admonition-title:before,.md-typeset .admonition.help>summary:before,.md-typeset .admonition.question>.admonition-title:before,.md-typeset .admonition.question>summary:before,.md-typeset details.faq>.admonition-title:before,.md-typeset details.faq>summary:before,.md-typeset details.help>.admonition-title:before,.md-typeset details.help>summary:before,.md-typeset details.question>.admonition-title:before,.md-typeset details.question>summary:before{color:#64dd17;content:"\E887"}.md-typeset .admonition.attention,.md-typeset .admonition.caution,.md-typeset .admonition.warning,.md-typeset details.attention,.md-typeset details.caution,.md-typeset details.warning{border-left-color:#ff9100}[dir=rtl] .md-typeset .admonition.attention,[dir=rtl] .md-typeset .admonition.caution,[dir=rtl] .md-typeset .admonition.warning,[dir=rtl] .md-typeset details.attention,[dir=rtl] .md-typeset details.caution,[dir=rtl] .md-typeset details.warning{border-right-color:#ff9100}.md-typeset .admonition.attention>.admonition-title,.md-typeset .admonition.attention>summary,.md-typeset .admonition.caution>.admonition-title,.md-typeset .admonition.caution>summary,.md-typeset .admonition.warning>.admonition-title,.md-typeset .admonition.warning>summary,.md-typeset details.attention>.admonition-title,.md-typeset details.attention>summary,.md-typeset details.caution>.admonition-title,.md-typeset details.caution>summary,.md-typeset details.warning>.admonition-title,.md-typeset details.warning>summary{border-bottom-color:.1rem solid rgba(255,145,0,.1);background-color:rgba(255,145,0,.1)}.md-typeset .admonition.attention>.admonition-title:before,.md-typeset .admonition.attention>summary:before,.md-typeset .admonition.caution>.admonition-title:before,.md-typeset .admonition.caution>summary:before,.md-typeset .admonition.warning>.admonition-title:before,.md-typeset .admonition.warning>summary:before,.md-typeset details.attention>.admonition-title:before,.md-typeset details.attention>summary:before,.md-typeset details.caution>.admonition-title:before,.md-typeset details.caution>summary:before,.md-typeset details.warning>.admonition-title:before,.md-typeset details.warning>summary:before{color:#ff9100;content:"\E002"}.md-typeset .admonition.fail,.md-typeset .admonition.failure,.md-typeset .admonition.missing,.md-typeset details.fail,.md-typeset details.failure,.md-typeset details.missing{border-left-color:#ff5252}[dir=rtl] .md-typeset .admonition.fail,[dir=rtl] .md-typeset .admonition.failure,[dir=rtl] .md-typeset .admonition.missing,[dir=rtl] .md-typeset details.fail,[dir=rtl] .md-typeset details.failure,[dir=rtl] .md-typeset details.missing{border-right-color:#ff5252}.md-typeset .admonition.fail>.admonition-title,.md-typeset .admonition.fail>summary,.md-typeset .admonition.failure>.admonition-title,.md-typeset .admonition.failure>summary,.md-typeset .admonition.missing>.admonition-title,.md-typeset .admonition.missing>summary,.md-typeset details.fail>.admonition-title,.md-typeset details.fail>summary,.md-typeset details.failure>.admonition-title,.md-typeset details.failure>summary,.md-typeset details.missing>.admonition-title,.md-typeset details.missing>summary{border-bottom-color:.1rem solid rgba(255,82,82,.1);background-color:rgba(255,82,82,.1)}.md-typeset .admonition.fail>.admonition-title:before,.md-typeset .admonition.fail>summary:before,.md-typeset .admonition.failure>.admonition-title:before,.md-typeset .admonition.failure>summary:before,.md-typeset .admonition.missing>.admonition-title:before,.md-typeset .admonition.missing>summary:before,.md-typeset details.fail>.admonition-title:before,.md-typeset details.fail>summary:before,.md-typeset details.failure>.admonition-title:before,.md-typeset details.failure>summary:before,.md-typeset details.missing>.admonition-title:before,.md-typeset details.missing>summary:before{color:#ff5252;content:"\E14C"}.md-typeset .admonition.danger,.md-typeset .admonition.error,.md-typeset details.danger,.md-typeset details.error{border-left-color:#ff1744}[dir=rtl] .md-typeset .admonition.danger,[dir=rtl] .md-typeset .admonition.error,[dir=rtl] .md-typeset details.danger,[dir=rtl] .md-typeset details.error{border-right-color:#ff1744}.md-typeset .admonition.danger>.admonition-title,.md-typeset .admonition.danger>summary,.md-typeset .admonition.error>.admonition-title,.md-typeset .admonition.error>summary,.md-typeset details.danger>.admonition-title,.md-typeset details.danger>summary,.md-typeset details.error>.admonition-title,.md-typeset details.error>summary{border-bottom-color:.1rem solid rgba(255,23,68,.1);background-color:rgba(255,23,68,.1)}.md-typeset .admonition.danger>.admonition-title:before,.md-typeset .admonition.danger>summary:before,.md-typeset .admonition.error>.admonition-title:before,.md-typeset .admonition.error>summary:before,.md-typeset details.danger>.admonition-title:before,.md-typeset details.danger>summary:before,.md-typeset details.error>.admonition-title:before,.md-typeset details.error>summary:before{color:#ff1744;content:"\E3E7"}.md-typeset .admonition.bug,.md-typeset details.bug{border-left-color:#f50057}[dir=rtl] .md-typeset .admonition.bug,[dir=rtl] .md-typeset details.bug{border-right-color:#f50057}.md-typeset .admonition.bug>.admonition-title,.md-typeset .admonition.bug>summary,.md-typeset details.bug>.admonition-title,.md-typeset details.bug>summary{border-bottom-color:.1rem solid rgba(245,0,87,.1);background-color:rgba(245,0,87,.1)}.md-typeset .admonition.bug>.admonition-title:before,.md-typeset .admonition.bug>summary:before,.md-typeset details.bug>.admonition-title:before,.md-typeset details.bug>summary:before{color:#f50057;content:"\E868"}.md-typeset .admonition.example,.md-typeset details.example{border-left-color:#651fff}[dir=rtl] .md-typeset .admonition.example,[dir=rtl] .md-typeset details.example{border-right-color:#651fff}.md-typeset .admonition.example>.admonition-title,.md-typeset .admonition.example>summary,.md-typeset details.example>.admonition-title,.md-typeset details.example>summary{border-bottom-color:.1rem solid rgba(101,31,255,.1);background-color:rgba(101,31,255,.1)}.md-typeset .admonition.example>.admonition-title:before,.md-typeset .admonition.example>summary:before,.md-typeset details.example>.admonition-title:before,.md-typeset details.example>summary:before{color:#651fff;content:"\E242"}.md-typeset .admonition.cite,.md-typeset .admonition.quote,.md-typeset details.cite,.md-typeset details.quote{border-left-color:#9e9e9e}[dir=rtl] .md-typeset .admonition.cite,[dir=rtl] .md-typeset .admonition.quote,[dir=rtl] .md-typeset details.cite,[dir=rtl] .md-typeset details.quote{border-right-color:#9e9e9e}.md-typeset .admonition.cite>.admonition-title,.md-typeset .admonition.cite>summary,.md-typeset .admonition.quote>.admonition-title,.md-typeset .admonition.quote>summary,.md-typeset details.cite>.admonition-title,.md-typeset details.cite>summary,.md-typeset details.quote>.admonition-title,.md-typeset details.quote>summary{border-bottom-color:.1rem solid hsla(0,0%,62%,.1);background-color:hsla(0,0%,62%,.1)}.md-typeset .admonition.cite>.admonition-title:before,.md-typeset .admonition.cite>summary:before,.md-typeset .admonition.quote>.admonition-title:before,.md-typeset .admonition.quote>summary:before,.md-typeset details.cite>.admonition-title:before,.md-typeset details.cite>summary:before,.md-typeset details.quote>.admonition-title:before,.md-typeset details.quote>summary:before{color:#9e9e9e;content:"\E244"}.codehilite .o,.codehilite .ow,.md-typeset .highlight .o,.md-typeset .highlight .ow{color:inherit}.codehilite .ge,.md-typeset .highlight .ge{color:#000}.codehilite .gr,.md-typeset .highlight .gr{color:#a00}.codehilite .gh,.md-typeset .highlight .gh{color:#999}.codehilite .go,.md-typeset .highlight .go{color:#888}.codehilite .gp,.md-typeset .highlight .gp{color:#555}.codehilite .gs,.md-typeset .highlight .gs{color:inherit}.codehilite .gu,.md-typeset .highlight .gu{color:#aaa}.codehilite .gt,.md-typeset .highlight .gt{color:#a00}.codehilite .gd,.md-typeset .highlight .gd{background-color:#fdd}.codehilite .gi,.md-typeset .highlight .gi{background-color:#dfd}.codehilite .k,.md-typeset .highlight .k{color:#3b78e7}.codehilite .kc,.md-typeset .highlight .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn,.md-typeset .highlight .kd,.md-typeset .highlight .kn{color:#3b78e7}.codehilite .kp,.md-typeset .highlight .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt,.md-typeset .highlight .kr,.md-typeset .highlight .kt{color:#3e61a2}.codehilite .c,.codehilite .cm,.md-typeset .highlight .c,.md-typeset .highlight .cm{color:#999}.codehilite .cp,.md-typeset .highlight .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs,.md-typeset .highlight .c1,.md-typeset .highlight .ch,.md-typeset .highlight .cs{color:#999}.codehilite .na,.codehilite .nb,.md-typeset .highlight .na,.md-typeset .highlight .nb{color:#c2185b}.codehilite .bp,.md-typeset .highlight .bp{color:#3e61a2}.codehilite .nc,.md-typeset .highlight .nc{color:#c2185b}.codehilite .no,.md-typeset .highlight .no{color:#3e61a2}.codehilite .nd,.codehilite .ni,.md-typeset .highlight .nd,.md-typeset .highlight .ni{color:#666}.codehilite .ne,.codehilite .nf,.md-typeset .highlight .ne,.md-typeset .highlight .nf{color:#c2185b}.codehilite .nl,.md-typeset .highlight .nl{color:#3b5179}.codehilite .nn,.md-typeset .highlight .nn{color:#ec407a}.codehilite .nt,.md-typeset .highlight .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi,.md-typeset .highlight .nv,.md-typeset .highlight .vc,.md-typeset .highlight .vg,.md-typeset .highlight .vi{color:#3e61a2}.codehilite .nx,.md-typeset .highlight .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo,.md-typeset .highlight .il,.md-typeset .highlight .m,.md-typeset .highlight .mf,.md-typeset .highlight .mh,.md-typeset .highlight .mi,.md-typeset .highlight .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc,.md-typeset .highlight .s,.md-typeset .highlight .sb,.md-typeset .highlight .sc{color:#0d904f}.codehilite .sd,.md-typeset .highlight .sd{color:#999}.codehilite .s2,.md-typeset .highlight .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx,.md-typeset .highlight .se,.md-typeset .highlight .sh,.md-typeset .highlight .si,.md-typeset .highlight .sx{color:#183691}.codehilite .sr,.md-typeset .highlight .sr{color:#009926}.codehilite .s1,.codehilite .ss,.md-typeset .highlight .s1,.md-typeset .highlight .ss{color:#0d904f}.codehilite .err,.md-typeset .highlight .err{color:#a61717}.codehilite .w,.md-typeset .highlight .w{color:transparent}.codehilite .hll,.md-typeset .highlight .hll{display:block;margin:0 -1.2rem;padding:0 1.2rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite,.md-typeset .highlight{position:relative;margin:1em 0;padding:0;border-radius:.2rem;background-color:hsla(0,0%,93%,.5);color:#37474f;line-height:1.4;-webkit-overflow-scrolling:touch}.md-typeset .codehilite code,.md-typeset .codehilite pre,.md-typeset .highlight code,.md-typeset .highlight pre{display:block;margin:0;padding:1.05rem 1.2rem;background-color:transparent;overflow:auto;vertical-align:top}.md-typeset .codehilite code::-webkit-scrollbar,.md-typeset .codehilite pre::-webkit-scrollbar,.md-typeset .highlight code::-webkit-scrollbar,.md-typeset .highlight pre::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset .codehilite code::-webkit-scrollbar-thumb,.md-typeset .codehilite pre::-webkit-scrollbar-thumb,.md-typeset .highlight code::-webkit-scrollbar-thumb,.md-typeset .highlight pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite code::-webkit-scrollbar-thumb:hover,.md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover,.md-typeset .highlight code::-webkit-scrollbar-thumb:hover,.md-typeset .highlight pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre.codehilite,.md-typeset pre.highlight{overflow:visible}.md-typeset pre.codehilite code,.md-typeset pre.highlight code{display:block;padding:1.05rem 1.2rem;overflow:auto}.md-typeset .codehilitetable{display:block;margin:1em 0;border-radius:.2em;font-size:1.6rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td{display:block;padding:0}.md-typeset .codehilitetable tr{display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .highlight,.md-typeset .codehilitetable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv{padding:1.05rem 1.2rem}.md-typeset .codehilitetable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code{flex:1;overflow:hidden}.md-typeset>.codehilitetable{box-shadow:none}.md-typeset [id^="fnref:"]{display:inline-block}.md-typeset [id^="fnref:"]:target{margin-top:-7.6rem;padding-top:7.6rem;pointer-events:none}.md-typeset [id^="fn:"]:before{display:none;height:0;content:""}.md-typeset [id^="fn:"]:target:before{display:block;margin-top:-7rem;padding-top:7rem;pointer-events:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{transition:color .25s}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-ref{display:inline-block;pointer-events:auto}.md-typeset .footnote-ref:before{display:inline;margin:0 .2em;border-left:.1rem solid rgba(0,0,0,.26);font-size:1.25em;content:"";vertical-align:-.5rem}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.5rem);transform:translateX(.5rem);transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}[dir=rtl] .md-typeset .footnote-backref{-webkit-transform:translateX(-.5rem);transform:translateX(-.5rem)}.md-typeset .footnote-backref:before{display:inline-block;font-size:1.6rem;content:"\E31B"}[dir=rtl] .md-typeset .footnote-backref:before{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.md-typeset .headerlink{display:inline-block;margin-left:1rem;-webkit-transform:translateY(.5rem);transform:translateY(.5rem);transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}[dir=rtl] .md-typeset .headerlink{margin-right:1rem;margin-left:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset h1[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h1[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink,.md-typeset h1[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h1[id] .headerlink:focus,.md-typeset h1[id]:hover .headerlink:hover,.md-typeset h1[id]:target .headerlink{color:#536dfe}.md-typeset h2[id]:before{display:block;margin-top:-.8rem;padding-top:.8rem;content:""}.md-typeset h2[id]:target:before{margin-top:-6.8rem;padding-top:6.8rem}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink,.md-typeset h2[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h2[id] .headerlink:focus,.md-typeset h2[id]:hover .headerlink:hover,.md-typeset h2[id]:target .headerlink{color:#536dfe}.md-typeset h3[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h3[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink,.md-typeset h3[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h3[id] .headerlink:focus,.md-typeset h3[id]:hover .headerlink:hover,.md-typeset h3[id]:target .headerlink{color:#536dfe}.md-typeset h4[id]:before{display:block;margin-top:-.9rem;padding-top:.9rem;content:""}.md-typeset h4[id]:target:before{margin-top:-6.9rem;padding-top:6.9rem}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink,.md-typeset h4[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h4[id] .headerlink:focus,.md-typeset h4[id]:hover .headerlink:hover,.md-typeset h4[id]:target .headerlink{color:#536dfe}.md-typeset h5[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h5[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink,.md-typeset h5[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h5[id] .headerlink:focus,.md-typeset h5[id]:hover .headerlink:hover,.md-typeset h5[id]:target .headerlink{color:#536dfe}.md-typeset h6[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem;content:""}.md-typeset h6[id]:target:before{margin-top:-7.1rem;padding-top:7.1rem}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink,.md-typeset h6[id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset h6[id] .headerlink:focus,.md-typeset h6[id]:hover .headerlink:hover,.md-typeset h6[id]:target .headerlink{color:#536dfe}.md-typeset .MJXc-display{margin:.75em 0;padding:.75em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .critic.comment,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,93%,.5);color:#37474f;box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"\E0B7";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:1.6rem;padding-left:1.6rem;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset details{display:block;padding-top:0}.md-typeset details[open]>summary:after{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-bottom:none}.md-typeset details summary{padding-right:4rem}[dir=rtl] .md-typeset details summary{padding-left:4rem}.no-details .md-typeset details:not([open])>*{display:none}.no-details .md-typeset details:not([open]) summary{display:block}.md-typeset summary{display:block;outline:none;cursor:pointer}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary:after{position:absolute;top:.8rem;right:1.2rem;color:rgba(0,0,0,.26);font-size:2rem;content:"\E313"}[dir=rtl] .md-typeset summary:after{right:auto;left:1.2rem}.md-typeset .emojione{width:2rem;vertical-align:text-top}.md-typeset code.codehilite,.md-typeset code.highlight{margin:0 .29412em;padding:.07353em 0}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:auto}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.15em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.25em;content:"\E835";vertical-align:-.25em}[dir=rtl] .md-typeset .task-list-control .task-list-indicator:before{right:-1.25em;left:auto}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"\E834"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code,.md-typeset pre{white-space:pre-wrap}.md-typeset code{box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-clipboard,.md-content__icon,.md-footer,.md-header,.md-sidebar,.md-tabs,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -1.6rem;border-radius:0}.md-typeset pre>code{padding:1.05rem 1.6rem}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}.codehilite .hll,.md-typeset .highlight .hll{margin:0 -1.6rem;padding:0 1.6rem}.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilite code,.md-typeset>.codehilite pre,.md-typeset>.highlight code,.md-typeset>.highlight pre{padding:1.05rem 1.6rem}.md-typeset>.codehilitetable{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilitetable .codehilite>code,.md-typeset>.codehilitetable .codehilite>pre,.md-typeset>.codehilitetable .highlight>code,.md-typeset>.codehilitetable .highlight>pre,.md-typeset>.codehilitetable .linenodiv{padding:1rem 1.6rem}.md-typeset>p>.MJXc-display{margin:.75em -1.6rem;padding:.25em 1.6rem}}@media only screen and (min-width:100em){html{font-size:68.75%}}@media only screen and (min-width:125em){html{font-size:75%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}html .md-nav__link[for=toc]{display:block;padding-right:4.8rem}html .md-nav__link[for=toc]:after{color:inherit;content:"\E8DE"}html .md-nav__link[for=toc]+.md-nav__link{display:none}html .md-nav__link[for=toc]~.md-nav{display:flex}html [dir=rtl] .md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav__source{display:block;padding:0 .4rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{position:absolute;top:.4rem;left:.4rem;width:3.6rem;height:3.6rem;-webkit-transform-origin:center;transform-origin:center;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:2rem;background-color:#fff;overflow:hidden;pointer-events:none}[dir=rtl] .md-search__overlay{right:.4rem;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;width:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);transition:right 0s .3s,left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:right 0s .3s,left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);transition:right 0s 0s,left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:right 0s 0s,left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:auto}html [dir=rtl] .md-search__inner{right:100%;left:auto;-webkit-transform:translateX(-5%);transform:translateX(-5%)}.md-search__input{width:100%;height:4.8rem;font-size:1.8rem}.md-search__icon[for=search]{top:1.2rem;left:1.6rem}.md-search__icon[for=search][for=search]:before{content:"\E5C4"}[dir=rtl] .md-search__icon[for=search][for=search]:before{content:"\E5C8"}.md-search__icon[type=reset]{top:1.2rem;right:1.6rem}.md-search__output{top:4.8rem;bottom:0}.md-search-result__article--document:before{display:none}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-hero__inner{margin-top:4.8rem;margin-bottom:2.4rem}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:flex;position:absolute;top:0;right:0;left:0;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:1.6rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:11.2rem;padding:6rem 1.6rem .4rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:4.8rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background-color:#fff;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.4rem;left:.4rem;width:6.4rem;height:6.4rem;font-size:4.8rem}html .md-nav--primary .md-nav__title--site:before{display:none}html [dir=rtl] .md-nav--primary .md-nav__title--site .md-nav__button,html [dir=rtl] .md-nav--primary .md-nav__title:before{right:.4rem;left:auto}.md-nav--primary .md-nav__list{flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.1rem solid rgba(0,0,0,.07)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:4.8rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:1.6rem;padding-left:4.8rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E315"}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"\E314"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:1.2rem 1.6rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:1.2rem;margin-top:-1.2rem;color:inherit;font-size:2.4rem}[dir=rtl] .md-nav--primary .md-nav__link:after{right:auto;left:1.2rem}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:2.8rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:2.8rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:4rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:5.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:5.2rem;padding-left:0}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:6.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:6.4rem;padding-left:0}.md-nav__toggle~.md-nav{display:flex;-webkit-transform:translateX(100%);transform:translateX(100%);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}[dir=rtl] .md-nav__toggle~.md-nav{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.no-csstransforms3d .md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.no-csstransforms3d .md-nav__toggle:checked~.md-nav{display:flex}.md-sidebar--primary{position:fixed;top:0;left:-24.2rem;width:24.2rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);background-color:#fff;z-index:3}[dir=rtl] .md-sidebar--primary{right:-24.2rem;left:auto}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(24.2rem);transform:translateX(24.2rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{-webkit-transform:translateX(-24.2rem);transform:translateX(-24.2rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:24.2rem}[dir=rtl] .md-content{margin-right:0;margin-left:24.2rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:23rem;max-width:23rem;margin-left:2.8rem;padding-right:1.2rem}[dir=rtl] .md-header-nav__source{margin-right:2.8rem;margin-left:0;padding-right:0;padding-left:1.2rem}.md-search{padding:.4rem}.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);cursor:pointer}[dir=rtl] .md-search__overlay{right:0;left:auto}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-search__inner{position:relative;width:23rem;padding:.2rem 0;float:right;transition:width .25s cubic-bezier(.1,.7,.1,1)}[dir=rtl] .md-search__inner{float:left}.md-search__form,.md-search__input{border-radius:.2rem}.md-search__input{width:100%;height:3.6rem;padding-left:4.4rem;transition:background-color .25s cubic-bezier(.1,.7,.1,1),color .25s cubic-bezier(.1,.7,.1,1);background-color:rgba(0,0,0,.26);color:inherit;font-size:1.6rem}[dir=rtl] .md-search__input{padding-right:4.4rem}.md-search__input+.md-search__icon{color:inherit}.md-search__input::-webkit-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:-ms-input-placeholder,.md-search__input::-ms-input-placeholder{color:hsla(0,0%,100%,.7)}.md-search__input::placeholder{color:hsla(0,0%,100%,.7)}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.2rem .2rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:none}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder,[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:3.8rem;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__meta{padding-left:4.4rem}[dir=rtl] .md-search-result__meta{padding-right:4.4rem;padding-left:0}.md-search-result__article{padding-left:4.4rem}[dir=rtl] .md-search-result__article{padding-right:4.4rem;padding-left:1.6rem}.md-sidebar--secondary{display:block;margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}[dir=rtl] .md-sidebar--secondary{margin-right:100%;margin-left:0;-webkit-transform:translate(100%);transform:translate(100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:24.2rem}[dir=rtl] .md-content{margin-right:24.2rem}.md-content__inner{margin-right:2.4rem;margin-left:2.4rem}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.no-js .md-nav__toggle~.md-nav{display:none}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.no-js .md-nav[data-md-state=expand],.no-js .md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:68.8rem}.md-sidebar--secondary{margin-left:122rem}[dir=rtl] .md-sidebar--secondary{margin-right:122rem;margin-left:0}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.no-js .md-tabs--active~.md-main .md-nav--primary .md-nav{display:block}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0;visibility:hidden}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:1.4rem;overflow:auto;visibility:visible}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none;overflow:visible}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}.md-tabs--active~.md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title{display:none}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}[dir=rtl] .md-footer-copyright{float:right}.md-footer-social{padding:1.2rem 0;float:right}[dir=rtl] .md-footer-social{float:left}}@media only screen and (max-width:29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__inner{width:46.8rem}.md-search-result__teaser{max-height:5rem;-webkit-line-clamp:3}} -/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24uOGQ0MGQ4OWIuY3NzIiwic291cmNlUm9vdCI6IiJ9*/ \ No newline at end of file diff --git a/material/base.html b/material/base.html deleted file mode 100644 index e1e9725644..0000000000 --- a/material/base.html +++ /dev/null @@ -1,246 +0,0 @@ -{% import "partials/language.html" as lang with context %} - -{% set config = { - 'site_name': 'Ibexa Developer Documentation', - 'repo_url': 'https://github.com/ezsystems/developer-documentation', - 'copyright': 'Copyright 1999-2020 Ibexa AS and others', - 'plugins': config.plugins, - 'theme': { - 'name': null, - 'custom_dir': 'material', - 'favicon': 'images/favicon.png', - 'language': 'en', - 'palette': { - 'primary': 'blue grey', - 'accent': 'deep orange' - }, - 'font': false, - 'logo': 'images/ibexa-dxp-logo.svg', - 'feature': { - 'tabs': false - }, - 'search_index_only': false - }, - 'extra': config.extra, -} %} - -{% set feature = config.theme.feature | default({tabs: false}) %} -{% set palette = config.theme.palette | default({primary: 'blue grey', accent: 'deep orange'}) %} -{% set font = config.theme.font | default(false) %} - - - - {% block site_meta %} - - - - {% if page and page.meta and page.meta.description %} - - {% elif config.site_description %} - - {% endif %} - {% if page.canonical_url %} - - {% endif %} - {% if page and page.meta and page.meta.author %} - - {% elif config.site_author %} - - {% endif %} - {% for key in [ - "clipboard.copy", - "clipboard.copied", - "search.language", - "search.pipeline.stopwords", - "search.pipeline.trimmer", - "search.result.none", - "search.result.one", - "search.result.other", - "search.tokenizer" - ] %} - - {% endfor %} - - - {% endblock %} - {% block htmltitle %} - {% if page and page.meta and page.meta.title %} - {{ page.meta.title }} - {% elif page and page.title and not page.is_homepage %} - {{ page.title }} - {{ config.site_name }} - {% else %} - {{ config.site_name }} - {% endif %} - {% endblock %} - {% block styles %} - - {% if palette.primary or palette.accent %} - - {% endif %} - {% endblock %} - {% block libs %} - - {% endblock %} - {% block fonts %} - - {% if font != false %} - - - {% endif %} - - {% endblock %} - {% for path in extra_css %} - - {% endfor %} - {% block extrahead %}{% endblock %} - - - {% if palette.primary or palette.accent %} - {% set primary = palette.primary | replace(" ", "-") | lower %} - {% set accent = palette.accent | replace(" ", "-") | lower %} - - {% else %} - - {% endif %} - - - {% set platform = config.extra.repo_icon or config.repo_url %} - {% if "github" in platform %} - {% include "assets/images/icons/github.a4034fb1.svg" %} - {% elif "gitlab" in platform %} - {% include "assets/images/icons/gitlab.348cdb3a.svg" %} - {% elif "bitbucket" in platform %} - {% include "assets/images/icons/bitbucket.4ebea66e.svg" %} - {% endif %} - - - - - - {% if page.toc | first is defined %} - - {{ lang.t('skip.link.title') }} - - {% endif %} -
    - -
    -
    - {% block header %} - {% include "partials/header.html" %} - {% endblock %} -
    - {% block hero %} - {% if page and page.meta and page.meta.hero %} - {% include "partials/hero.html" with context %} - {% endif %} - {% endblock %} - {% if feature.tabs %} - {% include "partials/tabs.html" %} - {% endif %} -
    -
    - {% block site_nav %} - {% if nav %} -
    -
    -
    - {% include "partials/nav.html" %} -
    -
    -
    - {% endif %} - {% if page.toc %} -
    -
    -
    - {% include "partials/toc.html" %} -
    -
    -
    - {% endif %} - {% endblock %} -
    - - {% set ns = namespace(bootstrap_extra_css='') %} - {% for md_file in config.extra.append_bootstrap if 'docs' + md_file in page.edit_url %} - {% set ns.bootstrap_extra_css = ' bootstrap-iso ez-guidelines' %} - {% include "partials/integrations/bootstrap.html" %} - {% endfor %} - -
    - {% block content %} - {% if not "\x3ch1" in page.content %} -

    {{ page.title | default(config.site_name, true)}}

    - {% endif %} - {{ page.content }} - {% block source %} - {% if page and page.meta and page.meta.source %} -

    {{ lang.t("meta.source") }}

    - {% set repo = config.repo_url %} - {% if repo | last == "/" %} - {% set repo = repo[:-1] %} - {% endif %} - {% set path = page.meta.path | default([""]) %} - {% set file = page.meta.source %} - - {{ file }} - - {% endif %} - {% endblock %} - {% endblock %} - {% block disqus %} - {% include "partials/integrations/disqus.html" %} - {% endblock %} -
    -
    -
    -
    - {% block footer %} - {% include "partials/footer.html" %} - {% endblock %} -
    - {% block scripts %} - - {% if lang.t("search.language") != "en" %} - {% set languages = lang.t("search.language").split(",") %} - {% if languages | length and languages[0] != "" %} - {% set path = base_url + "/assets/javascripts/lunr" %} - - {% for language in languages | map("trim") %} - {% if language != "en" %} - {% if language == "jp" %} - - {% endif %} - {% if language in ("da", "de", "du", "es", "fi", "fr", "hu", "it", "jp", "no", "pt", "ro", "ru", "sv", "tr") %} - - {% endif %} - {% endif %} - {% endfor %} - {% if languages | length > 1 %} - - {% endif %} - {% endif %} - {% endif %} - - {% for path in extra_javascript if 'search/main.js' not in path %} - {% if 'jquery' in path and 'bootstrap-iso' in ns.bootstrap_extra_css %} - {# jquery already loaded #} - {% else %} - - {% endif %} - {% endfor %} - - {% endblock %} - {% block analytics %} - {% if config.google_analytics %} - {% include "partials/integrations/analytics.html" %} - {% endif %} - {% endblock %} - - diff --git a/material/main.html b/material/main.html deleted file mode 100644 index 94d9808cc7..0000000000 --- a/material/main.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "base.html" %} diff --git a/material/mkdocs_theme.yml b/material/mkdocs_theme.yml deleted file mode 100644 index 2cd43ea533..0000000000 --- a/material/mkdocs_theme.yml +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2016-2018 Martin Donath - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -# Language for theme localization -language: en - -# Text direction (can be ltr or rtl), default: ltr -direction: - -# Feature flags for functionality that alters behavior significantly, and thus -# may be a matter of taste -feature: - - # Another layer on top of the main navigation for larger screens in the form - # of tabs, especially useful for larger documentation projects - tabs: false - -# Sets the primary and accent color palettes as defined in the Material Design -# documentation - possible values can be looked up in the getting started guide -palette: - - # Primary color used for header, sidebar and links, default: indigo - primary: - - # Accent color for highlighting user interaction, default: indigo - accent: - -# Fonts used by Material, automatically loaded from Google Fonts - see the site -# for a list of available fonts -font: - - # Default font for text - text: Roboto - - # Fixed-width font for code listings - code: Roboto Mono - -# Favicon to be rendered -favicon: assets/images/favicon.png - -# The logo of the documentation shown in the header and navigation can either -# be a Material Icon ligature (see https://material.io/icons/) or an image URL -logo: - icon: "\uE80C" - -# Material includes the search in the header as a partial, not as a separate -# template, so it's correct that search.html is missing -include_search_page: false - -# Material doesn't use MkDocs search functionality but provides its own. For -# this reason, only the search index needs to be built -search_index_only: true - -# Static pages to build -static_templates: - - 404.html diff --git a/material/partials/footer.html b/material/partials/footer.html deleted file mode 100644 index 3342473c84..0000000000 --- a/material/partials/footer.html +++ /dev/null @@ -1,58 +0,0 @@ -{% import "partials/language.html" as lang with context %} - diff --git a/material/partials/header.html b/material/partials/header.html deleted file mode 100644 index b835c8c858..0000000000 --- a/material/partials/header.html +++ /dev/null @@ -1,59 +0,0 @@ -
    - -
    diff --git a/material/partials/hero.html b/material/partials/hero.html deleted file mode 100644 index 9f6d77e92a..0000000000 --- a/material/partials/hero.html +++ /dev/null @@ -1,10 +0,0 @@ -{% set feature = config.theme.feature %} -{% set class = "md-hero" %} -{% if not feature.tabs %} - {% set class = "md-hero md-hero--expand" %} -{% endif %} -
    -
    - {{ page.meta.hero }} -
    -
    diff --git a/material/partials/integrations/bootstrap.html b/material/partials/integrations/bootstrap.html deleted file mode 100644 index a129f3b22c..0000000000 --- a/material/partials/integrations/bootstrap.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/material/partials/integrations/disqus.html b/material/partials/integrations/disqus.html deleted file mode 100644 index 967ae63432..0000000000 --- a/material/partials/integrations/disqus.html +++ /dev/null @@ -1,21 +0,0 @@ -{% set disqus = config.extra.disqus %} -{% if page and page.meta and page.meta.disqus is string %} - {% set disqus = page.meta.disqus %} -{% endif %} -{% if not page.is_homepage and disqus %} -

    {{ lang.t("meta.comments") }}

    -
    - -{% endif %} diff --git a/material/partials/language.html b/material/partials/language.html deleted file mode 100644 index a9b920418e..0000000000 --- a/material/partials/language.html +++ /dev/null @@ -1,13 +0,0 @@ -{% set language = config.theme.language | default('en') %} - -{% import "partials/language/" + language + ".html" as lang %} -{% import "partials/language/en.html" as fallback %} -{% macro t(key) %}{{ { - "direction": config.theme.direction, - "search.language": ( - config.extra.search | default({}) - ).language, - "search.tokenizer": ( - config.extra.search | default({}) - ).tokenizer | default("", true), -}[key] or lang.t(key) or fallback.t(key) }}{% endmacro %} diff --git a/material/partials/language/ar.html b/material/partials/language/ar.html deleted file mode 100644 index d81434230e..0000000000 --- a/material/partials/language/ar.html +++ /dev/null @@ -1,22 +0,0 @@ -{% macro t(key) %}{{ { - "language": "ar", - "direction": "rtl", - "clipboard.copy": "نسخ إلى الحافظة", - "clipboard.copied": "تم النسخ الى الحافظة", - "edit.link.title": "عدل الصفحة", - "footer.previous": "السابقة", - "footer.next": "التالية", - "meta.comments": "التعليقات", - "meta.source": "المصدر", - "search.language": "", - "search.pipeline.stopwords": false, - "search.pipeline.trimmer": false, - "search.placeholder": "بحث", - "search.result.placeholder": "اكتب لبدء البحث", - "search.result.none": "لا توجد نتائج", - "search.result.one": "نتائج البحث مستند واحد", - "search.result.other": "نتائج البحث # مستندات", - "skip.link.title": "انتقل إلى المحتوى", - "source.link.title": "اذهب إلى المصدر", - "toc.title": "جدول المحتويات" -}[key] }}{% endmacro %} diff --git a/material/partials/language/ca.html b/material/partials/language/ca.html deleted file mode 100644 index aa7bccb9e1..0000000000 --- a/material/partials/language/ca.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "ca", - "clipboard.copy": "Còpia al porta-retalls", - "clipboard.copied": "Copiat al porta-retalls", - "edit.link.title": "Edita aquesta pàgina", - "footer.previous": "Anterior", - "footer.next": "Següent", - "meta.comments": "Comentaris", - "meta.source": "Codi font", - "search.language": "", - "search.placeholder": "Cerca", - "search.result.placeholder": "Escriu per a començar a cercar", - "search.result.none": "Cap document coincideix", - "search.result.one": "1 document coincident", - "search.result.other": "# documents coincidents", - "skip.link.title": "Salta el contingut", - "source.link.title": "Ves al repositori", - "toc.title": "Taula de continguts" -}[key] }}{% endmacro %} diff --git a/material/partials/language/da.html b/material/partials/language/da.html deleted file mode 100644 index 8cf53e9b7e..0000000000 --- a/material/partials/language/da.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "da", - "clipboard.copy": "Kopiér til udklipsholderen", - "clipboard.copied": "Kopieret til udklipsholderen", - "edit.link.title": "Redigér denne side", - "footer.previous": "Forrige", - "footer.next": "Næste", - "meta.comments": "Kommentarer", - "meta.source": "Kilde", - "search.language": "da", - "search.placeholder": "Søg", - "search.result.placeholder": "Indtask søgeord", - "search.result.none": "Ingen resultater fundet", - "search.result.one": "1 resultat", - "search.result.other": "# resultater", - "skip.link.title": "Gå til indholdet", - "source.link.title": "Åbn arkiv", - "toc.title": "Indholdsfortegnelse" -}[key] }}{% endmacro %} diff --git a/material/partials/language/de.html b/material/partials/language/de.html deleted file mode 100644 index fda1804079..0000000000 --- a/material/partials/language/de.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "de", - "clipboard.copy": "In Zwischenablage kopieren", - "clipboard.copied": "In Zwischenablage kopiert", - "edit.link.title": "Seite editieren", - "footer.previous": "Zurück", - "footer.next": "Weiter", - "meta.comments": "Kommentare", - "meta.source": "Quellcode", - "search.language": "de", - "search.placeholder": "Suche", - "search.result.placeholder": "Suchbegriff eingeben", - "search.result.none": "Keine Suchergebnisse", - "search.result.one": "1 Suchergebnis", - "search.result.other": "# Suchergebnisse", - "skip.link.title": "Zum Inhalt", - "source.link.title": "Quellcode", - "toc.title": "Inhaltsverzeichnis" -}[key] }}{% endmacro %} diff --git a/material/partials/language/en.html b/material/partials/language/en.html deleted file mode 100644 index e259bb24b0..0000000000 --- a/material/partials/language/en.html +++ /dev/null @@ -1,24 +0,0 @@ -{% macro t(key) %}{{ { - "language": "en", - "direction": "ltr", - "clipboard.copy": "Copy to clipboard", - "clipboard.copied": "Copied to clipboard", - "edit.link.title": "Edit this page", - "show.link.title": "View on Github", - "footer.previous": "Previous", - "footer.next": "Next", - "meta.comments": "Comments", - "meta.source": "Source", - "search.language": "en", - "search.pipeline.stopwords": true, - "search.pipeline.trimmer": true, - "search.placeholder": "Search", - "search.result.placeholder": "Type to start searching", - "search.result.none": "No matching documents", - "search.result.one": "1 matching document", - "search.result.other": "# matching documents", - "search.tokenizer": "[\s\-]+", - "skip.link.title": "Skip to content", - "source.link.title": "Go to repository", - "toc.title": "On this page" -}[key] }}{% endmacro %} diff --git a/material/partials/language/es.html b/material/partials/language/es.html deleted file mode 100644 index ccac009717..0000000000 --- a/material/partials/language/es.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "es", - "clipboard.copy": "Copiar al portapapeles", - "clipboard.copied": "Copiado al portapapeles", - "edit.link.title": "Editar esta página", - "footer.previous": "Anterior", - "footer.next": "Siguiente", - "meta.comments": "Comentarios", - "meta.source": "Fuente", - "search.language": "es", - "search.placeholder": "Búsqueda", - "search.result.placeholder": "Teclee para comenzar búsqueda", - "search.result.none": "No se encontraron documentos", - "search.result.one": "1 documento encontrado", - "search.result.other": "# documentos encontrados", - "skip.link.title": "Saltar a contenido", - "source.link.title": "Ir al repositorio", - "toc.title": "Tabla de contenidos" -}[key] }}{% endmacro %} diff --git a/material/partials/language/fa.html b/material/partials/language/fa.html deleted file mode 100644 index 54a7d2860d..0000000000 --- a/material/partials/language/fa.html +++ /dev/null @@ -1,22 +0,0 @@ -{% macro t(key) %}{{ { - "language": "fa", - "direction": "rtl", - "clipboard.copy": "کپی کردن", - "clipboard.copied": "کپی شد", - "edit.link.title": "این صفحه را ویرایش کنید", - "footer.previous": "قبلی", - "footer.next": "بعدی", - "meta.comments": "نظرات", - "meta.source": "منبع", - "search.language": "", - "search.pipeline.stopwords": false, - "search.pipeline.trimmer": false, - "search.placeholder": "جستجو", - "search.result.placeholder": "برای شروع جستجو تایپ کنید", - "search.result.none": "سندی یافت نشد", - "search.result.one": "1 سند یافت شد", - "search.result.other": "# سند یافت شد", - "skip.link.title": "پرش به محتویات", - "source.link.title": "رفتن به مخزن", - "toc.title": "فهرست موضوعات" -}[key] }}{% endmacro %} diff --git a/material/partials/language/fr.html b/material/partials/language/fr.html deleted file mode 100644 index f6fa31640b..0000000000 --- a/material/partials/language/fr.html +++ /dev/null @@ -1,18 +0,0 @@ -{% macro t(key) %}{{ { - "language": "fr", - "clipboard.copy": "Copier dans le presse-papier", - "clipboard.copied": "Copié dans le presse-papier", - "edit.link.title": "Editer cette page", - "footer.previous": "Précédent", - "footer.next": "Suivant", - "meta.comments": "Commentaires", - "meta.source": "Source", - "search.language": "fr", - "search.placeholder": "Rechercher", - "search.result.placeholder": "Taper pour démarrer la recherche", - "search.result.none": "Aucun document trouvé", - "search.result.one": "1 document trouvé", - "search.result.other": "# documents trouvés", - "source.link.title": "Aller au dépôt", - "toc.title": "Table des matières" -}[key] }}{% endmacro %} diff --git a/material/partials/language/gl.html b/material/partials/language/gl.html deleted file mode 100644 index 14c4a86890..0000000000 --- a/material/partials/language/gl.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "gl", - "clipboard.copy": "Copiar no cortapapeis", - "clipboard.copied": "Copiado no cortapapeis", - "edit.link.title": "Editar esta páxina", - "footer.previous": "Anterior", - "footer.next": "Seguinte", - "meta.comments": "Comentarios", - "meta.source": "Fonte", - "search.language": "es", - "search.placeholder": "Busca", - "search.result.placeholder": "Insira un termo", - "search.result.none": "Sen resultados", - "search.result.one": "1 resultado atopado", - "search.result.other": "# resultados atopados", - "skip.link.title": "Ir ao contido", - "source.link.title": "Ir ao repositorio", - "toc.title": "Táboa de contidos" -}[key] }}{% endmacro %} diff --git a/material/partials/language/he.html b/material/partials/language/he.html deleted file mode 100644 index 1450982cda..0000000000 --- a/material/partials/language/he.html +++ /dev/null @@ -1,22 +0,0 @@ -{% macro t(key) %}{{ { - "language": "he", - "direction": "rtl", - "clipboard.copy": "העתק ללוח", - "clipboard.copied": "הועתק ללוח", - "edit.link.title": "ערוך דף זה", - "footer.previous": "קודם", - "footer.next": "הַבָּא", - "meta.comments": "הערות", - "meta.source": "מָקוֹר", - "search.language": "", - "search.pipeline.stopwords": false, - "search.pipeline.trimmer": false, - "search.placeholder": "לחפש", - "search.result.placeholder": "הקלד כדי להתחיל לחפש", - "search.result.none": "אין מסמכים תואמים", - "search.result.one": "1 מסמך תואם", - "search.result.other": "# מסמך תואם", - "skip.link.title": "דלג לתוכן", - "source.link.title": "עבור אל מאגר", - "toc.title": "תוכן העניינים" -}[key] }}{% endmacro %} diff --git a/material/partials/language/hu.html b/material/partials/language/hu.html deleted file mode 100644 index 9395b5dcbb..0000000000 --- a/material/partials/language/hu.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "hu", - "clipboard.copy": "Másolás vágólapra", - "clipboard.copied": "Vágólapra másolva", - "edit.link.title": "Oldal szerkesztése", - "footer.previous": "Előző", - "footer.next": "Következő", - "meta.comments": "Hozzászólások", - "meta.source": "Forrás", - "search.language": "hu", - "search.placeholder": "Keresés", - "search.result.placeholder": "Kereséshez írj ide valamit", - "search.result.none": "Nincs találat", - "search.result.one": "1 egyező dokumentum", - "search.result.other": "# egyező dokumentum", - "skip.link.title": "Kihagyás", - "source.link.title": "Főoldalra ugrás", - "toc.title": "Tartalomjegyzék" -}[key] }}{% endmacro %} diff --git a/material/partials/language/it.html b/material/partials/language/it.html deleted file mode 100644 index 954be2d321..0000000000 --- a/material/partials/language/it.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "it", - "clipboard.copy": "Copia", - "clipboard.copied": "Copiato", - "edit.link.title": "Modifica", - "footer.previous": "Precedente", - "footer.next": "Prossimo", - "meta.comments": "Commenti", - "meta.source": "Sorgente", - "search.language": "it", - "search.placeholder": "Cerca", - "search.result.placeholder": "Scrivi per iniziare a cercare", - "search.result.none": "Nessun documento trovato", - "search.result.one": "1 documento trovato", - "search.result.other": "# documenti trovati", - "skip.link.title": "Vai al contenuto", - "source.link.title": "Apri repository", - "toc.title": "Indice" -}[key] }}{% endmacro %} diff --git a/material/partials/language/ja.html b/material/partials/language/ja.html deleted file mode 100644 index 5e94d8b6f8..0000000000 --- a/material/partials/language/ja.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "ja", - "clipboard.copy": "クリップボードへコピー", - "clipboard.copied": "コピーしました", - "edit.link.title": "編集", - "footer.previous": "前", - "footer.next": "次", - "meta.comments": "コメント", - "meta.source": "ソース", - "search.language": "jp", - "search.placeholder": "検索", - "search.result.placeholder": "検索キーワードを入力してください", - "search.result.none": "何も見つかりませんでした", - "search.result.one": "1件見つかりました", - "search.result.other": "#件見つかりました", - "search.tokenizer": "[\s\- 、。,.]+", - "source.link.title": "リポジトリへ", - "toc.title": "目次" -}[key] }}{% endmacro %} diff --git a/material/partials/language/kr.html b/material/partials/language/kr.html deleted file mode 100644 index e7d88dd876..0000000000 --- a/material/partials/language/kr.html +++ /dev/null @@ -1,18 +0,0 @@ -{% macro t(key) %}{{ { - "language": "kr", - "clipboard.copy": "클립보드로 복사", - "clipboard.copied": "클립보드에 복사됨", - "edit.link.title": "이 페이지를 편집", - "footer.previous": "이전", - "footer.next": "다음", - "meta.comments": "댓글", - "meta.source": "출처", - "search.language": "jp", - "search.placeholder": "검색", - "search.result.placeholder": "검색어를 입력하세요", - "search.result.none": "검색어와 일치하는 문서가 없습니다", - "search.result.one": "1개의 일치하는 문서", - "search.result.other": "#개의 일치하는 문서", - "source.link.title": "저장소로 이동", - "toc.title": "목차" -}[key] }}{% endmacro %} diff --git a/material/partials/language/nl.html b/material/partials/language/nl.html deleted file mode 100644 index 36be6dd146..0000000000 --- a/material/partials/language/nl.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "nl", - "clipboard.copy": "Kopiëren naar klembord", - "clipboard.copied": "Gekopieerd naar klembord", - "edit.link.title": "Wijzig deze pagina", - "footer.previous": "Vorige", - "footer.next": "Volgende", - "meta.comments": "Reacties", - "meta.source": "Bron", - "search.language": "du", - "search.placeholder": "Zoeken", - "search.result.placeholder": "Typ om te beginnen met zoeken", - "search.result.none": "Geen overeenkomende documenten", - "search.result.one": "1 overeenkomende document", - "search.result.other": "# overeenkomende documenten", - "skip.link.title": "Ga naar inhoud", - "source.link.title": "Ga naar repository", - "toc.title": "Inhoudstafel" -}[key] }}{% endmacro %} diff --git a/material/partials/language/no.html b/material/partials/language/no.html deleted file mode 100644 index 8d3b3d1200..0000000000 --- a/material/partials/language/no.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "no", - "clipboard.copy": "Kopier til utklippstavlen", - "clipboard.copied": "Kopiert til utklippstavlen", - "edit.link.title": "Rediger denne siden", - "footer.previous": "Forrige", - "footer.next": "Neste", - "meta.comments": "Kommentarer", - "meta.source": "Kilde", - "search.language": "no", - "search.placeholder": "Søk", - "search.result.placeholder": "Skriv søkeord", - "search.result.none": "Ingen treff", - "search.result.one": "1 treff", - "search.result.other": "# treff", - "skip.link.title": "Gå til innhold", - "source.link.title": "Gå til kilde", - "toc.title": "Innholdsfortegnelse" -}[key] }}{% endmacro %} diff --git a/material/partials/language/pl.html b/material/partials/language/pl.html deleted file mode 100644 index b158a129c1..0000000000 --- a/material/partials/language/pl.html +++ /dev/null @@ -1,21 +0,0 @@ -{% macro t(key) %}{{ { - "language": "pl", - "clipboard.copy": "Kopiuj do schowka", - "clipboard.copied": "Skopiowane", - "edit.link.title": "Edytuj tę stronę", - "footer.previous": "Poprzednia strona", - "footer.next": "Następna strona", - "meta.comments": "Komentarze", - "meta.source": "Kod źródłowy", - "search.language": "", - "search.pipeline.stopwords": false, - "search.pipeline.trimmer": false, - "search.placeholder": "Szukaj", - "search.result.placeholder": "Zacznij pisać, aby szukać", - "search.result.none": "Brak wyników wyszukiwania", - "search.result.one": "Wyniki wyszukiwania: 1", - "search.result.other": "Wyniki wyszukiwania: #", - "skip.link.title": "Przejdź do treści", - "source.link.title": "Idź do repozytorium", - "toc.title": "Spis treści" -}[key] }}{% endmacro %} diff --git a/material/partials/language/pt.html b/material/partials/language/pt.html deleted file mode 100644 index e4fbe7d150..0000000000 --- a/material/partials/language/pt.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "pt", - "clipboard.copy": "Copiar para área de transferência", - "clipboard.copied": "Copiado para área de transferência", - "edit.link.title": "Editar esta página", - "footer.previous": "Anterior", - "footer.next": "Próximo", - "meta.comments": "Comentários", - "meta.source": "Fonte", - "search.language": "pt", - "search.placeholder": "Buscar", - "search.result.placeholder": "Digite para iniciar a busca", - "search.result.none": "Nenhum resultado encontrado", - "search.result.one": "1 resultado encontrado", - "search.result.other": "# resultados encontrados", - "skip.link.title": "Ir para o conteúdo", - "source.link.title": "Ir ao repositório", - "toc.title": "Índice" -}[key] }}{% endmacro %} diff --git a/material/partials/language/ru.html b/material/partials/language/ru.html deleted file mode 100644 index 424b22f6c4..0000000000 --- a/material/partials/language/ru.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "ru", - "clipboard.copy": "Копировать в буфер", - "clipboard.copied": "Скопировано в буфер", - "edit.link.title": "Редактировать страницу", - "footer.previous": "Назад", - "footer.next": "Вперед", - "meta.comments": "Комментарии", - "meta.source": "Исходный код", - "search.language": "ru", - "search.placeholder": "Поиск", - "search.result.placeholder": "Начните печатать для поиска", - "search.result.none": "Совпадений не найдено", - "search.result.one": "Найдено 1 совпадение", - "search.result.other": "Найдено # совпадений", - "skip.link.title": "Перейти к содержанию", - "source.link.title": "Перейти к репозиторию", - "toc.title": "Содержание" -}[key] }}{% endmacro %} diff --git a/material/partials/language/sv.html b/material/partials/language/sv.html deleted file mode 100644 index 5d22b98bbb..0000000000 --- a/material/partials/language/sv.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "sv", - "clipboard.copy": "Kopiera till urklipp", - "clipboard.copied": "Kopierat till urklipp", - "edit.link.title": "Redigera sidan", - "footer.previous": "Föregående", - "footer.next": "Nästa", - "meta.comments": "Kommentarer", - "meta.source": "Källa", - "search.language": "sv", - "search.placeholder": "Sök", - "search.result.placeholder": "Skriv sökord", - "search.result.none": "Inga sökresultat", - "search.result.one": "1 sökresultat", - "search.result.other": "# sökresultat", - "skip.link.title": "Gå till innehållet", - "source.link.title": "Gå till datakatalog", - "toc.title": "Innehållsförteckning" -}[key] }}{% endmacro %} diff --git a/material/partials/language/tr.html b/material/partials/language/tr.html deleted file mode 100644 index 7e0b1a372c..0000000000 --- a/material/partials/language/tr.html +++ /dev/null @@ -1,18 +0,0 @@ -{% macro t(key) %}{{ { - "language": "tr", - "clipboard.copy": "Kopyala", - "clipboard.copied": "Kopyalandı", - "edit.link.title": "Düzenle", - "footer.previous": "Önceki", - "footer.next": "Sonraki", - "meta.comments": "Yorumlar", - "meta.source": "Kaynak", - "search.language": "tr", - "search.placeholder": "Ara", - "search.result.placeholder": "Aramaya başlamak için yazın", - "search.result.none": "Eşleşen doküman bulunamadı", - "search.result.one": "1 doküman bulundu", - "search.result.other": "# doküman bulundu", - "source.link.title": "Depoya git", - "toc.title": "İçindekiler" -}[key] }}{% endmacro %} diff --git a/material/partials/language/uk.html b/material/partials/language/uk.html deleted file mode 100644 index 78f4f4ece9..0000000000 --- a/material/partials/language/uk.html +++ /dev/null @@ -1,19 +0,0 @@ -{% macro t(key) %}{{ { - "language": "uk", - "clipboard.copy": "Скопіювати в буфер", - "clipboard.copied": "Скопійовано в буфер", - "edit.link.title": "Редагувати сторінку", - "footer.previous": "Назад", - "footer.next": "Вперед", - "meta.comments": "Коментарі", - "meta.source": "Вихідний код", - "search.language": "ru", - "search.placeholder": "Пошук", - "search.result.placeholder": "Розпочніть писати для пошуку", - "search.result.none": "Збігів не знайдено", - "search.result.one": "Знайдено 1 збіг", - "search.result.other": "Знайдено # збігів", - "skip.link.title": "Перейти до змісту", - "source.link.title": "Перейти до репозиторію", - "toc.title": "Зміст" -}[key] }}{% endmacro %} diff --git a/material/partials/language/vi.html b/material/partials/language/vi.html deleted file mode 100644 index c9f3082b76..0000000000 --- a/material/partials/language/vi.html +++ /dev/null @@ -1,18 +0,0 @@ -{% macro t(key) %}{{ { - "language": "vi", - "clipboard.copy": "Sao chép vào bộ nhớ", - "clipboard.copied": "Sao chép xong", - "edit.link.title": "Chỉnh sửa", - "footer.previous": "Trước", - "footer.next": "Sau", - "meta.comments": "Bình luận", - "meta.source": "Mã nguồn", - "search.placeholder": "Tìm kiếm", - "search.result.placeholder": "Nhập để bắt đầu tìm kiếm", - "search.result.none": "Không tìm thấy tài liệu liên quan", - "search.result.one": "1 tài liệu liên quan", - "search.result.other": "# tài liệu liên quan", - "skip.link.title": "Vào thẳng nội dung", - "source.link.title": "Đến kho lưu trữ mã nguồn", - "toc.title": "Mục lục" -}[key] }}{% endmacro %} diff --git a/material/partials/language/zh-Hant.html b/material/partials/language/zh-Hant.html deleted file mode 100644 index 49948333c4..0000000000 --- a/material/partials/language/zh-Hant.html +++ /dev/null @@ -1,20 +0,0 @@ -{% macro t(key) %}{{ { - "language": "zh-Hant", - "clipboard.copy": "拷貝", - "clipboard.copied": "已拷貝", - "edit.link.title": "編輯此頁", - "footer.previous": "上一頁", - "footer.next": "下一頁", - "meta.comments": "評論", - "meta.source": "來源", - "search.language": "jp", - "search.placeholder": "搜尋", - "search.result.placeholder": "鍵入以開始檢索", - "search.result.none": "沒有找到符合條件的結果", - "search.result.one": "找到 1 个符合條件的結果", - "search.result.other": "# 個符合條件的結果", - "search.tokenizer": "[\,\。]+", - "skip.link.title": "跳轉至", - "source.link.title": "前往 Github 倉庫", - "toc.title": "目錄" -}[key] }}{% endmacro %} diff --git a/material/partials/language/zh.html b/material/partials/language/zh.html deleted file mode 100644 index 951663d9f5..0000000000 --- a/material/partials/language/zh.html +++ /dev/null @@ -1,20 +0,0 @@ -{% macro t(key) %}{{ { - "language": "zh", - "clipboard.copy": "复制", - "clipboard.copied": "已复制", - "edit.link.title": "编辑此页", - "footer.previous": "后退", - "footer.next": "前进", - "meta.comments": "评论", - "meta.source": "来源", - "search.language": "jp", - "search.placeholder": "搜索", - "search.result.placeholder": "键入以开始搜索", - "search.result.none": "没有找到符合条件的结果", - "search.result.one": "找到 1 个符合条件的结果", - "search.result.other": "# 个符合条件的结果", - "search.tokenizer": "[\,\。]+", - "skip.link.title": "跳转至", - "source.link.title": "前往 Github 仓库", - "toc.title": "目录" -}[key] }}{% endmacro %} diff --git a/material/partials/nav-item.html b/material/partials/nav-item.html deleted file mode 100644 index cf3a7eb359..0000000000 --- a/material/partials/nav-item.html +++ /dev/null @@ -1,54 +0,0 @@ -{% set class = "md-nav__item" %} -{% if nav_item.active %} - {% set class = "md-nav__item md-nav__item--active" %} -{% endif %} -{% if nav_item.children %} -
  • - {% if nav_item.active %} - - {% else %} - - {% endif %} - - -
  • -{% elif nav_item == page %} -
  • - {% set toc_ = page.toc %} - - {% if toc_ | first is defined and "\x3ch1 id=" in page.content %} - {% set toc_ = (toc_ | first).children %} - {% endif %} - {% if toc_ | first is defined %} - - {% endif %} - - {{ nav_item.title }} - - {% if toc_ | first is defined %} - {% include "partials/toc.html" %} - {% endif %} -
  • -{% else %} -
  • - - {{ nav_item.title }} - -
  • -{% endif %} diff --git a/material/partials/nav.html b/material/partials/nav.html deleted file mode 100644 index a45a425e64..0000000000 --- a/material/partials/nav.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/material/partials/search.html b/material/partials/search.html deleted file mode 100644 index e62f949063..0000000000 --- a/material/partials/search.html +++ /dev/null @@ -1,29 +0,0 @@ -{% import "partials/language.html" as lang with context %} - diff --git a/material/partials/social.html b/material/partials/social.html deleted file mode 100644 index cb68735adf..0000000000 --- a/material/partials/social.html +++ /dev/null @@ -1,9 +0,0 @@ -{% if config.extra.social %} - -{% endif %} diff --git a/material/partials/source.html b/material/partials/source.html deleted file mode 100644 index 34dbc26eff..0000000000 --- a/material/partials/source.html +++ /dev/null @@ -1,30 +0,0 @@ -{% import "partials/language.html" as lang with context %} -{% set platform = config.extra.repo_icon or config.repo_url %} -{% if "github" in platform %} - {% set repo_type = "github" %} -{% elif "gitlab" in platform %} - {% set repo_type = "gitlab" %} -{% elif "bitbucket" in platform %} - {% set repo_type = "bitbucket" %} -{% else %} - {% set repo_type = "" %} -{% endif %} -{% block repo %} - {% if '/latest/' in page.edit_url %} - {% set repo_url = page.edit_url|replace('/latest/', '/' ~ config.extra.version_warning.latest.0 ~ '/') %} - {% else %} - {% set repo_url = page.edit_url %} - {% endif %} - - {% if repo_type %} -
    - - - -
    - {% endif %} -
    - {{ config.repo_name }} -
    -
    -{% endblock %} diff --git a/material/partials/tabs-item.html b/material/partials/tabs-item.html deleted file mode 100644 index 6f644b8406..0000000000 --- a/material/partials/tabs-item.html +++ /dev/null @@ -1,31 +0,0 @@ -{% if nav_item.is_homepage %} -
  • - {% if not page.ancestors | length and nav | selectattr("url", page.url) %} - - {{ nav_item.title }} - - {% else %} - - {{ nav_item.title }} - - {% endif %} -
  • -{% elif nav_item.children and nav_item.children | length > 0 %} - {% set title = title | default(nav_item.title) %} - {% if (nav_item.children | first).children | length > 0 %} - {% set nav_item = nav_item.children | first %} - {% include "partials/tabs-item.html" %} - {% else %} -
  • - {% if nav_item.active %} - - {{ title }} - - {% else %} - - {{ title }} - - {% endif %} -
  • - {% endif %} -{% endif %} diff --git a/material/partials/tabs.html b/material/partials/tabs.html deleted file mode 100644 index e040436bf1..0000000000 --- a/material/partials/tabs.html +++ /dev/null @@ -1,13 +0,0 @@ -{% set class = "md-tabs" %} -{% if page.ancestors | length > 0 %} - {% set class = "md-tabs md-tabs--active" %} -{% endif %} - diff --git a/material/partials/toc.html b/material/partials/toc.html deleted file mode 100644 index 45545c658d..0000000000 --- a/material/partials/toc.html +++ /dev/null @@ -1,58 +0,0 @@ -{% import "partials/language.html" as lang with context %} - diff --git a/material/search/search.js b/material/search/search.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/material/versions.html b/material/versions.html deleted file mode 100644 index 42f6fff4be..0000000000 --- a/material/versions.html +++ /dev/null @@ -1,8 +0,0 @@ -
    - - Read the Docs - - -
    -
    -
    diff --git a/mkdocs.yml b/mkdocs.yml index 690a7fe65e..2b3ccd286e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,27 +1,31 @@ -site_name: Ibexa Developer Documentation +INHERIT: plugins.yml + +site_name: Developer Documentation repo_url: https://github.com/ezsystems/developer-documentation -copyright: "Copyright 1999-2020 Ibexa and others" +site_url: https://doc.ibexa.co/en/latest/ +copyright: "Copyright 1999-2023 Ibexa AS and others" nav: - 'Ibexa Developer Documentation': 'index.md' - Getting started: - 'Requirements': 'getting_started/requirements.md' - 'Install Ibexa DXP': 'getting_started/install_ez_platform.md' + - 'Install on Ibexa Cloud': 'getting_started/install_on_ibexa_cloud.md' + - 'Install on MacOS and Windows': 'getting_started/install-on-mac-os-and-windows.md' - 'First steps': 'getting_started/first_steps.md' - - 'Bundles starter pack': 'getting_started/bundles_starter_pack.md' - 'Troubleshooting': 'getting_started/troubleshooting.md' - Tutorials: - - Ibexa DXP beginner: - - 'Ibexa DXP beginner tutorial': 'tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md' + - Beginner tutorial: + - 'Beginner tutorial': 'tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md' - '1. Get ready': 'tutorials/platform_beginner/1_get_ready.md' - '2. Create the content model': 'tutorials/platform_beginner/2_create_the_content_model.md' - '3. Customize the front page': 'tutorials/platform_beginner/3_customize_the_front_page.md' - - '4. Display a single content item': 'tutorials/platform_beginner/4_display_single_content_item.md' - - '5. Display a list of content items': 'tutorials/platform_beginner/5_display_a_list_of_content_items.md' + - '4. Display a single Content item': 'tutorials/platform_beginner/4_display_single_content_item.md' + - '5. Display a list of Content items': 'tutorials/platform_beginner/5_display_a_list_of_content_items.md' - '6. Improve configuration': 'tutorials/platform_beginner/6_improve_configuration.md' - '7. Embed content': 'tutorials/platform_beginner/7_embed_content.md' - '8. Enable account registration': 'tutorials/platform_beginner/8_enable_account_registration.md' - - Ibexa Experience beginner: - - 'Ibexa Experience beginner tutorial': 'tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md' + - Page and Form tutorial: + - 'Page and Form tutorial': 'tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md' - '1. Get a starter website': 'tutorials/enterprise_beginner/1_get_a_starter_website.md' - '2. Prepare the Landing Page': 'tutorials/enterprise_beginner/2_prepare_the_landing_page.md' - '3. Use existing blocks': 'tutorials/enterprise_beginner/3_use_existing_blocks.md' @@ -33,52 +37,34 @@ nav: - '2. Define Field Type': 'tutorials/generic_field_type/2_define_point2d_field_type.md' - '3. Create a form': 'tutorials/generic_field_type/3_create_form_for_point2d.md' - '4. Introduce a template': 'tutorials/generic_field_type/4_introduce_a_template.md' - - '5. Add a new field': 'tutorials/generic_field_type/5_add_a_field.md' + - '5. Add a new Field': 'tutorials/generic_field_type/5_add_a_field.md' - '6. Implement settings': 'tutorials/generic_field_type/6_settings.md' - '7. Add basic validation': 'tutorials/generic_field_type/7_add_a_validation.md' - '8. Data migration': 'tutorials/generic_field_type/8_data_migration.md' - - Extending Admin UI: - - 'Extending Admin UI': 'tutorials/extending_admin_ui/extending_admin_ui.md' - - '1. Creating a My dashboard tab': 'tutorials/extending_admin_ui/1_creating_a_dashboard_tab.md' - - '2. Creating a top menu item': 'tutorials/extending_admin_ui/2_creating_a_content_list.md' - - '3a. Filtering query results': 'tutorials/extending_admin_ui/3_filtering_the_content_list.md' - - '3b. Adding an edit button': 'tutorials/extending_admin_ui/3b_adding_edit_button_content_list.md' - - '4. Creating a custom tag': 'tutorials/extending_admin_ui/4_adding_a_custom_tag.md' - API: - - REST API guide: - - 'REST API guide': 'api/rest_api_guide.md' - - 'REST API usage': 'api/general_rest_usage.md' - - 'Extending the REST API': 'api/extending_the_rest_api.md' - - REST API reference: - - 'REST API reference': 'https://doc.ezplatform.com/rest-api-reference' - - 'REST API best practices': 'api/rest_api_best_practices.md' - - 'REST API authentication': 'api/rest_api_authentication.md' - - 'Accept header-based REST API response': 'api/creating_custom_rest_api_response.md' - - 'Binary attachments via REST API': 'api/creating_content_with_binary_attachments_via_rest_api.md' - - 'Cross-origin HTTP requests': 'api/making_cross_origin_http_requests.md' - - 'Commerce REST API': 'api/commerce_rest_api.md' - - Public PHP API: - - 'Public PHP API': 'api/public_php_api.md' - - 'Browsing Content': 'api/public_php_api_browsing.md' - - 'Content search': 'api/public_php_api_search.md' - - 'Creating Content': 'api/public_php_api_creating_content.md' - - 'Managing Content': 'api/public_php_api_managing_content.md' - - 'Managing Repository': 'api/public_php_api_managing_repository.md' - - 'Managing Users': 'api/public_php_api_managing_users.md' - - 'URL Service': 'api/public_php_api_url_service.md' - - URL reference: - - 'Id Sort Clause': 'api/url_reference/id_sort_clause.md' - - 'Url Sort Clause': 'api/url_reference/url_sort_clause.md' - - 'MatchAll Criterion': 'api/url_reference/matchall_criterion.md' - - 'MatchNone Criterion': 'api/url_reference/matchnone_criterion.md' - - 'Pattern Criterion': 'api/url_reference/pattern_criterion.md' - - 'SectionId Criterion': 'api/url_reference/sectionid_criterion.md' - - 'SectionIdentifier Criterion': 'api/url_reference/sectionidentifier_criterion.md' - - 'Validity Criterion': 'api/url_reference/validity_criterion.md' - - 'VisibleOnly Criterion': 'api/url_reference/visibleonly_criterion.md' - - 'LogicalAnd Criterion': 'api/url_reference/logicaland_criterion.md' - - 'LogicalNot Criterion': 'api/url_reference/logicalnot_criterion.md' - - 'LogicalOr Criterion': 'api/url_reference/logicalor_criterion.md' + - REST API: + - 'REST API usage': + - 'REST API usage': 'api/rest_api_usage.md' + - 'REST requests': 'api/rest_api_requests.md' + - 'REST responses': 'api/rest_api_responses.md' + - 'Testing REST API': 'api/rest_api_testing.md' + - 'REST API reference': 'api/rest_api_reference/rest_api_reference.html' + - 'Extending REST API': + - 'Adding custom media type': 'api/rest_api_extension_media_type.md' + - 'Creating new REST resource': 'api/rest_api_extension_resource.md' + - 'REST API authentication': 'api/rest_api_authentication.md' + - PHP API: + - 'PHP API': 'api/public_php_api.md' + - 'Browsing content': 'api/public_php_api_browsing.md' + - 'Search API': 'api/public_php_api_search.md' + - 'Creating content': 'api/public_php_api_creating_content.md' + - 'Managing content': 'api/public_php_api_managing_content.md' + - 'Form API': 'api/public_php_api_managing_forms.md' + - 'Repository API': 'api/public_php_api_managing_repository.md' + - 'Data migration API': 'api/public_php_api_managing_migrations.md' + - 'User API': 'api/public_php_api_managing_users.md' + - 'Shop API': 'api/shop_business_api.md' + - 'URL API': 'api/public_php_api_url_service.md' - Field Type: - 'Field Type API': 'api/field_type_api.md' - 'Type and Value': 'api/field_type_type_and_value.md' @@ -86,48 +72,49 @@ nav: - 'Storage': 'api/field_type_storage.md' - 'Validation': 'api/field_type_validation.md' - 'Searching': 'api/field_type_search.md' - - 'Reference': 'api/field_type_reference.md' + - 'Create custom generic Field Type': 'api/field_type/create_custom_generic_field_type.md' + - 'Create custom Field Type comparison': 'api/field_type/create_custom_field_type_comparison.md' + - 'Field Type reference': 'api/field_type_reference.md' + - Field Type reference: + - 'Author Field Type': 'api/field_types_reference/authorfield.md' + - 'BinaryFile Field Type': 'api/field_types_reference/binaryfilefield.md' + - 'Checkbox Field Type': 'api/field_types_reference/checkboxfield.md' + - 'Content query Field Type': 'api/field_types_reference/contentqueryfield.md' + - 'Country Field Type': 'api/field_types_reference/countryfield.md' + - 'DateAndTime Field Type': 'api/field_types_reference/dateandtimefield.md' + - 'Date Field Type': 'api/field_types_reference/datefield.md' + - 'EmailAddress Field Type': 'api/field_types_reference/emailaddressfield.md' + - 'Float Field Type': 'api/field_types_reference/floatfield.md' + - 'Form Field Type': 'api/field_types_reference/formfield.md' + - 'Image Field Type': 'api/field_types_reference/imagefield.md' + - 'ImageAsset Field Type': 'api/field_types_reference/imageassetfield.md' + - 'Integer Field Type': 'api/field_types_reference/integerfield.md' + - 'ISBN Field Type': 'api/field_types_reference/isbnfield.md' + - 'Keyword Field Type': 'api/field_types_reference/keywordfield.md' + - 'MapLocation Field Type': 'api/field_types_reference/maplocationfield.md' + - 'Matrix Field Type': 'api/field_types_reference/matrixfield.md' + - 'Media Field Type': 'api/field_types_reference/mediafield.md' + - 'Null Field Type': 'api/field_types_reference/nullfield.md' + - 'Page Field Type': 'api/field_types_reference/pagefield.md' + - 'Relation Field Type': 'api/field_types_reference/relationfield.md' + - 'RelationList Field Type': 'api/field_types_reference/relationlistfield.md' + - 'RichText Field Type': 'api/field_types_reference/richtextfield.md' + - 'Selection Field Type': 'api/field_types_reference/selectionfield.md' + - 'SesExternalData': 'api/field_types_reference/sesexternaldata.md' + - 'SesProfileData': 'api/field_types_reference/sesprofiledata.md' + - 'SesSelection': 'api/field_types_reference/sesselection.md' + - 'SpecificationsType': 'api/field_types_reference/specificationstype.md' + - 'TextBlock Field Type': 'api/field_types_reference/textblockfield.md' + - 'TextLine Field Type': 'api/field_types_reference/textlinefield.md' + - 'Time Field Type': 'api/field_types_reference/timefield.md' + - 'URL Field Type': 'api/field_types_reference/urlfield.md' + - 'User Field Type': 'api/field_types_reference/userfield.md' + - 'VariantType': 'api/field_types_reference/varianttype.md' - GraphQL: - 'GraphQL': 'api/graphql.md' - 'GraphQL queries': 'api/graphql_queries.md' - 'GraphQL operations': 'api/graphql_operations.md' - 'GraphQL customization': 'api/graphql_customization.md' - - Commerce API: - - Field Type: - - 'Commerce Field Types': 'api/commerce_api/field_type_reference/field_type_reference.md' - - 'ProductSelection': 'api/commerce_api/field_type_reference/productselection.md' - - 'SesExternalData': 'api/commerce_api/field_type_reference/sesexternaldata.md' - - 'SesProfileData': 'api/commerce_api/field_type_reference/sesprofiledata.md' - - 'SesSelection': 'api/commerce_api/field_type_reference/sesselection.md' - - 'SpecificationsType': 'api/commerce_api/field_type_reference/specificationstype.md' - - 'VariantType': 'api/commerce_api/field_type_reference/varianttype.md' - - 'silver.module': 'api/commerce_api/field_type_reference/silver.module.md' - - 'Fields for eCommerce data': 'api/commerce_api/fields_for_ecommerce_data/fields_for_ecommerce_data.md' - - 'ArrayField': 'api/commerce_api/fields_for_ecommerce_data/arrayfield.md' - - 'FileField': 'api/commerce_api/fields_for_ecommerce_data/filefield.md' - - 'ImageField': 'api/commerce_api/fields_for_ecommerce_data/imagefield.md' - - 'PriceField': 'api/commerce_api/fields_for_ecommerce_data/pricefield.md' - - 'StockField': 'api/commerce_api/fields_for_ecommerce_data/stockfield.md' - - 'TextBlockField': 'api/commerce_api/fields_for_ecommerce_data/textblockfield.md' - - 'TextLineField': 'api/commerce_api/fields_for_ecommerce_data/textlinefield.md' - - Helper services: - - 'CountryService': 'api/commerce_api/helper_services/country_service.md' - - 'EzHelperService': 'api/commerce_api/helper_services/ezhelperservice.md' - - 'MailHelperService': 'api/commerce_api/helper_services/mailhelperservice.md' - - 'Mail Logging': 'api/commerce_api/helper_services/mail_logging.md' - - Business API: - - 'Business API': 'api/commerce_api/business_api/business_api.md' - - 'Business API Invocation Service': 'api/commerce_api/business_api/businessapi_invocation_service.md' - - 'BaseOperation': 'api/commerce_api/business_api/baseoperation/baseoperation.md' - - 'addProducts': 'api/commerce_api/business_api/baseoperation/addproducts.md' - - 'getBasket': 'api/commerce_api/business_api/baseoperation/getbasket.md' - - 'loadProducts': 'api/commerce_api/business_api/baseoperation/loadproducts.md' - - 'SisoRestApiBundle': - - 'SisoRestApiBundle': 'api/commerce_api/sisorestapibundle/sisorestapibundle.md' - - 'Basket functions': 'api/commerce_api/sisorestapibundle//basket_functions.md' - - 'Checkout functions': 'api/commerce_api/sisorestapibundle/checkout_functions.md' - - 'Common functions': 'api/commerce_api/sisorestapibundle/common.md' - - 'Customer functions': 'api/commerce_api/sisorestapibundle/customer_functions.md' - Guide: - Project organization: - 'Project organization': 'guide/project_organization.md' @@ -135,57 +122,95 @@ nav: - 'Bundles': 'guide/bundles.md' - 'Content model': 'guide/content_model.md' - Configuration: - - 'Configuration': 'guide/configuration.md' - - 'Dynamic config': 'guide/config_dynamic.md' - - 'Back Office config': 'guide/config_back_office.md' - - 'Repository config': 'guide/config_repository.md' - - 'Connector config': 'guide/config_connector.md' - - Shop configuration: - - 'Shop configuration': 'guide/shop_configuration/shop_configuration.md' - - 'Session handling': 'guide/shop_configuration/session_handling.md' - - 'Storing sessions in Memcache': 'guide/shop_configuration/storing_sessions_in_memcache.md' - - 'Set up a new language': 'guide/shop_configuration/set_up_a_new_language.md' - - 'Required crontab tasks': 'guide/shop_configuration/required_crontab_tasks.md' - - 'Rotation for logfiles': 'guide/shop_configuration/rotation_for_logfiles.md' - - 'Advanced configuration': 'guide/shop_configuration/advanced_configuration.md' - - 'Email server': 'guide/shop_configuration/email_server.md' - - 'Configure ERP connection': 'guide/shop_configuration/configure_erp_connection.md' - - 'Admin management': 'guide/admin_panel.md' + - 'Configuration': 'guide/configuration/configuration.md' + - 'Dynamic configuration': 'guide/configuration/config_dynamic.md' + - 'Repository configuration': 'guide/configuration/config_repository.md' + - 'Admin panel': 'guide/admin_panel.md' - Content rendering: - - 'Content rendering': 'guide/content_rendering.md' - - 'Controllers': 'guide/controllers.md' - - 'Templates': 'guide/templates.md' - - 'Design engine': 'guide/design_engine.md' - - 'Page rendering': 'guide/page_rendering.md' - - 'Displaying Content children': 'guide/displaying_children_of_a_content_item.md' - - 'Retrieving root Location': 'guide/retrieving_root_location.md' - - 'Twig functions reference': 'guide/twig_functions_reference.md' - - Shop templates: - - 'Shop templates': 'guide/shop_templates/shop_templates.md' - - 'Reusable address template': 'guide/shop_templates/reusable_address_template.md' - - 'Reusable message template': 'guide/shop_templates/reusable_message_template.md' - - 'Template resolver': 'guide/shop_templates/template_resolver.md' - - 'Twig extension': 'guide/shop_templates/twig_extension.md' - - 'st_tag selector': 'guide/shop_templates/st_tag_selector.md' - - 'Design engine in the shop': 'guide/shop_templates/design_engine.md' - - 'Setting up new project design': 'guide/shop_templates/set_up_new_project_design.md' - - 'Product images': 'guide/shop_templates/product_images.md' + - Render content: + - 'Render content': 'guide/content_rendering/render_content/render_content.md' + - 'Render Page': 'guide/content_rendering/render_content/render_page.md' + - 'Render product': 'guide/content_rendering/render_content/render_product.md' + - Templates: + - 'Templates': 'guide/content_rendering/templates/templates.md' + - 'Template configuration': 'guide/content_rendering/templates/template_configuration.md' + - 'View matcher reference': 'guide/content_rendering/templates/view_matcher_reference.md' + - 'Create custom view matcher': 'guide/content_rendering/templates/custom_view_matcher.md' + - 'Shop templates': 'guide/content_rendering/templates/overriding_shop_templates.md' + - 'Assets': 'guide/content_rendering/assets.md' + - 'Image variations': 'guide/content_rendering/image_variations.md' + - Twig functions reference: + - 'Twig functions reference': 'guide/content_rendering/twig_function_reference/twig_functions_reference.md' + - 'Content Twig functions': 'guide/content_rendering/twig_function_reference/content_twig_functions.md' + - 'Field Twig functions': 'guide/content_rendering/twig_function_reference/field_twig_functions.md' + - 'Product Twig functions': 'guide/content_rendering/twig_function_reference/product_twig_functions.md' + - 'Image Twig functions': 'guide/content_rendering/twig_function_reference/image_twig_functions.md' + - 'URL Twig functions': 'guide/content_rendering/twig_function_reference/url_twig_functions.md' + - 'Date Twig filters': 'guide/content_rendering/twig_function_reference/date_twig_filters.md' + - 'Other Twig filters': 'guide/content_rendering/twig_function_reference/other_twig_filters.md' + - 'URLs and routes': 'guide/content_rendering/urls_and_routes.md' + - Design engine: + - 'Design engine': 'guide/content_rendering/design_engine/design_engine.md' + - 'Add new design': 'guide/content_rendering/design_engine/add_new_design.md' + - Queries and controllers: + - 'Content queries': 'guide/content_rendering/queries_and_controllers/content_queries.md' + - 'Built-in Query types': 'guide/content_rendering/queries_and_controllers/built-in_query_types.md' + - 'Create custom Query type': 'guide/content_rendering/queries_and_controllers/custom_query_type.md' + - 'Controllers': 'guide/content_rendering/queries_and_controllers/controllers.md' + - Embed and list content: + - 'List content': 'guide/content_rendering/embed_and_list_content/list_content.md' + - 'Embed content': 'guide/content_rendering/embed_and_list_content/embed_content.md' + - 'Embed product': 'guide/content_rendering/embed_and_list_content/embed_product.md' + - 'Render images': 'guide/content_rendering/embed_and_list_content/render_images.md' + - Layout: + - 'Add breadcrumbs': 'guide/content_rendering/layout/add_breadcrumbs.md' + - 'Add forgot password option': 'guide/content_rendering/layout/add_forgot_password.md' + - 'Add login form': 'guide/content_rendering/layout/add_login_form.md' + - 'Add navigation menu': 'guide/content_rendering/layout/add_menu.md' + - 'Create user registration form': 'guide/content_rendering/layout/add_register_user_template.md' + - 'Customize basket': 'guide/content_rendering/layout/customize_basket.md' - Content management: - 'Content management': 'guide/content_management.md' - - 'Images': 'guide/images.md' - - 'File management': 'guide/file_management.md' + - Images: + - 'Images': 'guide/images/images.md' + - 'Configure Image Editor': 'guide/images/image_editor.md' + - 'Extend Image Editor': 'guide/images/extending_image_editor.md' + - 'Add Image Asset': 'guide/images/config_connector.md' + - Rich Text: + - 'Extend Online Editor': 'extending/extending_online_editor.md' + - 'Create Online Editor button': 'extending/online_editor_button.md' + - 'Create Online Editor plugin': 'extending/online_editor_plugin.md' + - 'Create custom RichText block': 'extending/richtext_block.md' + - File management: + - 'File management': 'guide/file_management/file_management.md' + - 'Binary and Media download': 'guide/file_management/binary_media_download.md' + - 'File URL handling': 'guide/file_management/handling_file_url.md' + - Page: + - 'Page blocks': 'guide/page/page_blocks.md' + - 'Page block attributes': 'guide/page/page_block_attributes.md' + - 'Page block validators': 'guide/page/page_block_validators.md' + - 'Create custom Page block': 'guide/page/create_custom_page_block.md' + - Forms: + - 'Forms': 'guide/form_builder/forms.md' + - 'Create custom Form field': 'guide/form_builder/create_custom_form_field.md' + - 'Create Form attribute': 'guide/form_builder/create_form_attribute.md' + - 'Customize email notifications': 'guide/form_builder/customize_email_notifications.md' + - Workflow: + - 'Workflow': 'guide/workflow/workflow.md' + - 'Add custom workflow action': 'guide/workflow/add_custom_workflow_action.md' - 'URL management': 'guide/url_management.md' - - 'RouteReference': 'guide/routereference.md' - 'User-generated content': 'guide/user_generated_content.md' - - 'Editorial workflow': 'guide/workflow.md' - Permissions: - 'Permissions': 'guide/permissions.md' + - 'Permission use cases': 'guide/permission_use_cases.md' - 'Limitations': 'guide/limitations.md' - 'Limitation reference': 'guide/limitation_reference.md' - 'Custom Policies': 'guide/custom_policies.md' - User management: - 'User management': 'guide/user_management/user_management.md' - 'Login and registration': 'guide/user_management/login_and_registration.md' + - 'OAuth authentication': 'guide/user_management/oauth.md' + - 'Add login through external service': 'guide/user_management/login_via_external_service.md' - 'Token': 'guide/user_management/token.md' - 'Delegate function': 'guide/user_management/delegate_function.md' - Customers: @@ -195,21 +220,9 @@ nav: - 'VAT handling': 'guide/customers/vat_handling.md' - 'Customer API': 'guide/customers/customer_api/customer_api.md' - 'Customer profile data': 'guide/customers/customer_api/customer_profile_data.md' - - 'Configuration for customer data': 'guide/customers/customer_api/configuration_for_customer_data.md' - - Newsletter: - - 'Newsletter': 'guide/newsletter/newsletter.md' - - 'Newsletter interface': 'guide/newsletter/newsletter_interface.md' - - 'Newsletter templates': 'guide/newsletter/newsletter_templates.md' - - Shop features: - - 'Shop features': 'guide/shop_features/shop_features.md' - - 'Product management': 'guide/shop_features/content_management.md' - - 'Catalog': 'guide/shop_features/catalog.md' - - 'Shop search': 'guide/shop_features/search.md' - - 'Pricing': 'guide/shop_features/pricing.md' - - 'Payment and shipping': 'guide/shop_features/payment_and_shipping.md' - - 'User management': 'guide/shop_features/user_management.md' - - 'ERP integration': 'guide/shop_features/erp_integration.md' + - 'Customer data configuration': 'guide/customers/customer_api/configuration_for_customer_data.md' - Shop process: + - Shop process: 'guide/shop_process.md' - Basket: - 'Basket': 'guide/basket/basket.md' - 'Basket configuration': 'guide/basket/basket_configuration.md' @@ -254,14 +267,10 @@ nav: - 'Quick order configuration': 'guide/quick_order/quick_order_configuration.md' - 'Quick order templates': 'guide/quick_order/quick_order_templates.md' - Order history: - - 'Orderhistory': 'guide/order_history/orderhistory.md' - - 'ERP messages': 'guide/order_history/order_history_features/orderhistory_erp_messages.md' - - 'Local orders': 'guide/order_history/order_history_features/orderhistory_local_orders.md' - - 'Orderhistory configuration': 'guide/order_history/orderhistory_configuration.md' - - 'Orderhistory API': 'guide/order_history/orderhistory_api.md' - - 'Orderhistory templates': 'guide/order_history/orderhistory_templates.md' - - 'Displaying custom column': 'guide/order_history/displaying_custom_column.md' - - 'Overriding semantic configuration': 'guide/order_history/overriding_semantic_configuration.md' + - 'Order history': 'guide/order_history/order_history.md' + - 'Local orders': 'guide/order_history/order_history_local_orders.md' + - 'Order history configuration': 'guide/order_history/order_history_configuration.md' + - 'Order history templates': 'guide/order_history/order_history_templates.md' - Vouchers: - 'Vouchers': 'guide/vouchers/vouchers.md' - 'Voucher templates': 'guide/vouchers/voucher_templates.md' @@ -269,6 +278,7 @@ nav: - Catalog: - 'Catalog ': 'guide/catalog/catalog.md' - 'Catalog templates': 'guide/catalog/catalog_templates.md' + - 'Product': 'guide/catalog/product.md' - 'Product rendering': 'guide/catalog/product_rendering.md' - 'Product variants': 'guide/catalog/product_variants/product_variants.md' - 'Product variant API': 'guide/catalog/product_variants/product_variant_api.md' @@ -301,72 +311,25 @@ nav: - 'Pre-data processors': 'guide/forms/form_api/predataprocessors.md' - 'Data processor events': 'guide/forms/form_api/data_processor_events.md' - 'Form templates': 'guide/forms/form_templates.md' - # - 'Implement custom one-page form': 'guide/forms/implement_custom_one_page_form.md' - 'Using reCAPTCHA': 'guide/forms/using_recaptcha.md' - ERP integration: - 'ERP integration': 'guide/erp_integration/erp_integration.md' - - 'Connecting shop to ERP': 'guide/erp_integration/connecting_shop_to_erp.md' - - ERP communication: - - 'ERP communication': 'guide/erp_integration/erp_communication/erp_communication.md' - - 'Getting productsdata from ERP': 'guide/erp_integration/erp_communication/guides/getting_product_data_from_the_erp.md' - - 'Creating new ERP message': 'guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/creating_a_new_erp_message.md' - - 'Create standard message': 'guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_standard_message.md' - - 'Create project-specific message': 'guide/erp_integration/erp_communication/guides/creating_a_new_erp_message/create_project_specific_message.md' - - 'Implementing ERP delivery address creation and updates': 'guide/erp_integration/erp_communication/guides/implementing_erp_delivery_address_creation_and_updates.md' - - 'ERP Logging': 'guide/erp_integration/erp_communication/erp_logging.md' - - 'AdditionalLines': 'guide/erp_integration/erp_communication/additionallines.md' - - 'ERP Components': 'guide/erp_integration/erp_communication/erp_components/erp_components.md' - - 'ERP mapping component': 'guide/erp_integration/erp_communication/erp_components/erp_component_mapping.md' - - 'ERP messages component': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_component_messages.md' - - 'ERP Message Instantiation': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_instantiation.md' - - 'ERP Message-Class-Generator': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_class_generator.md' - - 'ERP Message: CalculateSalesOrder / CreateSalesOrder': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_calculatesalesorder_createsalesorder.md' - - 'ERP Message: SelectContact': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_selectcontact.md' - - 'ERP Message: SelectCustomer': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_select_customer.md' - - 'ERP Message: InvoiceDetail': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_invoice_detail.md' - - 'ERP Message: UpdateCustomer': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_message_updatecustomer.md' - - 'ERP Messages: ReadDeliveryAddress, UpdateDeliveryAddress, CreateDeliveryAddress, DeleteDeliveryAddress': 'guide/erp_integration/erp_communication/erp_components/erp_component_messages/erp_messages_addresses.md' - - 'ERP service component': 'guide/erp_integration/erp_communication/erp_components/erp_component_service.md' - - 'ERP transport component': 'guide/erp_integration/erp_communication/erp_components/erp_component_transport.md' - - 'ERP FAQ': 'guide/erp_integration/erp_communication/erp_faq.md' - - 'Adapt the mappings for ERP functions': 'guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/adapt_the_mappings_for_erp_functions.md' - - 'Example UBL for price calculation': 'guide/erp_integration/erp_communication/erp_configuration/adapt_the_mappings_for_erp_functions/example_ubl_for_price_calculation.md' - - 'Configuration for Webservice based ERPs': 'guide/erp_integration/erp_communication/erp_configuration/configuration_for_webservice_based_erps.md' - - 'cURL configuration': 'guide/erp_integration/erp_communication/erp_configuration/curl_configuration.md' - - 'Web.Connector configuration': 'guide/erp_integration/erp_communication/erp_configuration/web_connector_configuration.md' + - 'ERP communication': 'guide/erp_integration/erp_communication.md' + - 'ERP logging': 'guide/erp_integration/erp_logging.md' - 'RemotePriceProvider': 'guide/erp_integration/remotepriceprovider.md' - - Checkout order: - - 'Failed order process': 'guide/erp_integration/checkout_order/failed_order_process/failed_order_process.md' - - 'Lost orders': 'guide/erp_integration/checkout_order/failed_order_process/lost_orders.md' - - 'Order submission': 'guide/erp_integration/checkout_order/order_submission/order_submission.md' - - 'Order format': 'guide/erp_integration/checkout_order/order_submission/order_format.md' + - 'Lost orders': 'guide/erp_integration/lost_orders.md' - Data providers: - 'Data providers': 'guide/data_providers/data_providers.md' - - 'Content model data provider': 'guide/data_providers/content_model_dataprovider.md' - - 'Accessing data provider via PHP': 'guide/data_providers/access_dataprovider_via_php.md' + - 'Repository data provider': 'guide/data_providers/repository_data_provider.md' - eContent: - 'eContent': 'guide/econtent/econtent.md' - - 'eContent database model': 'guide/econtent/econtent_features/econtent_database_model.md' - - 'Catalog segmentation': 'guide/econtent/econtent_features/catalog_segmentation.md' - - 'Variants in eContent': 'guide/econtent/econtent_features/variants_in_econtent.md' - - 'Indexing eContent data': 'guide/econtent/econtent_features/indexing_econtent_data/indexing_econtent_data.md' - - 'Solr cores for eContent': 'guide/econtent/econtent_features/indexing_econtent_data/solr_cores_for_econtent.md' - - 'eContent indexer': 'guide/econtent/econtent_features/indexing_econtent_data/econtent_indexer.md' - - 'Staging system': 'guide/econtent/econtent_features/staging_system.md' - - 'eContent Back Office': 'guide/econtent/econtent_features/econtent_back_office.md' - - 'eContent API': 'guide/econtent/econtent_api.md' - - 'eContent FAQ': 'guide/econtent/econtent_faq.md' - - 'Extending EcontentCatalogFactory': 'guide/econtent/econtent_cookbook/extending_econtentcatalogfactory.md' - - 'Custom sorting handler for eContent': 'guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_sorting_handler_for_econtent.md' - - 'Solr Minimum Should Match': 'guide/econtent/econtent_cookbook/econtent_search_cookbook/solr_minimum_should_match.md' - - 'Custom indexer plugin for eContent': 'guide/econtent/econtent_cookbook/econtent_search_cookbook/custom_indexer_plugin_for_econtent.md' - - 'Import products (API)': 'guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_products_api.md' - - 'Importing and indexing data (SQL)': 'guide/econtent/econtent_cookbook/econtent_how_to_import_products/importing_and_indexing_data_sql.md' - 'eContent configuration': 'guide/econtent/econtent_configuration.md' + - 'Indexing eContent data': 'guide/econtent/indexing_econtent_data.md' + - 'Staging system': 'guide/econtent/staging_system.md' - Search: - 'Search': 'guide/search/search.md' - - 'Search Criteria reference': 'guide/search/search_criteria_reference.md' - Search Criteria reference: + - 'Search Criteria reference': 'guide/search/criteria_reference/search_criteria_reference.md' - 'Ancestor': 'guide/search/criteria_reference/ancestor_criterion.md' - 'ContentId': 'guide/search/criteria_reference/contentid_criterion.md' - 'ContentTypeGroupId': 'guide/search/criteria_reference/contenttypegroupid_criterion.md' @@ -404,8 +367,8 @@ nav: - 'LogicalAnd Criterion': 'guide/search/criteria_reference/logicaland_criterion.md' - 'LogicalNot Criterion': 'guide/search/criteria_reference/logicalnot_criterion.md' - 'LogicalOr Criterion': 'guide/search/criteria_reference/logicalor_criterion.md' - - 'Sort Clause reference': 'guide/search/sort_clause_reference.md' - - Sort Clause Reference: + - Sort Clause reference: + - 'Sort Clause reference': 'guide/search/sort_clause_reference/sort_clause_reference.md' - 'ContentId': 'guide/search/sort_clause_reference/contentid_sort_clause.md' - 'ContentName': 'guide/search/sort_clause_reference/contentname_sort_clause.md' - 'ContentTranslatedName': 'guide/search/sort_clause_reference/contenttranslatedname_sort_clause.md' @@ -427,12 +390,13 @@ nav: - 'SectionName': 'guide/search/sort_clause_reference/sectionname_sort_clause.md' - 'UserLogin': 'guide/search/sort_clause_reference/userlogin_sort_clause.md' - 'Visibility': 'guide/search/sort_clause_reference/visibility_sort_clause.md' - - 'Aggregation reference': 'guide/search/aggregation_reference.md' - Aggregation reference: + - 'Aggregation reference': 'guide/search/aggregation_reference/aggregation_reference.md' - 'ContentTypeTermAggregation': 'guide/search/aggregation_reference/contenttypeterm_aggregation.md' - 'ContentTypeGroupTermAggregation': 'guide/search/aggregation_reference/contenttypegroupterm_aggregation.md' - 'DateMetadataRangeAggregation': 'guide/search/aggregation_reference/datemetadatarange_aggregation.md' - 'LanguageTermAggregation': 'guide/search/aggregation_reference/languageterm_aggregation.md' + - 'LocationChildrenTermAggregation': 'guide/search/aggregation_reference/locationchildrenterm_aggregation.md' - 'ObjectStateTermAggregation': 'guide/search/aggregation_reference/objectstateterm_aggregation.md' - 'RawRangeAggregation': 'guide/search/aggregation_reference/rawrange_aggregation.md' - 'RawStatsAggregation': 'guide/search/aggregation_reference/rawstats_aggregation.md' @@ -453,11 +417,31 @@ nav: - 'KeywordTermAggregation': 'guide/search/aggregation_reference/keywordterm_aggregation.md' - 'SelectionTermAggregation': 'guide/search/aggregation_reference/selectionterm_aggregation.md' - 'TimeRangeAggregation': 'guide/search/aggregation_reference/timerange_aggregation.md' - - 'Searching in trash reference': 'guide/search/search_in_trash_reference.md' + - URL search reference: + - 'URL search reference': 'guide/search/url_search_reference/url_search_reference.md' + - 'Id Sort Clause': 'guide/search/url_search_reference/id_url_sort_clause.md' + - 'Url Sort Clause': 'guide/search/url_search_reference/url_url_sort_clause.md' + - 'MatchAll Criterion': 'guide/search/url_search_reference/matchall_url_criterion.md' + - 'MatchNone Criterion': 'guide/search/url_search_reference/matchnone_url_criterion.md' + - 'Pattern Criterion': 'guide/search/url_search_reference/pattern_url_criterion.md' + - 'SectionId Criterion': 'guide/search/url_search_reference/sectionid_url_criterion.md' + - 'SectionIdentifier Criterion': 'guide/search/url_search_reference/sectionidentifier_url_criterion.md' + - 'Validity Criterion': 'guide/search/url_search_reference/validity_url_criterion.md' + - 'VisibleOnly Criterion': 'guide/search/url_search_reference/visibleonly_url_criterion.md' + - 'LogicalAnd Criterion': 'guide/search/url_search_reference/logicaland_url_criterion.md' + - 'LogicalNot Criterion': 'guide/search/url_search_reference/logicalnot_url_criterion.md' + - 'LogicalOr Criterion': 'guide/search/url_search_reference/logicalor_url_criterion.md' + - 'Search in trash reference': 'guide/search/search_in_trash_reference.md' - 'Elasticsearch search engine': 'guide/search/elastic.md' - - 'Elasticsearch extensibility': 'guide/search/extend_elasticsearch.md' - 'Solr search engine': 'guide/search/solr.md' - - 'Other search engines': 'guide/search/search_engines.md' + - Extend search: + - 'Create custom Search Criterion': 'guide/search/extensibility/create_custom_search_criterion.md' + - 'Create custom Sort Clause': 'guide/search/extensibility/create_custom_sort_clause.md' + - 'Create custom Aggregation': 'guide/search/extensibility/create_custom_aggregation.md' + - 'Solr document field mappers': 'guide/search/extensibility/solr_document_field_mappers.md' + - 'Index custom Elasticsearch data': 'guide/search/extensibility/index_custom_elasticsearch_data.md' + - 'Customize Elasticsearch index structure': 'guide/search/extensibility/customize_elasticsearch_index_structure.md' + - 'Manipulate Elasticsearch query': 'guide/search/extensibility/manipulate_elasticsearch_query.md' - Shop search: - 'Shop search': 'guide/search/shop_search/shop_search.md' - 'Search templates': 'guide/search/shop_search/search_templates.md' @@ -468,34 +452,29 @@ nav: - 'Search synonyms': 'guide/search/shop_search/search_synonyms.md' - 'Autosuggestion': 'guide/search/shop_search/search_autosuggest.md' - 'Solr spellcheck': 'guide/search/shop_search/using_solr_spellcheck.md' - # - Extending search: - # - 'Adding elements to autosuggestion': 'guide/search/shop_search/extending_search/adding_elements_to_autosuggestion.md' - # - 'Indexing file content': 'guide/search/shop_search/extending_search/indexing_file_content.md' - # - 'Indexing email data': 'guide/search/shop_search/extending_search/indexing_email_data.md' - # - 'Implement custom search': 'guide/search/shop_search/extending_search/custom_search.md' - # - 'Implement custom search condition': 'guide/search/shop_search/extending_search/custom_search_conditions.md' - # - 'Implement custom sorting option for search': 'guide/search/shop_search/extending_search/custom_sorting_options.md' - # - 'Modifying the search query': 'guide/search/shop_search/extending_search/modifying_the_search_query.md' - # - 'Implement an indexer plugin for custom Field Types': 'guide/search/shop_search/extending_search/indexer_plugin_for_custom_field_types.md' - Multisite: - - 'Multisite': 'guide/multisite.md' - - 'Site Factory': 'guide/site_factory.md' - - 'SiteAccess': 'guide/siteaccess.md' - - 'SiteAccess Matching': 'guide/siteaccess_matching.md' - - 'Multi-language SiteAccesses': 'guide/multi_language_siteaccesses.md' + - 'Multisite': 'guide/multisite/multisite.md' + - 'Multisite configuration': 'guide/multisite/multisite_configuration.md' + - 'Set up campaign SiteAccess': 'guide/multisite/set_up_campaign_siteaccess.md' + - 'Set up translation SiteAccess': 'guide/multisite/set_up_translation_siteaccess.md' + - 'Site Factory': 'guide/multisite/site_factory.md' + - 'Site Factory configuration': 'guide/multisite/site_factory_configuration.md' + - 'SiteAccess matching': 'guide/multisite/siteaccess_matching.md' + - 'Injecting SiteAccess': 'guide/multisite/injecting_siteaccess.md' + - 'SiteAccess-aware configuration': 'guide/multisite/siteaccess_aware_configuration.md' - Languages: - 'Languages': 'guide/internationalization.md' - 'Back Office translations': 'guide/back_office_translations.md' - 'Shop translations': 'guide/shop_translations.md' - Personalization: - 'Personalization': 'guide/personalization/personalization.md' - - 'Personalization quickstart': 'guide/personalization/personalization_quickstart.md' + - 'Enable personalization': 'guide/personalization/enabling_personalization.md' + - 'Integrate recommendation service': 'guide/personalization/basic_integration.md' - Developer guide: - 'Tracking API': 'guide/personalization/developer_guide/tracking_api.md' - 'Importing historical user tracking data': 'guide/personalization/developer_guide/importing_historical_user_tracking_data.md' - - 'Tracking with yct.js': 'guide/personalization/developer_guide/tracking_with_yct.md' + - 'Track with yct.js': 'guide/personalization/developer_guide/tracking_with_yct.md' - 'Recommendation API': 'guide/personalization/developer_guide/recommendation_api.md' - - 'Legacy Recommendation API': 'guide/personalization/developer_guide/legacy_recommendation_api.md' - 'Content API': 'guide/personalization/developer_guide/content_api.md' - 'User API': 'guide/personalization/developer_guide/user_api.md' - Best practices: @@ -504,18 +483,41 @@ nav: - 'Recommendation client': 'guide/personalization/recommendation_client.md' - Repository: - 'Repository': 'guide/repository.md' + - 'Request lifecycle': 'guide/request_lifecycle.md' - 'Databases': 'guide/databases.md' + - Data migration: + - 'Data migration': 'guide/data_migration/data_migration.md' + - 'Exporting and importing data': 'guide/data_migration/exporting_and_importing_data.md' + - 'Migration management': 'guide/data_migration/migration_management.md' + - 'Data migration actions': 'guide/data_migration/data_migration_actions.md' + - 'Create data migration action': 'guide/data_migration/create_migration_action.md' + - 'Create data migration step': 'guide/data_migration/create_migration_step.md' + - 'Add data migration matcher': 'guide/data_migration/add_data_migration_matcher.md' + - Event reference: + - 'Event reference': 'guide/repository/event_reference/event_reference.md' + - 'Content events': 'guide/repository/event_reference/content_events.md' + - 'Content Type events': 'guide/repository/event_reference/content_type_events.md' + - 'Location events': 'guide/repository/event_reference/location_events.md' + - 'Language events': 'guide/repository/event_reference/language_events.md' + - 'Section events': 'guide/repository/event_reference/section_events.md' + - 'Object state events': 'guide/repository/event_reference/object_state_events.md' + - 'Role events': 'guide/repository/event_reference/role_events.md' + - 'User events': 'guide/repository/event_reference/user_events.md' + - 'Page events': 'guide/repository/event_reference/page_events.md' + - 'Site events': 'guide/repository/event_reference/site_events.md' + - 'URL events': 'guide/repository/event_reference/url_events.md' + - 'Trash events': 'guide/repository/event_reference/trash_events.md' + - 'Other events': 'guide/repository/event_reference/other_events.md' - Cache: - - 'HTTP cache': 'guide/http_cache.md' + - 'HTTP cache': + - 'HTTP cache': 'guide/cache/http_cache.md' + - 'HTTP cache configuration': 'guide/cache/http_cache_config.md' + - 'Reverse proxy': 'guide/cache/symfony_reverse_proxy.md' + - 'Context-aware HTTP cache': 'guide/cache/context_aware_cache.md' + - 'Content-aware HTTP cache': 'guide/cache/content_aware_cache.md' + - 'Configure and customize Fastly': 'guide/cache/fastly.md' - 'Persistence cache': 'guide/persistence_cache.md' - - Shop caching: - - 'Shop caching': 'guide/cache/cache.md' - - 'Content cache refresh': 'guide/cache/content_cache_refresh/content_cache_refresh.md' - - 'HTTP caching': 'guide/cache/content_cache_refresh/http_caching.md' - - 'Navigation cache': 'guide/cache/navigation_cache.md' - - 'Caching FAQ': 'guide/cache/caching_faq.md' - - 'Translation cache for Text modules': 'guide/cache/translations_cache_for_textmodules.md' - - 'Basketpreview cache and user-specific data': 'guide/cache/basketpreview_cache_and_user_specific_data.md' + - 'Shop caching': 'guide/cache/shop_caching.md' - Clustering: - 'Clustering': 'guide/clustering.md' - 'AWS S3 clustering': 'guide/clustering_aws_s3.md' @@ -525,123 +527,101 @@ nav: - 'Performance': 'guide/performance.md' - 'Environments': 'guide/environments.md' - 'Sessions': 'guide/sessions.md' - - Logging: - - 'Logging': 'guide/logging/logging.md' - - 'Logging API': 'guide/logging/logging_api.md' - - 'Logging FAQ': 'guide/logging/logging_faq.md' - - 'TokenController logging': 'guide/logging/tokencontroller_logging.md' - - 'Job system': 'guide/job_system.md' + - 'Logging': 'guide/logging/logging.md' + - 'Logfile rotation': 'guide/logging/logfile_rotation.md' - Security: - - 'Development Security': 'guide/security.md' + - 'Development security': 'guide/security.md' - 'Security checklist': 'guide/security_checklist.md' - - 'Reporting Issues': 'guide/reporting_issues.md' + - 'Reporting issues': 'guide/reporting_issues.md' - 'Notifications': 'guide/sending_notifications.md' - - 'Service container': 'guide/service_container.md' - - Extending Ibexa DXP: - - Extending Back Office: - - 'Extending Back Office': 'extending/extending_back_office.md' - - 'Extending Dashboard': 'extending/extending_dashboard.md' - - 'Extending Menus': 'extending/extending_menus.md' - - 'Extending tabs': 'extending/extending_tabs.md' - - 'Extending settings': 'extending/extending_settings.md' - - 'Extending date and time': 'extending/extending_date_and_time.md' - - 'Creating custom Page blocks': 'extending/extending_page.md' - - 'Creating custom RichText blocks': 'extending/richtext_block.md' - - 'Creating custom drop-downs': 'extending/creating_custom_dropdowns.md' - - 'Creating custom icons': 'extending/custom_icons.md' + - Extend Back Office: + - 'Back Office': 'extending/extending_back_office.md' + - 'Configuration': 'extending/config_back_office.md' + - 'Content Tree': 'extending/content_tree.md' + - Back Office elements: + - 'Add drop-downs': 'extending/creating_custom_dropdowns.md' + - 'Custom icons': 'extending/custom_icons.md' + - 'Add drag and drop': 'extending/drag_and_drop.md' + - 'Custom components': 'extending/custom_components.md' + - 'Formatting date and time': 'extending/extending_date_and_time.md' - 'Extending thumbnails': 'extending/extending_thumbnails.md' - - 'Injecting custom components': 'extending/custom_components.md' - - 'Extending Form Builder': 'extending/extending_form_builder.md' - - 'Extending Workflow': 'extending/extending_workflow.md' - - 'Extending Calendar': 'extending/customizing_calendar.md' - - 'Extending Online Editor': - - 'Extending Online Editor': 'extending/extending_online_editor.md' - - 'Creating Online Editor button': 'extending/online_editor_button.md' - - 'Creating Online Editor plugin': 'extending/online_editor_plugin.md' - - 'Extending Modules': - - 'Extending UDW': 'extending/extending_udw.md' - - 'Creating a UDW tab': 'extending/adding_tab_to_udw.md' - - 'Extending Multi-file Upload': 'extending/extending_multifile_upload.md' - - 'Extending Sub-items List': 'extending/extending_subitems_list.md' - - 'Creating drag and drop interface': 'extending/drag_and_drop.md' - - 'Field Types': - - 'Creating custom Field Type': 'extending/extending_field_type.md' - - 'Creating custom Field Type comparison': 'extending/custom_fieldtype_comparison.md' + - 'Importing assets from a bundle': 'extending/import_assets_from_bundle.md' + - Back Office tabs: + - 'Back Office tabs': 'extending/tabs/back_office_tabs.md' + - 'Create dashboard tab': 'extending/tabs/add_dashboard_tab.md' + - Back Office menus: + - 'Back Office menus': 'extending/menus/back_office_menus.md' + - 'Add menu item': 'extending/menus/add_menu_item.md' + - 'Add user setting': 'extending/add_user_setting.md' + - 'Customize calendar': 'extending/customize_calendar.md' + - Browser: + - 'Browser': 'extending/extending_udw.md' + - 'Add browser tab': 'extending/adding_tab_to_udw.md' + - 'Multi-file upload': 'extending/extending_multifile_upload.md' + - 'Sub-items list': 'extending/extending_subitems_list.md' + - CDP (Customer Data Platform): + - Customer Data Platform: cdp/cdp.md + - CDP installation: cdp/cdp_installation.md + - CDP activation: cdp/cdp_activation.md - Resources and community: - - 'Resources': 'community_resources/resources.md' - 'Release process and roadmap': 'community_resources/release_process.md' - - 'Support and Maintenance FAQ': 'community_resources/support_maintenance_faq.md' + - 'Support and maintenance FAQ': 'community_resources/support_maintenance_faq.md' + - 'Ibexa DXP PhpStorm plugin': 'community_resources/phpstorm_plugin.md' + - New in documentation: community_resources/new_in_doc.md - Contributing: - - 'Install Ibexa DXP on macOS or Windows': 'community_resources/installing-on-mac-os-and-windows.md' - - 'How to contribute': 'community_resources/contributing.md' - 'Report and follow issues': 'community_resources/report_follow_issues.md' - - 'Contribute code': 'community_resources/code.md' - - 'Development guidelines': 'community_resources/development_guidelines.md' - 'Contribute translations': 'community_resources/translations.md' - - 'Contribute to documentation': 'community_resources/documentation.md' - 'Package structure': 'community_resources/package_structure.md' - # - UI Guidelines: - # - 'Introduction': 'guidelines/Introduction.md' - # - Components: - # - 'Badges': 'guidelines/components/badges.md' - # - 'Buttons': 'guidelines/components/buttons.md' - # - 'Form components': 'guidelines/components/form_components.md' - # - 'Modals': 'guidelines/components/modals.md' - # - 'Pagination': 'guidelines/components/pagination.md' - # - 'Switchers': 'guidelines/components/switchers.md' - # - 'Tables': 'guidelines/components/tables.md' - # - 'Tabs': 'guidelines/components/tabs.md' - # - 'Tooltips': 'guidelines/components/tooltips.md' - # - Resources: - # - 'Typography': 'guidelines/resources/typography.md' - # - 'Colors': 'guidelines/resources/colors.md' - # - 'Icons': 'guidelines/resources/icons.md' - # - Design Principles: - # - 'Philosophy': 'guidelines/design_principles/Philosophy.md' - # - 'Accessibility': 'guidelines/design_principles/Accessibility.md' - - Migrating to Ibexa DXP: - - 'Migrating from eZ Publish Platform': 'migrating/migrating_from_ez_publish_platform.md' - - 'Migrating from eZ Publish': 'migrating/migrating_from_ez_publish.md' - - 'Common issues': 'migrating/common_issues.md' - - Upgrading Ibexa DXP: - - 'Upgrading to eZ Platform v3': 'upgrading/upgrading_to_v3.md' - - '1. Check out a tagged version': 'upgrading/1_check_out_version.md' - - '2. Merge composer.json': 'upgrading/2_merge_composer.md' - - '3. Update the app': 'upgrading/3_upgrade_app.md' - - '4. Upgrade the code': - - '4. Upgrade the code': 'upgrading/4_upgrade_the_code.md' - - '4.1. Upgrade templates': 'upgrading/4_1_upgrade_templates.md' - - '4.2. Upgrade configuration': 'upgrading/4_2_upgrade_configuration.md' - - '4.3. Upgrade Field Types': 'upgrading/4_3_upgrade_field_types.md' - - '4.4. Upgrade Signal Slots': 'upgrading/4_4_upgrade_signal_slots.md' - - '4.5. Upgrade Online Editor': 'upgrading/4_5_upgrade_online_editor.md' - - '4.6. Upgrade workflow': 'upgrading/4_6_upgrade_workflow.md' - - '4.7. Upgrade extended code': 'upgrading/4_7_upgrade_extensions.md' - - '4.8. Upgrade REST': 'upgrading/4_8_upgrade_rest.md' - - '4.9 Other code upgrades': 'upgrading/4_9_upgrade_other.md' - - '5. Upgrade the database': 'upgrading/5_upgrade_database.md' - - '6. Platform.sh changes': 'upgrading/6_platform_sh_changes.md' - - '7. Dump assets': 'upgrading/7_dump_assets.md' - - '8. Commit, test and merge': 'upgrading/8_commit_test_merge.md' - - Updating Ibexa DXP: - - 'Updating Ibexa DXP': 'updating/updating_ez_platform.md' - - '1. Check out a tagged version': 'updating/1_check_out_version.md' - - '2. Merge composer.json': 'updating/2_merge_composer.md' - - '3. Update the app': 'updating/3_update_app.md' - - '4. Update database': - - 'Update database': 'updating/4_update_database.md' - - 'Updating from <1.7': 'updating/4_update_1.7.md' - - 'Updating from <1.13': 'updating/4_update_1.13.md' - - 'Updating from <2.2': 'updating/4_update_2.2.md' - - 'Updating from <2.3': 'updating/4_update_2.3.md' - - 'Updating from <2.4': 'updating/4_update_2.4.md' - - 'Updating from <2.5': 'updating/4_update_2.5.md' - - 'Updating from <3.1': 'updating/4_update_3.1.md' - - 'Updating from <3.2': 'updating/4_update_3.2.md' - - '5. Platform.sh changes': 'updating/5_platform_sh_changes.md' - - '6. Dump assets': 'updating/6_dump_assets.md' - - '7. Commit, test and merge': 'updating/7_commit_test_merge.md' + - Update and migration: + - 'Update Ibexa DXP': 'updating/update_ibexa_dxp.md' + - Update from v1.13 and v2.x: + - 'Update from v1.13 and v2.x': 'updating/from_1.x_2.x/update_from_1.x_2.x.md' + - 'Update app to v2.5': 'updating/from_1.x_2.x/update_app_to_2.5.md' + - 'Update database to v2.5': 'updating/from_1.x_2.x/update_db_to_2.5.md' + - Update from v2.5: + - 'Update from v2.5': 'updating/from_2.5/update_from_2.5.md' + - 'Update to v3.2': 'updating/from_2.5/to_3.2.md' + - Adapt code to v3: + - 'Adapt code to v3': 'updating/from_2.5/adapt_code_to_v3.md' + - '1. Update templates': 'updating/from_2.5/update_code/1_update_templates.md' + - '2. Update configuration': 'updating/from_2.5/update_code/2_update_configuration.md' + - '3. Update Field Types': 'updating/from_2.5/update_code/3_update_field_types.md' + - '4. Update Signal Slots': 'updating/from_2.5/update_code/4_update_signal_slots.md' + - '5. Update Online Editor': 'updating/from_2.5/update_code/5_update_online_editor.md' + - '6. Update workflow': 'updating/from_2.5/update_code/6_update_workflow.md' + - '7. Update extended code': 'updating/from_2.5/update_code/7_update_extensions.md' + - '8. Update REST': 'updating/from_2.5/update_code/8_update_rest.md' + - '9. Other code updates': 'updating/from_2.5/update_code/9_update_other.md' + - 'Update to v3.3': 'updating/from_2.5/to_3.3.md' + - 'Update to latest v3.3': 'updating/from_2.5/to_3.3.latest.md' + - Update from v3.3: + - 'Update to v3.3.latest': 'updating/from_3.3/update_from_3.3.md' + - 'Update to v4.0': 'updating/from_3.3/to_4.0.md' + - Update from v4.0: + - 'Update to v4.1': 'updating/from_4.0/to_4.1.md' + - Update from v4.1: + - 'Update to v4.2': 'updating/from_4.1/update_from_4.1.md' + - Update from v4.2: + - 'Update to v4.3': 'updating/from_4.2/update_from_4.2.md' + - Update from v4.3: + - Update to v4.4: 'updating/from_4.3/update_from_4.3.md' + - Use new Commerce packages: 'updating/from_4.3/update_from_4.3_new_commerce.md' + - Keep old Commerce packages: 'updating/from_4.3/update_from_4.3_old_commerce.md' + - Update from v4.4: + - 'Update to v4.5': 'updating/from_4.4/update_from_4.4.md' + - Migrate to Ibexa DXP: + - 'Migrate from eZ Publish Platform': 'migrating/migrating_from_ez_publish_platform.md' + - 'Migrate from eZ Publish': 'migrating/migrating_from_ez_publish.md' + - 'Common migration issues': 'migrating/common_issues.md' - Releases: + - 'Ibexa DXP v4.5': 'releases/ibexa_dxp_v4.5.md' + - 'Ibexa DXP v4.4': 'releases/ibexa_dxp_v4.4.md' + - 'Ibexa DXP v4.3': 'releases/ibexa_dxp_v4.3.md' + - 'Ibexa DXP v4.2': 'releases/ibexa_dxp_v4.2.md' + - 'Ibexa DXP v4.1': 'releases/ibexa_dxp_v4.1.md' + - 'Ibexa DXP v4.0': 'releases/ibexa_dxp_v4.0.md' + - 'Ibexa DXP v4.0 deprecations and BC breaks': 'releases/ibexa_dxp_v4.0_deprecations.md' + - 'Ibexa DXP v3.3 LTS': 'releases/ibexa_dxp_v3.3.md' - 'Ibexa DXP v3.2': 'releases/ibexa_dxp_v3.2.md' - 'eZ Platform v3.1': 'releases/ez_platform_v3.1.md' - 'eZ Platform v3.0': 'releases/ez_platform_v3.0.md' @@ -660,74 +640,77 @@ nav: - 'eZ Platform v1.8.0': 'releases/ez_platform_v1.8.0.md' - 'eZ Platform v1.7.0 LTS': 'releases/ez_platform_v1.7.0_lts.md' - 'References': 'references.md' -theme: - name: null - custom_dir: 'material' - favicon: 'img/favicon.ico' - language: 'en' - palette: - primary: 'blue grey' - accent: 'deep orange' - - font: false - feature: - tabs: false - search_index_only: false - -# theme_dir: material -plugins: - - search - - macros: - j2_block_start_string: '[[%' - j2_block_end_string: '%]]' - j2_variable_start_string: '[[=' - j2_variable_end_string: '=]]' +theme: + name: material + features: + - content.code.annotate + - content.code.copy + - navigation.tracking + - navigation.top + - content.tabs.link + custom_dir: 'theme' + favicon: 'images/favicon.png' + language: 'en' + palette: + primary: 'blue grey' + accent: 'deep orange' + logo: 'images/ibexa-dxp-logo.svg' extra: - version_warning: - latest: ['3.2'] - previous_lts: ['1.13'] - lts: ['2.5'] - ft: ['3.0','3.1'] - dev: ['master'] append_bootstrap: - '/guidelines/components/' - '/guidelines/resources/' - '/index.md' related_docs: - - title: 'Ibexa Developer Documentation' + - title: 'Developer Documentation' url: '/' - - title: 'Ibexa User Documentation' + - title: 'User Documentation' url: '/projects/userguide' # Global variables + site_display_name: 'Ibexa Documentation' product_name: 'Ibexa DXP' product_name_content: 'Ibexa Content' product_name_exp: 'Ibexa Experience' product_name_com: 'Ibexa Commerce' + # Global variables - latest tag versions + latest_tag_2_5: '2.5.31' + latest_tag_3_3: '3.3.35' + latest_tag_4_0: '4.0.8' + latest_tag_4_1: '4.1.5' + latest_tag_4_2: '4.2.4' + latest_tag_4_3: '4.3.5' + latest_tag_4_4: '4.4.4' + latest_tag_4_5: '4.5.3' + + symfony_doc: 'http://symfony.com/doc/5.4' + user_doc: 'https://doc.ibexa.co/projects/userguide/en/master' extra_css: - fonts/MavenPro.css + - css/codehilite.css + - css/switcher.css - css/custom.css + - css/page-not-found.css + - css/navigation.css - css/docs.switcher.css + - css/jquery-ui.min.css - '//cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css' extra_javascript: - js/jquery.min.js + - js/jquery-ui.min.js - js/custom.js - js/docs.switcher.js - - js/ez-guidelines-switchers.js - - js/ez-guidelines-modals.js - - js/ez-guidelines-tooltips.js - '//cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js' + markdown_extensions: - admonition - - codehilite: - guess_lang: false - linenums: true + - attr_list - def_list - footnotes - meta + - md_in_html - toc: permalink: true - pymdownx.arithmatex @@ -739,7 +722,6 @@ markdown_extensions: - pymdownx.highlight: guess_lang: false linenums: true - css_class: codehilite extend_pygments_lang: - name: php lang: php @@ -748,10 +730,11 @@ markdown_extensions: - pymdownx.magiclink - pymdownx.mark - pymdownx.smartsymbols + - pymdownx.snippets - pymdownx.superfences - - pymdownx.tabbed + - pymdownx.tabbed: + alternate_style: true - pymdownx.tasklist: custom_checkbox: true - pymdownx.tilde - - pymdownx.details - - ez_code_example + - pymdownx.details \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000..60122873a4 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "developer-documentation", + "version": "1.0.0", + "main": "index.js", + "repository": "git@github.com:ezsystems/developer-documentation.git", + "scripts": { + "scss": "node-sass --watch scss -o docs/css" + }, + "dependencies": { + "node-sass": "^9.0.0" + } +} diff --git a/pip_require.txt b/pip_require.txt index 9e1ff37996..7e420a0f38 100644 --- a/pip_require.txt +++ b/pip_require.txt @@ -1 +1 @@ -pip==20.0.2 +pip==21.1 diff --git a/plugins.yml b/plugins.yml new file mode 100644 index 0000000000..e23f966e92 --- /dev/null +++ b/plugins.yml @@ -0,0 +1,452 @@ +plugins: + - search + - autolinks + - macros: + j2_block_start_string: '[[%' + j2_block_end_string: '%]]' + j2_variable_start_string: '[[=' + j2_variable_end_string: '=]]' + - tags + - redirects: + redirect_maps: + 'api/field_types_reference/xmltextfield.md': 'api/field_type_reference.md' + 'extending/extending_image_editor.md': 'guide/images/extending_image_editor.md' + 'extending/reusable_back_office_templates.md': 'extending/extending_back_office.md' + 'guide/configuration/config_connector.md': 'guide/images/config_connector.md' + 'guide/image_editor.md': 'guide/images/image_editor.md' + 'guide/images.md': 'guide/images/images.md' + 'guide/config_back_office.md': 'extending/config_back_office.md' + 'guide/config_connector.md': 'guide/images/config_connector.md' + 'guide/config_dynamic.md': 'guide/configuration/config_dynamic.md' + 'guide/config_repository.md': 'guide/configuration/config_repository.md' + 'guide/configuration.md': 'guide/configuration/configuration.md' + 'guide/shop_configuration.md': 'guide/basket/basket.md' + 'guide/search/extend_solr.md': 'guide/search/solr.md' + 'guide/search/extend_elasticsearch.md': 'guide/search/elastic.md' + + + 'api/php_api/php_api.md': 'api/public_php_api.md' + 'api/rest_api/rest_api_usage/rest_api_usage.md': 'api/rest_api_usage.md' + 'api/rest_api/rest_api_usage/rest_requests.md': 'api/rest_api_requests.md' + 'api/rest_api/rest_api_usage/rest_responses.md': 'api/rest_api_responses.md' + 'api/rest_api/rest_api_usage/testing_rest_api.md': 'api/rest_api_testing.md' + 'api/rest_api/extending_rest_api/adding_custom_media_type.md': 'api/rest_api_extension_media_type.md' + 'api/rest_api/extending_rest_api/creating_new_rest_resource.md': 'api/rest_api_extension_resource.md' + 'api/rest_api/rest_api_authentication.md': 'api/rest_api_authentication.md' + 'api/graphql/graphql.md': 'api/graphql.md' + 'api/graphql/graphql_queries.md': 'api/graphql_queries.md' + 'api/graphql/graphql_operations.md': 'api/graphql_operations.md' + 'api/graphql/graphql_customization.md': 'api/graphql_customization.md' + 'api/event_reference/event_reference.md': 'guide/repository/event_reference/event_reference.md' + 'api/event_reference/content_events.md': 'guide/repository/event_reference/content_events.md' + 'api/event_reference/content_type_events.md': 'guide/repository/event_reference/content_type_events.md' + 'api/event_reference/location_events.md': 'guide/repository/event_reference/location_events.md' + 'api/event_reference/language_events.md': 'guide/repository/event_reference/language_events.md' + 'api/event_reference/section_events.md': 'guide/repository/event_reference/section_events.md' + 'api/event_reference/object_state_events.md': 'guide/repository/event_reference/object_state_events.md' + 'api/event_reference/role_events.md': 'guide/repository/event_reference/role_events.md' + 'api/event_reference/user_events.md': 'guide/repository/event_reference/user_events.md' + 'api/event_reference/page_events.md': 'guide/repository/event_reference/page_events.md' + 'api/event_reference/site_events.md': 'guide/repository/event_reference/site_events.md' + 'api/event_reference/url_events.md': 'guide/repository/event_reference/url_events.md' + 'api/event_reference/trash_events.md': 'guide/repository/event_reference/trash_events.md' + 'api/event_reference/other_events.md': 'guide/repository/event_reference/other_events.md' + 'administration/project_organization/project_organization.md': 'guide/project_organization.md' + 'administration/project_organization/architecture.md': 'guide/architecture.md' + 'administration/project_organization/bundles.md': 'guide/bundles.md' + 'administration/admin_panel.md': 'guide/admin_panel.md' + 'administration/configuration/configuration.md': 'guide/configuration/configuration.md' + 'administration/configuration/dynamic_configuration.md': 'guide/configuration/config_dynamic.md' + 'administration/configuration/repository_configuration.md': 'guide/configuration/config_repository.md' + 'administration/back_office/back_office.md': 'extending/extending_back_office.md' + 'administration/back_office/back_office_configuration.md': 'extending/config_back_office.md' + 'administration/back_office/content_tree.md': 'extending/content_tree.md' + 'administration/back_office/back_office_elements/reusable_components.md': 'extending/extending_back_office.md' + 'administration/back_office/back_office_elements/add_dropdowns.md': 'extending/creating_custom_dropdowns.md' + 'administration/back_office/back_office_elements/custom_icons.md': 'extending/custom_icons.md' + 'administration/back_office/back_office_elements/add_drag_and_drop.md': 'extending/drag_and_drop.md' + 'administration/back_office/back_office_elements/custom_components.md': 'extending/custom_components.md' + 'administration/back_office/back_office_elements/formatting_date_and_time.md': 'extending/extending_date_and_time.md' + 'administration/back_office/back_office_elements/extending_thumbnails.md': 'extending/extending_thumbnails.md' + 'administration/back_office/back_office_elements/importing_assets_from_bundle.md': 'extending/import_assets_from_bundle.md' + 'administration/back_office/back_office_tabs/back_office_tabs.md': 'extending/tabs/back_office_tabs.md' + 'administration/back_office/back_office_tabs/create_dashboard_tab.md': 'extending/tabs/add_dashboard_tab.md' + 'administration/back_office/back_office_menus/back_office_menus.md': 'extending/menus/back_office_menus.md' + 'administration/back_office/back_office_menus/add_menu_item.md': 'extending/menus/add_menu_item.md' + 'administration/back_office/add_user_setting.md': 'extending/add_user_setting.md' + 'administration/back_office/customize_calendar.md': 'extending/customize_calendar.md' + 'administration/back_office/browser/browser.md': 'extending/extending_udw.md' + 'administration/back_office/multifile_upload.md': 'extending/extending_multifile_upload.md' + 'administration/back_office/subitems_list.md': 'extending/extending_subitems_list.md' + 'administration/back_office/notifications.md': 'guide/sending_notifications.md' + 'content_management/content_management.md': 'guide/content_management.md' + 'content_management/content_model.md': 'guide/content_model.md' + 'content_management/images/images.md': 'guide/images/images.md' + 'content_management/images/configure_image_editor.md': 'guide/images/image_editor.md' + 'content_management/images/extend_image_editor.md': 'guide/images/extending_image_editor.md' + 'content_management/images/add_image_asset_from_dam.md': 'guide/images/config_connector.md' + 'content_management/rich_text/extend_online_editor.md': 'extending/extending_online_editor.md' + 'content_management/rich_text/create_custom_richtext_block.md': 'extending/richtext_block.md' + 'content_management/file_management/file_management.md': 'guide/file_management/file_management.md' + 'content_management/file_management/binary_and_media_download.md': 'guide/file_management/binary_media_download.md' + 'content_management/file_management/file_url_handling.md': 'guide/file_management/handling_file_url.md' + 'content_management/pages/page_blocks.md': 'guide/page/page_blocks.md' + 'content_management/pages/page_block_attributes.md': 'guide/page/page_block_attributes.md' + 'content_management/pages/page_block_validators.md': 'guide/page/page_block_validators.md' + 'content_management/pages/create_custom_page_block.md': 'guide/page/create_custom_page_block.md' + 'content_management/forms/forms.md': 'guide/form_builder/forms.md' + 'content_management/forms/form_api.md': 'api/public_php_api_managing_forms.md' + 'content_management/forms/create_custom_form_field.md': 'guide/form_builder/create_custom_form_field.md' + 'content_management/forms/customize_email_notifications.md': 'guide/form_builder/customize_email_notifications.md' + 'content_management/workflow/workflow.md': 'guide/workflow/workflow.md' + 'content_management/workflow/add_custom_workflow_action.md': 'guide/workflow/add_custom_workflow_action.md' + 'content_management/url_management/url_management.md': 'guide/url_management.md' + 'content_management/url_management/url_api.md': 'api/public_php_api_url_service.md' + 'content_management/user_generated_content.md': 'guide/user_generated_content.md' + 'content_management/content_api/browsing_content.md': 'api/public_php_api_browsing.md' + 'content_management/content_api/creating_content.md': 'api/public_php_api_creating_content.md' + 'content_management/content_api/managing_content.md': 'api/public_php_api_managing_content.md' + 'content_management/data_migration/data_migration.md': 'guide/data_migration/data_migration.md' + 'content_management/data_migration/managing_migrations.md': 'guide/data_migration/migration_management.md' + 'content_management/data_migration/data_migration_actions.md': 'guide/data_migration/data_migration_actions.md' + 'content_management/data_migration/create_data_migration_action.md': 'guide/data_migration/create_migration_action.md' + 'content_management/data_migration/create_data_migration_step.md': 'guide/data_migration/create_migration_step.md' + 'content_management/data_migration/add_data_migration_matcher.md': 'guide/data_migration/add_data_migration_matcher.md' + 'content_management/data_migration/data_migration_api.md': 'api/public_php_api_managing_migrations.md' + 'content_management/field_types/field_types.md': 'api/field_type_api.md' + 'content_management/field_types/type_and_value.md': 'api/field_type_type_and_value.md' + 'content_management/field_types/form_and_template.md': 'api/field_type_form_and_template.md' + 'content_management/field_types/field_type_storage.md': 'api/field_type_storage.md' + 'content_management/field_types/field_type_validation.md': 'api/field_type_validation.md' + 'content_management/field_types/field_type_search.md': 'api/field_type_search.md' + 'content_management/field_types/create_custom_generic_field_type.md': 'api/field_type/create_custom_generic_field_type.md' + 'content_management/field_types/create_custom_field_type_comparison.md': 'api/field_type/create_custom_field_type_comparison.md' + 'content_management/field_types/field_type_reference/field_type_reference.md': 'api/field_type_reference.md' + 'content_management/field_types/field_type_reference/authorfield.md': 'api/field_types_reference/authorfield.md' + 'content_management/field_types/field_type_reference/binaryfilefield.md': 'api/field_types_reference/binaryfilefield.md' + 'content_management/field_types/field_type_reference/checkboxfield.md': 'api/field_types_reference/checkboxfield.md' + 'content_management/field_types/field_type_reference/contentqueryfield.md': 'api/field_types_reference/contentqueryfield.md' + 'content_management/field_types/field_type_reference/countryfield.md': 'api/field_types_reference/countryfield.md' + 'content_management/field_types/field_type_reference/dateandtimefield.md': 'api/field_types_reference/dateandtimefield.md' + 'content_management/field_types/field_type_reference/datefield.md': 'api/field_types_reference/datefield.md' + 'content_management/field_types/field_type_reference/emailaddressfield.md': 'api/field_types_reference/emailaddressfield.md' + 'content_management/field_types/field_type_reference/floatfield.md': 'api/field_types_reference/floatfield.md' + 'content_management/field_types/field_type_reference/formfield.md': 'api/field_types_reference/formfield.md' + 'content_management/field_types/field_type_reference/imagefield.md': 'api/field_types_reference/imagefield.md' + 'content_management/field_types/field_type_reference/imageassetfield.md': 'api/field_types_reference/imageassetfield.md' + 'content_management/field_types/field_type_reference/integerfield.md': 'api/field_types_reference/integerfield.md' + 'content_management/field_types/field_type_reference/isbnfield.md': 'api/field_types_reference/isbnfield.md' + 'content_management/field_types/field_type_reference/keywordfield.md': 'api/field_types_reference/keywordfield.md' + 'content_management/field_types/field_type_reference/maplocationfield.md': 'api/field_types_reference/maplocationfield.md' + 'content_management/field_types/field_type_reference/matrixfield.md': 'api/field_types_reference/matrixfield.md' + 'content_management/field_types/field_type_reference/mediafield.md': 'api/field_types_reference/mediafield.md' + 'content_management/field_types/field_type_reference/nullfield.md': 'api/field_types_reference/nullfield.md' + 'content_management/field_types/field_type_reference/pagefield.md': 'api/field_types_reference/pagefield.md' + 'content_management/field_types/field_type_reference/relationfield.md': 'api/field_types_reference/relationfield.md' + 'content_management/field_types/field_type_reference/relationlistfield.md': 'api/field_types_reference/relationlistfield.md' + 'content_management/field_types/field_type_reference/richtextfield.md': 'api/field_types_reference/richtextfield.md' + 'content_management/field_types/field_type_reference/selectionfield.md': 'api/field_types_reference/selectionfield.md' + 'content_management/field_types/field_type_reference/sesexternaldata.md': 'api/field_types_reference/sesexternaldata.md' + 'content_management/field_types/field_type_reference/sesprofiledata.md': 'api/field_types_reference/sesprofiledata.md' + 'content_management/field_types/field_type_reference/sesselection.md': 'api/field_types_reference/sesselection.md' + 'content_management/field_types/field_type_reference/specificationstype.md': 'api/field_types_reference/specificationstype.md' + 'content_management/field_types/field_type_reference/textblockfield.md': 'api/field_types_reference/textblockfield.md' + 'content_management/field_types/field_type_reference/textlinefield.md': 'api/field_types_reference/textlinefield.md' + 'content_management/field_types/field_type_reference/timefield.md': 'api/field_types_reference/timefield.md' + 'content_management/field_types/field_type_reference/urlfield.md': 'api/field_types_reference/urlfield.md' + 'content_management/field_types/field_type_reference/userfield.md': 'api/field_types_reference/userfield.md' + 'templating/render_content/render_content.md': 'guide/content_rendering/render_content/render_content.md' + 'templating/render_content/render_page.md': 'guide/content_rendering/render_content/render_page.md' + 'templating/render_content/render_product.md': 'guide/content_rendering/render_content/render_product.md' + 'templating/templates/templates.md': 'guide/content_rendering/templates/templates.md' + 'templating/templates/template_configuration.md': 'guide/content_rendering/templates/template_configuration.md' + 'templating/templates/view_matcher_reference.md': 'guide/content_rendering/templates/view_matcher_reference.md' + 'templating/templates/create_custom_view_matcher.md': 'guide/content_rendering/templates/custom_view_matcher.md' + 'templating/assets.md': 'guide/content_rendering/assets.md' + 'templating/image_variations.md': 'guide/content_rendering/image_variations.md' + 'templating/twig_function_reference/twig_function_reference.md': 'guide/content_rendering/twig_function_reference/twig_functions_reference.md' + 'templating/twig_function_reference/content_twig_functions.md': 'guide/content_rendering/twig_function_reference/content_twig_functions.md' + 'templating/twig_function_reference/field_twig_functions.md': 'guide/content_rendering/twig_function_reference/field_twig_functions.md' + 'templating/twig_function_reference/product_twig_functions.md': 'guide/content_rendering/twig_function_reference/product_twig_functions.md' + 'templating/twig_function_reference/image_twig_functions.md': 'guide/content_rendering/twig_function_reference/image_twig_functions.md' + 'templating/twig_function_reference/url_twig_functions.md': 'guide/content_rendering/twig_function_reference/url_twig_functions.md' + 'templating/twig_function_reference/date_twig_filters.md': 'guide/content_rendering/twig_function_reference/date_twig_filters.md' + 'templating/twig_function_reference/other_twig_filters.md': 'guide/content_rendering/twig_function_reference/other_twig_filters.md' + 'templating/urls_and_routes/urls_and_routes.md': 'guide/content_rendering/urls_and_routes.md' + 'templating/design_engine/design_engine.md': 'guide/content_rendering/design_engine/design_engine.md' + 'templating/design_engine/add_new_design.md': 'guide/content_rendering/design_engine/add_new_design.md' + 'templating/queries_and_controllers/content_queries.md': 'guide/content_rendering/queries_and_controllers/content_queries.md' + 'templating/queries_and_controllers/built-in_query_types.md': 'guide/content_rendering/queries_and_controllers/built-in_query_types.md' + 'templating/queries_and_controllers/create_custom_query_type.md': 'guide/content_rendering/queries_and_controllers/custom_query_type.md' + 'templating/queries_and_controllers/controllers.md': 'guide/content_rendering/queries_and_controllers/controllers.md' + 'templating/embed_and_list_content/list_content.md': 'guide/content_rendering/embed_and_list_content/list_content.md' + 'templating/embed_and_list_content/embed_content.md': 'guide/content_rendering/embed_and_list_content/embed_content.md' + 'templating/embed_and_list_content/render_images.md': 'guide/content_rendering/embed_and_list_content/render_images.md' + 'templating/layout/add_breadcrumbs.md': 'guide/content_rendering/layout/add_breadcrumbs.md' + 'templating/layout/add_forgot_password_option.md': 'guide/content_rendering/layout/add_forgot_password.md' + 'templating/layout/add_login_form.md': 'guide/content_rendering/layout/add_login_form.md' + 'templating/layout/add_navigation_menu.md': 'guide/content_rendering/layout/add_menu.md' + 'templating/layout/create_user_registration_form.md': 'guide/content_rendering/layout/add_register_user_template.md' + 'templating/layout/customize_basket.md': 'guide/content_rendering/layout/customize_basket.md' + 'commerce/pim/catalog.md': 'guide/catalog/catalog.md' + 'commerce/pim/prices.md': 'guide/pricing/price_engine.md' + 'commerce/pim/bestsellers.md': 'guide/bestsellers.md' + 'commerce/purchasing/basket.md': 'guide/basket/basket.md' + 'commerce/purchasing/wishlist_and_stored_baskets.md': 'guide/basket/wishlist_and_stored_baskets.md' + 'commerce/purchasing/checkout/checkout.md': 'guide/checkout/checkout.md' + 'commerce/purchasing/checkout/order_confirmation.md': 'guide/checkout/order_confirmation.md' + 'commerce/purchasing/checkout/return_process.md': 'guide/checkout/return_process.md' + 'commerce/purchasing/payment/payment.md': 'guide/payment/payment.md' + 'commerce/purchasing/payment/payment_api.md': 'guide/payment/payment_api.md' + 'commerce/purchasing/payment/payment_troubleshooting.md': 'guide/payment/payment_troubleshooting.md' + 'commerce/purchasing/payment/paypal.md': 'guide/payment/paypal.md' + 'commerce/purchasing/quick_order/quick_order.md': 'guide/quick_order/quick_order.md' + 'commerce/purchasing/quick_order/quick_order_configuration.md': 'guide/quick_order/quick_order_configuration.md' + 'commerce/purchasing/order_history/order_history.md': 'guide/order_history/order_history.md' + 'commerce/purchasing/order_history/order_history_configuration.md': 'guide/order_history/order_history_configuration.md' + 'commerce/shop_api.md': 'api/shop_business_api.md' + 'multisite/multisite.md': 'guide/multisite/multisite.md' + 'multisite/multisite_configuration.md': 'guide/multisite/multisite_configuration.md' + 'multisite/set_up_campaign_siteaccess.md': 'guide/multisite/set_up_campaign_siteaccess.md' + 'multisite/set_up_translation_siteaccess.md': 'guide/multisite/set_up_translation_siteaccess.md' + 'multisite/site_factory.md': 'guide/multisite/site_factory.md' + 'multisite/site_factory_configuration.md': 'guide/multisite/site_factory_configuration.md' + 'multisite/siteaccess_matching.md': 'guide/multisite/siteaccess_matching.md' + 'multisite/injecting_siteaccess.md': 'guide/multisite/injecting_siteaccess.md' + 'multisite/siteaccess_aware_configuration.md': 'guide/multisite/siteaccess_aware_configuration.md' + 'multisite/languages/languages.md': 'guide/internationalization.md' + 'multisite/languages/back_office_translations.md': 'guide/back_office_translations.md' + 'multisite/languages/shop_translations.md': 'guide/shop_translations.md' + 'permissions/permissions.md': 'guide/permissions.md' + 'permissions/permission_use_cases.md': 'guide/permission_use_cases.md' + 'permissions/limitations.md': 'guide/limitations.md' + 'permissions/limitation_reference.md': 'guide/limitation_reference.md' + 'permissions/custom_policies.md': 'guide/custom_policies.md' + 'users/user_management.md': 'guide/user_management/user_management.md' + 'users/segment_api.md': 'api/public_php_api_managing_users.md' + 'users/login_and_registration.md': 'guide/user_management/login_and_registration.md' + 'users/oauth_authentication.md': 'guide/user_management/oauth.md' + 'users/add_login_through_external_service.md': 'guide/user_management/login_via_external_service.md' + 'users/token.md': 'guide/user_management/token.md' + 'users/customers/customers.md': 'guide/customers/customers.md' + 'users/customers/customer_api.md': 'guide/customers/customer_api/customer_api.md' + 'users/customers/customer_profile_data.md': 'guide/customers/customer_api/customer_profile_data.md' + 'users/customers/customer_data_configuration.md': 'guide/customers/customer_api/configuration_for_customer_data.md' + 'personalization/personalization.md': 'guide/personalization/personalization.md' + 'personalization/enable_personalization.md': 'guide/personalization/enabling_personalization.md' + 'personalization/integrate_recommendation_service.md': 'guide/personalization/basic_integration.md' + 'personalization/developer_guide/tracking_api.md': 'guide/personalization/developer_guide/tracking_api.md' + 'personalization/developer_guide/importing_historical_user_tracking_data.md': 'guide/personalization/developer_guide/importing_historical_user_tracking_data.md' + 'personalization/developer_guide/tracking_with_yct.md': 'guide/personalization/developer_guide/tracking_with_yct.md' + 'personalization/developer_guide/recommendation_api.md': 'guide/personalization/developer_guide/recommendation_api.md' + 'personalization/developer_guide/content_api.md': 'guide/personalization/developer_guide/content_api.md' + 'personalization/developer_guide/user_api.md': 'guide/personalization/developer_guide/user_api.md' + 'personalization/best_practices/tracking_integration.md': 'guide/personalization/best_practices/tracking_integration.md' + 'personalization/best_practices/recommendation_integration.md': 'guide/personalization/best_practices/recommendation_integration.md' + 'search/search.md': 'guide/search/search.md' + 'search/elasticsearch_search_engine.md': 'guide/search/elastic.md' + 'search/solr_search_engine.md': 'guide/search/solr.md' + 'search/search_api.md': 'api/public_php_api_search.md' + 'search/criteria_reference/search_criteria_reference.md': 'guide/search/criteria_reference/search_criteria_reference.md' + 'search/criteria_reference/ancestor_criterion.md': 'guide/search/criteria_reference/ancestor_criterion.md' + 'search/criteria_reference/contentid_criterion.md': 'guide/search/criteria_reference/contentid_criterion.md' + 'search/criteria_reference/contenttypegroupid_criterion.md': 'guide/search/criteria_reference/contenttypegroupid_criterion.md' + 'search/criteria_reference/contenttypeid_criterion.md': 'guide/search/criteria_reference/contenttypeid_criterion.md' + 'search/criteria_reference/contenttypeidentifier_criterion.md': 'guide/search/criteria_reference/contenttypeidentifier_criterion.md' + 'search/criteria_reference/datemetadata_criterion.md': 'guide/search/criteria_reference/datemetadata_criterion.md' + 'search/criteria_reference/depth_criterion.md': 'guide/search/criteria_reference/depth_criterion.md' + 'search/criteria_reference/field_criterion.md': 'guide/search/criteria_reference/field_criterion.md' + 'search/criteria_reference/fieldrelation_criterion.md': 'guide/search/criteria_reference/fieldrelation_criterion.md' + 'search/criteria_reference/fulltext_criterion.md': 'guide/search/criteria_reference/fulltext_criterion.md' + 'search/criteria_reference/isfieldempty_criterion.md': 'guide/search/criteria_reference/isfieldempty_criterion.md' + 'search/criteria_reference/ismainlocation_criterion.md': 'guide/search/criteria_reference/ismainlocation_criterion.md' + 'search/criteria_reference/isuserbased_criterion.md': 'guide/search/criteria_reference/isuserbased_criterion.md' + 'search/criteria_reference/isuserenabled_criterion.md': 'guide/search/criteria_reference/isuserenabled_criterion.md' + 'search/criteria_reference/languagecode_criterion.md': 'guide/search/criteria_reference/languagecode_criterion.md' + 'search/criteria_reference/locationid_criterion.md': 'guide/search/criteria_reference/locationid_criterion.md' + 'search/criteria_reference/locationremoteid_criterion.md': 'guide/search/criteria_reference/locationremoteid_criterion.md' + 'search/criteria_reference/maplocationdistance_criterion.md': 'guide/search/criteria_reference/maplocationdistance_criterion.md' + 'search/criteria_reference/matchall_criterion.md': 'guide/search/criteria_reference/matchall_criterion.md' + 'search/criteria_reference/matchnone_criterion.md': 'guide/search/criteria_reference/matchnone_criterion.md' + 'search/criteria_reference/objectstateid_criterion.md': 'guide/search/criteria_reference/objectstateid_criterion.md' + 'search/criteria_reference/objectstateidentifier_criterion.md': 'guide/search/criteria_reference/objectstateidentifier_criterion.md' + 'search/criteria_reference/parentlocationid_criterion.md': 'guide/search/criteria_reference/parentlocationid_criterion.md' + 'search/criteria_reference/priority_criterion.md': 'guide/search/criteria_reference/priority_criterion.md' + 'search/criteria_reference/remoteid_criterion.md': 'guide/search/criteria_reference/remoteid_criterion.md' + 'search/criteria_reference/sectionid_criterion.md': 'guide/search/criteria_reference/sectionid_criterion.md' + 'search/criteria_reference/sectionidentifier_criterion.md': 'guide/search/criteria_reference/sectionidentifier_criterion.md' + 'search/criteria_reference/sibling_criterion.md': 'guide/search/criteria_reference/sibling_criterion.md' + 'search/criteria_reference/subtree_criterion.md': 'guide/search/criteria_reference/subtree_criterion.md' + 'search/criteria_reference/useremail_criterion.md': 'guide/search/criteria_reference/useremail_criterion.md' + 'search/criteria_reference/userid_criterion.md': 'guide/search/criteria_reference/userid_criterion.md' + 'search/criteria_reference/userlogin_criterion.md': 'guide/search/criteria_reference/userlogin_criterion.md' + 'search/criteria_reference/usermetadata_criterion.md': 'guide/search/criteria_reference/usermetadata_criterion.md' + 'search/criteria_reference/visibility_criterion.md': 'guide/search/criteria_reference/visibility_criterion.md' + 'search/criteria_reference/logicaland_criterion.md': 'guide/search/criteria_reference/logicaland_criterion.md' + 'search/criteria_reference/logicalnot_criterion.md': 'guide/search/criteria_reference/logicalnot_criterion.md' + 'search/criteria_reference/logicalor_criterion.md': 'guide/search/criteria_reference/logicalor_criterion.md' + 'search/sort_clause_reference/sort_clause_reference.md': 'guide/search/sort_clause_reference/sort_clause_reference.md' + 'search/sort_clause_reference/contentid_sort_clause.md': 'guide/search/sort_clause_reference/contentid_sort_clause.md' + 'search/sort_clause_reference/contentname_sort_clause.md': 'guide/search/sort_clause_reference/contentname_sort_clause.md' + 'search/sort_clause_reference/contenttranslatedname_sort_clause.md': 'guide/search/sort_clause_reference/contenttranslatedname_sort_clause.md' + 'search/sort_clause_reference/contenttypename_sort_clause.md': 'guide/search/sort_clause_reference/contenttypename_sort_clause.md' + 'search/sort_clause_reference/customfield_sort_clause.md': 'guide/search/sort_clause_reference/customfield_sort_clause.md' + 'search/sort_clause_reference/datemodified_sort_clause.md': 'guide/search/sort_clause_reference/datemodified_sort_clause.md' + 'search/sort_clause_reference/datepublished_sort_clause.md': 'guide/search/sort_clause_reference/datepublished_sort_clause.md' + 'search/sort_clause_reference/datetrashed_sort_clause.md': 'guide/search/sort_clause_reference/datetrashed_sort_clause.md' + 'search/sort_clause_reference/depth_sort_clause.md': 'guide/search/sort_clause_reference/depth_sort_clause.md' + 'search/sort_clause_reference/field_sort_clause.md': 'guide/search/sort_clause_reference/field_sort_clause.md' + 'search/sort_clause_reference/id_sort_clause.md': 'guide/search/sort_clause_reference/id_sort_clause.md' + 'search/sort_clause_reference/ismainlocation_sort_clause.md': 'guide/search/sort_clause_reference/ismainlocation_sort_clause.md' + 'search/sort_clause_reference/maplocationdistance_sort_clause.md': 'guide/search/sort_clause_reference/maplocationdistance_sort_clause.md' + 'search/sort_clause_reference/path_sort_clause.md': 'guide/search/sort_clause_reference/path_sort_clause.md' + 'search/sort_clause_reference/priority_sort_clause.md': 'guide/search/sort_clause_reference/priority_sort_clause.md' + 'search/sort_clause_reference/random_sort_clause.md': 'guide/search/sort_clause_reference/random_sort_clause.md' + 'search/sort_clause_reference/score_sort_clause.md': 'guide/search/sort_clause_reference/score_sort_clause.md' + 'search/sort_clause_reference/sectionidentifier_sort_clause.md': 'guide/search/sort_clause_reference/sectionidentifier_sort_clause.md' + 'search/sort_clause_reference/sectionname_sort_clause.md': 'guide/search/sort_clause_reference/sectionname_sort_clause.md' + 'search/sort_clause_reference/userlogin_sort_clause.md': 'guide/search/sort_clause_reference/userlogin_sort_clause.md' + 'search/sort_clause_reference/visibility_sort_clause.md': 'guide/search/sort_clause_reference/visibility_sort_clause.md' + 'search/aggregation_reference/aggregation_reference.md': 'guide/search/aggregation_reference/aggregation_reference.md' + 'search/aggregation_reference/contenttypeterm_aggregation.md': 'guide/search/aggregation_reference/contenttypeterm_aggregation.md' + 'search/aggregation_reference/contenttypegroupterm_aggregation.md': 'guide/search/aggregation_reference/contenttypegroupterm_aggregation.md' + 'search/aggregation_reference/datemetadatarange_aggregation.md': 'guide/search/aggregation_reference/datemetadatarange_aggregation.md' + 'search/aggregation_reference/languageterm_aggregation.md': 'guide/search/aggregation_reference/languageterm_aggregation.md' + 'search/aggregation_reference/locationchildrenterm_aggregation.md': 'guide/search/aggregation_reference/locationchildrenterm_aggregation.md' + 'search/aggregation_reference/objectstateterm_aggregation.md': 'guide/search/aggregation_reference/objectstateterm_aggregation.md' + 'search/aggregation_reference/rawrange_aggregation.md': 'guide/search/aggregation_reference/rawrange_aggregation.md' + 'search/aggregation_reference/rawstats_aggregation.md': 'guide/search/aggregation_reference/rawstats_aggregation.md' + 'search/aggregation_reference/rawterm_aggregation.md': 'guide/search/aggregation_reference/rawterm_aggregation.md' + 'search/aggregation_reference/sectionterm_aggregation.md': 'guide/search/aggregation_reference/sectionterm_aggregation.md' + 'search/aggregation_reference/subtreeterm_aggregation.md': 'guide/search/aggregation_reference/subtreeterm_aggregation.md' + 'search/aggregation_reference/usermetadataterm_aggregation.md': 'guide/search/aggregation_reference/usermetadataterm_aggregation.md' + 'search/aggregation_reference/visibilityterm_aggregation.md': 'guide/search/aggregation_reference/visibilityterm_aggregation.md' + 'search/aggregation_reference/authorterm_aggregation.md': 'guide/search/aggregation_reference/authorterm_aggregation.md' + 'search/aggregation_reference/checkboxterm_aggregation.md': 'guide/search/aggregation_reference/checkboxterm_aggregation.md' + 'search/aggregation_reference/countryterm_aggregation.md': 'guide/search/aggregation_reference/countryterm_aggregation.md' + 'search/aggregation_reference/daterange_aggregation.md': 'guide/search/aggregation_reference/daterange_aggregation.md' + 'search/aggregation_reference/datetimerange_aggregation.md': 'guide/search/aggregation_reference/datetimerange_aggregation.md' + 'search/aggregation_reference/floatrange_aggregation.md': 'guide/search/aggregation_reference/floatrange_aggregation.md' + 'search/aggregation_reference/floatstats_aggregation.md': 'guide/search/aggregation_reference/floatstats_aggregation.md' + 'search/aggregation_reference/integerrange_aggregation.md': 'guide/search/aggregation_reference/integerrange_aggregation.md' + 'search/aggregation_reference/integerstats_aggregation.md': 'guide/search/aggregation_reference/integerstats_aggregation.md' + 'search/aggregation_reference/keywordterm_aggregation.md': 'guide/search/aggregation_reference/keywordterm_aggregation.md' + 'search/aggregation_reference/selectionterm_aggregation.md': 'guide/search/aggregation_reference/selectionterm_aggregation.md' + 'search/aggregation_reference/timerange_aggregation.md': 'guide/search/aggregation_reference/timerange_aggregation.md' + 'search/url_search_reference/url_search_reference.md': 'guide/search/url_search_reference/url_search_reference.md' + 'search/url_search_reference/id_url_sort_clause.md': 'guide/search/url_search_reference/id_url_sort_clause.md' + 'search/url_search_reference/url_url_sort_clause.md': 'guide/search/url_search_reference/url_url_sort_clause.md' + 'search/url_search_reference/matchall_url_criterion.md': 'guide/search/url_search_reference/matchall_url_criterion.md' + 'search/url_search_reference/matchnone_url_criterion.md': 'guide/search/url_search_reference/matchnone_url_criterion.md' + 'search/url_search_reference/pattern_url_criterion.md': 'guide/search/url_search_reference/pattern_url_criterion.md' + 'search/url_search_reference/sectionid_url_criterion.md': 'guide/search/url_search_reference/sectionid_url_criterion.md' + 'search/url_search_reference/sectionidentifier_url_criterion.md': 'guide/search/url_search_reference/sectionidentifier_url_criterion.md' + 'search/url_search_reference/validity_url_criterion.md': 'guide/search/url_search_reference/validity_url_criterion.md' + 'search/url_search_reference/visibleonly_url_criterion.md': 'guide/search/url_search_reference/visibleonly_url_criterion.md' + 'search/url_search_reference/logicaland_url_criterion.md': 'guide/search/url_search_reference/logicaland_url_criterion.md' + 'search/url_search_reference/logicalnot_url_criterion.md': 'guide/search/url_search_reference/logicalnot_url_criterion.md' + 'search/url_search_reference/logicalor_url_criterion.md': 'guide/search/url_search_reference/logicalor_url_criterion.md' + 'search/search_in_trash_reference.md': 'guide/search/search_in_trash_reference.md' + 'search/extensibility/create_custom_search_criterion.md': 'guide/search/extensibility/create_custom_search_criterion.md' + 'search/extensibility/create_custom_sort_clause.md': 'guide/search/extensibility/create_custom_sort_clause.md' + 'search/extensibility/create_custom_aggregation.md': 'guide/search/extensibility/create_custom_aggregation.md' + 'search/extensibility/solr_document_field_mappers.md': 'guide/search/extensibility/solr_document_field_mappers.md' + 'search/extensibility/index_custom_elasticsearch_data.md': 'guide/search/extensibility/index_custom_elasticsearch_data.md' + 'search/extensibility/manipulate_elasticsearch_query.md': 'guide/search/extensibility/manipulate_elasticsearch_query.md' + 'search/shop_search/shop_search.md': 'guide/search/shop_search/shop_search.md' + 'search/shop_search/search_configuration.md': 'guide/search/shop_search/search_configuration.md' + 'search/shop_search/shop_search_api.md': 'guide/search/shop_search/search_api.md' + 'search/shop_search/search_indexing.md': 'guide/search/shop_search/search_indexing.md' + 'search/shop_search/search_synonyms.md': 'guide/search/shop_search/search_synonyms.md' + 'search/shop_search/search_autosuggest.md': 'guide/search/shop_search/search_autosuggest.md' + 'infrastructure_and_maintenance/repository_api.md': 'api/public_php_api_managing_repository.md' + 'infrastructure_and_maintenance/request_lifecycle.md': 'guide/request_lifecycle.md' + 'infrastructure_and_maintenance/databases.md': 'guide/databases.md' + 'infrastructure_and_maintenance/cache/http_cache/http_cache.md': 'guide/cache/http_cache.md' + 'infrastructure_and_maintenance/cache/http_cache/http_cache_configuration.md': 'guide/cache/http_cache_config.md' + 'infrastructure_and_maintenance/cache/http_cache/reverse_proxy.md': 'guide/cache/symfony_reverse_proxy.md' + 'infrastructure_and_maintenance/cache/http_cache/context_aware_cache.md': 'guide/cache/context_aware_cache.md' + 'infrastructure_and_maintenance/cache/http_cache/content_aware_cache.md': 'guide/cache/content_aware_cache.md' + 'infrastructure_and_maintenance/cache/persistence_cache.md': 'guide/persistence_cache.md' + 'infrastructure_and_maintenance/cache/shop_caching.md': 'guide/cache/shop_caching.md' + 'infrastructure_and_maintenance/clustering/clustering.md': 'guide/clustering.md' + 'infrastructure_and_maintenance/clustering/clustering_with_aws_s3.md': 'guide/clustering_aws_s3.md' + 'infrastructure_and_maintenance/devops.md': 'guide/devops.md' + 'infrastructure_and_maintenance/backup.md': 'guide/backup.md' + 'infrastructure_and_maintenance/performance.md': 'guide/performance.md' + 'infrastructure_and_maintenance/environments.md': 'guide/environments.md' + 'infrastructure_and_maintenance/sessions.md': 'guide/sessions.md' + 'infrastructure_and_maintenance/logging.md': 'guide/logging/logging.md' + 'infrastructure_and_maintenance/logfile_rotation.md': 'guide/logging/logfile_rotation.md' + 'infrastructure_and_maintenance/security/development_security.md': 'guide/security.md' + 'infrastructure_and_maintenance/security/security_checklist.md': 'guide/security_checklist.md' + 'infrastructure_and_maintenance/security/reporting_issues.md': 'guide/reporting_issues.md' + 'infrastructure_and_maintenance/support_and_maintenance_faq.md': 'community_resources/support_maintenance_faq.md' + 'resources/release_process_and_roadmap.md': 'community_resources/release_process.md' + 'resources/phpstorm_plugin.md': 'community_resources/phpstorm_plugin.md' + 'resources/contributing/report_and_follow_issues.md': 'community_resources/report_follow_issues.md' + 'resources/contributing/contribute_translations.md': 'community_resources/translations.md' + 'resources/contributing/package_structure.md': 'community_resources/package_structure.md' + 'update_and_migration/update_ibexa_dxp.md': 'updating/update_ibexa_dxp.md' + 'update_and_migration/from_1.x_2.x/update_from_1.x_2.x.md': 'updating/from_1.x_2.x/update_from_1.x_2.x.md' + 'update_and_migration/from_1.x_2.x/update_app_to_2.5.md': 'updating/from_1.x_2.x/update_app_to_2.5.md' + 'update_and_migration/from_1.x_2.x/update_db_to_2.5.md': 'updating/from_1.x_2.x/update_db_to_2.5.md' + 'update_and_migration/from_2.5/update_from_2.5.md': 'updating/from_2.5/update_from_2.5.md' + 'update_and_migration/from_2.5/to_3.2.md': 'updating/from_2.5/to_3.2.md' + 'update_and_migration/from_2.5/adapt_code_to_v3.md': 'updating/from_2.5/adapt_code_to_v3.md' + 'update_and_migration/from_2.5/update_code/1_update_templates.md': 'updating/from_2.5/update_code/1_update_templates.md' + 'update_and_migration/from_2.5/update_code/2_update_configuration.md': 'updating/from_2.5/update_code/2_update_configuration.md' + 'update_and_migration/from_2.5/update_code/3_update_field_types.md': 'updating/from_2.5/update_code/3_update_field_types.md' + 'update_and_migration/from_2.5/update_code/4_update_signal_slots.md': 'updating/from_2.5/update_code/4_update_signal_slots.md' + 'update_and_migration/from_2.5/update_code/5_update_online_editor.md': 'updating/from_2.5/update_code/5_update_online_editor.md' + 'update_and_migration/from_2.5/update_code/6_update_workflow.md': 'updating/from_2.5/update_code/6_update_workflow.md' + 'update_and_migration/from_2.5/update_code/7_update_extensions.md': 'updating/from_2.5/update_code/7_update_extensions.md' + 'update_and_migration/from_2.5/update_code/8_update_rest.md': 'updating/from_2.5/update_code/8_update_rest.md' + 'update_and_migration/from_2.5/update_code/9_update_other.md': 'updating/from_2.5/update_code/9_update_other.md' + 'update_and_migration/from_2.5/to_3.3.md': 'updating/from_2.5/to_3.3.md' + 'update_and_migration/from_2.5/to_3.3.latest.md': 'updating/from_2.5/to_3.3.latest.md' + 'update_and_migration/from_3.3/update_from_3.3.md': 'updating/from_3.3/update_from_3.3.md' + 'update_and_migration/from_3.3/to_4.0.md': 'updating/from_3.3/to_4.0.md' + 'update_and_migration/from_4.0/to_4.1.md': 'updating/from_4.0/to_4.1.md' + 'update_and_migration/from_4.1/update_from_4.1.md': 'updating/from_4.1/update_from_4.1.md' + 'update_and_migration/migrate_to_ibexa_dxp/migrating_from_ez_publish_platform.md': 'migrating/migrating_from_ez_publish_platform.md' + 'update_and_migration/migrate_to_ibexa_dxp/migrating_from_ez_publish.md': 'migrating/migrating_from_ez_publish.md' + 'update_and_migration/migrate_to_ibexa_dxp/common_issues.md': 'migrating/common_issues.md' + 'release_notes/ibexa_dxp_v4.2.md': 'releases/ibexa_dxp_v4.2.md' + 'release_notes/ibexa_dxp_v4.1.md': 'releases/ibexa_dxp_v4.1.md' + 'release_notes/ibexa_dxp_v4.0.md': 'releases/ibexa_dxp_v4.0.md' + 'release_notes/ibexa_dxp_v4.0_deprecations.md': 'releases/ibexa_dxp_v4.0_deprecations.md' + 'release_notes/ibexa_dxp_v3.3.md': 'releases/ibexa_dxp_v3.3.md' + 'release_notes/ibexa_dxp_v3.2.md': 'releases/ibexa_dxp_v3.2.md' + 'release_notes/ez_platform_v3.1.md': 'releases/ez_platform_v3.1.md' + 'release_notes/ez_platform_v3.0.md': 'releases/ez_platform_v3.0.md' + 'release_notes/ez_platform_v3.0_deprecations.md': 'releases/ez_platform_v3.0_deprecations.md' + 'release_notes/ez_platform_v2.5.md': 'releases/ez_platform_v2.5.md' + 'release_notes/ez_platform_v2.4.md': 'releases/ez_platform_v2.4.md' + 'release_notes/ez_platform_v2.3.md': 'releases/ez_platform_v2.3.md' + 'release_notes/ez_platform_v2.2.0.md': 'releases/ez_platform_v2.2.0.md' + 'release_notes/ez_platform_v2.1.0.md': 'releases/ez_platform_v2.1.0.md' + 'release_notes/ez_platform_v2.0.0.md': 'releases/ez_platform_v2.0.0.md' + 'release_notes/ez_platform_v1.13.0_lts.md': 'releases/ez_platform_v1.13.0_lts.md' + 'release_notes/ez_platform_v1.12.0.md': 'releases/ez_platform_v1.12.0.md' + 'release_notes/ez_platform_v1.11.0.md': 'releases/ez_platform_v1.11.0.md' + 'release_notes/ez_platform_v1.10.0.md': 'releases/ez_platform_v1.10.0.md' + 'release_notes/ez_platform_v1.9.0.md': 'releases/ez_platform_v1.9.0.md' + 'release_notes/ez_platform_v1.8.0.md': 'releases/ez_platform_v1.8.0.md' + 'release_notes/ez_platform_v1.7.0_lts.md': 'releases/ez_platform_v1.7.0_lts.md' + 'getting_started/install_ibexa_dxp.md': 'getting_started/install_ez_platform.md' + 'tutorials/beginner_tutorial/beginner_tutorial.md': 'tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform.md' + 'tutorials/beginner_tutorial/1_get_ready.md': 'tutorials/platform_beginner/1_get_ready.md' + 'tutorials/beginner_tutorial/2_create_the_content_model.md': 'tutorials/platform_beginner/2_create_the_content_model.md' + 'tutorials/beginner_tutorial/3_customize_the_front_page.md': 'tutorials/platform_beginner/3_customize_the_front_page.md' + 'tutorials/beginner_tutorial/4_display_single_content_item.md': 'tutorials/platform_beginner/4_display_single_content_item.md' + 'tutorials/beginner_tutorial/5_display_a_list_of_content_items.md': 'tutorials/platform_beginner/5_display_a_list_of_content_items.md' + 'tutorials/beginner_tutorial/6_improve_configuration.md': 'tutorials/platform_beginner/6_improve_configuration.md' + 'tutorials/beginner_tutorial/7_embed_content.md': 'tutorials/platform_beginner/7_embed_content.md' + 'tutorials/beginner_tutorial/8_enable_account_registration.md': 'tutorials/platform_beginner/8_enable_account_registration.md' + 'tutorials/page_and_form_tutorial/page_and_form_tutorial.md': 'tutorials/enterprise_beginner/ez_enterprise_beginner_tutorial_-_its_a_dogs_world.md' + 'tutorials/page_and_form_tutorial/1_get_a_starter_website.md': 'tutorials/enterprise_beginner/1_get_a_starter_website.md' + 'tutorials/page_and_form_tutorial/2_prepare_the_landing_page.md': 'tutorials/enterprise_beginner/2_prepare_the_landing_page.md' + 'tutorials/page_and_form_tutorial/3_use_existing_blocks.md': 'tutorials/enterprise_beginner/3_use_existing_blocks.md' + 'tutorials/page_and_form_tutorial/4_create_a_custom_block.md': 'tutorials/enterprise_beginner/4_create_a_custom_block.md' + 'tutorials/page_and_form_tutorial/5_create_newsletter_form.md': 'tutorials/enterprise_beginner/5_create_newsletter_form.md' diff --git a/requirements.txt b/requirements.txt index 992703ca98..1a56374c65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ -mkdocs>=1.1 -Pygments>=2.4 -pymdown-extensions>=7.0 -Markdown>=3.2.1 -mkdocs-material>=5.1.0 -mkdocs-macros-plugin>=0.4.0 -Jinja2>=2.10 -file:./extension#egg=mdx_ez_code_example +mkdocs==1.5.1 +Pygments==2.15.0 +pymdown-extensions==10.0.1 +Markdown==3.2.1 +mkdocs-material==9.1.1 +mkdocs-macros-plugin==0.7.0 +mkdocs-redirects==1.2.0 +mkdocs-autolinks-plugin==0.7.0 +Jinja2==3.1.2 diff --git a/scss/front-page.scss b/scss/front-page.scss new file mode 100644 index 0000000000..c01dbb0963 --- /dev/null +++ b/scss/front-page.scss @@ -0,0 +1,339 @@ + +$ibexa-color-primary: #ae1164; + +$ibexa-color-danger: #db0032; + +$ibexa-color-info-700: #275799; +$ibexa-color-info-600: #3474cc; + +$ibexa-color-dark: #131c26; +$ibexa-color-dark-400: #71767c; + +$ibexa-color-light: #e0e0e8; +$ibexa-color-light-300: #f3f3f6; +$ibexa-color-light-400: #ececf1; + +$ibexa-color-white: #ffffff; + +$ibexa-color-complementary: #47bedb; +$ibexa-color-complementary-800: #1c4c58; +$ibexa-color-complementary-700: #2b7283; +$ibexa-color-complementary-300: #b5e5f0; +$ibexa-color-complementary-200: #daf2f7; +$ibexa-color-complementary-100: #ecf8fb; + +@mixin list() { + margin: 0 0 0 12px; + list-style: none; + + li { + padding: 4px; + margin: 0; + + & + li { + margin-top: 16px; + } + } +} + +html { + height: 100%; +} + +body { + margin: 0; + font-family: "Noto Sans"; + letter-spacing: 0.12px; + line-height: 1.5; +} + +.md-typeset .front-page { + h1, h2 { + line-height: 1.2; + font-weight: 600; + font-family: "Work Sans"; + } + + h1 { + font-size: 28px; + margin-bottom: 32px; + } + + h2 { + font-size: 22px; + margin-top: 8px; + margin-bottom: 0; + } + + .row { + padding-top: 12px; + padding-bottom: 12px; + margin: 0 -12px; + align-items: stretch; + row-gap: 24px; + } + + .col-12 { + padding-left: 12px; + padding-right: 12px; + } + + .info-tile { + border: 1px solid $ibexa-color-light-400; + border-radius: 12px; + padding: 12px; + display: flex; + align-items: center; + flex: 1; + box-shadow: 4px 22px 47px rgba(52, 116, 204, 0.05); + height: 100%; + + &--link-card { + padding: 24px; + + .info-tile__content { + font-size: 14px; + margin-bottom: 0; + } + + h3 { + svg { + fill: $ibexa-color-primary; + width: 16px; + height: 16px; + margin-right: 4px; + } + + } + + a { + color: $ibexa-color-dark; + text-decoration: underline; + } + } + + &__circle { + width: 72px; + height: 72px; + border-radius: 50%; + margin: 12px; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; + } + + &__content { + padding: 0 4px; + margin-bottom: 18px; + height: 100%; + font-size: 12px; + line-height: 18px; + color: $ibexa-color-dark-400; + display: flex; + flex-direction: column; + justify-content: center; + flex-grow: 1; + + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + + strong { + font-family: "Work Sans"; + display: block; + font-weight: 600; + font-size: 22px; + line-height: 26px; + margin-top: 8px; + color: $ibexa-color-dark; + } + } + + &__details { + display: flex; + justify-content: flex-end; + align-items: center; + font-size: 10px; + color: $ibexa-color-primary; + } + + &__arrow-icon { + width: 13px; + height: 13px; + margin-left: 4px; + fill: $ibexa-color-primary; + } + + h3 { + font-size: 18px; + font-weight: 600; + margin: 0 0 8px; + padding: 4px; + + a { + text-decoration: none; + } + } + + ul { + @include list(); + + margin-left: 24px; + } + } + + a.info-tile { + &:hover { + border-color: $ibexa-color-primary; + box-shadow: 0px 22px 24px 0px rgba(174, 17, 100, 0.10); + text-decoration: none; + } + } + + .notification { + background-color: $ibexa-color-light-300; + border-radius: 12px; + color: $ibexa-color-dark; + font-size: 12px; + padding: 32px; + margin: 30px 40px 40px 0; + position: relative; + + &__content { + padding-right: 260px; + padding-bottom: 32px; + + h2 { + margin: 0 0 14px; + } + + a { + color: $ibexa-color-dark; + text-decoration: underline; + + &:hover { + color: $ibexa-color-info-600; + } + } + } + + &__cta { + a { + display: inline-block; + background: linear-gradient(to right, $ibexa-color-danger 0%, $ibexa-color-primary 100%); + color: $ibexa-color-white; + text-decoration: none; + padding: 15px 16px; + border-radius: 12px; + font-size: 14px; + } + } + + &__image { + position: absolute; + right: -40px; + top: -30px; + height: 272px; + + img { + height: 275px; + } + } + } + + .accordion { + $self: &; + + border: 1px solid $ibexa-color-light-400; + border-radius: 12px; + padding: 12px; + + details { + margin: 0; + padding: 0; + border: none; + + &[open] { + box-shadow: none; + + summary { + border-bottom: 1px solid $ibexa-color-light; + padding-bottom: 16px; + margin-bottom: 16px; + + .accordion__toggler { + svg { + transform: rotate(180deg); + } + } + } + + .row { + padding: 8px; + font-size: 14px; + + a { + color: $ibexa-color-dark; + text-decoration: underline; + + &:hover { + color: $ibexa-color-info-600; + } + } + + ul { + @include list(); + } + } + } + } + + summary { + padding: 4px 8px; + margin: 0; + list-style: none; + border: none; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + + &::before, + &::after, + &::marker, + &::-webkit-details-marker { + display: none; + } + + h2 { + margin: 0; + flex-grow: 1; + } + + .accordion__toggler { + flex-shrink: 0; + + svg { + width: 13px; + height: 13px; + transform-origin: center; + transition: transform 0.3s; + } + } + } + } +} + +@media (min-width: 1900px) { + .bootstrap-iso .col-fhd-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } +} diff --git a/tests/source_files/step4/RandomBlockListener.php b/tests/source_files/step4/RandomBlockListener.php index 9f6a1f617f..5151296c84 100644 --- a/tests/source_files/step4/RandomBlockListener.php +++ b/tests/source_files/step4/RandomBlockListener.php @@ -8,15 +8,15 @@ namespace App\Event; +use eZ\Publish\API\Repository\ContentService; +use eZ\Publish\API\Repository\LocationService; +use eZ\Publish\API\Repository\SearchService; use eZ\Publish\API\Repository\Values\Content\Location; +use eZ\Publish\API\Repository\Values\Content\Query; +use eZ\Publish\API\Repository\Values\Content\Query\Criterion; use EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\BlockRenderEvents; use EzSystems\EzPlatformPageFieldType\FieldType\Page\Block\Renderer\Event\PreRenderEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\SearchService; class RandomBlockListener implements EventSubscriberInterface { @@ -79,7 +79,7 @@ public function onBlockPreRender(PreRenderEvent $event): void } /** - * @param Location $location + * @param \eZ\Publish\API\Repository\Values\Content\Location $location * * @return \eZ\Publish\API\Repository\Values\Content\Content[] * diff --git a/tests/src/ConfigurationEditor.php b/tests/src/ConfigurationEditor.php index 4aad9d9791..da1221107a 100644 --- a/tests/src/ConfigurationEditor.php +++ b/tests/src/ConfigurationEditor.php @@ -1,9 +1,10 @@ -basePath = $basePath; } + /** * @Given I set the Landing Page template configuration in :filePath */ @@ -137,48 +137,48 @@ public function addRandomBlockConfig($filePath): void ]); } - /** - * @Given I create configuration of Form block to :filePath - */ - public function addFormBlockConfig($filePath): void - { - $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $filePath)); - $config->add([ - 'ezplatform_page_fieldtype.blocks.form.views.newsletter.template' => 'blocks/form/newsletter.html.twig', - 'ezplatform_page_fieldtype.blocks.form.views.newsletter.name' => 'Newsletter Form View', - ]); - } - - /** - * @Given I create configuration of Form field to :filePath - */ - public function addFormFieldConfig($configPath): void - { - $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $configPath)); - $config->add([ - 'ezpublish.system.site.field_templates' => [['template' => 'fields/form_field.html.twig', 'priority' => 30,]], - ]); - } - - /** - * @Given I create configuration of Captcha field to :filePath - */ - public function addCaptchaConfig($configPath): void - { - $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $configPath)); - $config->add([ - 'gregwar_captcha.width' => '150', - 'gregwar_captcha.invalid_message' => 'Please, enter again.', - 'gregwar_captcha.reload' => 'true', - 'gregwar_captcha.length' => '4', - ]); - } + /** + * @Given I create configuration of Form block to :filePath + */ + public function addFormBlockConfig($filePath): void + { + $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $filePath)); + $config->add([ + 'ezplatform_page_fieldtype.blocks.form.views.newsletter.template' => 'blocks/form/newsletter.html.twig', + 'ezplatform_page_fieldtype.blocks.form.views.newsletter.name' => 'Newsletter Form View', + ]); + } + + /** + * @Given I create configuration of Form field to :filePath + */ + public function addFormFieldConfig($configPath): void + { + $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $configPath)); + $config->add([ + 'ezpublish.system.site.field_templates' => [['template' => 'fields/form_field.html.twig', 'priority' => 30]], + ]); + } + + /** + * @Given I create configuration of Captcha field to :filePath + */ + public function addCaptchaConfig($configPath): void + { + $config = new ConfigurationEditor(sprintf('%s/%s', $this->basePath, $configPath)); + $config->add([ + 'gregwar_captcha.width' => '150', + 'gregwar_captcha.invalid_message' => 'Please, enter again.', + 'gregwar_captcha.reload' => 'true', + 'gregwar_captcha.length' => '4', + ]); + } /** * @Given I rebuild Webpack Encore assets */ public function rebuildYarn(): void { - shell_exec("bin/console ezplatform:encore:compile"); + shell_exec('bin/console ezplatform:encore:compile'); } } diff --git a/tests/src/Context/ContentContext.php b/tests/src/Context/ContentContext.php index 3b4535c412..5f03214859 100644 --- a/tests/src/Context/ContentContext.php +++ b/tests/src/Context/ContentContext.php @@ -1,27 +1,25 @@ - + Not found +

    Page not found.

    + Back to front page +
    +{% endblock %} diff --git a/theme/main.html b/theme/main.html new file mode 100644 index 0000000000..bb3616a273 --- /dev/null +++ b/theme/main.html @@ -0,0 +1,73 @@ +{% extends "base.html" %} + {% block htmltitle %} + {%- if page and page.meta and page.meta.title -%} + {{ page.meta.title }} ({{ config.extra.site_display_name }}) + {%- elif page and page.title and not page.is_homepage -%} + {{ page.title }} ({{ config.extra.site_display_name }}) + {%- else -%} + {{ config.extra.site_display_name }} + {%- endif -%} + {% endblock %} + + {% block extrahead %} + + + {% endblock %} + {% block site_nav %} + {% if nav %} + + {% endif %} + {% if page.toc %} +
    +
    +
    + {% include "partials/toc.html" %} +
    +
    +
    + {% endif %} + {% endblock %} + + + {% block content %} + {% set ns = namespace(bootstrap_extra_css='') %} + {% for md_file in config.extra.append_bootstrap if 'docs' + md_file in page.edit_url %} + {% set ns.bootstrap_extra_css = ' bootstrap-iso ez-guidelines' %} + {% include "partials/integrations/bootstrap.html" %} + {% endfor %} + +
    + + {% if page.meta.edition %} +
    + {% if page.meta.edition == 'commerce' %} + + {% elif page.meta.edition == 'experience' %} + + + {% endif %} +
    + {% endif %} + {{ page.content }} + {% include "partials/tags.html" %} +
    + {% endblock %} diff --git a/theme/partials/doc_switcher.html b/theme/partials/doc_switcher.html new file mode 100644 index 0000000000..a3d2f970c2 --- /dev/null +++ b/theme/partials/doc_switcher.html @@ -0,0 +1,20 @@ +
    +
    +
    {{ config.site_name }}
    +
    +
    +
    + {% for related_doc in config.extra.related_docs %} + {% if config.site_name == related_doc.title %} + + {% endif %} +
    + {{ related_doc.title }} +
    + {% if config.site_name == related_doc.title %} +
    + {% endif %} + {% endfor %} +
    +
    +
    diff --git a/theme/partials/footer.html b/theme/partials/footer.html new file mode 100644 index 0000000000..c5c0758fd1 --- /dev/null +++ b/theme/partials/footer.html @@ -0,0 +1,10 @@ +{% import "partials/language.html" as lang with context %} + diff --git a/theme/partials/header.html b/theme/partials/header.html new file mode 100644 index 0000000000..ba5ca0931d --- /dev/null +++ b/theme/partials/header.html @@ -0,0 +1,29 @@ +{% set site_url = config.site_url | default(nav.homepage.url, true) | url %} +{% if not config.use_directory_urls and site_url[0] == site_url[-1] == "." %} + {% set site_url = site_url ~ "/index.html" %} +{% endif %} +
    + +
    diff --git a/material/partials/integrations/analytics.html b/theme/partials/integrations/analytics.html similarity index 100% rename from material/partials/integrations/analytics.html rename to theme/partials/integrations/analytics.html diff --git a/theme/partials/integrations/bootstrap.html b/theme/partials/integrations/bootstrap.html new file mode 100644 index 0000000000..2ff801e4c6 --- /dev/null +++ b/theme/partials/integrations/bootstrap.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/theme/partials/nav-item.html b/theme/partials/nav-item.html new file mode 100644 index 0000000000..af5e454d9a --- /dev/null +++ b/theme/partials/nav-item.html @@ -0,0 +1,51 @@ +{% set class = "md-nav__item" %} +{% if nav_item.active %} + {% set class = "md-nav__item md-nav__item--active" %} +{% endif %} +{% if nav_item.children %} +
  • + {% set checked = "checked" if nav_item.active %} + + + +
  • +{% elif nav_item == page %} +
  • + {% set toc = page.toc %} + + {% if toc | first is defined and "\x3ch1 id=" in page.content %} + {% set toc = (toc | first).children %} + {% endif %} + {% if toc | first is defined %} + + {% endif %} + + {{ nav_item.title }} + + {% if toc | first is defined %} + {% include "partials/toc.html" %} + {% endif %} +
  • +{% else %} +
  • + + {{ nav_item.title }} + +
  • +{% endif %} diff --git a/theme/partials/nav.html b/theme/partials/nav.html new file mode 100644 index 0000000000..a63cd746be --- /dev/null +++ b/theme/partials/nav.html @@ -0,0 +1,9 @@ + diff --git a/theme/partials/search.html b/theme/partials/search.html new file mode 100644 index 0000000000..029a80e1f7 --- /dev/null +++ b/theme/partials/search.html @@ -0,0 +1,24 @@ + +{% import "partials/language.html" as lang with context %} + diff --git a/theme/partials/source.html b/theme/partials/source.html new file mode 100644 index 0000000000..76e885dc14 --- /dev/null +++ b/theme/partials/source.html @@ -0,0 +1,7 @@ +{% import "partials/language.html" as lang with context %} + +
    + {% include ".icons/fontawesome/brands/github-alt.svg" %} +
    + View on GitHub +
    diff --git a/theme/partials/toc.html b/theme/partials/toc.html new file mode 100644 index 0000000000..b4e70672aa --- /dev/null +++ b/theme/partials/toc.html @@ -0,0 +1,37 @@ +{% import "partials/language.html" as lang with context %} + diff --git a/theme/partials/version_switcher.html b/theme/partials/version_switcher.html new file mode 100644 index 0000000000..46e435a7e1 --- /dev/null +++ b/theme/partials/version_switcher.html @@ -0,0 +1,9 @@ +
    +
    +
    + Version +
    +
    +
    +
    +
    diff --git a/tools/prepare_guidelines_css/README.md b/tools/prepare_guidelines_css/README.md deleted file mode 100644 index 10bbb8312b..0000000000 --- a/tools/prepare_guidelines_css/README.md +++ /dev/null @@ -1,23 +0,0 @@ -prepare: - -npm install -g less - -run: - -sh build.sh - -Copy bootstrap-iso.css and ez-guidelines.css to docs/css/ directory - -# tmp: -delete @font-face's for 'Maven Pro' from ez-guidelines.css - -# tmp2: - -bootstrap-iso.css: - -Find all instance of: .bootstrap-iso body and .bootstrap-iso html -Replace with: .bootstrap-iso - -ez-guidelines.css: - -.ez-guidelines body -> .ez-guidelines diff --git a/tools/prepare_guidelines_css/build.sh b/tools/prepare_guidelines_css/build.sh deleted file mode 100755 index 2d189a81e9..0000000000 --- a/tools/prepare_guidelines_css/build.sh +++ /dev/null @@ -1,5 +0,0 @@ -lessc prefix_bootstrap.less bootstrap-iso.css -lessc prefix_ez.less ez-guidelines.css - -echo "Build OK" -echo "Copy bootstrap-iso.css and ez-guidelines.css to docs/css/ directory" diff --git a/tools/prepare_guidelines_css/prefix_bootstrap.less b/tools/prepare_guidelines_css/prefix_bootstrap.less deleted file mode 100644 index 3fe702930a..0000000000 --- a/tools/prepare_guidelines_css/prefix_bootstrap.less +++ /dev/null @@ -1,3 +0,0 @@ -.bootstrap-iso { - @import (less) 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css'; -} diff --git a/tools/prepare_guidelines_css/prefix_ez.less b/tools/prepare_guidelines_css/prefix_ez.less deleted file mode 100644 index faead405f3..0000000000 --- a/tools/prepare_guidelines_css/prefix_ez.less +++ /dev/null @@ -1,23 +0,0 @@ -.ez-guidelines { - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/guidelines.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/badges.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/buttons.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/form.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/modals.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/tabs.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/contentbox.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/tables.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/components/tooltips.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/resources/typography.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/resources/colors.css'; - @import (less) 'https://ezsystems.github.io/ui-guidelines/css/resources/icons.css'; - - @font-face { - font-family: 'ez-icons'; - src: url('https://ezsystems.github.io/ui-guidelines/fonts/ez-icons.ttf?nclra3') format('truetype'), - url('https://ezsystems.github.io/ui-guidelines/fonts/ez-icons.woff?nclra3') format('woff'), - url('https://ezsystems.github.io/ui-guidelines/fonts/ez-icons.svg?nclra3#ez-icons') format('svg'); - font-weight: normal; - font-style: normal; - } -} diff --git a/tools/raml2html/.gitignore b/tools/raml2html/.gitignore new file mode 100644 index 0000000000..813afa9a39 --- /dev/null +++ b/tools/raml2html/.gitignore @@ -0,0 +1,4 @@ +var +vendor +composer.lock +.php_cs.cache diff --git a/tools/raml2html/.php_cs b/tools/raml2html/.php_cs new file mode 100644 index 0000000000..17d70332ee --- /dev/null +++ b/tools/raml2html/.php_cs @@ -0,0 +1,8 @@ +setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__ . '/src') + ->in(__DIR__ . '/tests') + ->files()->name('*.php') +); diff --git a/tools/raml2html/README.md b/tools/raml2html/README.md new file mode 100644 index 0000000000..4dc562afaa --- /dev/null +++ b/tools/raml2html/README.md @@ -0,0 +1,18 @@ +# raml2html + +Tool for generating static HTML from RAML definitions. + +## Installation + +Install required dependencies before use. Go to raml2html root directory and run: + +``` +composer install +``` + +To generate static HTML from RAML definitions, use the following code: + + +```sh +php tools/raml2html/raml2html.php build --non-standard-http-methods=COPY,MOVE,PUBLISH,SWAP -t default -o docs/api/rest_api_reference/output/ docs/api/rest_api_reference/input/ez.raml +``` diff --git a/tools/raml2html/composer.json b/tools/raml2html/composer.json new file mode 100644 index 0000000000..adc9a1f5b3 --- /dev/null +++ b/tools/raml2html/composer.json @@ -0,0 +1,39 @@ +{ + "name": "ezsystems/raml2html", + "type": "project", + "license": "MIT", + "authors": [ + { + "name": "Adam Wójs", + "email": "adam@wojs.pl" + } + ], + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/adamwojs/raml-php-parser" + } + ], + "require": { + "php": "^7.4", + "symfony/console": "^4.2", + "raml-org/raml-php-parser": "dev-master", + "twig/twig": "^2.0", + "symfony/filesystem": "^4.2", + "ramsey/uuid": "^3.9", + "twig/markdown-extra": "^3.5", + "league/commonmark": "^2.3" + }, + "require-dev": { + "symfony/var-dumper": "^4.2", + "ezsystems/ezplatform-code-style": "^0.1.0" + }, + "autoload": { + "psr-4": { + "EzSystems\\Raml2Html\\": "src" + } + }, + "scripts": { + "fix-cs": "php-cs-fixer fix -v --show-progress=estimating" + } +} diff --git a/tools/raml2html/raml2html.php b/tools/raml2html/raml2html.php new file mode 100644 index 0000000000..cb1c01d3f1 --- /dev/null +++ b/tools/raml2html/raml2html.php @@ -0,0 +1,10 @@ +#!/usr/bin/env php +run(); + diff --git a/tools/raml2html/src/Application.php b/tools/raml2html/src/Application.php new file mode 100644 index 0000000000..4125437b4f --- /dev/null +++ b/tools/raml2html/src/Application.php @@ -0,0 +1,82 @@ +getGenerator(), + $this->getRamlParserFactory() + ), + new LintTypesCommand( + $this->getRamlParserFactory() + ), + new ClearCacheCommand(self::CACHE_DIR), + ]); + } + + public function getTwig(): Twig\Environment + { + if ($this->twig === null) { + $loader = new Twig\Loader\FilesystemLoader(__DIR__ . '/../themes'); + $options = [ + 'debug' => true, + 'cache' => self::CACHE_DIR, + 'strict_variables' => true, + ]; + + $this->twig = new Twig\Environment($loader, $options); + $this->twig->addExtension(new RenderExtension()); + $this->twig->addExtension(new MarkdownExtension()); + $this->twig->addRuntimeLoader(new class implements RuntimeLoaderInterface { + public function load($class): ?MarkdownRuntime { + if (MarkdownRuntime::class === $class) { + return new MarkdownRuntime(new DefaultMarkdown()); + } + + return null; + } + }); + $this->twig->addExtension(new HashExtension()); + } + + return $this->twig; + } + + private function getGenerator(): Generator + { + return new Generator($this->getTwig()); + } + + private function getRamlParserFactory(): ParserFactory + { + return new ParserFactory(); + } +} diff --git a/tools/raml2html/src/Command/BuildCommand.php b/tools/raml2html/src/Command/BuildCommand.php new file mode 100644 index 0000000000..3b3e23e16d --- /dev/null +++ b/tools/raml2html/src/Command/BuildCommand.php @@ -0,0 +1,89 @@ +generator = $generator; + $this->ramlParserFactory = $ramlParserFactory; + } + + /** + * {@inheritdoc} + */ + protected function configure(): void + { + $this->setName('build'); + $this->addArgument('definition'); + $this->addOption(self::OPTION_THEME, 't', InputOption::VALUE_REQUIRED, 'Theme', 'default'); + $this->addOption(self::OPTION_OUTPUT_DIR, 'o', InputOption::VALUE_REQUIRED, 'Output directory', getcwd()); + $this->addOption(self::OPTION_NON_STANDARD_HTTP_METHODS, null, InputOption::VALUE_OPTIONAL, 'Non standard HTTP methods'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $ramlParser = $this->ramlParserFactory->createParser( + $this->createParserConfigurationFromInput($input) + ); + + $this->generator->generate( + $ramlParser->parse($input->getArgument('definition')), + $this->createGeneratorOptionsFromInput($input) + ); + + return 0; + } + + private function createParserConfigurationFromInput(InputInterface $input): ParserConfiguration + { + $configuration = new ParserConfiguration(); + + $nonStandardHTTPMethods = $input->getOption(self::OPTION_NON_STANDARD_HTTP_METHODS); + if ($nonStandardHTTPMethods !== null) { + $configuration->setNonStandardHttpMethods(explode(',', $nonStandardHTTPMethods)); + } + + return $configuration; + } + + private function createGeneratorOptionsFromInput(InputInterface $input): GeneratorOptions + { + $generatorOptions = new GeneratorOptions(); + $generatorOptions->setOutputDir($input->getOption(self::OPTION_OUTPUT_DIR)); + $generatorOptions->setTheme($input->getOption(self::OPTION_THEME)); + + return $generatorOptions; + } +} diff --git a/tools/raml2html/src/Command/ClearCacheCommand.php b/tools/raml2html/src/Command/ClearCacheCommand.php new file mode 100644 index 0000000000..cc0148d15e --- /dev/null +++ b/tools/raml2html/src/Command/ClearCacheCommand.php @@ -0,0 +1,46 @@ +cacheDir = $cacheDir; + } + + protected function configure(): void + { + $this->setName('cache:clear'); + $this->setDescription('Clears the cache'); + } + + protected function execute(InputInterface $input, OutputInterface $output): void + { + $fs = new Filesystem(); + $io = new SymfonyStyle($input, $output); + + try { + $fs->remove($this->cacheDir); + $io->success('Cache was successfully cleared.'); + } catch (IOException $e) { + if ($output->isVerbose()) { + $io->warning($e->getMessage()); + } + } + } +} diff --git a/tools/raml2html/src/Command/LintTypesCommand.php b/tools/raml2html/src/Command/LintTypesCommand.php new file mode 100644 index 0000000000..7c411107ce --- /dev/null +++ b/tools/raml2html/src/Command/LintTypesCommand.php @@ -0,0 +1,117 @@ +ramlParserFactory = $ramlParserFactory; + } + + protected function configure(): void + { + $this->setName('lint:types'); + $this->addArgument('definition'); + + $this->addOption(self::OPTION_NON_STANDARD_HTTP_METHODS, null, InputOption::VALUE_OPTIONAL, 'Non standard HTTP methods'); + } + + protected function execute(InputInterface $input, OutputInterface $output): void + { + $ramlParser = $this->ramlParserFactory->createParser( + $this->createParserConfigurationFromInput($input) + ); + + $this->lintTypes($ramlParser->parse($input->getArgument('definition'))); + + $nonExisting = array_unique($this->nonExisting); + foreach ($nonExisting as $typeName) { + $output->writeln($typeName); + } + } + + private function createParserConfigurationFromInput(InputInterface $input): ParserConfiguration + { + $configuration = new ParserConfiguration(); + + $nonStandardHTTPMethods = $input->getOption(self::OPTION_NON_STANDARD_HTTP_METHODS); + if ($nonStandardHTTPMethods !== null) { + $configuration->setNonStandardHttpMethods(explode(',', $nonStandardHTTPMethods)); + } + + return $configuration; + } + + private function lintTypes(ApiDefinition $definition, iterable $resources = null) + { + if ($resources === null) { + $resources = $definition->getResources(); + } + + /** @var resource $resource */ + foreach ($resources as $resource) { + foreach ($resource->getMethods() as $method) { + foreach ($method->getBodies() as $body) { + if (!$body instanceof Body) { + continue; + } + + $this->doCheckType($definition, $body->getType()); + } + } + + foreach ($method->getResponses() as $response) { + foreach ($response->getBodies() as $body) { + if (!$body instanceof Body) { + continue; + } + + $this->doCheckType($definition, $body->getType()); + } + } + + $this->lintTypes($definition, $resource->getResources()); + } + } + + private function doCheckType(ApiDefinition $definition, TypeInterface $needle): void + { + if (!$this->exists($definition, $needle)) { + $this->nonExisting[] = $needle->getName(); + } + } + + private function exists(ApiDefinition $definition, TypeInterface $needle): bool + { + foreach ($definition->getTypes() as $type) { + if ($type->getName() == $needle->getName()) { + return true; + } + } + + return false; + } +} diff --git a/tools/raml2html/src/Generator/Generator.php b/tools/raml2html/src/Generator/Generator.php new file mode 100644 index 0000000000..608bd6aaf9 --- /dev/null +++ b/tools/raml2html/src/Generator/Generator.php @@ -0,0 +1,40 @@ +twig = $twig; + } + + public function generate(ApiDefinition $apiDefinition, GeneratorOptions $options): void + { + $theme = $options->getTheme(); + + $output = $this->twig->render("$theme/index.html.twig", [ + 'api' => $apiDefinition, + 'theme' => $theme, + ]); + + $filename = $this->getOutputFilePath($apiDefinition, $options); + + $fs = new Filesystem(); + $fs->dumpFile($filename, $output); + } + + private function getOutputFilePath(ApiDefinition $apiDefinition, GeneratorOptions $options): string + { + return $options->getOutputDir() . \DIRECTORY_SEPARATOR . 'rest_api_reference.html'; + } +} diff --git a/tools/raml2html/src/Generator/GeneratorOptions.php b/tools/raml2html/src/Generator/GeneratorOptions.php new file mode 100644 index 0000000000..7ee64517b9 --- /dev/null +++ b/tools/raml2html/src/Generator/GeneratorOptions.php @@ -0,0 +1,42 @@ +theme; + } + + public function setTheme(string $theme): void + { + $this->theme = $theme; + } + + public function getOutputDir(): string + { + return $this->outputDir; + } + + public function setOutputDir(string $outputDir): void + { + $this->outputDir = $outputDir; + } +} diff --git a/tools/raml2html/src/RAML/ParserConfiguration.php b/tools/raml2html/src/RAML/ParserConfiguration.php new file mode 100644 index 0000000000..7648c5efe2 --- /dev/null +++ b/tools/raml2html/src/RAML/ParserConfiguration.php @@ -0,0 +1,32 @@ +nonStandardHttpMethods; + } + + public function hasNonStandardHttpMethods(): bool + { + return !empty($this->nonStandardHttpMethods); + } + + public function setNonStandardHttpMethods(array $nonStandardHttpMethods): void + { + $this->nonStandardHttpMethods = $nonStandardHttpMethods; + } +} diff --git a/tools/raml2html/src/RAML/ParserFactory.php b/tools/raml2html/src/RAML/ParserFactory.php new file mode 100644 index 0000000000..b1ed408d81 --- /dev/null +++ b/tools/raml2html/src/RAML/ParserFactory.php @@ -0,0 +1,35 @@ +getDefaultConfiguration(); + } + + if ($configuration->hasNonStandardHttpMethods()) { + Method::$validMethods = array_merge(Method::$validMethods, $configuration->getNonStandardHttpMethods()); + } + + $parser = new Parser(); + $parser->setConfiguration($configuration); + + return $parser; + } + + private function getDefaultConfiguration(): ParserConfiguration + { + $configuration = new ParserConfiguration(); + $configuration->enableDirectoryTraversal(); + + return $configuration; + } +} diff --git a/tools/raml2html/src/Twig/Extension/HashExtension.php b/tools/raml2html/src/Twig/Extension/HashExtension.php new file mode 100644 index 0000000000..c1459fec88 --- /dev/null +++ b/tools/raml2html/src/Twig/Extension/HashExtension.php @@ -0,0 +1,35 @@ +hashes[$hash])) { + throw new RuntimeException('Hash is generated twice for ' . json_encode($values)); + } + + $this->hashes[$hash] = $hash; + + return $hash; + }), + ]; + } +} diff --git a/tools/raml2html/src/Twig/Extension/RenderExtension.php b/tools/raml2html/src/Twig/Extension/RenderExtension.php new file mode 100644 index 0000000000..b64eace71a --- /dev/null +++ b/tools/raml2html/src/Twig/Extension/RenderExtension.php @@ -0,0 +1,197 @@ +toString(); + }), + new TwigFunction('dump', function ($var, ...$moreVars) { + ob_start(); + dump($var, ...$moreVars); + $output = ob_get_contents(); + ob_end_flush(); + + return $output; + }), + new TwigFunction('method_types', function (TypeCollection $typeCollection, Method $method) { + $types = []; + $methodTypes = $this->getTypes($method); + + foreach ($methodTypes as $type) { + $types[$type] = $typeCollection->getTypeByName($type)->getDefinition()['description'] ?? ''; + } + + return $types; + }), + new TwigFunction('schema_format', [$this, 'getSchemaFormat']), + new TwigFunction('method_name_id', [$this, 'prepareMethodNameId']), + new TwigFunction('examples_tabs', [$this, 'getExamplesTabs']), + new TwigFunction('examples_body', [$this, 'getExamplesBody']), + ]; + } + + public function prepareMethodNameId(string $methodName): string + { + $methodName = strtolower($methodName); + $methodNameNoWhitespaces = preg_replace('/[\s\/]/', '-', $methodName); + + $methodId = preg_replace('/[(){}]/', '', $methodNameNoWhitespaces); + + if (preg_match('/\\W/', substr($methodId, 0, 1))) { + $methodId = substr($methodId, 1); + } + + return $methodId; + } + + public function getExamplesTabs(array $responseBodies): array + { + $tabs = []; + + /** @var \Raml\Body $body */ + foreach ($responseBodies as $body) { + if (!empty($body->getExamples()) && !empty($body->getExample())) { + $tabs[] = $this->getSchemaFormat($body->getMediaType()); + } + } + + return array_unique($tabs); + } + + public function getExamplesBody(array $responseBodies): array + { + $examples = []; + + /** @var \Raml\Body $body */ + foreach ($responseBodies as $body) { + $schemaFormat = $this->getSchemaFormat($body->getMediaType()); + + if (!empty($body->getExamples()) && !empty($body->getExample())) { + $examples[$schemaFormat][] = $body->getExample(); + } + } + + return $examples; + } + + public function getSchemaFormat(string $mediaType): string + { + $format = explode('+', $mediaType)[1] ?? ''; + + if (1 === preg_match('/[\W]/', $format, $matches)) { + $splittedFormat = str_split($format, strpos($format, ';')); + + return current($splittedFormat); + } + + return $format; + } + + public function getTests(): array + { + return [ + new TwigTest('scalar type', function ($type) { + return $this->isScalarType($type); + }), + new TwigTest('array type', function (TypeInterface $type) { + return $this->isArrayType($type); + }), + new TwigTest('object type', function (TypeInterface $type) { + return $this->isObjectType($type); + }), + new TwigTest('standard type', function ($type) { + return $this->isStandardType((string)$type); + }), + ]; + } + + private function isScalarType($type): bool + { + if ($type instanceof TypeInterface) { + $type = $type->getName(); + } + + return in_array($type, [ + 'time-only', + 'datetime', + 'datetime-only', + 'date-only', + 'number', + 'boolean', + 'string', + 'null', + 'nil', + 'file', + 'integer', + ]); + } + + private function isArrayType(TypeInterface $type): bool + { + return $type instanceof ArrayType; + } + + private function isObjectType(TypeInterface $type): bool + { + if ($type instanceof LazyProxyType) { + $type = $type->getWrappedObject(); + } + + return $type instanceof ObjectType; + } + + private function isStandardType($type): bool + { + if ($type instanceof TypeInterface) { + $type = $type->getName(); + } + + return $this->isScalarType($type) || $type === 'object'; + } + + + private function getTypes(Method $method) + { + $requestTypes = $this->getTypesFromBodies($method->getBodies()); + + foreach ($method->getResponses() as $response) { + $requestTypes += array_merge($requestTypes, $this->getTypesFromBodies($response->getBodies())); + } + + return array_unique($requestTypes); + } + + /** + * @param \Raml\BodyInterface[] $bodies + * + * @return array + */ + private function getTypesFromBodies(array $bodies): array + { + $types = []; + + foreach ($bodies as $body) { + $types[] = $body->getType()->getName(); + } + + return $types; + } +} diff --git a/material/search/require.js b/tools/raml2html/tests/.gitkeep similarity index 100% rename from material/search/require.js rename to tools/raml2html/tests/.gitkeep diff --git a/tools/raml2html/themes/default/example-modal.html.twig b/tools/raml2html/themes/default/example-modal.html.twig new file mode 100644 index 0000000000..393ad68e69 --- /dev/null +++ b/tools/raml2html/themes/default/example-modal.html.twig @@ -0,0 +1,82 @@ + diff --git a/tools/raml2html/themes/default/example.html.twig b/tools/raml2html/themes/default/example.html.twig new file mode 100644 index 0000000000..3914822ed5 --- /dev/null +++ b/tools/raml2html/themes/default/example.html.twig @@ -0,0 +1,32 @@ +{% set tabId = tabId is defined ? tabId : '' %} +
    +
    + {% if examples is defined and examples|length > 0 %} + {% for type, example in examples %} + {% set elemId = tabId ~ '-' ~ type %} +
    + {% for body in example %} + {% set margin = example|length > 1 and loop.index == 1 ? 'mb-4' : '' %} + {% set border = loop.index > 1 ? 'border-top' : '' %} +
    + {% set clipboardId = hash(type, tabId, loop.index) %} +

    + file_copy + +

    +
    +                                {{ body|slice(0,1000) }}
    +                            
    +
    + View more + {% endfor %} +
    + {% endfor %} + {% endif %} +
    +
    diff --git a/tools/raml2html/themes/default/header-parameters.html.twig b/tools/raml2html/themes/default/header-parameters.html.twig new file mode 100644 index 0000000000..f73e5b9bf3 --- /dev/null +++ b/tools/raml2html/themes/default/header-parameters.html.twig @@ -0,0 +1,47 @@ + + Type + + {% set is_structured_value = not item.type is scalar type or not item.type == 'array' %} + + {% if not is_structured_value %} + + {% if item.enum %} + {% if item.enum|length > 1 %} + (one of {{ item.enum | join(', ') }}) + {% else %} + {{ item.enum | first }} + {% endif %} + {% else %} + {% if item.type == 'array' %} + (array of X) + {% else %} + {{ item.type }} + {% endif %} + {% endif %} + + {% if item.isRequired() %} (required){% endif %} + + {% if item.canRepeat() %} repeat: {{ item.repeat }}{% endif %} + + {% if item.type == 'string' %} + {% if item.minLength or item.minLength is same as(0) %} | minLength: {{ item.minLength }}{% endif %} + {% if item.maxLength or item.maxLength is same as(0) %} | maxLength: {{ item.maxLength }}{% endif %} + {% else %} + {% if item.minimum or item.minimum is same as(0) %} | minimum: {{ item.minimum }}{% endif %} + {% if item.maximum or item.maximum is same as(0) %} | maximum: {{ item.maximum }}{% endif %} + {% endif %} + + {% if item.validationPattern %} | pattern: {{ item.validationPattern }}{% endif %} + {% endif %} + + +{% if item.examples %} + + Examples + + {% for example in item.examples %} + {{ example }} + {% endfor %} + + +{% endif %} diff --git a/tools/raml2html/themes/default/index.html.twig b/tools/raml2html/themes/default/index.html.twig new file mode 100644 index 0000000000..bd11abe526 --- /dev/null +++ b/tools/raml2html/themes/default/index.html.twig @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + {{ api.title }} + + + +
    +
    +
    +
    + + ez-logo + + +
    +
    +
    +
    +
    +
    +
    + +
    +
    + {% for resource in api.resources %} +
    + {% set rootParentName = resource.displayName|default(resource.uri) %} + {% set rootElemId = resource.displayName|replace({' ': '-'})|lower %} +

    + {{ rootParentName }} + +

    + {% include theme ~ "/resource-group.html.twig" %} +
    + {% endfor %} + {% if api.types is not empty %} + {% for type in api.types %} + + {% endfor %} + {% endif %} +
    +
    +
    +
    +
    +
    +

    Copyright 1999- Ibexa AS and others.

    +
    +
    + + + + + + + + + + + + + + + diff --git a/tools/raml2html/themes/default/method-headers.html.twig b/tools/raml2html/themes/default/method-headers.html.twig new file mode 100644 index 0000000000..ac099fd9ee --- /dev/null +++ b/tools/raml2html/themes/default/method-headers.html.twig @@ -0,0 +1,24 @@ +{% if method.headers %} +
    +
    Header parameters
    + {% for item in method.headers %} +
    +

    {{ item.key }}

    +

    {{ item.description }}

    +
    + + + + + + + + + {% include theme ~ "/header-parameters.html.twig" %} + +
    PropertyValue
    +
    +
    + {% endfor %} +
    +{% endif %} diff --git a/tools/raml2html/themes/default/method-possible-responses.html.twig b/tools/raml2html/themes/default/method-possible-responses.html.twig new file mode 100644 index 0000000000..3b57311bd7 --- /dev/null +++ b/tools/raml2html/themes/default/method-possible-responses.html.twig @@ -0,0 +1,34 @@ +{% if method.responses %} +
    +
    Possible responses
    +
    + + + + + + + + + {% for response in method.responses %} + + + + + {% endfor %} + +
    CodeDescription
    + {% if response.bodies|length > 0%} + + {{ response.statusCode }} + + {% else %} + {{ response.statusCode }} + {% endif %} + +

    {{ response.description }}

    +
    +
    +
    +{% endif %} diff --git a/tools/raml2html/themes/default/method-query-parameters.html.twig b/tools/raml2html/themes/default/method-query-parameters.html.twig new file mode 100644 index 0000000000..feb3dcbe35 --- /dev/null +++ b/tools/raml2html/themes/default/method-query-parameters.html.twig @@ -0,0 +1,21 @@ +{% if method.queryParameters %} +
    +
    Query parameters
    +
    + + + + + + + + + + {% for item in method.queryParameters %} + {% include theme ~ "/named-parameter.html.twig" %} + {% endfor %} + +
    PropertyTypeValue
    +
    +
    +{% endif %} diff --git a/tools/raml2html/themes/default/named-parameter.html.twig b/tools/raml2html/themes/default/named-parameter.html.twig new file mode 100644 index 0000000000..3df5ebe44f --- /dev/null +++ b/tools/raml2html/themes/default/named-parameter.html.twig @@ -0,0 +1,44 @@ + + + {{ item.key }} + + + {% set is_structured_value = not item.type is scalar type or not item.type == 'array' %} + + {% if not is_structured_value %} + + {% if item.enum %} + {% if item.enum|length > 1 %} + (one of {{ item.enum | join(', ') }}) + {% else %} + {{ item.enum | first }} + {% endif %} + {% else %} + {% if item.type == 'array' %} + (array of X) + {% else %} + {{ item.type }} + {% endif %} + {% endif %} + + {% if item.isRequired() %} (required){% endif %} + + {% if item.canRepeat() %} repeat: {{ item.repeat }}{% endif %} + + {% if item.type == 'string' %} + {% if item.minLength or item.minLength is same as(0) %} | minLength: {{ item.minLength }}{% endif %} + {% if item.maxLength or item.maxLength is same as(0) %} | maxLength: {{ item.maxLength }}{% endif %} + {% else %} + {% if item.minimum or item.minimum is same as(0) %} | minimum: {{ item.minimum }}{% endif %} + {% if item.maximum or item.maximum is same as(0) %} | maximum: {{ item.maximum }}{% endif %} + {% endif %} + + {% if item.validationPattern %} | pattern: {{ item.validationPattern }}{% endif %} + {% endif %} + + + {% if item.description %} + {{ item.description }} + {% endif %} + + diff --git a/tools/raml2html/themes/default/resource-endpoints.html.twig b/tools/raml2html/themes/default/resource-endpoints.html.twig new file mode 100644 index 0000000000..c5ec19cd7d --- /dev/null +++ b/tools/raml2html/themes/default/resource-endpoints.html.twig @@ -0,0 +1,26 @@ +{% if (resource.methods or (resource.description and resource.parentUrl)) %} + {% set sectionId = resource.displayName|replace({' ': '-'})|lower %} +
    +
    +

    {{ resource.uri }}

    +
      + {% for method in resource.methods %} +
    • + {% set methodNameId = method_name_id(method.displayName) %} + + + {{ method.type }} + + +
    • + {% endfor %} +
    +
    +
    + {% for method in resource.methods %} + {% include theme ~ "/resource.html.twig" %} + {% endfor %} +{% endif %} +{% for resource in resource.resources %} + {% include theme ~ "/resource-endpoints.html.twig" %} +{% endfor %} diff --git a/tools/raml2html/themes/default/resource-group.html.twig b/tools/raml2html/themes/default/resource-group.html.twig new file mode 100644 index 0000000000..cf87410f41 --- /dev/null +++ b/tools/raml2html/themes/default/resource-group.html.twig @@ -0,0 +1,10 @@ +{% if resource.resources|length > 1 %} + {% for resource in resource.resources %} +
    +

    {{ resource.uri }}

    +
    + {% include theme ~ "/resource-endpoints.html.twig" %} + {% endfor %} +{% else %} + {% include theme ~ "/resource-endpoints.html.twig" %} +{% endif %} diff --git a/tools/raml2html/themes/default/resource-request-content.html.twig b/tools/raml2html/themes/default/resource-request-content.html.twig new file mode 100644 index 0000000000..bd20656071 --- /dev/null +++ b/tools/raml2html/themes/default/resource-request-content.html.twig @@ -0,0 +1,35 @@ +
    + {% if method.bodies %} + {% set tabId = method_name_id(sectionId ~ '-' ~ method.type|lower ~ '-request') %} +
    +
    + +
    + {% set examples = examples_body(method.bodies) %} + {% if examples|length > 0 %} + {% include theme ~ "/example.html.twig" %} + {% include theme ~ "/example-modal.html.twig" %} + {% endif %} +
    + {% endif %} +
    diff --git a/tools/raml2html/themes/default/resource-response-content.html.twig b/tools/raml2html/themes/default/resource-response-content.html.twig new file mode 100644 index 0000000000..7514d77b61 --- /dev/null +++ b/tools/raml2html/themes/default/resource-response-content.html.twig @@ -0,0 +1,44 @@ +
    + {% for response in method.responses %} + {% if response.headers or response.bodies %} + {% if response.bodies %} + {% set tabId = method_name_id(sectionId ~ '-' ~ method.type|lower ~ '-response-' ~ response.statusCode) %} +
    +
    + + {% if tabItems|length > 0 %} +
    + Code: {{ response.statusCode }} +
    + {% endif %} +
    + {% set examples = examples_body(response.bodies) %} + {% if examples|length > 0 %} + {% include theme ~ "/example.html.twig" %} + {% include theme ~ "/example-modal.html.twig" %} + {% endif %} +
    + {% endif %} + {% endif %} + {% endfor %} +
    diff --git a/tools/raml2html/themes/default/resource-securedby-content.html.twig b/tools/raml2html/themes/default/resource-securedby-content.html.twig new file mode 100644 index 0000000000..3613bdd8e4 --- /dev/null +++ b/tools/raml2html/themes/default/resource-securedby-content.html.twig @@ -0,0 +1,98 @@ +
    + + + {% for securityScheme in method.securitySchemes %} +
    + {% if securityScheme.description %} +
      +
    • Description
    • +
    • + {{ securityScheme.description }} +
    • +
    + {% endif %} + + {% if securityScheme.type is constant('Raml\\SecurityScheme\\SecuritySettings\\OAuth2SecuritySettings::TYPE') %} + {% if securityScheme.settings.scopes is not empty %} +
      +
    • Scopes
    • +
    • +
        + {% for scope in securityScheme.settings.scopes %} +
      • {{ scope }}
      • + {% endfor %} +
      +
    • +
    + {% endif %} + {% endif %} + + {% if securityScheme.describedBy %} + {% if securityScheme.describedBy.headers %} +
      +
    • Headers
    • +
    • +
        + {% for item in securityScheme.describedBy.headers %} + {% include theme ~ "/named-parameter.html.twig" %} + {% endfor %} +
      +
    • +
    + {% endif %} + + {% if securityScheme.describedBy.queryParameters %} +
      +
    • Query Parameters
    • +
    • +
        + {% for item in securityScheme.describedBy.queryParameters %} + {% include theme ~ "/named-parameter.html.twig" %} + {% endfor %} +
      +
    • +
    + {% endif %} + + {% if securityScheme.describedBy.responses %} + + + {% for response in securityScheme.describedBy.responses %} +
    +

    {{ response.description }}

    + + {% if response.headers %} +
      +
    • Headers
    • +
    • +
        + {% for item in response.headers %} + {% include theme ~ "/named-parameter.html.twig" %} + {% endfor %} +
      +
    • +
    + {% endif %} +
    + {% endfor %} + {% endif %} + {% endif %} +
    + {% endfor %} +
    diff --git a/tools/raml2html/themes/default/resource.html.twig b/tools/raml2html/themes/default/resource.html.twig new file mode 100644 index 0000000000..bbf2e4b0e7 --- /dev/null +++ b/tools/raml2html/themes/default/resource.html.twig @@ -0,0 +1,46 @@ +{% set methodNameId = method_name_id(method.displayName) %} +
    +
    +
    +
    + {{ method.displayName }} + +
    +
    +
    +

    + {{ method.type }} + {{ resource.uri }} +

    +

    {{ method.description|default('')|markdown_to_html }}

    + {% include theme ~ "/method-headers.html.twig" %} + + {% include theme ~ "/method-query-parameters.html.twig" %} + + {% include theme ~ "/method-possible-responses.html.twig" %} + + {% include theme ~ "/types.html.twig" %} +
    +
    + {% set has_request = method.queryParameters or method.headers or method.bodies %} + {% set has_response = method.responses is not empty %} + {% set has_securedby = method.securitySchemes is not empty %} + + {% if has_request or has_response or has_securedby %} + {% if has_request %} + {% include theme ~ "/resource-request-content.html.twig" %} + {% endif %} + {% if has_securedby %} +
    Security
    + {% include theme ~ "/resource-securedby-content.html.twig" %} + {% endif %} + {% if has_response %} + {% include theme ~ "/resource-response-content.html.twig" %} + {% endif %} + {% endif %} +
    +
    +
    diff --git a/tools/raml2html/themes/default/type.html.twig b/tools/raml2html/themes/default/type.html.twig new file mode 100644 index 0000000000..22b1847121 --- /dev/null +++ b/tools/raml2html/themes/default/type.html.twig @@ -0,0 +1,43 @@ +{{ type.name }} + +{% if type.getRequired() %} + * +{% endif %} + +( + {%- if type is array type %} + {{- type.items.name -}}[] + {% else %} + {{- type.type -}} + {% endif -%} +) + - + + {% if type.definition.description is defined %} + {{- type.definition.description -}} + {% endif %} + + +{% if type.enum is not empty %} +

    + Allowed values: + {% for value in type.enum %} + {{ value }} + {% if not loop.last %}, {% endif %} + {% endfor %} +

    +{% endif %} + +{% if type is object type %} +
    +
      + {% for property in type.properties %} +
    • + {% include theme ~ '/type.html.twig' with { + type: property + } %} +
    • + {% endfor %} +
    +
    +{% endif %} diff --git a/tools/raml2html/themes/default/types.html.twig b/tools/raml2html/themes/default/types.html.twig new file mode 100644 index 0000000000..002a86b199 --- /dev/null +++ b/tools/raml2html/themes/default/types.html.twig @@ -0,0 +1,29 @@ +{% set types = method_types(api.types, method) %} +{% if types is defined and types|length > 0 %} +
    +
    Types
    +
    + + + + + + + + + {% for typeName, typeDescription in types %} + + + + + {% endfor %} + +
    TypeDescription
    + + {{ typeName }} + + {{ typeDescription }}
    +
    +
    +{% endif %} +