editor.module 25 KB
 webchick committed Jan 16, 2013 1 2 3 4 5 6 7 ' . t('About') . '';  alexpott committed Sep 21, 2015 28  $output .= ' ' . t('The Text Editor module provides a framework that other modules (such as CKEditor module) can use to provide toolbars and other functionality that allow users to format text more easily than typing HTML tags directly. For more information, see the online documentation for the Text Editor module.', array(':documentation' => 'https://www.drupal.org/documentation/modules/editor', ':ckeditor' => (\Drupal::moduleHandler()->moduleExists('ckeditor')) ? \Drupal::url('help.page', array('name' => 'ckeditor')) : '#')) . ' ';  webchick committed Jan 16, 2013 29 30 $output .= '

' . t('Uses') . '

'; $output .= ' ';  31 $output .= '
' . t('Installing text editors') . '
';  alexpott committed Sep 21, 2015 32  $output .= ' ' . t('The Text Editor module provides a framework for managing editors. To use it, you also need to enable a text editor. This can either be the core CKEditor module, which can be enabled on the Extend page, or a contributed module for any other text editor. When installing a contributed text editor module, be sure to check the installation instructions, because you will most likely need to download and install an external library as well as the Drupal module.', array(':ckeditor' => (\Drupal::moduleHandler()->moduleExists('ckeditor')) ? \Drupal::url('help.page', array('name' => 'ckeditor')) : '#', ':extend' => \Drupal::url('system.modules_list'))) . ' ';  33 $output .= '
' . t('Enabling a text editor for a text format') . '
';  alexpott committed Sep 21, 2015 34  $output .= ' ' . t('On the Text formats and editors page you can see which text editor is associated with each text format. You can change this by clicking on the Configure link, and then choosing a text editor or none from the Text editor drop-down list. The text editor will then be displayed with any text field for which this text format is chosen.', array(':formats' => \Drupal::url('filter.admin_overview'))) . ' ';  35 36 37 38 $output .= '
' . t('Configuring a text editor') . '
'; $output .= ' ' . t('Once a text editor is associated with a text format, you can configure it by clicking on the Configure link for this format. Depending on the specific text editor, you can configure it for example by adding buttons to its toolbar. Typically these buttons provide formatting or editing tools, and they often insert HTML tags into the field source. For details, see the help page of the specific text editor.') . ' ';$output .= '
' . t('Using different text editors and formats') . '
'; $output .= ' ' . t('If you change the text format on a text field, the text editor will change as well because the text editor configuration is associated with the individual text format. This allows the use of the same text editor with different options for different text formats. It also allows users to choose between text formats with different text editors if they are installed.') . ' ';  webchick committed Jan 16, 2013 39 40 41 42 43 44 $output .= '
'; return $output; } } /**  alexpott committed Jul 30, 2014 45  * Implements hook_menu_links_discovered_alter().  webchick committed Jan 16, 2013 46 47 48 49  * * Rewrites the menu entries for filter module that relate to the configuration * of text editors. */  alexpott committed Jul 30, 2014 50 function editor_menu_links_discovered_alter(array &$links) {  effulgentsia committed Oct 01, 2015 51  $links['filter.admin_overview']['title'] = new TranslatableMarkup('Text formats and editors');  webchick committed Oct 04, 2015 52 $links['filter.admin_overview']['description'] = new TranslatableMarkup('Select and configure text editors, and how content is filtered when displayed.');  webchick committed Jan 16, 2013 53 54 55 } /**  56  * Implements hook_element_info_alter().  webchick committed Jan 16, 2013 57 58 59 60 61  * * Extends the functionality of text_format elements (provided by Filter * module), so that selecting a text format notifies a client-side text editor * when it should be enabled or disabled. *  catch committed Aug 28, 2015 62  * @see \Drupal\filter\Element\TextFormat  webchick committed Jan 16, 2013 63  */  64 65 function editor_element_info_alter(&$types) {$types['text_format']['#pre_render'][] = 'element.editor:preRenderTextFormat';  webchick committed Jan 16, 2013 66 67 68 } /**  catch committed May 06, 2016 69  * Implements hook_form_FORM_ID_alter() for \Drupal\filter\FilterFormatListBuilder.  catch committed Jun 15, 2016 70 71 72 73 74 75 76 77 78 79 80 81 82 83  * * Implements hook_field_formatter_info_alter(). * * @see quickedit_field_formatter_info_alter() */ function editor_field_formatter_info_alter(&$info) { // Update \Drupal\text\Plugin\Field\FieldFormatter\TextDefaultFormatter's // annotation to indicate that it supports the 'editor' in-place editor // provided by this module.$info['text_default']['quickedit'] = ['editor' => 'editor']; } /** * Implements hook_form_FORM_ID_alter().  webchick committed Jan 16, 2013 84  */  alexpott committed Aug 04, 2014 85 function editor_form_filter_admin_overview_alter(&$form, FormStateInterface$form_state) {  86  // @todo Cleanup column injection: https://www.drupal.org/node/1876718.  webchick committed Jan 16, 2013 87 88 89 90 91 92  // Splice in the column for "Text editor" into the header. $position = array_search('name',$form['formats']['#header']) + 1; $start = array_splice($form['formats']['#header'], 0, $position, array('editor' => t('Text editor')));$form['formats']['#header'] = array_merge($start,$form['formats']['#header']); // Then splice in the name of each text editor for each text format.  webchick committed Sep 15, 2013 93  $editors = \Drupal::service('plugin.manager.editor')->getDefinitions();  94  foreach (Element::children($form['formats']) as $format_id) {  webchick committed Jan 16, 2013 95 $editor = editor_load($format_id);  catch committed Aug 21, 2015 96 $editor_name = ($editor && isset($editors[$editor->getEditor()])) ?$editors[$editor->getEditor()]['label'] : '—';  webchick committed Jan 16, 2013 97 98 99 100 101 102 103 104 $editor_column['editor'] = array('#markup' => $editor_name);$position = array_search('name', array_keys($form['formats'][$format_id])) + 1; $start = array_splice($form['formats'][$format_id], 0,$position, $editor_column);$form['formats'][$format_id] = array_merge($start, $form['formats'][$format_id]); } } /**  catch committed May 06, 2016 105  * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\filter\FilterFormatEditForm.  webchick committed Jan 16, 2013 106  */  107 function editor_form_filter_format_form_alter(&$form, FormStateInterface$form_state) {  alexpott committed Jan 23, 2015 108 109  $editor =$form_state->get('editor'); if ($editor === NULL) {  alexpott committed Mar 22, 2015 110 111 $format = $form_state->getFormObject()->getEntity();$format_id = $format->isNew() ? NULL :$format->id();  webchick committed Sep 11, 2014 112 113  $editor = editor_load($format_id); $form_state->set('editor',$editor);  webchick committed Jan 16, 2013 114 115 116  } // Associate a text editor with this text format.  webchick committed Feb 06, 2014 117  $manager = \Drupal::service('plugin.manager.editor');  webchick committed Jan 16, 2013 118 119 $editor_options = $manager->listOptions();$form['editor'] = array(  Dries committed Feb 08, 2013 120 121 122 123 124  // Position the editor selection before the filter settings (weight of 0), // but after the filter label and name (weight of -20). '#weight' => -9, ); $form['editor']['editor'] = array(  webchick committed Jan 16, 2013 125 126 127 128  '#type' => 'select', '#title' => t('Text editor'), '#options' =>$editor_options, '#empty_option' => t('None'),  webchick committed May 06, 2014 129  '#default_value' => $editor ?$editor->getEditor() : '',  webchick committed Jan 16, 2013 130 131 132 133 134  '#ajax' => array( 'trigger_as' => array('name' => 'editor_configure'), 'callback' => 'editor_form_filter_admin_form_ajax', 'wrapper' => 'editor-settings-wrapper', ),  Dries committed Feb 08, 2013 135  '#weight' => -10,  webchick committed Jan 16, 2013 136  );  Dries committed Feb 08, 2013 137  $form['editor']['configure'] = array(  webchick committed Jan 16, 2013 138 139 140 141 142 143 144 145 146  '#type' => 'submit', '#name' => 'editor_configure', '#value' => t('Configure'), '#limit_validation_errors' => array(array('editor')), '#submit' => array('editor_form_filter_admin_format_editor_configure'), '#ajax' => array( 'callback' => 'editor_form_filter_admin_form_ajax', 'wrapper' => 'editor-settings-wrapper', ),  Dries committed Feb 08, 2013 147  '#weight' => -10,  webchick committed Jan 16, 2013 148 149 150 151 152  '#attributes' => array('class' => array('js-hide')), ); // If there aren't any options (other than "None"), disable the select list. if (empty($editor_options)) {  Dries committed Feb 08, 2013 153 154  $form['editor']['editor']['#disabled'] = TRUE;$form['editor']['editor']['#description'] = t('This option is disabled because no modules that provide a text editor are currently enabled.');  webchick committed Jan 16, 2013 155 156  }  Dries committed Feb 08, 2013 157  $form['editor']['settings'] = array(  webchick committed Jan 16, 2013 158 159 160 161  '#tree' => TRUE, '#weight' => -8, '#type' => 'container', '#id' => 'editor-settings-wrapper',  alexpott committed Jun 18, 2013 162 163  '#attached' => array( 'library' => array(  alexpott committed Mar 09, 2014 164  'editor/drupal.editor.admin',  alexpott committed Jun 18, 2013 165 166  ), ),  webchick committed Jan 16, 2013 167 168 169 170  ); // Add editor-specific validation and submit handlers. if ($editor) {  webchick committed May 06, 2014 171  $plugin =$manager->createInstance($editor->getEditor());  webchick committed Jan 16, 2013 172 173 $settings_form = array(); $settings_form['#element_validate'][] = array($plugin, 'settingsFormValidate');  Dries committed Feb 08, 2013 174 175  $form['editor']['settings']['subform'] =$plugin->settingsForm($settings_form,$form_state, $editor);$form['editor']['settings']['subform']['#parents'] = array('editor', 'settings');  alexpott committed Jun 26, 2013 176  $form['actions']['submit']['#submit'][] = array($plugin, 'settingsFormSubmit');  webchick committed Jan 16, 2013 177 178  }  alexpott committed Jun 13, 2013 179  $form['#validate'][] = 'editor_form_filter_admin_format_validate';  alexpott committed Jun 26, 2013 180 $form['actions']['submit']['#submit'][] = 'editor_form_filter_admin_format_submit';  webchick committed Jan 16, 2013 181 182 183 } /**  alexpott committed Jun 26, 2013 184  * Button submit handler for filter_format_form()'s 'editor_configure' button.  webchick committed Jan 16, 2013 185  */  186 function editor_form_filter_admin_format_editor_configure($form, FormStateInterface$form_state) {  webchick committed Sep 11, 2014 187  $editor =$form_state->get('editor');  alexpott committed Jan 23, 2015 188 189  $editor_value =$form_state->getValue(array('editor', 'editor')); if ($editor_value !== NULL) {  190  if ($editor_value === '') {  webchick committed Sep 11, 2014 191  $form_state->set('editor', FALSE);  webchick committed Jan 16, 2013 192  }  193  elseif (empty($editor) || $editor_value !==$editor->getEditor()) {  alexpott committed Mar 22, 2015 194  $format =$form_state->getFormObject()->getEntity();  catch committed Feb 16, 2016 195  $editor = Editor::create([  alexpott committed Mar 22, 2015 196  'format' =>$format->isNew() ? NULL : $format->id(),  197  'editor' =>$editor_value,  catch committed Feb 16, 2016 198  ]);  webchick committed Sep 11, 2014 199  $form_state->set('editor',$editor);  webchick committed Jan 16, 2013 200 201  } }  webchick committed Sep 11, 2014 202  $form_state->setRebuild();  webchick committed Jan 16, 2013 203 204 205 } /**  alexpott committed Jun 26, 2013 206  * AJAX callback handler for filter_format_form().  webchick committed Jan 16, 2013 207  */  208 function editor_form_filter_admin_form_ajax($form, FormStateInterface $form_state) {  Dries committed Feb 08, 2013 209  return$form['editor']['settings'];  webchick committed Jan 16, 2013 210 211 }  alexpott committed Jun 13, 2013 212 /**  alexpott committed Jun 26, 2013 213  * Additional validate handler for filter_format_form().  alexpott committed Jun 13, 2013 214  */  215 function editor_form_filter_admin_format_validate($form, FormStateInterface$form_state) {  alexpott committed Jun 13, 2013 216  // This validate handler is not applicable when using the 'Configure' button.  webchick committed Sep 11, 2014 217  if ($form_state->getTriggeringElement()['#name'] === 'editor_configure') {  alexpott committed Jun 13, 2013 218 219 220  return; }  alexpott committed Apr 01, 2015 221  // When using this form with JavaScript disabled in the browser, the  alexpott committed Jun 13, 2013 222 223 224  // 'Configure' button won't be clicked automatically. So, when the user has // selected a text editor and has then clicked 'Save configuration', we should // point out that the user must still configure the text editor.  webchick committed Sep 11, 2014 225  if ($form_state->getValue(['editor', 'editor']) !== '' && !$form_state->get('editor')) {  226 $form_state->setErrorByName('editor][editor', t('You must configure the selected text editor.'));  alexpott committed Jun 13, 2013 227 228 229  } }  webchick committed Jan 16, 2013 230 /**  alexpott committed Jun 26, 2013 231  * Additional submit handler for filter_format_form().  webchick committed Jan 16, 2013 232  */  233 function editor_form_filter_admin_format_submit($form, FormStateInterface$form_state) {  webchick committed Jan 16, 2013 234  // Delete the existing editor if disabling or switching between editors.  alexpott committed Mar 22, 2015 235 236  $format =$form_state->getFormObject()->getEntity(); $format_id =$format->isNew() ? NULL : $format->id();  webchick committed Jan 16, 2013 237 $original_editor = editor_load($format_id);  238  if ($original_editor && $original_editor->getEditor() !=$form_state->getValue(array('editor', 'editor'))) {  webchick committed Jan 16, 2013 239 240 241 242  $original_editor->delete(); } // Create a new editor or update the existing editor.  webchick committed Sep 11, 2014 243  if ($editor = $form_state->get('editor')) {  alexpott committed Apr 08, 2013 244 245  // Ensure the text format is set: when creating a new text format, this // would equal the empty string.  webchick committed Sep 11, 2014 246 247 248 $editor->set('format', $format_id);$editor->setSettings($form_state->getValue(['editor', 'settings']));$editor->save();  webchick committed Jan 16, 2013 249 250 251 252 253 254  } } /** * Loads an individual configured text editor based on text format ID. *  alexpott committed Jan 05, 2016 255 256 257  * @param int $format_id * A text format ID. *  webchick committed Aug 18, 2013 258  * @return \Drupal\editor\Entity\Editor|null  259  * A text editor object, or NULL.  webchick committed Jan 16, 2013 260 261 262 263 264 265  */ function editor_load($format_id) { // Load all the editors at once here, assuming that either no editors or more // than one editor will be needed on a page (such as having multiple text // formats for administrators). Loading a small number of editors all at once // is more efficient than loading multiple editors individually.  catch committed May 17, 2016 266  $editors = Editor::loadMultiple();  267  return isset($editors[$format_id]) ?$editors[$format_id] : NULL;  webchick committed Jan 16, 2013 268 269 }  webchick committed Feb 08, 2014 270 271 272 273 274 /** * Applies text editor XSS filtering. * * @param string$html * The HTML string that will be passed to the text editor.  webchick committed Aug 26, 2015 275 276 277  * @param \Drupal\filter\FilterFormatInterface|null $format * The text format whose text editor will be used or NULL if the previously * defined text format is now disabled.  webchick committed Feb 08, 2014 278 279 280 281 282 283 284  * @param \Drupal\filter\FilterFormatInterface$original_format|null * (optional) The original text format (i.e. when switching text formats, * $format is the text format that is going to be used,$original_format is * the one that was being used initially, the one that is stored in the * database when editing). * * @return string|false  webchick committed Aug 26, 2015 285 286 287 288 289 290  * The XSS filtered string or FALSE when no XSS filtering needs to be applied, * because one of the next conditions might occur: * - No text editor is associated with the text format, * - The previously defined text format is now disabled, * - The text editor is safe from XSS, * - The text format does not use any XSS protection filters.  webchick committed Feb 08, 2014 291  *  xjm committed May 24, 2015 292  * @see https://www.drupal.org/node/2099741  webchick committed Feb 08, 2014 293  */  webchick committed Aug 26, 2015 294 295 function editor_filter_xss($html, FilterFormatInterface$format = NULL, FilterFormatInterface $original_format = NULL) {$editor = $format ? editor_load($format->id()) : NULL;  webchick committed Feb 08, 2014 296   webchick committed Aug 26, 2015 297 298 299  // If no text editor is associated with this text format or the previously // defined text format is now disabled, then we don't need text editor XSS // filtering either.  webchick committed Feb 08, 2014 300 301 302 303 304 305  if (!isset($editor)) { return FALSE; } // If the text editor associated with this text format guarantees security, // then we also don't need text editor XSS filtering.  webchick committed May 06, 2014 306 $definition = \Drupal::service('plugin.manager.editor')->getDefinition($editor->getEditor());  webchick committed Feb 08, 2014 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344  if ($definition['is_xss_safe'] === TRUE) { return FALSE; } // If there is no filter preventing XSS attacks in the text format being used, // then no text editor XSS filtering is needed either. (Because then the // editing user can already be attacked by merely viewing the content.) // e.g.: an admin user creates content in Full HTML and then edits it, no text // format switching happens; in this case, no text editor XSS filtering is // desirable, because it would strip style attributes, amongst others. $current_filter_types =$format->getFilterTypes(); if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $current_filter_types, TRUE)) { if ($original_format === NULL) { return FALSE; } // Unless we are switching from another text format, in which case we must // first check whether a filter preventing XSS attacks is used in that text // format, and if so, we must still apply XSS filtering. // e.g.: an anonymous user creates content in Restricted HTML, an admin user // edits it (then no XSS filtering is applied because no text editor is // used), and switches to Full HTML (for which a text editor is used). Then // we must apply XSS filtering to protect the admin user. else { $original_filter_types =$original_format->getFilterTypes(); if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $original_filter_types, TRUE)) { return FALSE; } } } // Otherwise, apply the text editor XSS filter. We use the default one unless // a module tells us to use a different one.$editor_xss_filter_class = '\Drupal\editor\EditorXssFilter\Standard'; \Drupal::moduleHandler()->alter('editor_xss_filter', $editor_xss_filter_class,$format, $original_format); return call_user_func($editor_xss_filter_class . '::filterXss', $html,$format, $original_format); }  webchick committed Aug 29, 2013 345 346 347 348 /** * Implements hook_entity_insert(). */ function editor_entity_insert(EntityInterface$entity) {  catch committed Oct 01, 2013 349  // Only act on content entities.  350  if (!($entity instanceof FieldableEntityInterface)) {  catch committed Oct 01, 2013 351 352  return; }  webchick committed Aug 29, 2013 353 354 355 356 357 358 359 360 361 362 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity); foreach ($referenced_files_by_field as $field =>$uuids) { _editor_record_file_usage($uuids,$entity); } } /** * Implements hook_entity_update(). */ function editor_entity_update(EntityInterface $entity) {  catch committed Oct 01, 2013 363  // Only act on content entities.  364  if (!($entity instanceof FieldableEntityInterface)) {  catch committed Oct 01, 2013 365 366 367  return; }  webchick committed Aug 29, 2013 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401  // On new revisions, all files are considered to be a new usage and no // deletion of previous file usages are necessary. if (!empty($entity->original) &&$entity->getRevisionId() != $entity->original->getRevisionId()) {$referenced_files_by_field = _editor_get_file_uuids_by_field($entity); foreach ($referenced_files_by_field as $field =>$uuids) { _editor_record_file_usage($uuids,$entity); } } // On modified revisions, detect which file references have been added (and // record their usage) and which ones have been removed (delete their usage). // File references that existed both in the previous version of the revision // and in the new one don't need their usage to be updated. else { $original_uuids_by_field = _editor_get_file_uuids_by_field($entity->original); $uuids_by_field = _editor_get_file_uuids_by_field($entity); // Detect file usages that should be incremented. foreach ($uuids_by_field as$field => $uuids) {$added_files = array_diff($uuids_by_field[$field], $original_uuids_by_field[$field]); _editor_record_file_usage($added_files,$entity); } // Detect file usages that should be decremented. foreach ($original_uuids_by_field as$field => $uuids) {$removed_files = array_diff($original_uuids_by_field[$field], $uuids_by_field[$field]); _editor_delete_file_usage($removed_files,$entity, 1); } } } /** * Implements hook_entity_delete(). */ function editor_entity_delete(EntityInterface $entity) {  catch committed Oct 01, 2013 402  // Only act on content entities.  403  if (!($entity instanceof FieldableEntityInterface)) {  catch committed Oct 01, 2013 404 405  return; }  webchick committed Aug 29, 2013 406 407 408 409 410 411 412 413 414 415  $referenced_files_by_field = _editor_get_file_uuids_by_field($entity); foreach ($referenced_files_by_field as$field => $uuids) { _editor_delete_file_usage($uuids, $entity, 0); } } /** * Implements hook_entity_revision_delete(). */ function editor_entity_revision_delete(EntityInterface$entity) {  catch committed Oct 01, 2013 416  // Only act on content entities.  417  if (!($entity instanceof FieldableEntityInterface)) {  catch committed Oct 01, 2013 418 419  return; }  webchick committed Aug 29, 2013 420 421 422 423 424 425 426 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity); foreach ($referenced_files_by_field as $field =>$uuids) { _editor_delete_file_usage($uuids,$entity, 1); } } /**  alexpott committed Sep 17, 2014 427  * Records file usage of files referenced by formatted text fields.  webchick committed Aug 29, 2013 428 429 430 431 432 433 434 435 436 437 438  * * Every referenced file that does not yet have the FILE_STATUS_PERMANENT state, * will be given that state. * * @param array $uuids * An array of file entity UUIDs. * @param EntityInterface$entity * An entity whose fields to inspect for file references. */ function _editor_record_file_usage(array $uuids, EntityInterface$entity) { foreach ($uuids as$uuid) {  webchick committed Mar 16, 2015 439  if ($file = \Drupal::entityManager()->loadEntityByUuid('file',$uuid)) {  alexpott committed Sep 09, 2014 440 441 442 443 444  if ($file->status !== FILE_STATUS_PERMANENT) {$file->status = FILE_STATUS_PERMANENT; $file->save(); } \Drupal::service('file.usage')->add($file, 'editor', $entity->getEntityTypeId(),$entity->id());  webchick committed Aug 29, 2013 445 446 447 448 449  } } } /**  alexpott committed Sep 17, 2014 450  * Deletes file usage of files referenced by formatted text fields.  webchick committed Aug 29, 2013 451 452 453 454 455 456 457 458 459  * * @param array $uuids * An array of file entity UUIDs. * @param EntityInterface$entity * An entity whose fields to inspect for file references. * @param $count * The number of references to delete. Should be 1 when deleting a single * revision and 0 when deleting an entity entirely. *  alexpott committed Oct 03, 2013 460  * @see \Drupal\file\FileUsage\FileUsageInterface::delete()  webchick committed Aug 29, 2013 461 462 463  */ function _editor_delete_file_usage(array$uuids, EntityInterface $entity,$count) { foreach ($uuids as$uuid) {  webchick committed Mar 16, 2015 464  if ($file = \Drupal::entityManager()->loadEntityByUuid('file',$uuid)) {  alexpott committed Sep 09, 2014 465 466  \Drupal::service('file.usage')->delete($file, 'editor',$entity->getEntityTypeId(), $entity->id(),$count); }  webchick committed Aug 29, 2013 467 468 469  } }  alexpott committed Sep 02, 2016 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 /** * Implements hook_file_download(). * * @see file_file_download() * @see file_get_file_references() */ function editor_file_download($uri) { // Get the file record based on the URI. If not in the database just return. /** @var \Drupal\file\FileInterface[]$files */ $files = \Drupal::entityTypeManager() ->getStorage('file') ->loadByProperties(['uri' =>$uri]); if (count($files)) { foreach ($files as $item) { // Since some database servers sometimes use a case-insensitive comparison // by default, double check that the filename is an exact match. if ($item->getFileUri() === $uri) {$file = $item; break; } } } if (!isset($file)) { return; } // Temporary files are handled by file_file_download(), so nothing to do here // about them. // @see file_file_download() // Find out if any editor-backed field contains the file. $usage_list = \Drupal::service('file.usage')->listUsage($file); // Stop processing if there are no references in order to avoid returning // headers for files controlled by other modules. Make an exception for // temporary files where the host entity has not yet been saved (for example, // an image preview on a node creation form) in which case, allow download by // the file's owner. if (empty($usage_list['editor']) && ($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())) { return; } // Editor.module MUST NOT call$file->access() here (like file_file_download() // does) as checking the 'download' access to a file entity would end up in // FileAccessControlHandler->checkAccess() and ->getFileReferences(), which // calls file_get_file_references(). This latter one would allow downloading // files only handled by the file.module, which is exactly not the case right // here. // Access is granted. $headers = file_get_content_headers($file); return $headers; }  webchick committed Aug 29, 2013 524 /**  alexpott committed Nov 25, 2014 525  * Finds all files referenced (data-entity-uuid) by formatted text fields.  webchick committed Aug 29, 2013 526 527 528 529 530 531 532 533 534 535  * * @param EntityInterface$entity * An entity whose fields to analyze. * * @return array * An array of file entity UUIDs. */ function _editor_get_file_uuids_by_field(EntityInterface $entity) {$uuids = array();  alexpott committed Sep 17, 2014 536 537  $formatted_text_fields = _editor_get_formatted_text_fields($entity); foreach ($formatted_text_fields as$formatted_text_field) {  catch committed Nov 12, 2015 538 539 540 541  $text = '';$field_items = $entity->get($formatted_text_field); foreach ($field_items as$field_item) { $text .=$field_item->value;  xjm committed May 24, 2016 542 543 544  if ($field_item->getFieldDefinition()->getType() == 'text_with_summary') {$text .= $field_item->summary; }  catch committed Nov 12, 2015 545  }  alexpott committed Sep 17, 2014 546 $uuids[$formatted_text_field] = _editor_parse_file_uuids($text);  webchick committed Aug 29, 2013 547 548 549 550 551  } return $uuids; } /**  alexpott committed Sep 17, 2014 552  * Determines the formatted text fields on an entity.  webchick committed Aug 29, 2013 553  *  554  * @param \Drupal\Core\Entity\FieldableEntityInterface$entity  webchick committed Aug 29, 2013 555 556 557  * An entity whose fields to analyze. * * @return array  alexpott committed Sep 17, 2014 558  * The names of the fields on this entity that support formatted text.  webchick committed Aug 29, 2013 559  */  560 function _editor_get_formatted_text_fields(FieldableEntityInterface $entity) {  alexpott committed Feb 24, 2014 561 562 $field_definitions = $entity->getFieldDefinitions(); if (empty($field_definitions)) {  webchick committed Aug 29, 2013 563 564 565  return array(); }  alexpott committed Sep 17, 2014 566 567 568  // Only return formatted text fields. return array_keys(array_filter($field_definitions, function (FieldDefinitionInterface$definition) { return in_array($definition->getType(), array('text', 'text_long', 'text_with_summary'), TRUE);  alexpott committed Mar 22, 2014 569  }));  webchick committed Aug 29, 2013 570 571 572 } /**  alexpott committed Nov 25, 2014 573  * Parse an HTML snippet for any linked file with data-entity-uuid attributes.  webchick committed Aug 29, 2013 574 575 576 577 578 579 580 581 582  * * @param string$text * The partial (X)HTML snippet to load. Invalid markup will be corrected on * import. * * @return array * An array of all found UUIDs. */ function _editor_parse_file_uuids($text) {  alexpott committed Feb 16, 2014 583 $dom = Html::load($text);  webchick committed Aug 29, 2013 584 585 $xpath = new \DOMXPath($dom);$uuids = array();  alexpott committed Nov 25, 2014 586 587  foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid]') as$node) { $uuids[] =$node->getAttribute('data-entity-uuid');  webchick committed Aug 29, 2013 588 589 590  } return $uuids; }  alexpott committed Oct 06, 2015 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617  /** * Implements hook_ENTITY_TYPE_presave(). * * Synchronizes the editor status to its paired text format status. */ function editor_filter_format_presave(FilterFormatInterface$format) { // The text format being created cannot have a text editor yet. if ($format->isNew()) { return; } /** @var \Drupal\filter\FilterFormatInterface$original */ $original = \Drupal::entityManager() ->getStorage('filter_format') ->loadUnchanged($format->getOriginalId()); // If the text format status is the same, return early. if (($status =$format->status()) === $original->status()) { return; } /** @var \Drupal\editor\EditorInterface$editor */ if ($editor = Editor::load($format->id())) { $editor->setStatus($status)->save(); } }