Example #1
0
/**
 * Implements hook_workflow_operations().
 *
 * Adds extra operations to ListBuilders.
 * - workflow_ui: Workflow, State;
 * - workflow: WorkflowTransition;
 *
 * @param string $op
 *   'top_actions': Allow modules to insert their own front page action links.
 *   'operations': Allow modules to insert their own workflow operations.
 *   'state':  Allow modules to insert state operations.
 * @param \Drupal\workflow\Entity\Workflow|NULL $workflow
 *   The current workflow object.
 * @param \Drupal\workflow\Entity\WorkflowState|NULL $state
 *   The current state object.
 * @param \Drupal\workflow\Entity\WorkflowTransitionInterface|NULL $transition
 *   The current transition object.
 *
 * @return array
 *   The new actions, to be added to the entity list.
 */
function hook_workflow_operations($op, EntityInterface $entity = NULL)
{
    $operations = array();
    switch ($op) {
        case 'top_actions':
            // As of D8, below hook_workflow_operations is removed, in favour of core hooks.
            // @see file workflow_ui.links.action.yml for an example top action.
            return $operations;
        case 'operations':
            break;
        case 'workflow':
            // This example adds an operation to the 'operations column' of the Workflow List.
            /* @var $workflow Workflow */
            $workflow = $entity;
            $alt = t('Control content access for @wf', array('@wf' => $workflow->label()));
            $attributes = array('alt' => $alt, 'title' => $alt);
            $operations['workflow_access_form'] = array('title' => t('Access'), 'weight' => 50, 'url' => \Drupal\Core\Url::fromRoute('entity.workflow_type.access_form', ['workflow_type' => $workflow->id()]), 'query' => \Drupal::destination()->getAsArray());
            return $operations;
        case 'state':
            /* @var $state WorkflowState */
            $state = $entity;
            break;
        case 'workflow_transition':
            // As of D8, below hook_workflow_operations is removed, in favour of core hooks.
            // @see EntityListBuilder::getOperations, workflow_operations, workflow.api.php.
            // Your module may add operations to the Entity list.
            /* @var $transition WorkflowTransitionInterface */
            $transition = $entity;
            break;
            return $operations;
        default:
            return $operations;
    }
}
Example #2
0
 /**
  * Gets the list of links used by this field.
  *
  * @return array
  *   The links which are used by the render function.
  */
 protected function getLinks()
 {
     $links = array();
     foreach ($this->options['fields'] as $field) {
         if (empty($this->view->field[$field]->last_render_text)) {
             continue;
         }
         $title = $this->view->field[$field]->last_render_text;
         $path = '';
         $url = NULL;
         if (!empty($this->view->field[$field]->options['alter']['path'])) {
             $path = $this->view->field[$field]->options['alter']['path'];
         } elseif (!empty($this->view->field[$field]->options['alter']['url']) && $this->view->field[$field]->options['alter']['url'] instanceof UrlObject) {
             $url = $this->view->field[$field]->options['alter']['url'];
         }
         // Make sure that tokens are replaced for this paths as well.
         $tokens = $this->getRenderTokens(array());
         $path = strip_tags(Html::decodeEntities($this->viewsTokenReplace($path, $tokens)));
         $links[$field] = array('url' => $path ? UrlObject::fromUri('internal:/' . $path) : $url, 'title' => $title);
         if (!empty($this->options['destination'])) {
             $links[$field]['query'] = \Drupal::destination()->getAsArray();
         }
     }
     return $links;
 }
 /**
  * Returns the redirect destination service.
  *
  * @return \Drupal\Core\Routing\RedirectDestinationInterface
  *   The redirect destination helper.
  */
 protected function getRedirectDestination()
 {
     if (!isset($this->redirectDestination)) {
         $this->redirectDestination = \Drupal::destination();
     }
     return $this->redirectDestination;
 }
Example #4
0
  /**
   * Prepares the link to the profile.
   *
   * @param \Drupal\Core\Entity\EntityInterface $profile
   *   The profile entity this field belongs to.
   * @param ResultRow $values
   *   The values retrieved from the view's result set.
   *
   * @return string
   *   Returns a string for the link text.
   */
  protected function renderLink($profile, ResultRow $values) {
    // Ensure user has access to edit this profile.
    if (!$profile->access('update')) {
      return;
    }

    $this->options['alter']['make_link'] = TRUE;
    $this->options['alter']['path'] = "user/" . $profile->getOwnerId() . "/profile/" . $profile->bundle() . "/" . $profile->id();
    $this->options['alter']['query'] = \Drupal::destination()->getAsArray();
    $text = !empty($this->options['text']) ? $this->options['text'] : t('Edit');
    return $text;
  }
Example #5
0
  /**
   * Prepares the link to delete a profile.
   *
   * @param \Drupal\Core\Entity\EntityInterface $profile
   *   The profile entity this field belongs to.
   * @param \Drupal\views\ResultRow $values
   *   The values retrieved from the view's result set.
   *
   * @return string
   *   Returns a string for the link text.
   */
  protected function renderLink($profile, ResultRow $values) {
    // Ensure user has access to delete this node.
    if (!$profile->access('delete')) {
      return;
    }

    $this->options['alter']['make_link'] = TRUE;
    $this->options['alter']['path'] = 'profile/' . $profile->id() . '/delete';
    $this->options['alter']['query'] = \Drupal::destination()->getAsArray();
    $text = !empty($this->options['text']) ? $this->options['text'] : t('Delete');
    return $text;
  }
Example #6
0
 /**
  * Pre-render callback: Renders a link into #markup.
  *
  * Doing so during pre_render gives modules a chance to alter the link parts.
  *
  * @param array $element
  *   A structured array whose keys form the arguments to Drupal::l():
  *   - #title: The link text to pass as argument to Drupal::l().
  *   - One of the following:
  *     - #route_name and (optionally) a #route_parameters array; The route
  *       name and route parameters which will be passed into the link
  *       generator.
  *     - #href: The system path or URL to pass as argument to Drupal::l().
  *   - #options: (optional) An array of options to pass to Drupal::l() or the
  *     link generator.
  *
  * @return array
  *   The passed-in element containing the system compact link default values.
  */
 public static function preRenderCompactLink($element)
 {
     // By default, link options to pass to l() are normally set in #options.
     $element += array('#options' => array());
     if (system_admin_compact_mode()) {
         $element['#title'] = t('Show descriptions');
         $element['#url'] = BaseUrl::fromRoute('system.admin_compact_page', array('mode' => 'off'));
         $element['#options'] = array('attributes' => array('title' => t('Expand layout to include descriptions.')), 'query' => \Drupal::destination()->getAsArray());
     } else {
         $element['#title'] = t('Hide descriptions');
         $element['#url'] = BaseUrl::fromRoute('system.admin_compact_page', array('mode' => 'on'));
         $element['#options'] = array('attributes' => array('title' => t('Compress layout by hiding descriptions.')), 'query' => \Drupal::destination()->getAsArray());
     }
     $options = NestedArray::mergeDeep($element['#url']->getOptions(), $element['#options']);
     $element['#markup'] = \Drupal::l($element['#title'], $element['#url']->setOptions($options));
     return $element;
 }
Example #7
0
 /**
  * {@inheritdoc}
  */
 public function buildConfigurationForm(array $form, FormStateInterface $form_state)
 {
     $form = parent::buildConfigurationForm($form, $form_state);
     $configuration = $this->getConfiguration();
     $regions = $this->getRegionDefinitions();
     // Add wrappers
     $wrapper_options = array('div' => 'Div', 'span' => 'Span', 'section' => 'Section', 'article' => 'Article', 'header' => 'Header', 'footer' => 'Footer', 'aside' => 'Aside', 'figure' => 'Figure');
     $form['region_wrapper'] = array('#group' => 'additional_settings', '#type' => 'details', '#title' => t('Custom wrappers'), '#description' => t('Choose a wrapper. All Display Suite layouts support this option.'), '#tree' => TRUE);
     foreach ($regions as $region_name => $region_definition) {
         $form['region_wrapper'][$region_name] = array('#type' => 'select', '#options' => $wrapper_options, '#title' => t('Wrapper for @region', array('@region' => $region_definition['label'])), '#default_value' => !empty($configuration['wrappers'][$region_name]) ? $configuration['wrappers'][$region_name] : 'div');
     }
     $form['region_wrapper']['outer_wrapper'] = array('#type' => 'select', '#options' => $wrapper_options, '#title' => t('Outer wrapper'), '#default_value' => $configuration['outer_wrapper'], '#weight' => 10);
     $form['region_wrapper']['attributes'] = array('#type' => 'textfield', '#title' => t('Layout attributes'), '#description' => 'E.g. role|navigation,data-something|some value', '#default_value' => $configuration['attributes'], '#weight' => 11);
     $form['region_wrapper']['link_attribute'] = array('#type' => 'select', '#options' => array('' => t('No link'), 'content' => t('Link to content'), 'custom' => t('Custom'), 'tokens' => t('Tokens')), '#title' => t('Add link'), '#description' => t('This will add an onclick attribute on the layout wrapper.'), '#default_value' => $configuration['link_attribute'], '#weight' => 12);
     $form['region_wrapper']['link_custom'] = array('#type' => 'textfield', '#title' => t('Custom link'), '#description' => t('You may use tokens for this link if you selected tokens.'), '#default_value' => $configuration['link_custom'], '#weight' => 13, '#states' => array('visible' => array(array(':input[name="region_wrapper[link_attribute]"]' => array(array("value" => "tokens"), array("value" => "custom"))))));
     if (\Drupal::moduleHandler()->moduleExists('token')) {
         $form['region_wrapper']['tokens'] = array('#title' => t('Tokens'), '#type' => 'container', '#weight' => 14, '#states' => array('visible' => array(':input[name="region_wrapper[link_attribute]"]' => array("value" => "tokens"))));
         $form['region_wrapper']['tokens']['help'] = array('#theme' => 'token_tree', '#token_types' => 'all', '#global_types' => FALSE, '#dialog' => TRUE);
     }
     // Add extra classes for the regions to have more control while theming.
     $form['ds_classes'] = array('#group' => 'additional_settings', '#type' => 'details', '#title' => t('Custom classes'), '#tree' => TRUE, '#collapsible' => TRUE, '#collapsed' => TRUE);
     $classes_access = \Drupal::currentUser()->hasPermission('admin_classes');
     $classes = Ds::getClasses();
     if (!empty($classes)) {
         $form['ds_classes']['layout_class'] = array('#type' => 'select', '#multiple' => TRUE, '#options' => $classes, '#title' => t('Class for layout'), '#default_value' => !empty($configuration['classes']['layout_class']) ? $configuration['classes']['layout_class'] : []);
         foreach ($regions as $region_name => $region_definition) {
             $form['ds_classes'][$region_name] = array('#type' => 'select', '#multiple' => TRUE, '#options' => $classes, '#title' => t('Class for @region', array('@region' => $region_definition['label'])), '#default_value' => isset($configuration['classes'][$region_name]) ? $configuration['classes'][$region_name] : []);
         }
         if ($classes_access) {
             $url = Url::fromRoute('ds.classes');
             $destination = \Drupal::destination()->getAsArray();
             $url->setOption('query', $destination);
             $form['ds_classes']['info'] = array('#markup' => \Drupal::l(t('Manage region and field CSS classes'), $url));
         }
     } else {
         if ($classes_access) {
             $url = Url::fromRoute('ds.classes');
             $destination = \Drupal::destination()->getAsArray();
             $url->setOption('query', $destination);
             $form['ds_classes']['info'] = array('#markup' => '<p>' . t('You have not defined any CSS classes which can be used on regions.') . '</p><p>' . \Drupal::l(t('Manage region and field CSS classes'), $url) . '</p>');
         } else {
             $form['ds_classes']['#access'] = FALSE;
         }
     }
     return $form;
 }
 public function checkRedirection(FilterResponseEvent $event)
 {
     $request_uri = $event->getRequest()->getRequestUri();
     if (strpos($request_uri, 'user/login') || strpos($request_uri, 'user/register')) {
         $response = $event->getResponse();
         if (!CosignSharedFunctions::cosign_is_https()) {
             //settargeturl will not work if not an event from a redirect
             //the controller takes care of a straight user/login url
             //we can intercept the redirect route here and throw to https
             //there may be a better way to handle this
             //        if (!strpos($response->getTargetUrl(), 'user/login') || !strpos($response->getTargetUrl(), 'user/register')) {
             $https_url = 'https://' . $_SERVER['HTTP_HOST'] . $request_uri;
             $response->setTargetUrl($https_url);
             //        }
         } else {
             $destination = \Drupal::destination()->getAsArray()['destination'];
             $username = CosignSharedFunctions::cosign_retrieve_remote_user();
             global $base_path;
             if (!$username && \Drupal::config('cosign.settings')->get('cosign_allow_anons_on_https') == 1) {
                 $request_uri = \Drupal::config('cosign.settings')->get('cosign_login_path') . '?cosign-' . $_SERVER['HTTP_HOST'] . '&https://' . $_SERVER['HTTP_HOST'];
                 if ($destination == $base_path . 'user/login' || $destination == $base_path . 'user/register') {
                     $destination = $base_path;
                 }
                 $request_uri = $request_uri . $destination;
             } else {
                 CosignSharedFunctions::cosign_user_status($username);
                 if ($request_uri == $base_path . 'user/login' || $request_uri == $base_path . 'user/register') {
                     $request_uri = $base_path;
                 } else {
                     $request_uri = $destination;
                 }
             }
             if ($response instanceof TrustedRedirectResponse) {
                 $response->setTargetUrl($request_uri);
             } else {
                 $event->setResponse(new TrustedRedirectResponse($request_uri));
             }
         }
     }
 }
 /**
  * {@inheritdoc}
  */
 public function getDefaultOperations(EntityInterface $entity)
 {
     $operations = parent::getDefaultOperations($entity);
     /* @var $transition WorkflowTransitionInterface */
     $transition = $entity;
     if (isset($operations['edit'])) {
         $destination = \Drupal::destination()->getAsArray();
         $operations['edit']['query'] = $destination;
     }
     return $operations;
 }
Example #10
0
 /**
  * Bootstraps a kernel for a test.
  */
 private function bootKernel()
 {
     $this->setSetting('container_yamls', []);
     // Allow for test-specific overrides.
     $settings_services_file = $this->root . '/sites/default' . '/testing.services.yml';
     if (file_exists($settings_services_file)) {
         // Copy the testing-specific service overrides in place.
         $testing_services_file = $this->root . '/' . $this->siteDirectory . '/services.yml';
         copy($settings_services_file, $testing_services_file);
         $this->setSetting('container_yamls', [$testing_services_file]);
     }
     // Allow for global test environment overrides.
     if (file_exists($test_env = $this->root . '/sites/default/testing.services.yml')) {
         $GLOBALS['conf']['container_yamls']['testing'] = $test_env;
     }
     // Add this test class as a service provider.
     $GLOBALS['conf']['container_service_providers']['test'] = $this;
     $modules = self::getModulesToEnable(get_class($this));
     // Prepare a precompiled container for all tests of this class.
     // Substantially improves performance, since ContainerBuilder::compile()
     // is very expensive. Encourages testing best practices (small tests).
     // Normally a setUpBeforeClass() operation, but object scope is required to
     // inject $this test class instance as a service provider (see above).
     $rc = new \ReflectionClass(get_class($this));
     $test_method_count = count(array_filter($rc->getMethods(), function ($method) {
         // PHPUnit's @test annotations are intentionally ignored/not supported.
         return strpos($method->getName(), 'test') === 0;
     }));
     if ($test_method_count > 1 && !$this->isTestInIsolation()) {
         // Clone a precompiled, empty ContainerBuilder instance for each test.
         $container = $this->getCompiledContainerBuilder($modules);
     }
     // Bootstrap the kernel. Do not use createFromRequest() to retain Settings.
     $kernel = new DrupalKernel('testing', $this->classLoader, FALSE);
     $kernel->setSitePath($this->siteDirectory);
     // Boot the precompiled container. The kernel will enhance it with synthetic
     // services.
     if (isset($container)) {
         $kernel->setContainer($container);
         unset($container);
     } elseif ($modules && ($extensions = $this->getExtensionsForModules($modules))) {
         $kernel->updateModules($extensions, $extensions);
     }
     // DrupalKernel::boot() is not sufficient as it does not invoke preHandle(),
     // which is required to initialize legacy global variables.
     $request = Request::create('/');
     $kernel->prepareLegacyRequest($request);
     // register() is only called if a new container was built/compiled.
     $this->container = $kernel->getContainer();
     // Ensure database tasks have been run.
     require_once __DIR__ . '/../../../includes/install.inc';
     $connection = Database::getConnection();
     $errors = db_installer_object($connection->driver())->runTasks();
     if (!empty($errors)) {
         $this->fail('Failed to run installer database tasks: ' . implode(', ', $errors));
     }
     if ($modules) {
         $this->container->get('module_handler')->loadAll();
     }
     $this->container->get('request_stack')->push($request);
     // Setup the destion to the be frontpage by default.
     \Drupal::destination()->set('/');
     // Write the core.extension configuration.
     // Required for ConfigInstaller::installDefaultConfig() to work.
     $this->container->get('config.storage')->write('core.extension', array('module' => array_fill_keys($modules, 0), 'theme' => array()));
     $settings = Settings::getAll();
     $settings['php_storage']['default'] = ['class' => '\\Drupal\\Component\\PhpStorage\\FileStorage'];
     new Settings($settings);
     // Manually configure the test mail collector implementation to prevent
     // tests from sending out emails and collect them in state instead.
     // While this should be enforced via settings.php prior to installation,
     // some tests expect to be able to test mail system implementations.
     $GLOBALS['config']['system.mail']['interface']['default'] = 'test_mail_collector';
 }
Example #11
0
/**
 * The user just logged in.
 *
 * @param object $account
 *   The user object on which the operation was just performed.
 */
function hook_user_login($account)
{
    $config = \Drupal::config('system.date');
    // If the user has a NULL time zone, notify them to set a time zone.
    if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
        drupal_set_message(t('Configure your <a href=":user-edit">account time zone setting</a>.', array(':user-edit' => $account->url('edit-form', array('query' => \Drupal::destination()->getAsArray(), 'fragment' => 'edit-timezone')))));
    }
}
Example #12
0
 /**
  * Builds the translation status render array with source and job item status.
  *
  * @param int $status
  *   The source status: original, missing, current or outofdate.
  * @param \Drupal\tmgmt\JobItemInterface|NULL $job_item
  *   The existing job item for the source.
  *
  * @return array
  *   The render array for displaying the status.
  */
 function buildTranslationStatus($status, JobItemInterface $job_item = NULL)
 {
     switch ($status) {
         case 'original':
             $label = t('Source language');
             $icon = 'core/misc/icons/bebebe/house.svg';
             break;
         case 'missing':
             $label = t('Not translated');
             $icon = 'core/misc/icons/bebebe/ex.svg';
             break;
         case 'outofdate':
             $label = t('Translation Outdated');
             $icon = drupal_get_path('module', 'tmgmt') . '/icons/outdated.svg';
             break;
         default:
             $label = t('Translation up to date');
             $icon = 'core/misc/icons/73b355/check.svg';
     }
     $build['source'] = ['#theme' => 'image', '#uri' => $icon, '#title' => $label, '#alt' => $label];
     // If we have an active job item, wrap it in a link.
     if ($job_item) {
         $states_labels = JobItem::getStates();
         $state_label = $states_labels[$job_item->getState()];
         $label = t('Active job item: @state', array('@state' => $state_label));
         $url = $job_item->toUrl();
         $job = $job_item->getJob();
         switch ($job_item->getState()) {
             case JobItem::STATE_ACTIVE:
                 if ($job->isUnprocessed()) {
                     $url = $job->toUrl();
                     $label = t('Active job item: @state', array('@state' => $state_label));
                 }
                 $icon = drupal_get_path('module', 'tmgmt') . '/icons/hourglass.svg';
                 break;
             case JobItem::STATE_REVIEW:
                 $icon = drupal_get_path('module', 'tmgmt') . '/icons/ready.svg';
                 break;
         }
         $url->setOption('query', \Drupal::destination()->getAsArray());
         $url->setOption('attributes', array('title' => $label));
         $item_icon = ['#theme' => 'image', '#uri' => $icon, '#title' => $label, '#alt' => $label];
         $build['job_item'] = ['#type' => 'link', '#url' => $url, '#title' => $item_icon];
     }
     return $build;
 }
Example #13
0
 /**
  * Prints a destination query parameter.
  *
  * @return \Symfony\Component\HttpFoundation\Response
  *   A new Response object containing a string with the destination query
  *   parameter.
  */
 public function destination()
 {
     $destination = \Drupal::destination()->getAsArray();
     $output = "The destination: " . SafeMarkup::checkPlain($destination['destination']);
     return new Response($output);
 }
Example #14
0
 /**
  * Prints a destination query parameter.
  *
  * @return \Symfony\Component\HttpFoundation\Response
  *   A new Response object containing a string with the destination query
  *   parameter.
  */
 public function destination()
 {
     $destination = \Drupal::destination()->getAsArray();
     $output = "The destination: " . Html::escape($destination['destination']);
     return new Response($output);
 }
 public function redirectUser($variable_path = '')
 {
     $user = \Drupal::currentUser();
     $config = \Drupal::config('hostedpage.settings');
     //Advanced module LR Code Hook Start
     // Make sure at least one module implements our hook.
     if (count(\Drupal::moduleHandler()->getImplementations('before_user_redirect')) > 0) {
         // Call all modules that implement the hook, and let them make changes to $variables.
         $use_data = array('userprofile' => $userprofile, 'form' => $form, 'account' => $account);
         $data = \Drupal::moduleHandler()->invokeAll('before_user_redirect', $use_data);
         if (!empty($data) && $data != 'true') {
             return $data;
         }
     }
     //Advanced module LR Code Hook End
     $variable_path = !empty($variable_path) ? $variable_path : 'login_redirection';
     $variable_custom_path = $variable_path == 'login_redirection' ? 'custom_login_url' : 'custom_register_url';
     $request_uri = \Drupal::request()->getRequestUri();
     if ($this->module_config->get($variable_path) == 1) {
         // Redirect to profile.
         $response = new RedirectResponse($user->id() . '/edit');
         return $response->send();
     } elseif ($this->module_config->get($variable_path) == 2) {
         // Redirect to custom page.
         $custom_url = $this->module_config->get($variable_custom_path);
         if (!empty($custom_url)) {
             $response = new RedirectResponse($custom_url);
             return $response->send();
         } else {
             return new RedirectResponse(Url::fromRoute('<front>')->toString());
         }
     } else {
         // Redirect to same page.
         $enablehosted = $config->get('lr_hosted_page_enable');
         if (isset($enablehosted) && $enablehosted == '1') {
             return new RedirectResponse(Url::fromRoute('<front>')->toString());
         } else {
             $destination = \Drupal::destination()->getAsArray();
             $response = new RedirectResponse($destination['destination']);
             return $response->send();
         }
     }
 }
 /**
  * Returns the form element for caption settings.
  *
  * @param \Drupal\Core\Field\FormatterBase $formatter
  * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
  *
  * @return array
  */
 protected function captionSettings(FormatterBase $formatter, FieldDefinitionInterface $field_definition)
 {
     $field_settings = $field_definition->getSettings();
     // Set the caption options.
     $caption_options = array(0 => $formatter->t('None'), 1 => $formatter->t('Image title'), 'alt' => $formatter->t('Image ALT attribute'));
     // Remove the options that are not available.
     $action_fields = array();
     if ($field_settings['title_field'] == FALSE) {
         unset($caption_options[1]);
         // User action required on the image title.
         $action_fields[] = 'title';
     }
     if ($field_settings['alt_field'] == FALSE) {
         unset($caption_options['alt']);
         // User action required on the image alt.
         $action_fields[] = 'alt';
     }
     // Create the caption element.
     $element['caption'] = array('#title' => $formatter->t('Choose a caption source'), '#type' => 'select', '#options' => $caption_options);
     // If the image field doesn't have all of the suitable caption sources, tell the user.
     if ($action_fields) {
         $action_text = $formatter->t('enable the @action_field field', array('@action_field' => join(' and/or ', $action_fields)));
         /* This may be a base field definition (e.g. in Views UI) which means it
          * is not associated with a bundle and will not have the toUrl() method.
          * So we need to check for the existence of the method before we can
          * build a link to the image field edit form.
          */
         if (method_exists($field_definition, 'toUrl')) {
             // Build the link to the image field edit form for this bundle.
             $rel = "{$field_definition->getTargetEntityTypeId()}-field-edit-form";
             $action = $field_definition->toLink($action_text, $rel, array('fragment' => 'edit-settings-alt-field', 'query' => \Drupal::destination()->getAsArray()))->toRenderable();
         } else {
             // Just use plain text if we can't build the field edit link.
             $action = ['#markup' => $action_text];
         }
         $element['caption']['#description'] = $formatter->t('You need to @action for this image field to be able to use it as a caption.', array('@action' => render($action)));
         // If there are no suitable caption sources, disable the caption element.
         if (count($action_fields) >= 2) {
             $element['caption']['#disabled'] = TRUE;
         }
     } else {
         $element['caption']['#default_value'] = $formatter->getSetting('caption');
     }
     return $element;
 }