drupal-media /
entity_browser
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Drupal\entity_browser\Element; |
||
| 4 | |||
| 5 | use Drupal\Component\Utility\Html; |
||
| 6 | use Drupal\Core\Form\FormStateInterface; |
||
| 7 | use Drupal\Core\Render\Element\FormElement; |
||
| 8 | use Drupal\entity_browser\Entity\EntityBrowser; |
||
| 9 | use Drupal\Core\Entity\EntityInterface; |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Provides an Entity Browser form element. |
||
| 13 | * |
||
| 14 | * Properties: |
||
| 15 | * - #entity_browser: Entity browser or ID of the Entity browser to be used. |
||
| 16 | * - #cardinality: (optional) Maximum number of items that are expected from |
||
| 17 | * the entity browser. Unlimited by default. |
||
| 18 | * - #default_value: (optional) Array of entities that Entity browser should be |
||
| 19 | * initialized with. It's only applicable when edit selection mode is used. |
||
| 20 | * - #entity_browser_validators: (optional) Array of validators that are to be |
||
| 21 | * passed to the entity browser. Array keys are plugin IDs and array values |
||
| 22 | * are plugin configuration values. Cardinality validator will be set |
||
| 23 | * automatically. |
||
| 24 | * - #selection_mode: (optional) Determines how selection in entity browser will |
||
| 25 | * be handled. Will selection be appended/prepended or it will be replaced |
||
| 26 | * in case of editing. Defaults to append. |
||
| 27 | * - #widget_context: (optional) Widget configuration overrides which enable |
||
| 28 | * use cases where the instance of a widget needs awareness of contextual |
||
| 29 | * configuration like field settings. |
||
| 30 | * |
||
| 31 | * Return value will be an array of selected entities, which will appear under |
||
| 32 | * 'entities' key on the root level of the element's values in the form state. |
||
| 33 | * |
||
| 34 | * @FormElement("entity_browser") |
||
| 35 | */ |
||
| 36 | class EntityBrowserElement extends FormElement { |
||
| 37 | |||
| 38 | /** |
||
| 39 | * Indicating an entity browser can return an unlimited number of values. |
||
| 40 | * |
||
| 41 | * Note: When entity browser is used in Fields, cardinality is directly |
||
| 42 | * propagated from Field settings, that's why this constant should be equal to |
||
| 43 | * FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED. |
||
| 44 | */ |
||
| 45 | const CARDINALITY_UNLIMITED = -1; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Selection from entity browser will be appended to existing list. |
||
| 49 | * |
||
| 50 | * When this selection mode is used, then entity browser will not be |
||
| 51 | * populated with existing selection. Preselected list will be empty. |
||
| 52 | * |
||
| 53 | * Note: This option is also used by "js/entity_browser.common.js". |
||
| 54 | */ |
||
| 55 | const SELECTION_MODE_APPEND = 'selection_append'; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Selection from entity browser will be prepended to existing list. |
||
| 59 | * |
||
| 60 | * When this selection mode is used, then entity browser will not be |
||
| 61 | * populated with existing selection. Preselected list will be empty. |
||
| 62 | * |
||
| 63 | * Note: This option is also used by "js/entity_browser.common.js". |
||
| 64 | */ |
||
| 65 | const SELECTION_MODE_PREPEND = 'selection_prepend'; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Selection from entity browser will replace existing. |
||
| 69 | * |
||
| 70 | * When this selection mode is used, then entity browser will be populated |
||
| 71 | * with existing selection and returned selected list will replace existing |
||
| 72 | * selection. This option requires entity browser selection display with |
||
| 73 | * preselection support. |
||
| 74 | * |
||
| 75 | * Note: This option is also used by "js/entity_browser.common.js". |
||
| 76 | */ |
||
| 77 | const SELECTION_MODE_EDIT = 'selection_edit'; |
||
| 78 | |||
| 79 | /** |
||
| 80 | * {@inheritdoc} |
||
| 81 | */ |
||
| 82 | public function getInfo() { |
||
| 83 | $class = get_class($this); |
||
| 84 | return [ |
||
| 85 | '#input' => TRUE, |
||
| 86 | '#tree' => TRUE, |
||
| 87 | '#cardinality' => static::CARDINALITY_UNLIMITED, |
||
| 88 | '#selection_mode' => static::SELECTION_MODE_APPEND, |
||
| 89 | '#process' => [[$class, 'processEntityBrowser']], |
||
| 90 | '#default_value' => [], |
||
| 91 | '#entity_browser_validators' => [], |
||
| 92 | '#widget_context' => [], |
||
| 93 | '#attached' => ['library' => ['entity_browser/common']], |
||
| 94 | ]; |
||
| 95 | } |
||
| 96 | |||
| 97 | /** |
||
| 98 | * Get selection mode options. |
||
| 99 | * |
||
| 100 | * @return array |
||
| 101 | * Selection mode options. |
||
| 102 | */ |
||
| 103 | public static function getSelectionModeOptions() { |
||
| 104 | return [ |
||
| 105 | static::SELECTION_MODE_APPEND => t('Append to selection'), |
||
| 106 | static::SELECTION_MODE_PREPEND => t('Prepend selection'), |
||
| 107 | static::SELECTION_MODE_EDIT => t('Edit selection'), |
||
| 108 | ]; |
||
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * Check whether entity browser should be available for selection of entities. |
||
| 113 | * |
||
| 114 | * @param string $selection_mode |
||
| 115 | * Used selection mode. |
||
| 116 | * @param int $cardinality |
||
| 117 | * Used cardinality. |
||
| 118 | * @param int $preselection_size |
||
| 119 | * Preseletion size, if it's available. |
||
| 120 | * |
||
| 121 | * @return bool |
||
| 122 | * Returns positive if entity browser can be used. |
||
| 123 | */ |
||
| 124 | public static function isEntityBrowserAvailable($selection_mode, $cardinality, $preselection_size) { |
||
| 125 | if ($selection_mode == static::SELECTION_MODE_EDIT) { |
||
| 126 | return TRUE; |
||
| 127 | } |
||
| 128 | |||
| 129 | $cardinality_exceeded = |
||
| 130 | $cardinality != static::CARDINALITY_UNLIMITED |
||
| 131 | && $preselection_size >= $cardinality; |
||
| 132 | |||
| 133 | return !$cardinality_exceeded; |
||
| 134 | } |
||
| 135 | |||
| 136 | /** |
||
| 137 | * Render API callback: Processes the entity browser element. |
||
| 138 | */ |
||
| 139 | public static function processEntityBrowser(&$element, FormStateInterface $form_state, &$complete_form) { |
||
| 140 | /** @var \Drupal\entity_browser\EntityBrowserInterface $entity_browser */ |
||
| 141 | if (is_string($element['#entity_browser'])) { |
||
| 142 | $entity_browser = EntityBrowser::load($element['#entity_browser']); |
||
| 143 | } |
||
| 144 | else { |
||
| 145 | $entity_browser = $element['#entity_browser']; |
||
| 146 | } |
||
| 147 | |||
| 148 | // Propagate selection if edit selection mode is used. |
||
| 149 | $entity_browser_preselected_entities = []; |
||
| 150 | if ($element['#selection_mode'] === static::SELECTION_MODE_EDIT) { |
||
| 151 | $entity_browser->getSelectionDisplay()->checkPreselectionSupport(); |
||
| 152 | |||
| 153 | $entity_browser_preselected_entities = $element['#default_value']; |
||
| 154 | } |
||
| 155 | |||
| 156 | $default_value = implode(' ', array_map( |
||
| 157 | function (EntityInterface $item) { |
||
| 158 | return $item->getEntityTypeId() . ':' . $item->id(); |
||
| 159 | }, |
||
| 160 | $entity_browser_preselected_entities |
||
| 161 | )); |
||
| 162 | $validators = array_merge( |
||
| 163 | $element['#entity_browser_validators'], |
||
| 164 | ['cardinality' => ['cardinality' => $element['#cardinality']]] |
||
| 165 | ); |
||
| 166 | |||
| 167 | // Display error message if the entity browser was not found. |
||
| 168 | if (!$entity_browser) { |
||
| 169 | $element['entity_browser'] = [ |
||
| 170 | '#type' => 'markup', |
||
| 171 | '#markup' => is_string($element['#entity_browser']) ? t('Entity browser @browser not found.', ['@browser' => $element['#entity_browser']]) : t('Entity browser not found.'), |
||
| 172 | ]; |
||
| 173 | } |
||
| 174 | // Display entity_browser |
||
| 175 | else { |
||
| 176 | $display = $entity_browser->getDisplay(); |
||
| 177 | $display->setUuid(sha1(implode('-', array_merge([$complete_form['#build_id']], $element['#parents'])))); |
||
| 178 | $element['entity_browser'] = [ |
||
| 179 | '#eb_parents' => array_merge($element['#parents'], ['entity_browser']), |
||
| 180 | ]; |
||
| 181 | $element['entity_browser'] = $display->displayEntityBrowser( |
||
| 182 | $element['entity_browser'], |
||
| 183 | $form_state, |
||
| 184 | $complete_form, |
||
| 185 | [ |
||
| 186 | 'validators' => $validators, |
||
| 187 | 'selected_entities' => $entity_browser_preselected_entities, |
||
| 188 | 'widget_context' => $element['#widget_context'], |
||
| 189 | ] |
||
| 190 | ); |
||
| 191 | |||
| 192 | $hidden_id = Html::getUniqueId($element['#id'] . '-target'); |
||
| 193 | $element['entity_ids'] = [ |
||
| 194 | '#type' => 'hidden', |
||
| 195 | '#id' => $hidden_id, |
||
| 196 | // We need to repeat ID here as it is otherwise skipped when rendering. |
||
| 197 | '#attributes' => ['id' => $hidden_id, 'class' => ['eb-target']], |
||
| 198 | '#default_value' => $default_value, |
||
| 199 | ]; |
||
| 200 | |||
| 201 | $element['#attached']['drupalSettings']['entity_browser'] = [ |
||
| 202 | $entity_browser->getDisplay()->getUuid() => [ |
||
| 203 | 'cardinality' => $element['#cardinality'], |
||
| 204 | 'selection_mode' => $element['#selection_mode'], |
||
| 205 | 'selector' => '#' . $hidden_id, |
||
| 206 | ], |
||
| 207 | ]; |
||
| 208 | } |
||
| 209 | |||
| 210 | return $element; |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * {@inheritdoc} |
||
| 215 | */ |
||
| 216 | public static function valueCallback(&$element, $input, FormStateInterface $form_state) { |
||
| 217 | if ($input === FALSE) { |
||
| 218 | return $element['#default_value'] ?: []; |
||
| 219 | } |
||
| 220 | |||
| 221 | $entities = []; |
||
| 222 | if ($input['entity_ids']) { |
||
| 223 | $entities = static::processEntityIds($input['entity_ids']); |
||
| 224 | } |
||
| 225 | |||
| 226 | return ['entities' => $entities]; |
||
| 227 | } |
||
| 228 | |||
| 229 | /** |
||
| 230 | * Processes entity IDs and gets array of loaded entities. |
||
| 231 | * |
||
| 232 | * @param array|string $ids |
||
| 233 | * Processes entity IDs as they are returned from the entity browser. They |
||
| 234 | * are in [entity_type_id]:[entity_id] form. Array of IDs or a |
||
| 235 | * space-delimited string is supported. |
||
| 236 | * |
||
| 237 | * @return \Drupal\Core\Entity\EntityInterface[] |
||
| 238 | * Array of entity objects. |
||
| 239 | */ |
||
| 240 | public static function processEntityIds($ids) { |
||
| 241 | if (!is_array($ids)) { |
||
| 242 | $ids = array_filter(explode(' ', $ids)); |
||
| 243 | } |
||
| 244 | |||
| 245 | return array_map( |
||
| 246 | function ($item) { |
||
| 247 | list($entity_type, $entity_id) = explode(':', $item); |
||
| 248 | return \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id); |
||
| 249 | }, |
||
| 250 | $ids |
||
| 251 | ); |
||
| 252 | } |
||
| 253 | |||
| 254 | /** |
||
| 255 | * Processes entity IDs and gets array of loaded entities. |
||
| 256 | * |
||
| 257 | * @param string $id |
||
| 258 | * Processes entity ID as it is returned from the entity browser. ID should |
||
| 259 | * be in [entity_type_id]:[entity_id] form. |
||
| 260 | * |
||
| 261 | * @return \Drupal\Core\Entity\EntityInterface |
||
| 262 | * Entity object. |
||
| 263 | */ |
||
| 264 | public static function processEntityId($id) { |
||
| 265 | $return = static::processEntityIds([$id]); |
||
| 266 | return current($return); |
||
|
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
| 267 | } |
||
| 268 | |||
| 269 | } |
||
| 270 |