diff --git a/config/schema/fedora_resource_type.schema.yml b/config/schema/fedora_resource_type.schema.yml new file mode 100644 index 00000000..ec754ca3 --- /dev/null +++ b/config/schema/fedora_resource_type.schema.yml @@ -0,0 +1,24 @@ +islandoraclaw.fedora_resource_type.*: + type: config_entity + label: 'Fedora resource type config' + mapping: + id: + type: string + label: 'ID' + label: + type: label + label: 'Label' + uuid: + type: string + +# Plugin \Drupal\islandoraclaw\Plugin\Search\FedoraResourceSearch +search.plugin.node_search: + type: mapping + label: 'Fedora Resource search' + mapping: + rankings: + type: sequence + label: 'Content ranking' + sequence: + type: integer + label: 'Influence' \ No newline at end of file diff --git a/fedora_resource.page.inc b/fedora_resource.page.inc new file mode 100644 index 00000000..0e2eb399 --- /dev/null +++ b/fedora_resource.page.inc @@ -0,0 +1,62 @@ +query->all(); + foreach ($variables['content'] as $type) { + $variables['types'][$type->id()] = array( + 'link' => Link::fromTextAndUrl($type->label(), new Url('entity.fedora_resource.add_form', array( + 'fedora_resource_type' => $type->id() + ), array('query' => $query))), + 'description' => array( + '#markup' => $type->label(), + ), + 'title' => $type->label(), + 'localized_options' => array( + 'query' => $query, + ), + ); + } +} diff --git a/islandoraclaw.info.yml b/islandoraclaw.info.yml new file mode 100644 index 00000000..8883a00c --- /dev/null +++ b/islandoraclaw.info.yml @@ -0,0 +1,14 @@ +# This .info.yml files provides the basic information about our module to Drupal +# More: https://www.drupal.org/node/2000204 +name: Islandora CLAW +description: 'Learning Drupal 8 with Islandora Claw' +type: module +package: Islandora +core: 8.x +dependencies: + - block + - node + - path + - text + - options +version: '8.x-2.x-dev' \ No newline at end of file diff --git a/islandoraclaw.links.action.yml b/islandoraclaw.links.action.yml new file mode 100644 index 00000000..2666c978 --- /dev/null +++ b/islandoraclaw.links.action.yml @@ -0,0 +1,11 @@ +entity.fedora_resource.add_form: + route_name: 'fedora_resource.add_page' + title: 'Add Fedora resource' + appears_on: + - entity.fedora_resource.collection +entity.fedora_resource_type.add_form: + route_name: 'entity.fedora_resource_type.add_form' + title: 'Add Fedora resource type' + appears_on: + - entity.fedora_resource_type.collection + diff --git a/islandoraclaw.links.menu.yml b/islandoraclaw.links.menu.yml new file mode 100644 index 00000000..dbd6cdad --- /dev/null +++ b/islandoraclaw.links.menu.yml @@ -0,0 +1,16 @@ +# Fedora resource menu items definition +entity.fedora_resource.collection: + title: 'Fedora resource list' + route_name: entity.fedora_resource.collection + description: 'List Fedora resource entities' + parent: system.admin_structure + weight: 100 + +# Fedora resource type menu items definition +entity.fedora_resource_type.collection: + title: 'Fedora resource type' + route_name: entity.fedora_resource_type.collection + description: 'List Fedora resource type (bundles)' + parent: system.admin_structure + weight: 99 + diff --git a/islandoraclaw.links.task.yml b/islandoraclaw.links.task.yml new file mode 100644 index 00000000..f55e9aa0 --- /dev/null +++ b/islandoraclaw.links.task.yml @@ -0,0 +1,17 @@ +# Fedora resource routing definition +entity.fedora_resource.canonical: + route_name: entity.fedora_resource.canonical + base_route: entity.fedora_resource.canonical + title: 'View' + +entity.fedora_resource.edit_form: + route_name: entity.fedora_resource.edit_form + base_route: entity.fedora_resource.canonical + title: Edit + +entity.fedora_resource.delete_form: + route_name: entity.fedora_resource.delete_form + base_route: entity.fedora_resource.canonical + title: Delete + weight: 10 + diff --git a/islandoraclaw.module b/islandoraclaw.module new file mode 100644 index 00000000..dc8c0b58 --- /dev/null +++ b/islandoraclaw.module @@ -0,0 +1,58 @@ +' . t('About') . ''; + $output .= '

' . t('') . '

'; + return $output; + + default: + } +} + +/** + * Implements hook_theme(). + */ +function islandoraclaw_theme() { + $theme = []; + $theme['fedora_resource'] = [ + 'render element' => 'elements', + 'file' => 'fedora_resource.page.inc', + 'template' => 'fedora_resource', + ]; + $theme['fedora_resource_content_add_list'] = [ + 'render element' => 'content', + 'variables' => ['content' => NULL], + 'file' => 'fedora_resource.page.inc', + ]; + return $theme; +} + +/** +* Implements hook_theme_suggestions_HOOK(). +*/ +function islandoraclaw_theme_suggestions_fedora_resource(array $variables) { + $suggestions = array(); + $entity = $variables['elements']['#fedora_resource']; + $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); + + $suggestions[] = 'fedora_resource__' . $sanitized_view_mode; + $suggestions[] = 'fedora_resource__' . $entity->bundle(); + $suggestions[] = 'fedora_resource__' . $entity->bundle() . '__' . $sanitized_view_mode; + $suggestions[] = 'fedora_resource__' . $entity->id(); + $suggestions[] = 'fedora_resource__' . $entity->id() . '__' . $sanitized_view_mode; + return $suggestions; +} diff --git a/islandoraclaw.permissions.yml b/islandoraclaw.permissions.yml new file mode 100644 index 00000000..607d85c0 --- /dev/null +++ b/islandoraclaw.permissions.yml @@ -0,0 +1,19 @@ +add fedora resource entities: + title: 'Create new Fedora resource entities' + +administer fedora resource entities: + title: 'Administer Fedora resource entities' + description: 'Allow to access the administration form to configure Fedora resource entities.' + restrict access: true + +delete fedora resource entities: + title: 'Delete Fedora resource entities' + +edit fedora resource entities: + title: 'Edit Fedora resource entities' + +view published fedora resource entities: + title: 'View published Fedora resource entities' + +view unpublished fedora resource entities: + title: 'View unpublished Fedora resource entities' diff --git a/src/Controller/FedoraResourceAddController.php b/src/Controller/FedoraResourceAddController.php new file mode 100644 index 00000000..5314661d --- /dev/null +++ b/src/Controller/FedoraResourceAddController.php @@ -0,0 +1,97 @@ +storage = $storage; + $this->typeStorage = $type_storage; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + /** @var EntityTypeManagerInterface $entity_type_manager */ + $entity_type_manager = $container->get('entity_type.manager'); + return new static( + $entity_type_manager->getStorage('fedora_resource'), + $entity_type_manager->getStorage('fedora_resource_type') + ); + } + /** + * Displays add links for available bundles/types for entity fedora_resource . + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request object. + * + * @return array + * A render array for a list of the fedora_resource bundles/types that can be added or + * if there is only one type/bunlde defined for the site, the function returns the add page for that bundle/type. + */ + public function add(Request $request) { + $types = $this->typeStorage->loadMultiple(); + if ($types && count($types) == 1) { + $type = reset($types); + return $this->addForm($type, $request); + } + if (count($types) === 0) { + return array( + '#markup' => $this->t('You have not created any %bundle types yet. @link to add a new type.', [ + '%bundle' => 'Fedora resource', + '@link' => $this->l($this->t('Go to the type creation page'), Url::fromRoute('entity.fedora_resource_type.add_form')), + ]), + ); + } + return array('#theme' => 'fedora_resource_content_add_list', '#content' => $types); + } + + /** + * Presents the creation form for fedora_resource entities of given bundle/type. + * + * @param EntityInterface $fedora_resource_type + * The custom bundle to add. + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request object. + * + * @return array + * A form array as expected by drupal_render(). + */ + public function addForm(EntityInterface $fedora_resource_type, Request $request) { + $entity = $this->storage->create(array( + 'type' => $fedora_resource_type->id() + )); + return $this->entityFormBuilder()->getForm($entity); + } + + /** + * Provides the page title for this controller. + * + * @param EntityInterface $fedora_resource_type + * The custom bundle/type being added. + * + * @return string + * The page title. + */ + public function getAddFormTitle(EntityInterface $fedora_resource_type) { + return t('Create of bundle @label', + array('@label' => $fedora_resource_type->label()) + ); + } + +} diff --git a/src/Entity/FedoraResource.php b/src/Entity/FedoraResource.php new file mode 100644 index 00000000..aa40610a --- /dev/null +++ b/src/Entity/FedoraResource.php @@ -0,0 +1,391 @@ + \Drupal::currentUser()->id(), + ); + } + + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + // Reindex the entity when it is updated. The entity is automatically + // indexed when it is added, simply by being added to the {fedora_resource} table. + // Only required if using the core search index. + if ($update) { + if (\Drupal::moduleHandler()->moduleExists('search')) { + search_mark_for_reindex('fedora_resource_search', $this->id()); + } + } + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->bundle(); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return $this->get('name')->value; + } + + /** + * {@inheritdoc} + */ + public function setName($name) { + $this->set('name', $name); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCreatedTime() { + return $this->get('created')->value; + } + + /** + * {@inheritdoc} + */ + public function setCreatedTime($timestamp) { + $this->set('created', $timestamp); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getOwner() { + return $this->get('user_id')->entity; + } + + /** + * {@inheritdoc} + */ + public function getOwnerId() { + return $this->get('user_id')->target_id; + } + + /** + * {@inheritdoc} + */ + public function setOwnerId($uid) { + $this->set('user_id', $uid); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setOwner(UserInterface $account) { + $this->set('user_id', $account->id()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function isPublished() { + return (bool) $this->getEntityKey('status'); + } + + /** + * {@inheritdoc} + */ + public function setPublished($published) { + $this->set('status', $published ? NODE_PUBLISHED : NODE_NOT_PUBLISHED); + return $this; + } + + /** + * Default value callback for 'fedora_has_parent' base field definition. + * + * @see ::baseFieldDefinitions() + * + * @return array + * An array of default values. + */ + public static function getFedoraRoot() { + return array('root'); + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields['id'] = BaseFieldDefinition::create('integer') + ->setLabel(t('ID')) + ->setDescription(t('The ID of the Fedora resource entity.')) + ->setReadOnly(TRUE); + $fields['type'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Type')) + ->setDescription(t('The Fedora resource type/bundle.')) + ->setSetting('target_type', 'fedora_resource_type') + ->setRequired(TRUE); + $fields['uuid'] = BaseFieldDefinition::create('uuid') + ->setLabel(t('UUID')) + ->setDescription(t('The UUID of the Fedora resource entity.')) + ->setReadOnly(TRUE); + + $fields['user_id'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Authored by')) + ->setDescription(t('The user ID of author of the Fedora resource entity.')) + ->setRevisionable(TRUE) + ->setSetting('target_type', 'user') + ->setSetting('handler', 'default') + ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId') + ->setTranslatable(TRUE) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'author', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'entity_reference_autocomplete', + 'weight' => 5, + 'settings' => array( + 'match_operator' => 'CONTAINS', + 'size' => '60', + 'autocomplete_type' => 'tags', + 'placeholder' => '', + ), + )) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['fedora_has_parent'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Fedora has Parent')) + ->setDescription(t('Parent Fedora Resource.')) + ->setRevisionable(TRUE) + ->setSetting('target_type', 'fedora_resource') + ->setSetting('handler', 'default') + ->setDefaultValueCallback('Drupal\islandoraClaw\Entity\FedoraResource::getFedoraRoot') + ->setTranslatable(TRUE) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'author', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'entity_reference_autocomplete', + 'weight' => 5, + 'settings' => array( + 'match_operator' => 'CONTAINS', + 'size' => '60', + 'autocomplete_type' => 'tags', + 'placeholder' => '', + ), + )) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['ldp_containes'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('LDP Contains')) + ->setDescription(t('Contains Fedora Resource.')) + ->setRevisionable(TRUE) + ->setSetting('target_type', 'fedora_resource_type') + ->setSetting('handler', 'default') + ->setTranslatable(TRUE) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'author', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'entity_reference_autocomplete', + 'weight' => 5, + 'settings' => array( + 'match_operator' => 'CONTAINS', + 'size' => '60', + 'autocomplete_type' => 'tags', + 'placeholder' => '', + ), + )) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['name'] = BaseFieldDefinition::create('string') + ->setLabel(t('Name')) + ->setDescription(t('The name of the Fedora resource entity.')) + ->setSettings(array( + 'max_length' => 50, + 'text_processing' => 0, + )) + ->setDefaultValue('') + ->setDisplayOptions('view', array( + 'label' => 'above', + 'type' => 'string', + 'weight' => -4, + )) + ->setDisplayOptions('form', array( + 'type' => 'string_textfield', + 'weight' => -4, + )) + ->setDisplayConfigurable('form', TRUE) + ->setDisplayConfigurable('view', TRUE); + + $fields['status'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Publishing status')) + ->setDescription(t('A boolean indicating whether the Fedora resource is published.')) + ->setDefaultValue(TRUE); + + $fields['langcode'] = BaseFieldDefinition::create('language') + ->setLabel(t('Language code')) + ->setDescription(t('The language code for the Fedora resource entity.')) + ->setDisplayOptions('form', array( + 'type' => 'language_select', + 'weight' => 10, + )) + ->setDisplayConfigurable('form', TRUE); + + $fields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Created')) + ->setDescription(t('The time that the entity was created.')); + + $fields['changed'] = BaseFieldDefinition::create('changed') + ->setLabel(t('Changed')) + ->setDescription(t('The time that the entity was last edited.')); + + $fields['promote'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Promoted to front page')) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE) + ->setDefaultValue(TRUE) + ->setDisplayOptions('form', array( + 'type' => 'boolean_checkbox', + 'settings' => array( + 'display_label' => TRUE, + ), + 'weight' => 15, + )) + ->setDisplayConfigurable('form', TRUE); + + $fields['sticky'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Sticky at top of lists')) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE) + ->setDefaultValue(FALSE) + ->setDisplayOptions('form', array( + 'type' => 'boolean_checkbox', + 'settings' => array( + 'display_label' => TRUE, + ), + 'weight' => 16, + )) + ->setDisplayConfigurable('form', TRUE); + + $fields['revision_timestamp'] = BaseFieldDefinition::create('created') + ->setLabel(t('Revision timestamp')) + ->setDescription(t('The time that the current revision was created.')) + ->setQueryable(FALSE) + ->setRevisionable(TRUE); + + $fields['revision_uid'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Revision user ID')) + ->setDescription(t('The user ID of the author of the current revision.')) + ->setSetting('target_type', 'user') + ->setQueryable(FALSE) + ->setRevisionable(TRUE); + + $fields['revision_log'] = BaseFieldDefinition::create('string_long') + ->setLabel(t('Revision log message')) + ->setDescription(t('Briefly describe the changes you have made.')) + ->setRevisionable(TRUE) + ->setDefaultValue('') + ->setDisplayOptions('form', array( + 'type' => 'string_textarea', + 'weight' => 25, + 'settings' => array( + 'rows' => 4, + ), + )); + + $fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean') + ->setLabel(t('Revision translation affected')) + ->setDescription(t('Indicates if the last edit of a translation belongs to current revision.')) + ->setReadOnly(TRUE) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE); + return $fields; + } + +} diff --git a/src/Entity/FedoraResourceType.php b/src/Entity/FedoraResourceType.php new file mode 100644 index 00000000..fc81fc36 --- /dev/null +++ b/src/Entity/FedoraResourceType.php @@ -0,0 +1,58 @@ + 'id', + 'title' => $this->t('Fedora resource'), + 'help' => $this->t('The Fedora resource ID.'), + ); + + return $data; + } + +} diff --git a/src/FedoraResource.php b/src/FedoraResource.php new file mode 100644 index 00000000..d5ee5b6b --- /dev/null +++ b/src/FedoraResource.php @@ -0,0 +1,22 @@ +isPublished()) { + return AccessResult::allowedIfHasPermission($account, 'view unpublished fedora resource entities'); + } + return AccessResult::allowedIfHasPermission($account, 'view published fedora resource entities'); + + case 'update': + return AccessResult::allowedIfHasPermission($account, 'edit fedora resource entities'); + + case 'delete': + return AccessResult::allowedIfHasPermission($account, 'delete fedora resource entities'); + } + + // Unknown operation, no opinion. + return AccessResult::neutral(); + } + + /** + * {@inheritdoc} + */ + protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { + return AccessResult::allowedIfHasPermission($account, 'add fedora resource entities'); + } + +} diff --git a/src/FedoraResourceHtmlRouteProvider.php b/src/FedoraResourceHtmlRouteProvider.php new file mode 100644 index 00000000..865312a4 --- /dev/null +++ b/src/FedoraResourceHtmlRouteProvider.php @@ -0,0 +1,149 @@ +id(); + + if ($collection_route = $this->getCollectionRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.collection", $collection_route); + } + + if ($add_form_route = $this->getAddFormRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.add_form", $add_form_route); + } + + $add_page_route = $this->getAddPageRoute($entity_type); + $collection->add("$entity_type_id.add_page", $add_page_route); + + if ($settings_form_route = $this->getSettingsFormRoute($entity_type)) { + $collection->add("$entity_type_id.settings", $settings_form_route); + } + + return $collection; + } + + /** + * Gets the collection route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getCollectionRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass()) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('collection')); + $route + ->setDefaults([ + '_entity_list' => $entity_type_id, + '_title' => "{$entity_type->getLabel()} list", + ]) + ->setRequirement('_permission', 'view fedora resource entities') + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the add-form route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getAddFormRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('add-form')) { + $entity_type_id = $entity_type->id(); + $parameters = [ + $entity_type_id => ['type' => 'entity:' . $entity_type_id], + ]; + + $route = new Route($entity_type->getLinkTemplate('add-form')); + $bundle_entity_type_id = $entity_type->getBundleEntityType(); + // Content entities with bundles are added via a dedicated controller. + $route + ->setDefaults([ + '_controller' => 'Drupal\islandoraclaw\Controller\FedoraResourceAddController::addForm', + '_title_callback' => 'Drupal\islandoraclaw\Controller\FedoraResourceAddController::getAddFormTitle', + ]) + ->setRequirement('_entity_create_access', $entity_type_id . ':{' . $bundle_entity_type_id . '}'); + $parameters[$bundle_entity_type_id] = ['type' => 'entity:' . $bundle_entity_type_id]; + + $route + ->setOption('parameters', $parameters) + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the add page route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getAddPageRoute(EntityTypeInterface $entity_type) { + $route = new Route("/admin/structure/{$entity_type->id()}/add"); + $route + ->setDefaults([ + '_controller' => 'Drupal\islandoraclaw\Controller\FedoraResourceAddController::add', + '_title' => "Add {$entity_type->getLabel()}", + ]) + ->setRequirement('_entity_create_access', $entity_type->id()) + ->setOption('_admin_route', TRUE); + + return $route; + } + + /** + * Gets the settings form route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getSettingsFormRoute(EntityTypeInterface $entity_type) { + if (!$entity_type->getBundleEntityType()) { + $route = new Route("/admin/structure/{$entity_type->id()}/settings"); + $route + ->setDefaults([ + '_form' => 'Drupal\islandoraclaw\Form\FedoraResourceSettingsForm', + '_title' => "{$entity_type->getLabel()} settings", + ]) + ->setRequirement('_permission', $entity_type->getAdminPermission()) + ->setOption('_admin_route', TRUE); + + return $route; + } + } + +} diff --git a/src/FedoraResourceInterface.php b/src/FedoraResourceInterface.php new file mode 100644 index 00000000..7a6ff42f --- /dev/null +++ b/src/FedoraResourceInterface.php @@ -0,0 +1,85 @@ +t('Fedora resource ID'); + $header['name'] = $this->t('Name'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /* @var $entity \Drupal\islandoraclaw\Entity\FedoraResource */ + $row['id'] = $entity->id(); + $row['name'] = $this->l( + $entity->label(), + new Url( + 'entity.fedora_resource.edit_form', array( + 'fedora_resource' => $entity->id(), + ) + ) + ); + return $row + parent::buildRow($entity); + } + +} diff --git a/src/FedoraResourceTypeHtmlRouteProvider.php b/src/FedoraResourceTypeHtmlRouteProvider.php new file mode 100644 index 00000000..ec66688a --- /dev/null +++ b/src/FedoraResourceTypeHtmlRouteProvider.php @@ -0,0 +1,96 @@ +id(); + + if ($collection_route = $this->getCollectionRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.collection", $collection_route); + } + + if ($add_form_route = $this->getAddFormRoute($entity_type)) { + $collection->add("entity.{$entity_type_id}.add_form", $add_form_route); + } + + return $collection; + } + + /** + * Gets the collection route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getCollectionRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('collection') && $entity_type->hasListBuilderClass()) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('collection')); + $route + ->setDefaults([ + '_entity_list' => $entity_type_id, + // Make sure this is not a TranslatableMarkup object as the + // TitleResolver translates this string again. + '_title' => (string) $entity_type->getLabel(), + ]) + ->setRequirement('_permission', $entity_type->getAdminPermission()) + ->setOption('_admin_route', TRUE); + + return $route; + } + } + + /** + * Gets the add-form route. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * + * @return \Symfony\Component\Routing\Route|null + * The generated route, if available. + */ + protected function getAddFormRoute(EntityTypeInterface $entity_type) { + if ($entity_type->hasLinkTemplate('add-form')) { + $entity_type_id = $entity_type->id(); + $route = new Route($entity_type->getLinkTemplate('add-form')); + // Use the add form handler, if available, otherwise default. + $operation = 'default'; + if ($entity_type->getFormClass('add')) { + $operation = 'add'; + } + $route + ->setDefaults([ + '_entity_form' => "{$entity_type_id}.{$operation}", + '_title' => "Add {$entity_type->getLabel()}", + ]) + ->setRequirement('_entity_create_access', $entity_type_id) + ->setOption('parameters', [ + $entity_type_id => ['type' => 'entity:' . $entity_type_id], + ]) + ->setOption('_admin_route', TRUE); + + return $route; + } + } + +} diff --git a/src/FedoraResourceTypeInterface.php b/src/FedoraResourceTypeInterface.php new file mode 100644 index 00000000..750c9669 --- /dev/null +++ b/src/FedoraResourceTypeInterface.php @@ -0,0 +1,13 @@ +t('Fedora resource type'); + $header['id'] = $this->t('Machine name'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + $row['label'] = $entity->label(); + $row['id'] = $entity->id(); + // You probably want a few more properties here... + return $row + parent::buildRow($entity); + } + +} diff --git a/src/Form/FedoraResourceDeleteForm.php b/src/Form/FedoraResourceDeleteForm.php new file mode 100644 index 00000000..5092667e --- /dev/null +++ b/src/Form/FedoraResourceDeleteForm.php @@ -0,0 +1,15 @@ +entity; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $entity = $this->entity; + $status = parent::save($form, $form_state); + + switch ($status) { + case SAVED_NEW: + drupal_set_message($this->t('Created the %label Fedora resource.', [ + '%label' => $entity->label(), + ])); + break; + + default: + drupal_set_message($this->t('Saved the %label Fedora resource.', [ + '%label' => $entity->label(), + ])); + } + $form_state->setRedirect('entity.fedora_resource.canonical', ['fedora_resource' => $entity->id()]); + } + +} diff --git a/src/Form/FedoraResourceSettingsForm.php b/src/Form/FedoraResourceSettingsForm.php new file mode 100644 index 00000000..ccc324a8 --- /dev/null +++ b/src/Form/FedoraResourceSettingsForm.php @@ -0,0 +1,55 @@ +t('Are you sure you want to delete %name?', array('%name' => $this->entity->label())); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('entity.fedora_resource_type.collection'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Delete'); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $this->entity->delete(); + + drupal_set_message( + $this->t('content @type: deleted @label.', + [ + '@type' => $this->entity->bundle(), + '@label' => $this->entity->label(), + ] + ) + ); + + $form_state->setRedirectUrl($this->getCancelUrl()); + } + +} diff --git a/src/Form/FedoraResourceTypeForm.php b/src/Form/FedoraResourceTypeForm.php new file mode 100644 index 00000000..a36c71b7 --- /dev/null +++ b/src/Form/FedoraResourceTypeForm.php @@ -0,0 +1,67 @@ +entity; + $form['label'] = array( + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#maxlength' => 255, + '#default_value' => $fedora_resource_type->label(), + '#description' => $this->t("Label for the Fedora resource type."), + '#required' => TRUE, + ); + + $form['id'] = array( + '#type' => 'machine_name', + '#default_value' => $fedora_resource_type->id(), + '#machine_name' => array( + 'exists' => '\Drupal\islandoraclaw\Entity\FedoraResourceType::load', + ), + '#disabled' => !$fedora_resource_type->isNew(), + ); + + /* You will need additional form elements for your custom properties. */ + + return $form; + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $fedora_resource_type = $this->entity; + $status = $fedora_resource_type->save(); + + switch ($status) { + case SAVED_NEW: + drupal_set_message($this->t('Created the %label Fedora resource type.', [ + '%label' => $fedora_resource_type->label(), + ])); + break; + + default: + drupal_set_message($this->t('Saved the %label Fedora resource type.', [ + '%label' => $fedora_resource_type->label(), + ])); + } + $form_state->setRedirectUrl($fedora_resource_type->urlInfo('collection')); + } + +} diff --git a/src/Plugin/Search/FedoraEntitySearch.php b/src/Plugin/Search/FedoraEntitySearch.php new file mode 100644 index 00000000..2b3c56a7 --- /dev/null +++ b/src/Plugin/Search/FedoraEntitySearch.php @@ -0,0 +1,842 @@ + array( + 'column' => 'c.name', + ), + ); + + /** + * A constant for setting and checking the query string. + */ + const ADVANCED_FORM = 'advanced-form'; + + /** + * {@inheritdoc} + */ + static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('database'), + $container->get('entity.manager'), + $container->get('module_handler'), + $container->get('config.factory')->get('search.settings'), + $container->get('language_manager'), + $container->get('renderer'), + $container->get('current_user') + ); + } + + /** + * Constructs \Drupal\islandoraclaw\Plugin\Search\FedoraResourceSearch. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Database\Connection $database + * A database connection object. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * An entity manager object. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * A module manager object. + * @param \Drupal\Core\Config\Config $search_settings + * A config object for 'search.settings'. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Session\AccountInterface $account + * The $account object to use for checking for access to advanced search. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, AccountInterface $account = NULL) { + $this->database = $database; + $this->entityManager = $entity_manager; + $this->moduleHandler = $module_handler; + $this->searchSettings = $search_settings; + $this->languageManager = $language_manager; + $this->renderer = $renderer; + $this->account = $account; + parent::__construct($configuration, $plugin_id, $plugin_definition); + + $this->addCacheTags(['fedora_resource_list']); + } + + /** + * {@inheritdoc} + */ + public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) { + $result = AccessResult::allowedIfHasPermission($account, 'access content'); + return $return_as_object ? $result : $result->isAllowed(); + } + + /** + * {@inheritdoc} + */ + public function isSearchExecutable() { + // Contact search is executable if we have keywords or an advanced + // parameter. + // At least, we should parse out the parameters and see if there are any + // keyword matches in that case, rather than just printing out the + // "Please enter keywords" message. + return !empty($this->keywords) || (isset($this->searchParameters['f']) && count($this->searchParameters['f'])); + } + + /** + * {@inheritdoc} + */ + public function getType() { + return $this->getPluginId(); + } + + /** + * {@inheritdoc} + */ + public function execute() { + if ($this->isSearchExecutable()) { + $results = $this->findResults(); + + if ($results) { + return $this->prepareResults($results); + } + } + + return array(); + } + + /** + * Queries to find search results, and sets status messages. + * + * This method can assume that $this->isSearchExecutable() has already been + * checked and returned TRUE. + * + * @return \Drupal\Core\Database\StatementInterface|null + * Results from search query execute() method, or NULL if the search + * failed. + */ + protected function findResults() { + + $keys = $this->keywords; + + // Build matching conditions. + $query = $this->database + ->select('search_index', 'i', array('target' => 'replica')) + ->extend('Drupal\search\SearchQuery') + ->extend('Drupal\Core\Database\Query\PagerSelectExtender'); + + // Join on {contact} and {users_field_data} to get user's published status. + // This join on {contact} also serves the advanced search items + // (in $this->advanced) that are in {contact}. + $query->join('contact', 'c', 'c.id = i.sid'); + $query->join('users_field_data', 'ufd', 'ufd.uid = c.user_id'); + $query->condition('ufd.status', 1); + + $query + ->searchExpression($keys, $this->getPluginId()); + + // @todo =============== + // Handle advanced search filters in the f query string. + // \Drupal::request()->query->get('f') is an array that looks like this in + // the URL: ?f[]=first_name:Jane&f[]=gender:f + // So $parameters['f'] looks like: + // array('first_name:Jane', 'gender:f'); + // We need to parse this out into query conditions, some of which go into + // the keywords string, and some of which are separate conditions. + // + // Note: advanced form fields are added in searchFormAlter(), + // and the URL containing the "f" parameter is built in + // buildSearchUrlQuery(). + $parameters = $this->getParameters(); + if (!empty($parameters['f']) && is_array($parameters['f'])) { + // @todo This loop should probably be moved to a helper function. + $filters = array(); + // Match any query value that is an expected option and a value + // separated by ':' like 'first_name:Jane'. + $pattern = '/^(' . implode('|', array_keys($this->advanced)) . '):([^ ]*)/i'; + foreach ($parameters['f'] as $item) { + if (preg_match($pattern, $item, $m)) { + // Use the matched value as the array key to eliminate duplicates. + $filters[$m[1]][$m[2]] = $m[2]; + } + } + + // Now turn these into query conditions. This assumes that everything in + // $filters is a known type of advanced search as defined in + // $this->advanced. + foreach ($filters as $option => $matched) { + $info = $this->advanced[$option]; + // Insert additional conditions. By default, all use the OR operator. + $operator = empty($info['operator']) ? 'OR' : $info['operator']; + $where = new Condition($operator); + foreach ($matched as $value) { + $where->condition($info['column'], $value); + } + $query->condition($where); + if (!empty($info['join'])) { + $query->join($info['join']['table'], $info['join']['alias'], $info['join']['condition']); + } + } + } + // =============== + // Add the ranking expressions. + $this->addFedoraResourceRankings($query); + + // Run the query. + $find = $query + // Add the language code of the indexed item to the result of the query, + // since the entity will be rendered using the respective language. + ->fields('i', array('langcode')) + // And since SearchQuery makes these into GROUP BY queries, if we add + // a field, for PostgreSQL we also need to make it an aggregate or a + // GROUP BY. In this case, we want GROUP BY. + ->groupBy('i.langcode') + ->limit(10) + ->execute(); + + // Check query status and set messages if needed. + $status = $query->getStatus(); + + if ($status & SearchQuery::EXPRESSIONS_IGNORED) { + drupal_set_message($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $this->searchSettings->get('and_or_limit'))), 'warning'); + } + + if ($status & SearchQuery::LOWER_CASE_OR) { + drupal_set_message($this->t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.'), 'warning'); + } + + if ($status & SearchQuery::NO_POSITIVE_KEYWORDS) { + drupal_set_message($this->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one keyword to match in the content, and punctuation is ignored.', 'You must include at least one keyword to match in the content. Keywords must be at least @count characters, and punctuation is ignored.'), 'warning'); + } + + return $find; + } + + /** + * Prepares search results for rendering. + * + * @param \Drupal\Core\Database\StatementInterface $found + * Results found from a successful search query execute() method. + * + * @return array + * Array of search result item render arrays (empty array if no results). + */ + protected function prepareResults(StatementInterface $found) { + $results = array(); + + // 'fedora_resource' comes from the entity type id declared + // in the annotation for \Drupal\islandoraclaw\Entity\FedoraResource. + // Replace this with your entity's type id. + $entity_storage = $this->entityManager->getStorage('fedora_resource'); + $entity_render = $this->entityManager->getViewBuilder('fedora_resource'); + $keys = $this->keywords; + + foreach ($found as $item) { + // Render the contact. + /** @var \Drupal\content_entity_example\ContactInterface $entity */ + $entity = $entity_storage->load($item->sid)->getTranslation($item->langcode); + $build = $entity_render->view($entity, 'search_result', $item->langcode); + + unset($build['#theme']); + // Uncomment to use removeFromSnippet() for excluding data from snippet. + /* $build['#pre_render'][] = array($this, 'removeFromSnippet'); */ + + // Build the snippet. + $rendered = $this->renderer->renderPlain($build); + $this->addCacheableDependency(CacheableMetadata::createFromRenderArray($build)); + // Allow other modules to add to snippet. + $rendered .= ' ' . $this->moduleHandler->invokeAll('fedora_resource_update_index', [$entity]); + + $extra = $this->moduleHandler->invokeAll('fedora_resource_search_result', [$entity]); + + $language = $this->languageManager->getLanguage($item->langcode); + + $result = array( + 'link' => $entity->url( + 'canonical', + array( + 'absolute' => TRUE, + 'language' => $language, + ) + ), + 'type' => 'Fedora Resource', + 'title' => $entity->label(), + 'contact' => $entity, + 'extra' => $extra, + 'score' => $item->calculated_score, + 'snippet' => search_excerpt($keys, $rendered, $item->langcode), + 'langcode' => $entity->language()->getId(), + ); + + $this->addCacheableDependency($entity); + + // We have to separately add the contact owner's cache tags because search + // module doesn't use the rendering system, it does its own rendering + // without taking cacheability metadata into account. So we have to do it + // explicitly here. + $this->addCacheableDependency($entity->getOwner()); + + // @codingStandardsIgnoreStart + // Uncomment this to display owner name and last changed time. + // $username = array( + // '#theme' => 'username', + // '#account' => $entity->getOwner(), + // ); + // $result += array( + // 'user' => $this->renderer->renderPlain($username), + // 'date' => $entity->getChangedTime(), + // ); + // @codingStandardsIgnoreEnd + $results[] = $result; + + } + return $results; + } + + /** + * Removes results data from the build array. + * + * This information is being removed from the rendered entity that is used to + * build the search result snippet. + * + * @param array $build + * The build array. + * + * @return array + * The modified build array. + */ + public function removeFromSnippet(array $build) { + // Code to remove arbitrary data from $build goes here. + // Examples: + // - unset($build['created']); + // - unset($build['uid']);. + return $build; + } + + /** + * Adds the configured rankings to the search query. + * + * @param SelectExtender $query + * A query object that has been extended with the Search DB Extender. + */ + protected function addFedoraResourceRankings(SelectExtender $query) { + if ($ranking = $this->getRankings()) { + $tables = &$query->getTables(); + foreach ($ranking as $rank => $values) { + if (isset($this->configuration['rankings'][$rank]) && !empty($this->configuration['rankings'][$rank])) { + $entity_rank = $this->configuration['rankings'][$rank]; + // If the table defined in the ranking isn't already joined, add it. + if (isset($values['join']) && !isset($tables[$values['join']['alias']])) { + $query->addJoin($values['join']['type'], $values['join']['table'], $values['join']['alias'], $values['join']['on']); + } + $arguments = isset($values['arguments']) ? $values['arguments'] : array(); + $query->addScore($values['score'], $arguments, $entity_rank); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function updateIndex() { + // Interpret the cron limit setting as the maximum number of entities to + // index per cron run. + $limit = (int) $this->searchSettings->get('index.cron_limit'); + + $result = $this->database->queryRange("SELECT c.id, MAX(sd.reindex) FROM {fedora_resource} c LEFT JOIN {search_dataset} sd ON sd.sid = c.id AND sd.type = :type WHERE sd.sid IS NULL OR sd.reindex <> 0 GROUP BY c.id ORDER BY MAX(sd.reindex) is null DESC, MAX(sd.reindex) ASC, c.id ASC", 0, $limit, array(':type' => $this->getPluginId()), array('target' => 'replica')); + + $rids = $result->fetchCol(); + if (!$rids) { + return; + } + + // 'fedora_resource' comes from the entity type id declared + // in the annotation for \Drupal\islandoraclaw\Entity\FedoraResource. + // Replace this with your entity's type id. + $entity_storage = $this->entityManager->getStorage('fedora_resource'); + + foreach ($entity_storage->loadMultiple($rids) as $entity) { + $this->indexFedoraResource($entity); + } + } + + /** + * Indexes a single contact. + * + * @param \Drupal\content_entity_example\ContactInterface $entity + * The contact to index. + */ + protected function indexFedoraResource(FedoraResourceInterface $entity) { + $languages = $entity->getTranslationLanguages(); + // 'content_entity_example_contact' comes from the entity type id declared + // in the annotation for \Drupal\content_entity_example\Entity\Contact. + // Replace this with your entity's type id. + $entity_render = $this->entityManager->getViewBuilder('fedora_resource'); + + foreach ($languages as $language) { + $entity = $entity->getTranslation($language->getId()); + // Render the contact. + $build = $entity_render->view($entity, 'search_index', $language->getId()); + + unset($build['#theme']); + + // Add the title to text so it is searchable. + $build['search_title'] = [ + '#prefix' => '', + '#plain_text' => $entity->label(), + '#suffix' => '', + '#weight' => -1000, + ]; + $text = $this->renderer->renderPlain($build); + + // Fetch extra data normally not visible. + $extra = $this->moduleHandler->invokeAll('fedora_resource_update_index', [$entity]); + foreach ($extra as $t) { + $text .= $t; + } + + // Update index, using search index "type" equal to the plugin ID. + search_index($this->getPluginId(), $entity->id(), $language->getId(), $text); + } + } + + /** + * {@inheritdoc} + */ + public function indexClear() { + // All ContactSearch pages share a common search index "type" equal to + // the plugin ID. + search_index_clear($this->getPluginId()); + } + + /** + * {@inheritdoc} + */ + public function markForReindex() { + // All ContactSearch pages share a common search index "type" equal to + // the plugin ID. + search_mark_for_reindex($this->getPluginId()); + } + + /** + * {@inheritdoc} + */ + public function indexStatus() { + + $total = $this->database->query('SELECT COUNT(*) FROM {fedora_resource}')->fetchField(); + $remaining = $this->database->query("SELECT COUNT(DISTINCT c.id) FROM {contact} c LEFT JOIN {search_dataset} sd ON sd.sid = c.id AND sd.type = :type WHERE sd.sid IS NULL OR sd.reindex <> 0", array(':type' => $this->getPluginId()))->fetchField(); + + return array('remaining' => $remaining, 'total' => $total); + } + + /** + * {@inheritdoc} + */ + public function searchFormAlter(array &$form, FormStateInterface $form_state) { + $parameters = $this->getParameters(); + $keys = $this->getKeywords(); + $used_advanced = !empty($parameters[self::ADVANCED_FORM]); + if ($used_advanced) { + $f = isset($parameters['f']) ? (array) $parameters['f'] : array(); + $defaults = $this->parseAdvancedDefaults($f, $keys); + } + else { + $defaults = array('keys' => $keys); + } + + $form['basic']['keys']['#default_value'] = $defaults['keys']; + + // Add advanced search keyword-related boxes. + $form['advanced'] = array( + '#type' => 'details', + '#title' => t('Advanced search'), + '#attributes' => array('class' => array('search-advanced')), + '#access' => $this->account && $this->account->hasPermission('use advanced search'), + '#open' => $used_advanced, + ); + $form['advanced']['keywords-fieldset'] = array( + '#type' => 'fieldset', + '#title' => t('Keywords'), + ); + + $form['advanced']['keywords-fieldset']['keywords']['or'] = array( + '#type' => 'textfield', + '#title' => t('Containing any of the words'), + '#size' => 30, + '#maxlength' => 255, + '#default_value' => isset($defaults['or']) ? $defaults['or'] : '', + ); + + $form['advanced']['keywords-fieldset']['keywords']['phrase'] = array( + '#type' => 'textfield', + '#title' => t('Containing the phrase'), + '#size' => 30, + '#maxlength' => 255, + '#default_value' => isset($defaults['phrase']) ? $defaults['phrase'] : '', + ); + + $form['advanced']['keywords-fieldset']['keywords']['negative'] = array( + '#type' => 'textfield', + '#title' => t('Containing none of the words'), + '#size' => 30, + '#maxlength' => 255, + '#default_value' => isset($defaults['negative']) ? $defaults['negative'] : '', + ); + + $form['advanced']['misc-fieldset'] = array( + '#type' => 'fieldset', + ); + + // \Drupal\search\SearchQuery requires that there be valid keywords + // submitted in the standard fields. + $form['advanced']['misc-fieldset']['note'] = array( + '#markup' => t('You must still enter keyword(s) above when using these fields.'), + '#weight' => -10, + ); + + $form['advanced']['misc-fieldset']['name'] = array( + '#type' => 'textfield', + '#title' => t('Name'), + '#description' => t('Search %field field for exact matches.', array('%field' => 'Name')), + '#default_value' => isset($defaults['name']) ? $defaults['name'] : array(), + ); + + $form['advanced']['submit'] = array( + '#type' => 'submit', + '#value' => t('Advanced search'), + '#prefix' => '', + '#suffix' => '', + '#weight' => 100, + ); + } + + /** + * {@inheritdoc} + */ + public function buildSearchUrlQuery(FormStateInterface $form_state) { + // Read keyword and advanced search information from the form values, + // and put these into the GET parameters. + $keys = trim($form_state->getValue('keys')); + $advanced = FALSE; + + // Collect extra filters. + $filters = array(); + + // Advanced form, custom_content_entity_example_contact fields. + if ($form_state->hasValue('name') && !empty(($value = trim($form_state->getValue('name'))))) { + $filters[] = 'name:' . $value; + $advanced = TRUE; + } + + + // Advanced form, keywords fields. + if ($form_state->getValue('or') != '') { + if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('or'), $matches)) { + $keys .= ' ' . implode(' OR ', $matches[1]); + $advanced = TRUE; + } + } + if ($form_state->getValue('negative') != '') { + if (preg_match_all('/ ("[^"]+"|[^" ]+)/i', ' ' . $form_state->getValue('negative'), $matches)) { + $keys .= ' -' . implode(' -', $matches[1]); + $advanced = TRUE; + } + } + if ($form_state->getValue('phrase') != '') { + $keys .= ' "' . str_replace('"', ' ', $form_state->getValue('phrase')) . '"'; + $advanced = TRUE; + } + $keys = trim($keys); + + // Put the keywords and advanced parameters into GET parameters. Make sure + // to put keywords into the query even if it is empty, because the page + // controller uses that to decide it's time to check for search results. + $query = array('keys' => $keys); + if ($filters) { + $query['f'] = $filters; + } + // Record that the person used the advanced search form, if they did. + if ($advanced) { + $query[self::ADVANCED_FORM] = '1'; + } + + return $query; + } + + /** + * Parses the advanced search form default values. + * + * @param array $f + * The 'f' query parameter set up in self::buildUrlSearchQuery(), which + * contains the advanced query values. + * @param string $keys + * The search keywords string, which contains some information from the + * advanced search form. + * + * @return array + * Array of default form values for the advanced search form, including + * a modified 'keys' element for the bare search keywords. + */ + protected function parseAdvancedDefaults($f, $keys) { + $defaults = array(); + + // Split out the advanced search parameters. + foreach ($f as $advanced) { + list($key, $value) = explode(':', $advanced, 2); + if (!isset($defaults[$key])) { + $defaults[$key] = array(); + } + $defaults[$key][] = $value; + } + + // Split out the negative, phrase, and OR parts of keywords. + // For phrases, the form only supports one phrase. + $matches = array(); + $keys = ' ' . $keys . ' '; + if (preg_match('/ "([^"]+)" /', $keys, $matches)) { + $keys = str_replace($matches[0], ' ', $keys); + $defaults['phrase'] = $matches[1]; + } + + // Negative keywords: pull all of them out. + if (preg_match_all('/ -([^ ]+)/', $keys, $matches)) { + $keys = str_replace($matches[0], ' ', $keys); + $defaults['negative'] = implode(' ', $matches[1]); + } + + // OR keywords: pull up to one set of them out of the query. + if (preg_match('/ [^ ]+( OR [^ ]+)+ /', $keys, $matches)) { + $keys = str_replace($matches[0], ' ', $keys); + $words = explode(' OR ', trim($matches[0])); + $defaults['or'] = implode(' ', $words); + } + + // Put remaining keywords string back into keywords. + $defaults['keys'] = trim($keys); + + return $defaults; + } + + /** + * Gathers ranking definitions from hook_ranking(). + * + * @return array + * An array of ranking definitions. + */ + protected function getRankings() { + if (!$this->rankings) { + $this->rankings = $this->moduleHandler->invokeAll('ranking'); + } + return $this->rankings; + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + $configuration = array( + 'rankings' => array(), + ); + return $configuration; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + // Output form for defining rank factor weights. + $form['content_ranking'] = array( + '#type' => 'details', + '#title' => t('Content ranking'), + '#open' => TRUE, + ); + $form['content_ranking']['info'] = array( + '#markup' => '' . $this->t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '', + ); + // Prepare table. + $header = [$this->t('Factor'), $this->t('Influence')]; + $form['content_ranking']['rankings'] = array( + '#type' => 'table', + '#header' => $header, + ); + + // Note: reversed to reflect that higher number = higher ranking. + $range = range(0, 10); + $options = array_combine($range, $range); + foreach ($this->getRankings() as $var => $values) { + $form['content_ranking']['rankings'][$var]['name'] = array( + '#markup' => $values['title'], + ); + $form['content_ranking']['rankings'][$var]['value'] = array( + '#type' => 'select', + '#options' => $options, + '#attributes' => ['aria-label' => $this->t("Influence of '@title'", ['@title' => $values['title']])], + '#default_value' => isset($this->configuration['rankings'][$var]) ? $this->configuration['rankings'][$var] : 0, + ); + } + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + foreach ($this->getRankings() as $var => $values) { + if (!$form_state->isValueEmpty(['rankings', $var, 'value'])) { + $this->configuration['rankings'][$var] + = $form_state->getValue(['rankings', $var, 'value']); + } + else { + unset($this->configuration['rankings'][$var]); + } + } + } + +} \ No newline at end of file diff --git a/templates/fedora-resource-content-add-list.html.twig b/templates/fedora-resource-content-add-list.html.twig new file mode 100644 index 00000000..edb8bddd --- /dev/null +++ b/templates/fedora-resource-content-add-list.html.twig @@ -0,0 +1,23 @@ +{# +/** + * @file + * Default theme implementation to present a list of custom content entity types/bundles. + * + * Available variables: + * - types: A collection of all the available custom entity types/bundles. + * Each type/bundle contains the following: + * - link: A link to add a content entity of this type. + * - description: A description of this content entity types/bundle. + * + * @see template_preprocess_fedora_resource_content_add_list() + * + * @ingroup themeable + */ +#} +{% spaceless %} +
+ {% for type in types %} +
{{ type.link }}
+ {% endfor %} +
+{% endspaceless %} diff --git a/templates/fedora_resource.html.twig b/templates/fedora_resource.html.twig new file mode 100644 index 00000000..bfdd80d1 --- /dev/null +++ b/templates/fedora_resource.html.twig @@ -0,0 +1,22 @@ +{# +/** + * @file fedora_resource.html.twig + * Default theme implementation to present Fedora resource data. + * + * This template is used when viewing Fedora resource pages. + * + * + * Available variables: + * - content: A list of content items. Use 'content' to print all content, or + * - attributes: HTML attributes for the container element. + * + * @see template_preprocess_fedora_resource() + * + * @ingroup themeable + */ +#} + + {% if content %} + {{- content -}} + {% endif %} +