You are here

class AmpMetadataForm in Accelerated Mobile Pages (AMP) 8

Class AmpMetadataForm.

@package Drupal\amp\Form

Hierarchy

Expanded class hierarchy of AmpMetadataForm

File

src/Form/AmpMetadataForm.php, line 25

Namespace

Drupal\amp\Form
View source
class AmpMetadataForm extends EntityForm implements ContainerAwareInterface {

  /**
   * The container.
   *
   * @var \Symfony\Component\DependencyInjection\ContainerInterface
   */
  protected $container;

  /**
   * The AMP metadata info service.
   *
   * @var \Drupal\amp\AmpMetadataInfo
   */
  protected $ampMetadataInfo;

  /**
   * The file usage service.
   *
   * @var \Drupal\file\FileUsage\DatabaseFileUsageBackend
   */
  protected $fileUsage;

  /**
   * The cache tags invalidator.
   *
   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
   */
  protected $tagInvalidate;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Constructs a AmpMetadataForm object.
   *
   * @param \Drupal\amp\AmpMetadataInfo $amp_metadata_info
   *   The AMP metadata info service.
   * @param \Drupal\file\FileUsage\DatabaseFileUsageBackend
   *   The file usage service.
   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $tag_invalidate
   *   The cache tags invalidator.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   */
  public function __construct(AmpMetadataInfo $amp_metadata_info, DatabaseFileUsageBackend $file_usage, CacheTagsInvalidatorInterface $tag_invalidate, ModuleHandlerInterface $module_handler) {
    $this->ampMetadataInfo = $amp_metadata_info;
    $this->fileUsage = $file_usage;
    $this->tagInvalidate = $tag_invalidate;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Sets the Container.
   *
   * @param ContainerInterface|null $container A ContainerInterface instance or null
   */
  public function setContainer(ContainerInterface $container = null) {
    $this->container = $container;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('amp.metadata'), $container
      ->get('file.usage'), $container
      ->get('cache_tags.invalidator'), $container
      ->get('module_handler'));
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);

    /** @var AmpMetadata $amp_metadata */
    $amp_metadata = $this->entity;

    // Check if global metadata settings already exist.
    $amp_metadata_global_exists = $this->ampMetadataInfo
      ->ampMetadataHasGlobal();

    // Get AMP-enabled node types without existing settings.
    $node_type_options = $this->ampMetadataInfo
      ->getAmpNodeTypesWithoutMetadataSettings();
    if (!$this->moduleHandler
      ->moduleExists('token')) {

      // Provide message in case somebody has upgraded AMP module but has not
      // installed Token.
      drupal_set_message($this
        ->t('The AMP module now requires the <a href="@module">Token</a> module as a dependency. Please download and install Token in order for AMP metadata to appear properly.', [
        '@module' => 'https://www.drupal.org/project/token',
      ]), 'warning');
    }
    $introduction_title = $amp_metadata_global_exists && !$amp_metadata
      ->isGlobal() ? $this
      ->t('Content type override for metadata settings') : $this
      ->t('Global metadata settings');
    $top_stories_guidelines_url = Url::fromUri('https://developers.google.com/search/docs/data-types/articles#article_types');
    $form['introduction'] = [
      '#type' => 'item',
      '#title' => $introduction_title,
      '#description' => t('These metadata settings provide information used in the Top Stories carousel in Google Search. Find complete details in the <a href=":top_stories_guidelines">Top Stories guidelines</a>.', [
        ':top_stories_guidelines' => $top_stories_guidelines_url
          ->toString(),
      ]),
    ];

    // Show a node type selector if this is new metadata, and global metadata
    // settings already exist.
    if ($amp_metadata
      ->isNew() && $amp_metadata_global_exists) {
      $form['node_type'] = array(
        '#type' => 'select',
        '#title' => t('Content type'),
        '#description' => t('Select a content type for which you would like to override the global AMP metadata setting. Settings are overridden on a field by field basis.'),
        '#options' => $node_type_options,
        '#required' => TRUE,
        '#default_value' => NULL,
      );
    }

    // Add a collapsible section for organization information. Open by default
    // for global settings.
    $show_organization_fields = $amp_metadata_global_exists && !$amp_metadata
      ->isGlobal() ? FALSE : TRUE;
    $form['organization_group'] = [
      '#type' => 'details',
      '#title' => 'Organization information',
      '#open' => $show_organization_fields,
    ];
    $form['organization_group']['description'] = [
      '#type' => 'item',
      '#description' => t('Provide information about your organization for use in search metadata.'),
    ];

    // Add the token browser for ease in selecting token values.
    $form['organization_group']['token_link'] = [
      '#theme' => 'token_tree_link',
      '#token_types' => 'all',
      '#global_types' => TRUE,
      '#click_insert' => TRUE,
      '#show_restricted' => FALSE,
      '#recursion_limit' => 3,
      '#text' => t('Browse available tokens'),
    ];

    // Add an option to set the organization name that appears in the carousel.
    $org_name = $amp_metadata
      ->getOrganizationName();
    $form['organization_group']['amp_organization_name'] = [
      '#type' => 'textfield',
      '#title' => t('Organization name'),
      '#description' => t('The name of the organization that will appear in the Top Stories carousel. Tokens are allowed.'),
      '#default_value' => $org_name ? $org_name : '',
      '#attributes' => [
        'placeholder' => '[site:name]',
      ],
    ];

    // Add an option to upload an organizational logo for the carousel.
    $top_stories_logo_guidelines_url = Url::fromUri('https://developers.google.com/search/docs/data-types/articles#amp-logo-guidelines');
    $logo_config_fid = $amp_metadata
      ->getOrganizationLogoFid();
    $default_logo_fid = $logo_config_fid ? [
      $logo_config_fid,
    ] : NULL;
    $form['organization_group']['amp_organization_logo_fid_new'] = [
      '#type' => 'managed_file',
      '#title' => t('Organization logo'),
      '#description' => t('Upload a logo for your organization that will appear in the Top Stories carousel. <span class="warning">This logo must have a height of 60px and a width less than 600px. SVG logos are not allowed: please provide a JPG, JPEG, GIF or PNG. See the AMP <a href=":logo_guidelines">logo guidelines</a>.</span>', [
        ':logo_guidelines' => $top_stories_logo_guidelines_url
          ->toString(),
      ]),
      '#default_value' => $default_logo_fid,
      '#upload_location' => 'public://amp',
      '#upload_validators' => [
        'file_validate_is_image' => [],
        'file_validate_extensions' => [
          'png gif jpg jpeg',
        ],
        'file_validate_image_resolution' => [
          '600x60',
        ],
      ],
    ];

    // Store the initial logo file ID. This will help us determine if the logo
    // file is removed, in which case we should delete the file.
    $form['organization_group']['amp_organization_logo_fid_previous'] = [
      '#type' => 'hidden',
      '#value' => $default_logo_fid,
    ];

    // Add an option to select an image style for the organization logo.
    $org_logo_style = $amp_metadata
      ->getOrganizationLogoImageStyleId();
    $form['organization_group']['amp_organization_logo_image_style_id'] = [
      '#type' => 'select',
      '#title' => t('Organization logo image style'),
      '#options' => image_style_options(TRUE),
      '#description' => t('The image style to use for the organization logo.'),
      '#default_value' => $org_logo_style ? $org_logo_style : '',
    ];

    // Add a section for content information.
    $form['content_group'] = [
      '#type' => 'fieldset',
      '#title' => 'Content information',
    ];
    $form['content_group']['description'] = [
      '#type' => 'item',
      '#description' => t('Provide information about your content for use in the Top Stories carousel in Google Search.'),
    ];

    // Add an option to select the schema type for AMP pages.
    $schema_type_options = [
      'Article' => 'Article',
      'NewsArticle' => 'NewsArticle',
      'BlogPosting' => 'BlogPosting',
    ];
    $schema_type = $amp_metadata
      ->getContentSchemaType();
    $form['content_group']['amp_content_schema_type'] = [
      '#type' => 'select',
      '#title' => t('AMP schema type'),
      '#options' => $schema_type_options,
      '#description' => t('The type of schema to use on AMP pages'),
      '#default_value' => $schema_type ? $schema_type : 'NewsArticle',
    ];

    // Add an option to set the headline on AMP pages.
    $content_headline = $amp_metadata
      ->getContentHeadlineToken();
    $form['content_group']['amp_content_headline'] = [
      '#type' => 'textfield',
      '#title' => t('Article headline'),
      '#description' => t('A short headline for an AMP article, using fewer than 110 characters and no HTML markup. Use tokens to provide the correct headline for each article page.'),
      '#default_value' => $content_headline ? $content_headline : '',
      '#attributes' => [
        'placeholder' => '[node:title]',
      ],
    ];

    // Add an option to set the author on AMP pages.
    $content_author = $amp_metadata
      ->getContentAuthorToken();
    $form['content_group']['amp_content_author'] = [
      '#type' => 'textfield',
      '#title' => t('Author name'),
      '#description' => t('The name of the author to use on AMP pages. Use tokens to provide the correct author for each article page. Token output should be text only, no HTML markup.'),
      '#default_value' => $content_author ? $content_author : '',
      '#attributes' => [
        'placeholder' => '[node:author:display-name]',
      ],
    ];

    // Add an option to set the description on AMP pages.
    $content_description = $amp_metadata
      ->getContentDescriptionToken();
    $form['content_group']['amp_content_description'] = [
      '#type' => 'textfield',
      '#title' => t('Article description'),
      '#description' => t('A short description of an AMP article, using fewer than 150 characters and no HTML markup. Use tokens to provide the correct description for each article page.'),
      '#default_value' => $content_description ? $content_description : '',
      '#attributes' => [
        'placeholder' => '[node:summary]',
      ],
    ];

    // Add an option to set the image on AMP pages.
    $top_stories_image_guidelines_url = Url::fromUri('https://developers.google.com/search/docs/data-types/articles#article_types');
    $content_image = $amp_metadata
      ->getContentImageToken();
    $form['content_group']['amp_content_image'] = [
      '#type' => 'textfield',
      '#title' => t('Article image for carousel'),
      '#description' => t('An article image to appear in the Top Stories carousel. Images must be at least 696px wide: refer to <a href=":image_guidelines">article image guidelines</a> for further details. Use tokens to provide the correct image for each article page.', [
        ':image_guidelines' => $top_stories_image_guidelines_url
          ->toString(),
      ]),
      '#default_value' => $content_image ? $content_image : '',
      '#attributes' => [
        'placeholder' => '[node:field_image]',
      ],
    ];

    // Add an option to select an image style for the organization logo.
    $content_image_style = $amp_metadata
      ->getContentImageStyleId();
    $form['content_group']['amp_content_image_style_id'] = [
      '#type' => 'select',
      '#title' => t('Article image style'),
      '#options' => image_style_options(TRUE),
      '#description' => t('The image style to use for the article image'),
      '#default_value' => $content_image_style ? $content_image_style : '',
    ];

    // Add the token browser for ease in selecting token values.
    $form['content_group']['token_link'] = [
      '#theme' => 'token_tree_link',
      '#token_types' => 'all',
      '#global_types' => TRUE,
      '#click_insert' => TRUE,
      '#show_restricted' => FALSE,
      '#recursion_limit' => 3,
      '#text' => t('Browse available tokens'),
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {

    /** @var AmpMetadata $amp_metadata */
    $amp_metadata = $this->entity;
    $this->tagInvalidate
      ->invalidateTags([
      'amp_metadata',
    ]);
    if ($amp_metadata
      ->isNew()) {
      if (!$this->ampMetadataInfo
        ->ampMetadataHasGlobal()) {
        $amp_metadata
          ->setGlobal();
        $amp_metadata
          ->set('id', 'global');
        $amp_metadata
          ->set('label', $this
          ->t('Global settings for AMP metadata'));
      }
      else {
        $node_type = $form_state
          ->getValue('node_type');
        $amp_metadata
          ->setNodeType($node_type);
        $amp_metadata
          ->set('id', $node_type);
        $amp_metadata
          ->set('label', $this
          ->t('@type settings for AMP metadata', [
          '@type' => $this->entityTypeManager
            ->getStorage('node_type')
            ->load($node_type)
            ->label(),
        ]));
      }
      $this->tagInvalidate
        ->invalidateTags([
        'amp_available_metadata',
      ]);
    }

    // Save organization name.
    $amp_metadata
      ->setOrganizationName($form_state
      ->getValue('amp_organization_name'));

    // Get file IDs of the currently uploaded logo as well as the previously
    // uploaded logo.
    $logo_fid_new = '';
    $logo_value_new = $form_state
      ->getValue('amp_organization_logo_fid_new');
    if (!empty($logo_value_new) && isset($logo_value_new[0])) {
      $logo_fid_new = $logo_value_new[0];
    }
    $logo_fid_previous = '';
    $logo_value_previous = $form_state
      ->getValue('amp_organization_logo_fid_previous');
    if (!empty($logo_value_previous) && isset($logo_value_previous[0])) {
      $logo_fid_previous = $logo_value_previous[0];
    }

    // Save new organization logo.

    /** @var \Drupal\file\FileInterface $logo_file_new */
    if (!empty($logo_fid_new) && !empty($logo_file_new = File::load($logo_fid_new))) {
      $logo_file_new
        ->setPermanent();
      $logo_file_new
        ->save();

      // File usage requires an entity type and entity ID. Those don't exist
      // for simple configuration: we use organization and the fid for those
      // items. This causes an error if you go to the file usage page, as the
      // organization entity type does not exist. Life is full of hard choices
      // like this. Alternative is using config entity, which seems unwieldy.
      $this->fileUsage
        ->add($logo_file_new, 'amp', 'amp_metadata', $amp_metadata
        ->get('id'));
      $amp_metadata
        ->setOrganizationLogoFid($logo_fid_new);
    }

    // If no file ID is set for the logo but a value previously existed, or if
    // the new file ID does not match the previous file ID, then the previous
    // file needs to be removed. Delete the previous logo file.

    /** @var \Drupal\file\FileInterface $logo_file_previous */
    if (!empty($logo_fid_previous) && $logo_fid_previous !== $logo_fid_new && !empty($logo_file_previous = File::load($logo_fid_previous))) {
      $this->fileUsage
        ->delete($logo_file_previous, 'amp', 'amp_metadata', $amp_metadata
        ->get('id'));

      // Only delete the file if this is the only place it was in use.
      if (empty($this->fileUsage
        ->listUsage($logo_file_previous))) {
        $logo_file_previous
          ->setTemporary();
        $logo_file_previous
          ->delete();
      }
      $amp_metadata
        ->setOrganizationLogoFid(NULL);
    }
    $amp_metadata
      ->setOrganizationLogoImageStyleId($form_state
      ->getValue('amp_organization_logo_image_style_id'));

    // Save content settings.
    $amp_metadata
      ->setContentSchemaType($form_state
      ->getValue('amp_content_schema_type'));
    $amp_metadata
      ->setContentHeadlineToken($form_state
      ->getValue('amp_content_headline'));
    $amp_metadata
      ->setContentAuthorToken($form_state
      ->getValue('amp_content_author'));
    $amp_metadata
      ->setContentDescriptionToken($form_state
      ->getValue('amp_content_description'));
    $amp_metadata
      ->setContentImageToken($form_state
      ->getValue('amp_content_image'));
    $amp_metadata
      ->setContentImageStyleId($form_state
      ->getValue('amp_content_image_style_id'));

    // Save the metadata and set a message about saving the data.
    $status = $amp_metadata
      ->save();
    $action = $status == SAVED_UPDATED ? 'Updated' : 'Created';
    if ($amp_metadata
      ->isGlobal()) {
      drupal_set_message($this
        ->t('@action the global settings for  AMP Metadata.', [
        '@action' => $action,
      ]));
    }
    else {
      drupal_set_message($this
        ->t('@action the @type settings for AMP Metadata.', [
        '@action' => $action,
        '@type' => $this->entityTypeManager
          ->getStorage('node_type')
          ->load($amp_metadata
          ->getNodeType())
          ->label(),
      ]));
    }
    $form_state
      ->setRedirectUrl($amp_metadata
      ->urlInfo('collection'));
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AmpMetadataForm::$ampMetadataInfo protected property The AMP metadata info service.
AmpMetadataForm::$container protected property The container.
AmpMetadataForm::$fileUsage protected property The file usage service.
AmpMetadataForm::$moduleHandler protected property The module handler. Overrides EntityForm::$moduleHandler
AmpMetadataForm::$tagInvalidate protected property The cache tags invalidator.
AmpMetadataForm::create public static function Instantiates a new instance of this class. Overrides FormBase::create
AmpMetadataForm::form public function Gets the actual form array to be built. Overrides EntityForm::form
AmpMetadataForm::save public function Form submission handler for the 'save' action. Overrides EntityForm::save
AmpMetadataForm::setContainer public function Sets the Container.
AmpMetadataForm::__construct public function Constructs a AmpMetadataForm object.
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
EntityForm::$entity protected property The entity being used by this form. 7
EntityForm::$entityTypeManager protected property The entity type manager. 3
EntityForm::$operation protected property The name of the current operation.
EntityForm::$privateEntityManager private property The entity manager.
EntityForm::actions protected function Returns an array of supported actions for the current entity form. 29
EntityForm::actionsElement protected function Returns the action form element for the current entity form.
EntityForm::afterBuild public function Form element #after_build callback: Updates the entity with submitted data.
EntityForm::buildEntity public function Builds an updated entity object based upon the submitted form values. Overrides EntityFormInterface::buildEntity 2
EntityForm::buildForm public function Form constructor. Overrides FormInterface::buildForm 10
EntityForm::copyFormValuesToEntity protected function Copies top-level form values to entity properties 7
EntityForm::getBaseFormId public function Returns a string identifying the base form. Overrides BaseFormIdInterface::getBaseFormId 5
EntityForm::getEntity public function Gets the form entity. Overrides EntityFormInterface::getEntity
EntityForm::getEntityFromRouteMatch public function Determines which entity will be used by this form from a RouteMatch object. Overrides EntityFormInterface::getEntityFromRouteMatch 1
EntityForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId 10
EntityForm::getOperation public function Gets the operation identifying the form. Overrides EntityFormInterface::getOperation
EntityForm::init protected function Initialize the form state and the entity before the first form build. 3
EntityForm::prepareEntity protected function Prepares the entity object before the form is built first. 3
EntityForm::prepareInvokeAll protected function Invokes the specified prepare hook variant.
EntityForm::processForm public function Process callback: assigns weights and hides extra fields.
EntityForm::setEntity public function Sets the form entity. Overrides EntityFormInterface::setEntity
EntityForm::setEntityManager public function Sets the entity manager for this form. Overrides EntityFormInterface::setEntityManager
EntityForm::setEntityTypeManager public function Sets the entity type manager for this form. Overrides EntityFormInterface::setEntityTypeManager
EntityForm::setModuleHandler public function Sets the module handler for this form. Overrides EntityFormInterface::setModuleHandler
EntityForm::setOperation public function Sets the operation for this form. Overrides EntityFormInterface::setOperation
EntityForm::submitForm public function This is the default entity object builder function. It is called before any other submit handler to build the new entity object to be used by the following submit handlers. At this point of the form workflow the entity is validated and the form state… Overrides FormInterface::submitForm 17
EntityForm::__get public function
EntityForm::__set public function
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
FormBase::validateForm public function Form validation handler. Overrides FormInterface::validateForm 62
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.