diff --git a/.htaccess b/.htaccess index 3642f88..5cf52c8 100644 --- a/.htaccess +++ b/.htaccess @@ -3,7 +3,7 @@ # # Protect files and directories from prying eyes. - + Require all denied diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt index 379797f..9263aea 100644 --- a/core/MAINTAINERS.txt +++ b/core/MAINTAINERS.txt @@ -92,6 +92,11 @@ Entity system Extension system - ? +Field system +- Yves Chedemois 'yched' http://drupal.org/user/39567 +- Kristof De Jaeger 'swentel' http://drupal.org/user/107403 +- Andrei Mateescu 'amateescu' http://drupal.org/user/729614 + File system - Andrew Morton 'drewish' http://drupal.org/user/34869 - Aaron Winborn 'aaron' http://drupal.org/user/33420 @@ -187,9 +192,6 @@ Accessibility Documentation - Jennifer Hodgdon 'jhodgdon' http://drupal.org/user/155601 -Security -- Greg Knaddison 'greggles' http://drupal.org/user/36762 - Translations - ? @@ -203,6 +205,19 @@ Node Access - Jess Myrbo 'xjm' http://drupal.org/user/65776 +Security team +----------------- + +To report a security issue, see: https://drupal.org/security-team/report-issue + +The Drupal security team provides Security Advisories for vulnerabilities, +assists developers in resolving security issues, and provides security +documentation. See http://drupal.org/security-team for more information. The +security team lead is: + +- Michael Hess 'mlhess' https://drupal.org/user/102818 + + Module maintainers ------------------ @@ -269,10 +284,6 @@ DateTime module E-mail module - Nils Destoop 'zuuperman' https://drupal.org/user/361625 -Edit module -- Wim Leers 'Wim Leers' http://drupal.org/user/99777 -- Théodore Biadala 'nod_' http://drupal.org/user/598310 - Editor module - Wim Leers 'Wim Leers' http://drupal.org/user/99777 @@ -283,11 +294,6 @@ Entity Reference module - Amitai Burstein 'Amitaibu' http://drupal.org/user/57511 - Andrei Mateescu 'amateescu' http://drupal.org/user/729614 -Field module -- Yves Chedemois 'yched' http://drupal.org/user/39567 -- Kristof De Jaeger 'swentel' http://drupal.org/user/107403 -- Andrei Mateescu 'amateescu' http://drupal.org/user/729614 - Field UI module - Yves Chedemois 'yched' http://drupal.org/user/39567 - Kristof De Jaeger 'swentel' http://drupal.org/user/107403 @@ -353,6 +359,10 @@ Path module Phone module - Dave Reid 'davereid' http://drupal.org/user/53892 +Quick Edit module +- Wim Leers 'Wim Leers' http://drupal.org/user/99777 +- Théodore Biadala 'nod_' http://drupal.org/user/598310 + RDF module - Stéphane Corlosquet 'scor' http://drupal.org/user/52142 diff --git a/core/assets/vendor/ckeditor/CHANGES.md b/core/assets/vendor/ckeditor/CHANGES.md index 2ff3ced..826ee40 100644 --- a/core/assets/vendor/ckeditor/CHANGES.md +++ b/core/assets/vendor/ckeditor/CHANGES.md @@ -1,181 +1,506 @@ CKEditor 4 Changelog ==================== +## CKEditor 4.4 + +**Important Notes:** + +* Marked the [`editor.beforePaste`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-beforePaste) event as deprecated. +* The default class of captioned images has changed to `image` (was: `caption`). Please note that once edited in CKEditor 4.4+, all existing images of the `caption` class (`
`) will be [filtered out](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) unless the [`config.image2_captionedClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image2_captionedClass) option is set to `caption`. For backward compatibility (i.e. when upgrading), it is highly recommended to use this setting, which also helps prevent CSS conflicts, etc. This does not apply to new CKEditor integrations. +* Widgets without defined buttons are no longer registered automatically to the [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter). Before CKEditor 4.4 widgets were registered to the ACF which was an incorrect behavior ([#11567](http://dev.ckeditor.com/ticket/11567)). This change should not have any impact on standard scenarios, but if your button does not execute the widget command, you need to set [`allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.feature-property-allowedContent) and [`requiredContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.feature-property-requiredContent) properties for it manually, because the editor will not be able to find them. +* The [Show Borders](http://ckeditor.com/addon/showborders) plugin was added to the Standard installation package in order to ensure that unstyled tables are still visible for the user ([#11665](http://dev.ckeditor.com/ticket/11665)). +* Since CKEditor 4.4 the editor instance should be passed to [`CKEDITOR.style`](http://docs.ckeditor.com/#!/api/CKEDITOR.style) methods to ensure full compatibility with other features (e.g. applying styles to widgets requires that). We ensured backward compatibility though, so the [`CKEDITOR.style`](http://docs.ckeditor.com/#!/api/CKEDITOR.style) will work even when the editor instance is not provided. + +New Features: + +* [#11297](http://dev.ckeditor.com/ticket/11297): Styles can now be applied to widgets. The definition of a style which can be applied to a specific widget must contain two additional properties — `type` and `widget`. Read more in the [Widget Styles](http://docs.ckeditor.com/#!/guide/dev_styles-section-widget-styles) section of the "Syles Drop-down" guide. Note that by default, widgets support only classes and no other attributes or styles. Related changes and features: + * Introduced the [`CKEDITOR.style.addCustomHandler()`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-static-method-addCustomHandler) method for registering custom style handlers. + * The [`CKEDITOR.style.apply()`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-apply) and [`CKEDITOR.style.remove()`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-remove) methods are now called with an editor instance instead of the document so they can be reused by the [`CKEDITOR.editor.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) and [`CKEDITOR.editor.removeStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-removeStyle) methods. Backward compatibility was preserved, but from CKEditor 4.4 it is highly recommended to pass an editor instead of a document to these methods. + * Many new methods and properties were introduced in the [Widget API](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget) to make the handling of styles by widgets fully customizable. See: [`widget.definition.styleableElements`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-styleableElements), [`widget.definition.styleToAllowedContentRule`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-styleToAllowedContentRules), [`widget.addClass()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-addClass), [`widget.removeClass()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-removeClass), [`widget.getClasses()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-getClasses), [`widget.hasClass()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-hasClass), [`widget.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-applyStyle), [`widget.removeStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-removeStyle), [`widget.checkStyleActive()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-method-checkStyleActive). + * Integration with the [Allowed Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) required an introduction of the [`CKEDITOR.style.toAllowedContent()`](http://docs.ckeditor.com/#!/api/CKEDITOR.style-method-toAllowedContentRules) method which can be implemented by the custom style handler and if exists, it is used by the [`CKEDITOR.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.filter) to translate a style to [allowed content rules](http://docs.ckeditor.com/#!/api/CKEDITOR.filter.allowedContentRules). +* [#11300](http://dev.ckeditor.com/ticket/11300): Various changes in the [Enhanced Image](http://ckeditor.com/addon/image2) plugin: + * Introduced the [`config.image2_captionedClass`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image2_captionedClass) option to configure the class of captioned images. + * Introduced the [`config.image2_alignClasses`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image2_alignClasses) option to configure the way images are aligned with CSS classes. + If this setting is defined, the editor produces classes instead of inline styles for aligned images. + * Default image caption can be translated (customized) with the `editor.lang.image2.captionPlaceholder` string. +* [#11341](http://dev.ckeditor.com/ticket/11341): [Enhanced Image](http://ckeditor.com/addon/image2) plugin: It is now possible to add a link to any image type. +* [#10202](http://dev.ckeditor.com/ticket/10202): Introduced wildcard support in the [Allowed Content Rules](http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules) format. +* [#10276](http://dev.ckeditor.com/ticket/10276): Introduced blacklisting in the [Allowed Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter). +* [#10480](http://dev.ckeditor.com/ticket/10480): Introduced code snippets with code highlighting. There are two versions available so far — the default [Code Snippet](http://ckeditor.com/addon/codesnippet) which uses the [highlight.js](http://highlightjs.org) library and the [Code Snippet GeSHi](http://ckeditor.com/addon/codesnippetgeshi) which uses the [GeSHi](http://qbnz.com/highlighter/) library. +* [#11737](http://dev.ckeditor.com/ticket/11737): Introduced an option to prevent [filtering](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) of an element that matches custom criteria (see [`filter.addElementCallback()`](http://docs.ckeditor.com/#!/api/CKEDITOR.filter-method-addElementCallback)). +* [#11532](http://dev.ckeditor.com/ticket/11532): Introduced the [`editor.addContentsCss()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-addContentsCss) method that can be used for [adding custom CSS files](http://docs.ckeditor.com/#!/guide/plugin_sdk_styles). +* [#11536](http://dev.ckeditor.com/ticket/11536): Added the [`CKEDITOR.tools.htmlDecode()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-htmlDecode) method for decoding HTML entities. +* [#11225](http://dev.ckeditor.com/ticket/11225): Introduced the [`CKEDITOR.tools.transparentImageData`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-property-transparentImageData) property which contains transparent image data to be used in CSS or as image source. + +Other changes: + +* [#11377](http://dev.ckeditor.com/ticket/11377): Unified internal representation of empty anchors using the [fake objects](http://ckeditor.com/addon/fakeobjects). +* [#11422](http://dev.ckeditor.com/ticket/11422): Removed Firefox 3.x, Internet Explorer 6 and Opera 12.x leftovers in code. +* [#5217](http://dev.ckeditor.com/ticket/5217): Setting data (including switching between modes) creates a new undo snapshot. Besides that: + * Introduced the [`editable.status`](http://docs.ckeditor.com/#!/api/CKEDITOR.editable-property-status) property. + * Introduced a new `forceUpdate` option for the [`editor.lockSnapshot`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-lockSnapshot) event. + * Fixed: Selection not being unlocked in inline editor after setting data ([#11500](http://dev.ckeditor.com/ticket/11500)). +* The [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin was updated to the latest version. + +Fixed Issues: + +* [#10190](http://dev.ckeditor.com/ticket/10190): Fixed: Removing block style with [`editor.removeStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-removeStyle) should result in a paragraph and not a div. +* [#11727](http://dev.ckeditor.com/ticket/11727): Fixed: The editor tries to select a non-editable image which was clicked. + +## CKEditor 4.3.5 + +New Features: + +* Added new translation: Tatar. + +Fixed Issues: + +* [#11677](http://dev.ckeditor.com/ticket/11677): Fixed: Undo/Redo keystrokes are blocked in the source mode. +* [#11717](http://dev.ckeditor.com/ticket/11717): [Document Properties](http://ckeditor.com/addon/docprops) plugin requires the [Color Dialog](http://ckeditor.com/addon/colordialog) plugin to work. + +## CKEditor 4.3.4 + +Fixed Issues: + +* [#11597](http://dev.ckeditor.com/ticket/11597): [IE11] Fixed: Error thrown when trying to open the [preview](http://ckeditor.com/addon/preview) using the keyboard. +* [#11544](http://dev.ckeditor.com/ticket/11544): [Placeholders](http://ckeditor.com/addon/placeholder) will no longer be upcasted in parents not accepting `` elements. +* [#8663](http://dev.ckeditor.com/ticket/8663): Fixed [`element.renameNode()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.element-method-renameNode) not clearing the [`element.getName()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.element-method-getName) cache. +* [#11574](http://dev.ckeditor.com/ticket/11574): Fixed: *Backspace* destroying the DOM structure if an inline editable is placed in a list item. +* [#11603](http://dev.ckeditor.com/ticket/11603): Fixed: [Table Resize](http://ckeditor.com/addon/tableresize) attaches to tables outside the editable. +* [#9205](http://dev.ckeditor.com/ticket/9205), [#7805](http://dev.ckeditor.com/ticket/7805), [#8216](http://dev.ckeditor.com/ticket/8216): Fixed: `{cke_protected_1}` appearing in data in various cases where HTML comments are placed next to `"` or `'`. +* [#11635](http://dev.ckeditor.com/ticket/11635): Fixed: Some attributes are not protected before the content is passed through the fix bin. +* [#11660](http://dev.ckeditor.com/ticket/11660): [IE] Fixed: Table content is lost when some extra markup is inside the table. +* [#11641](http://dev.ckeditor.com/ticket/11641): Fixed: Switching between modes in the classic editor removes content styles for the inline editor. +* [#11568](http://dev.ckeditor.com/ticket/11568): Fixed: [Styles](http://ckeditor.com/addon/stylescombo) drop-down list is not enabled on selection change. + +## CKEditor 4.3.3 + +Fixed Issues: + +* [#11500](http://dev.ckeditor.com/ticket/11500): [Webkit/Blink] Fixed: Selection lost when setting data in another inline editor. Additionally, [`selection.removeAllRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-removeAllRanges) is now scoped to selection's [root](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-property-root). +* [#11104](http://dev.ckeditor.com/ticket/11104): [IE] Fixed: Various issues with scrolling and selection when focusing widgets. +* [#11487](http://dev.ckeditor.com/ticket/11487): Moving mouse over the [Enhanced Image](http://ckeditor.com/addon/image2) widget will no longer change the value returned by the [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) method. +* [#8673](http://dev.ckeditor.com/ticket/8673): [WebKit] Fixed: Cannot select and remove the [Page Break](http://ckeditor.com/addon/pagebreak). +* [#11413](http://dev.ckeditor.com/ticket/11413): Fixed: Incorrect [`editor.execCommand()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-execCommand) behavior. +* [#11438](http://dev.ckeditor.com/ticket/11438): Splitting table cells vertically is no longer changing table structure. +* [#8899](http://dev.ckeditor.com/ticket/8899): Fixed: Links in the [About CKEditor](http://ckeditor.com/addon/about) dialog window now open in a new browser window or tab. +* [#11490](http://dev.ckeditor.com/ticket/11490): Fixed: [Menu button](http://ckeditor.com/addon/menubutton) panel not showing in the source mode. +* [#11417](http://dev.ckeditor.com/ticket/11417): The [`widget.doubleclick`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-event-doubleclick) event is not canceled anymore after editing was triggered. +* [#11253](http://dev.ckeditor.com/ticket/11253): [IE] Fixed: Clipped upload button in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window. +* [#11359](http://dev.ckeditor.com/ticket/11359): Standardized the way anchors are discovered by the [Link](http://ckeditor.com/addon/link) plugin. +* [#11058](http://dev.ckeditor.com/ticket/11058): [IE8] Fixed: Error when deleting a table row. +* [#11508](http://dev.ckeditor.com/ticket/11508): Fixed: [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor) discovering protected attributes within other attributes' values. +* [#11533](http://dev.ckeditor.com/ticket/11533): Widgets: Avoid recurring upcasts if the DOM structure was modified during an upcast. +* [#11400](http://dev.ckeditor.com/ticket/11400): Fixed: The [`domObject.removeAllListeners()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.domObject-method-removeAllListeners) method does not remove custom listeners completely. +* [#11493](http://dev.ckeditor.com/ticket/11493): Fixed: The [`selection.getRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-getRanges) method does not override cached ranges when used with the `onlyEditables` argument. +* [#11390](http://dev.ckeditor.com/ticket/11390): [IE] All [XML](http://ckeditor.com/addon/xml) plugin [methods](http://docs.ckeditor.com/#!/api/CKEDITOR.xml) now work in IE10+. +* [#11542](http://dev.ckeditor.com/ticket/11542): [IE11] Fixed: Blurry toolbar icons when Right-to-Left UI language is set. +* [#11504](http://dev.ckeditor.com/ticket/11504): Fixed: When [`config.fullPage`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fullPage) is set to `true`, entities are not encoded in editor output. +* [#11004](http://dev.ckeditor.com/ticket/11004): Integrated [Enhanced Image](http://ckeditor.com/addon/image2) dialog window with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter). +* [#11439](http://dev.ckeditor.com/ticket/11439): Fixed: Properties get cloned in the Cell Properties dialog window if multiple cells are selected. + +## CKEditor 4.3.2 + +Fixed Issues: + +* [#11331](http://dev.ckeditor.com/ticket/11331): A menu button will have a changed label when selected instead of using the `aria-pressed` attribute. +* [#11177](http://dev.ckeditor.com/ticket/11177): Widget drag handler improvements: + * [#11176](http://dev.ckeditor.com/ticket/11176): Fixed: Initial position is not updated when the widget data object is empty. + * [#11001](http://dev.ckeditor.com/ticket/11001): Fixed: Multiple synchronous layout recalculations are caused by initial drag handler positioning causing performance issues. + * [#11161](http://dev.ckeditor.com/ticket/11161): Fixed: Drag handler is not repositioned in various situations. + * [#11281](http://dev.ckeditor.com/ticket/11281): Fixed: Drag handler and mask are duplicated after widget reinitialization. +* [#11207](http://dev.ckeditor.com/ticket/11207): [Firefox] Fixed: Misplaced [Enhanced Image](http://ckeditor.com/addon/image2) resizer in the inline editor. +* [#11102](http://dev.ckeditor.com/ticket/11102): `CKEDITOR.template` improvements: + * [#11102](http://dev.ckeditor.com/ticket/11102): Added newline character support. + * [#11216](http://dev.ckeditor.com/ticket/11216): Added "\\'" substring support. +* [#11121](http://dev.ckeditor.com/ticket/11121): [Firefox] Fixed: High Contrast mode is enabled when the editor is loaded in a hidden iframe. +* [#11350](http://dev.ckeditor.com/ticket/11350): The default value of [`config.contentsCss`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-contentsCss) is affected by [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl). +* [#11097](http://dev.ckeditor.com/ticket/11097): Improved the [Autogrow](http://ckeditor.com/addon/autogrow) plugin performance when dealing with very big tables. +* [#11290](http://dev.ckeditor.com/ticket/11290): Removed redundant code in the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin. +* [#11133](http://dev.ckeditor.com/ticket/11133): [Page Break](http://ckeditor.com/addon/pagebreak) becomes editable if pasted. +* [#11126](http://dev.ckeditor.com/ticket/11126): Fixed: Native Undo executed once the bottom of the snapshot stack is reached. +* [#11131](http://dev.ckeditor.com/ticket/11131): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Error thrown when switching to source mode if the selection was in widget's nested editable. +* [#11139](http://dev.ckeditor.com/ticket/11139): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Elements Path is not cleared after switching to source mode. +* [#10778](http://dev.ckeditor.com/ticket/10778): Fixed a bug with range enlargement. The range no longer expands to visible whitespace. +* [#11146](http://dev.ckeditor.com/ticket/11146): [IE] Fixed: Preview window switches Internet Explorer to Quirks Mode. +* [#10762](http://dev.ckeditor.com/ticket/10762): [IE] Fixed: JavaScript code displayed in preview window's URL bar. +* [#11186](http://dev.ckeditor.com/ticket/11186): Introduced the [`widgets.repository.addUpcastCallback()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-addUpcastCallback) method that allows to block upcasting given element to a widget. +* [#11307](http://dev.ckeditor.com/ticket/11307): Fixed: Paste as Plain Text conflict with the [MooTools](http://mootools.net) library. +* [#11140](http://dev.ckeditor.com/ticket/11140): [IE11] Fixed: Anchors are not draggable. +* [#11379](http://dev.ckeditor.com/ticket/11379): Changed default contents `line-height` to unitless values to avoid huge text overlapping (like in [#9696](http://dev.ckeditor.com/ticket/9696)). +* [#10787](http://dev.ckeditor.com/ticket/10787): [Firefox] Fixed: Broken replacement of text while pasting into `div`-based editor. +* [#10884](http://dev.ckeditor.com/ticket/10884): Widgets integration with the [Show Blocks](http://ckeditor.com/addon/showblocks) plugin. +* [#11021](http://dev.ckeditor.com/ticket/11021): Fixed: An error thrown when selecting entire editable contents while fake selection is on. +* [#11086](http://dev.ckeditor.com/ticket/11086): [IE8] Re-enable inline widgets drag&drop in Internet Explorer 8. +* [#11372](http://dev.ckeditor.com/ticket/11372): Widgets: Special characters encoded twice in nested editables. +* [#10068](http://dev.ckeditor.com/ticket/10068): Fixed: Support for protocol-relative URLs. +* [#11283](http://dev.ckeditor.com/ticket/11283): [Enhanced Image](http://ckeditor.com/addon/image2): A `
` element with `text-align: center` and an image inside is not recognised correctly. +* [#11196](http://dev.ckeditor.com/ticket/11196): [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp): Allowed additional keyboard button labels to be translated in the dialog window. + +## CKEditor 4.3.1 + +**Important Notes:** + +* To match the naming convention, the `language` button is now `Language` ([#11201](http://dev.ckeditor.com/ticket/11201)). +* [Enhanced Image](http://ckeditor.com/addon/image2) button, context menu, command, and icon names match those of the [Image](http://ckeditor.com/addon/image) plugin ([#11222](http://dev.ckeditor.com/ticket/11222)). + +Fixed Issues: + +* [#11244](http://dev.ckeditor.com/ticket/11244): Changed: The [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method now fires the [`widget.repository.checkWidgets`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-event-checkWidgets) event, so from CKEditor 4.3.1 it is preferred to use the method rather than fire the event. +* [#11171](http://dev.ckeditor.com/ticket/11171): Fixed: [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) and [`editor.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertText) methods do not call the [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method. +* [#11085](http://dev.ckeditor.com/ticket/11085): [IE8] Replaced preview generated by the [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget with a placeholder. +* [#11044](http://dev.ckeditor.com/ticket/11044): Enhanced WAI-ARIA support for the [Language](http://ckeditor.com/addon/language) plugin drop-down menu. +* [#11075](http://dev.ckeditor.com/ticket/11075): With drop-down menu button focused, pressing the *Down Arrow* key will now open the menu and focus its first option. +* [#11165](http://dev.ckeditor.com/ticket/11165): Fixed: The [File Browser](http://ckeditor.com/addon/filebrowser) plugin cannot be removed from the editor. +* [#11159](http://dev.ckeditor.com/ticket/11159): [IE9-10] [Enhanced Image](http://ckeditor.com/addon/image2): Fixed buggy discovery of image dimensions. +* [#11101](http://dev.ckeditor.com/ticket/11101): Drop-down lists no longer break when given double quotes. +* [#11077](http://dev.ckeditor.com/ticket/11077): [Enhanced Image](http://ckeditor.com/addon/image2): Empty undo step recorded when resizing the image. +* [#10853](http://dev.ckeditor.com/ticket/10853): [Enhanced Image](http://ckeditor.com/addon/image2): Widget has paragraph wrapper when de-captioning unaligned image. +* [#11198](http://dev.ckeditor.com/ticket/11198): Widgets: Drag handler is not fully visible when an inline widget is in a heading. +* [#11132](http://dev.ckeditor.com/ticket/11132): [Firefox] Fixed: Caret is lost after drag and drop of an inline widget. +* [#11182](http://dev.ckeditor.com/ticket/11182): [IE10-11] Fixed: Editor crashes (IE11) or works with minor issues (IE10) if a page is loaded in Quirks Mode. See [`env.quirks`](http://docs.ckeditor.com/#!/api/CKEDITOR.env-property-quirks) for more details. +* [#11204](http://dev.ckeditor.com/ticket/11204): Added `figure` and `figcaption` styles to the `contents.css` file so [Enhanced Image](http://ckeditor.com/addon/image2) looks nicer. +* [#11202](http://dev.ckeditor.com/ticket/11202): Fixed: No newline in [BBCode](http://ckeditor.com/addon/bbcode) mode. +* [#10890](http://dev.ckeditor.com/ticket/10890): Fixed: Error thrown when pressing the *Delete* key in a list item. +* [#10055](http://dev.ckeditor.com/ticket/10055): [IE8-10] Fixed: *Delete* pressed on a selected image causes the browser to go back. +* [#11183](http://dev.ckeditor.com/ticket/11183): Fixed: Inserting a horizontal rule or a table in multiple row selection causes a browser crash. Additionally, the [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) method does not insert the element into every range of a selection any more. +* [#11042](http://dev.ckeditor.com/ticket/11042): Fixed: Selection made on an element containing a non-editable element was not auto faked. +* [#11125](http://dev.ckeditor.com/ticket/11125): Fixed: Keyboard navigation through menu and drop-down items will now cycle. +* [#11011](http://dev.ckeditor.com/ticket/11011): Fixed: The [`editor.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) method removes attributes from nested elements. +* [#11179](http://dev.ckeditor.com/ticket/11179): Fixed: [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy) does not cleanup content generated by the [Table Resize](http://ckeditor.com/addon/tableresize) plugin for inline editors. +* [#11237](http://dev.ckeditor.com/ticket/11237): Fixed: Table border attribute value is deleted when pasting content from Microsoft Word. +* [#11250](http://dev.ckeditor.com/ticket/11250): Fixed: HTML entities inside the `");return""+encodeURIComponent(a)+""})}function m(a){return a.replace(E,function(a,b){return decodeURIComponent(b)})}function l(a){return a.replace(/<\!--(?!{cke_protected})[\s\S]+?--\>/g,function(a){return"<\!--"+v+"{C}"+encodeURIComponent(a).replace(/--/g,"%2D%2D")+"--\>"})}function t(a){return a.replace(/<\!--\{cke_protected\}\{C\}([\s\S]+?)--\>/g,function(a,b){return decodeURIComponent(b)})} -function o(a,b){var c=b._.dataStore;return a.replace(/<\!--\{cke_protected\}([\s\S]+?)--\>/g,function(a,b){return decodeURIComponent(b)}).replace(/\{cke_protected_(\d+)\}/g,function(a,b){return c&&c[b]||""})}function s(a,b){for(var c=[],d=b.config.protectedSource,f=b._.dataStore||(b._.dataStore={id:1}),g=/<\!--\{cke_temp(comment)?\}(\d*?)--\>/g,d=[//gi,//gi].concat(d),a=a.replace(/<\!--[\s\S]*?--\>/g,function(a){return"<\!--{cke_tempcomment}"+ -(c.push(a)-1)+"--\>"}),e=0;e"});a=a.replace(g,function(a,b,d){return"<\!--"+v+(b?"{C}":"")+encodeURIComponent(c[d]).replace(/--/g,"%2D%2D")+"--\>"});return a.replace(/(['"]).*?\1/g,function(a){return a.replace(/<\!--\{cke_protected\}([\s\S]+?)--\>/g,function(a,b){f[f.id]=decodeURIComponent(b);return"{cke_protected_"+f.id++ +"}"})})}CKEDITOR.htmlDataProcessor= -function(a){var b,c,f=this;this.editor=a;this.dataFilter=b=new CKEDITOR.htmlParser.filter;this.htmlFilter=c=new CKEDITOR.htmlParser.filter;this.writer=new CKEDITOR.htmlParser.basicWriter;b.addRules(r);b.addRules(d(a,"data"));c.addRules(x);c.addRules(d(a,"html"));a.on("toHtml",function(b){var b=b.data,c=b.dataValue,c=s(c,a),c=q(c,F),c=p(c),c=q(c,D),c=c.replace(K,"$1cke:$2"),c=c.replace(G,""),c=CKEDITOR.env.opera?c:c.replace(/(]*>)(\r\n|\n)/g,"$1$2$2"),d=b.context||a.editable().getName(), -f;if(CKEDITOR.env.ie&&CKEDITOR.env.version<9&&d=="pre"){d="div";c="
"+c+"
";f=1}d=a.document.createElement(d);d.setHtml("a"+c);c=d.getHtml().substr(1);c=c.replace(RegExp(" data-cke-"+CKEDITOR.rnd+"-","ig")," ");f&&(c=c.replace(/^
|<\/pre>$/gi,""));c=c.replace(I,"$1$2");c=m(c);c=t(c);b.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(c,b.context,b.fixForBody===false?false:e(a.config))},null,null,5);a.on("toHtml",function(a){a.data.dataValue.filterChildren(f.dataFilter,true)},null,null,
-10);a.on("toHtml",function(a){var a=a.data,b=a.dataValue,c=new CKEDITOR.htmlParser.basicWriter;b.writeChildrenHtml(c);b=c.getHtml(true);a.dataValue=l(b)},null,null,15);a.on("toDataFormat",function(b){b.data.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(b.data.dataValue,a.editable().getName(),e(a.config))},null,null,5);a.on("toDataFormat",function(a){a.data.dataValue.filterChildren(f.htmlFilter,true)},null,null,10);a.on("toDataFormat",function(b){var c=b.data.dataValue,d=f.writer;d.reset();c.writeChildrenHtml(d);
-c=d.getHtml(true);c=t(c);c=o(c,a);b.data.dataValue=c},null,null,15)};CKEDITOR.htmlDataProcessor.prototype={toHtml:function(a,b,c,d){var f=this.editor;!b&&b!==null&&(b=f.editable().getName());return f.fire("toHtml",{dataValue:a,context:b,fixForBody:c,dontFilter:!!d}).dataValue},toDataFormat:function(a){return this.editor.fire("toDataFormat",{dataValue:a}).dataValue}};var y=/(?: |\xa0)$/,v="{cke_protected}",z=CKEDITOR.dtd,u=["caption","colgroup","col","thead","tfoot","tbody"],w=CKEDITOR.tools.extend({},
-z.$blockLimit,z.$block),r={elements:{},attributeNames:[[/^on/,"data-cke-pa-on"]]},x={elementNames:[[/^cke:/,""],[/^\?xml:namespace$/,""]],attributeNames:[[/^data-cke-(saved|pa)-/,""],[/^data-cke-.*/,""],["hidefocus",""]],elements:{$:function(a){var b=a.attributes;if(b){if(b["data-cke-temp"])return false;for(var c=["name","href","src"],d,f=0;f-1&&d>-1&&c!=d)){c=j(a);d=j(b)}return c>d?1:-1})},embed:function(a){var b=a.parent;if(b&&b.name=="object"){var c=b.attributes.width,b=b.attributes.height;c&&(a.attributes.width=c);b&&(a.attributes.height=b)}},param:function(a){a.children=[];a.isEmpty=true;return a},a:function(a){if(!a.children.length&&!a.attributes.name&&!a.attributes["data-cke-saved-name"])return false},span:function(a){a.attributes["class"]==
-"Apple-style-span"&&delete a.name},html:function(a){delete a.attributes.contenteditable;delete a.attributes["class"]},body:function(a){delete a.attributes.spellcheck;delete a.attributes.contenteditable},style:function(a){var b=a.children[0];b&&b.value&&(b.value=CKEDITOR.tools.trim(b.value));if(!a.attributes.type)a.attributes.type="text/css"},title:function(a){var b=a.children[0];!b&&h(a,b=new CKEDITOR.htmlParser.text);b.value=a.attributes["data-cke-title"]||""}},attributes:{"class":function(a){return CKEDITOR.tools.ltrim(a.replace(/(?:^|\s+)cke_[^\s]*/g,
-""))||false}}};if(CKEDITOR.env.ie)x.attributes.style=function(a){return a.replace(/(^|;)([^\:]+)/g,function(a){return a.toLowerCase()})};for(var C in{input:1,textarea:1}){r.elements[C]=k;x.elements[C]=n}var A=/<(a|area|img|input|source)\b([^>]*)>/gi,B=/\s(on\w+|href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,D=/(?:])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,F=/(])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,E=/([^<]*)<\/cke:encoded>/gi,
-K=/(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,I=/(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi,G=/]*?)\/?>(?!\s*<\/cke:\1)/gi}(),"use strict",CKEDITOR.htmlParser.element=function(d,e){this.name=d;this.attributes=e||{};this.children=[];var c=d||"",a=c.match(/^cke:(.*)/);a&&(c=a[1]);c=!(!CKEDITOR.dtd.$nonBodyContent[c]&&!CKEDITOR.dtd.$block[c]&&!CKEDITOR.dtd.$listItem[c]&&!CKEDITOR.dtd.$tableContent[c]&&!(CKEDITOR.dtd.$nonEditable[c]||c=="br"));this.isEmpty=!!CKEDITOR.dtd.$empty[d];
-this.isUnknown=!CKEDITOR.dtd[d];this._={isBlockLike:c,hasInlineStarted:this.isEmpty||!c}},CKEDITOR.htmlParser.cssStyle=function(d){var e={};((d instanceof CKEDITOR.htmlParser.element?d.attributes.style:d)||"").replace(/"/g,'"').replace(/\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(c,a,b){a=="font-family"&&(b=b.replace(/["']/g,""));e[a.toLowerCase()]=b});return{rules:e,populate:function(c){var a=this.toString();if(a)c instanceof CKEDITOR.dom.element?c.setAttribute("style",a):c instanceof CKEDITOR.htmlParser.element?
-c.attributes.style=a:c.style=a},toString:function(){var c=[],a;for(a in e)e[a]&&c.push(a,":",e[a],";");return c.join("")}}},function(){function d(a){return function(b){return b.type==CKEDITOR.NODE_ELEMENT&&(typeof a=="string"?b.name==a:b.name in a)}}var e=function(a,b){a=a[0];b=b[0];return ab?1:0},c=CKEDITOR.htmlParser.fragment.prototype;CKEDITOR.htmlParser.element.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_ELEMENT,add:c.add,clone:function(){return new CKEDITOR.htmlParser.element(this.name,
-this.attributes)},filter:function(a){var b=this,c,d;if(!b.parent)a.onRoot(b);for(;;){c=b.name;if(!(d=a.onElementName(c))){this.remove();return false}b.name=d;if(!(b=a.onElement(b))){this.remove();return false}if(b!==this){this.replaceWith(b);return false}if(b.name==c)break;if(b.type!=CKEDITOR.NODE_ELEMENT){this.replaceWith(b);return false}if(!b.name){this.replaceWithChildren();return false}}c=b.attributes;var e,g;for(e in c){g=e;for(d=c[e];;)if(g=a.onAttributeName(e))if(g!=e){delete c[e];e=g}else break;
-else{delete c[e];break}g&&((d=a.onAttribute(b,g,d))===false?delete c[g]:c[g]=d)}b.isEmpty||this.filterChildren(a);return true},filterChildren:c.filterChildren,writeHtml:function(a,b){b&&this.filter(b);var c=this.name,d=[],h=this.attributes,g,j;a.openTag(c,h);for(g in h)d.push([g,h[g]]);a.sortAttributes&&d.sort(e);g=0;for(j=d.length;g0)this.children[a-1].next=null;this.parent.add(c,this.getIndex()+1);return c},removeClass:function(a){var b=this.attributes["class"];if(b)(b=CKEDITOR.tools.trim(b.replace(RegExp("(?:\\s+|^)"+a+"(?:\\s+|$)")," ")))?this.attributes["class"]=b:delete this.attributes["class"]}})}(),function(){var d={};CKEDITOR.template=function(e){if(d[e])this.output=
-d[e];else{var c=e.replace(/'/g,"\\'").replace(/{([^}]+)}/g,function(a,b){return"',data['"+b+"']==undefined?'{"+b+"}':data['"+b+"'],'"});this.output=d[e]=Function("data","buffer","return buffer?buffer.push('"+c+"'):['"+c+"'].join('');")}}}(),delete CKEDITOR.loadFullCore,CKEDITOR.instances={},CKEDITOR.document=new CKEDITOR.dom.document(document),CKEDITOR.add=function(d){CKEDITOR.instances[d.name]=d;d.on("focus",function(){if(CKEDITOR.currentInstance!=d){CKEDITOR.currentInstance=d;CKEDITOR.fire("currentInstance")}});
-d.on("blur",function(){if(CKEDITOR.currentInstance==d){CKEDITOR.currentInstance=null;CKEDITOR.fire("currentInstance")}});CKEDITOR.fire("instance",null,d)},CKEDITOR.remove=function(d){delete CKEDITOR.instances[d.name]},function(){var d={};CKEDITOR.addTemplate=function(e,c){var a=d[e];if(a)return a;a={name:e,source:c};CKEDITOR.fire("template",a);return d[e]=new CKEDITOR.template(a.source)};CKEDITOR.getTemplate=function(e){return d[e]}}(),function(){var d=[];CKEDITOR.addCss=function(e){d.push(e)};CKEDITOR.getCss=
-function(){return d.join("\n")}}(),CKEDITOR.on("instanceDestroyed",function(){CKEDITOR.tools.isEmpty(this.instances)&&CKEDITOR.fire("reset")}),CKEDITOR.TRISTATE_ON=1,CKEDITOR.TRISTATE_OFF=2,CKEDITOR.TRISTATE_DISABLED=0,function(){CKEDITOR.inline=function(d,e){if(!CKEDITOR.env.isCompatible)return null;d=CKEDITOR.dom.element.get(d);if(d.getEditor())throw'The editor instance "'+d.getEditor().name+'" is already attached to the provided element.';var c=new CKEDITOR.editor(e,d,CKEDITOR.ELEMENT_MODE_INLINE),
-a=d.is("textarea")?d:null;if(a){c.setData(a.getValue(),null,true);d=CKEDITOR.dom.element.createFromHtml('
'+a.getValue()+"
",CKEDITOR.document);d.insertAfter(a);a.hide();a.$.form&&c._attachToForm()}else c.setData(d.getHtml(),null,true);c.on("loaded",function(){c.fire("uiReady");c.editable(d);c.container=d;c.setData(c.getData(1));c.resetDirty();c.fire("contentDom");c.mode="wysiwyg";c.fire("mode");c.status="ready";c.fireOnce("instanceReady"); -CKEDITOR.fire("instanceReady",null,c)},null,null,1E4);c.on("destroy",function(){if(a){c.container.clearCustomData();c.container.remove();a.show()}c.element.clearCustomData();delete c.element});return c};CKEDITOR.inlineAll=function(){var d,e,c;for(c in CKEDITOR.dtd.$editable)for(var a=CKEDITOR.document.getElementsByTag(c),b=0,f=a.count();b{voiceLabel}<{outerEl} class="cke_inner cke_reset" role="presentation">{topHtml}<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation">{bottomHtml}')); -c=CKEDITOR.dom.element.createFromHtml(a.output({id:b.id,name:c,langDir:b.lang.dir,langCode:b.langCode,voiceLabel:[b.lang.editor,b.name].join(", "),topHtml:g?''+g+"":"",contentId:b.ui.spaceId("contents"),bottomHtml:j?''+j+"":"",outerEl:CKEDITOR.env.ie?"span":"div"}));if(e==CKEDITOR.ELEMENT_MODE_REPLACE){d.hide(); -c.insertAfter(d)}else d.append(c);b.container=c;g&&b.ui.space("top").unselectable();j&&b.ui.space("bottom").unselectable();d=b.config.width;e=b.config.height;d&&c.setStyle("width",CKEDITOR.tools.cssLength(d));e&&b.ui.space("contents").setStyle("height",CKEDITOR.tools.cssLength(e));c.disableContextMenu();CKEDITOR.env.webkit&&c.on("focus",function(){b.focus()});b.fireOnce("uiReady")}CKEDITOR.replace=function(a,c){return d(a,c,null,CKEDITOR.ELEMENT_MODE_REPLACE)};CKEDITOR.appendTo=function(a,c,e){return d(a, -c,e,CKEDITOR.ELEMENT_MODE_APPENDTO)};CKEDITOR.replaceAll=function(){for(var a=document.getElementsByTagName("textarea"),c=0;c",h="",a=e+a.replace(g,function(){return h+e})+h}a=a.replace(/\n/g,"
");b||(a=a.replace(RegExp("
(?=)"),function(a){return d.repeat(a,2)}));a=a.replace(/^ | $/g," ");a=a.replace(/(>|\s) /g, -function(a,b){return b+" "}).replace(/ (?=<)/g," ");k(this,"text",a)},insertElement:function(a){f(this);for(var b=this.editor,d=b.config.enterMode,g=b.getSelection(),e=g.getRanges(),h=a.getName(),j=CKEDITOR.dtd.$block[h],k,y,v,z=e.length-1;z>=0;z--){k=e[z];if(!k.checkReadOnly()){k.deleteContents(1);y=!z&&a||a.clone(1);var u,w;if(j)for(;(u=k.getCommonAncestor(0,1))&&(w=CKEDITOR.dtd[u.getName()])&&(!w||!w[h]);)if(u.getName()in CKEDITOR.dtd.span)k.splitElement(u);else if(k.checkStartOfBlock()&& -k.checkEndOfBlock()){k.setStartBefore(u);k.collapse(true);u.remove()}else k.splitBlock(d==CKEDITOR.ENTER_DIV?"div":"p",b.editable());k.insertNode(y);v||(v=y)}}if(v){k.moveToPosition(v,CKEDITOR.POSITION_AFTER_END);if(j)if((a=v.getNext(c))&&a.type==CKEDITOR.NODE_ELEMENT&&a.is(CKEDITOR.dtd.$block))a.getDtd()["#"]?k.moveToElementEditStart(a):k.moveToElementEditEnd(v);else if(!a&&d!=CKEDITOR.ENTER_BR){a=k.fixBlock(true,d==CKEDITOR.ENTER_DIV?"div":"p");k.moveToElementEditStart(a)}}g.selectRanges([k]);i(this, -CKEDITOR.env.opera)},setData:function(a,b){!b&&this.editor.dataProcessor&&(a=this.editor.dataProcessor.toHtml(a));this.setHtml(a);this.editor.fire("dataReady")},getData:function(a){var b=this.getHtml();!a&&this.editor.dataProcessor&&(b=this.editor.dataProcessor.toDataFormat(b));return b},setReadOnly:function(a){this.setAttribute("contenteditable",!a)},detach:function(){this.removeClass("cke_editable");var a=this.editor;this._.detach();delete a.document;delete a.window},isInline:function(){return this.getDocument().equals(CKEDITOR.document)}, -setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||a.config.ignoreEmptyParagraph!==false&&(b=b.replace(h,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data=this.getData(1)},this);this.attachListener(a,"afterSetData",function(){this.setData(a.getData(1))},this);this.attachListener(a,"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a,"beforeFocus", -function(){var b=a.getSelection();(b=b&&b.getNative())&&b.type=="Control"||this.focus()},this);this.attachListener(a,"insertHtml",function(a){this.insertHtml(a.data.dataValue,a.data.mode)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a,"insertText",function(a){this.insertText(a.data)},this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable");this.attachClass(a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"cke_editable_inline": -a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE||a.elementMode==CKEDITOR.ELEMENT_MODE_APPENDTO?"cke_editable_themed":"");this.attachClass("cke_contents_"+a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=+a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(a){CKEDITOR.env.opera&&CKEDITOR.document.getActive().equals(this.isInline()?this:this.getWindow().getFrame())?a.cancel():this.hasFocus=false},null,null,-1);this.on("focus",function(){this.hasFocus=true},null,null, --1);a.focusManager.add(this);if(this.equals(CKEDITOR.document.getActive())){this.hasFocus=true;a.once("contentDom",function(){a.focusManager.focus()})}this.isInline()&&this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var c=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var d=a.config.contentsLangDirection;this.getDirection(1)!=d&&this.changeAttr("dir",d);var f=CKEDITOR.getCss();if(f){d=c.getHead(); -if(!d.getCustomData("stylesheet")){f=c.appendStyleText(f);f=new CKEDITOR.dom.element(f.ownerNode||f.owningElement);d.setCustomData("stylesheet",f);f.data("cke-temp",1)}}d=c.getCustomData("stylesheet_ref")||0;c.setCustomData("stylesheet_ref",d+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){var a=a.data,b=a.getTarget();b.is("a")&&(a.$.button!=2&&b.isReadOnly())&&a.preventDefault()});this.attachListener(a,"key",function(c){if(a.readOnly)return true; -var d=c.data.keyCode,f;if(d in{8:1,46:1}){var e=a.getSelection(),c=e.getRanges()[0],h=c.startPath(),i,j,k,d=d==8;if(e=b(e)){a.fire("saveSnapshot");c.moveToPosition(e,CKEDITOR.POSITION_BEFORE_START);e.remove();c.select();a.fire("saveSnapshot");f=1}else if(c.collapsed)if((i=h.block)&&c[d?"checkStartOfBlock":"checkEndOfBlock"]()&&(k=i[d?"getPrevious":"getNext"](g))&&k.is("table")){a.fire("saveSnapshot");c[d?"checkEndOfBlock":"checkStartOfBlock"]()&&i.remove();c["moveToElementEdit"+(d?"End":"Start")](k); -c.select();a.fire("saveSnapshot");f=1}else if(h.blockLimit&&h.blockLimit.is("td")&&(j=h.blockLimit.getAscendant("table"))&&c.checkBoundaryOfElement(j,d?CKEDITOR.START:CKEDITOR.END)&&(k=j[d?"getPrevious":"getNext"](g))){a.fire("saveSnapshot");c["moveToElementEdit"+(d?"End":"Start")](k);c.checkStartOfBlock()&&c.checkEndOfBlock()?k.remove():c.select();a.fire("saveSnapshot");f=1}else if((j=h.contains(["td","th","caption"]))&&c.checkBoundaryOfElement(j,d?CKEDITOR.START:CKEDITOR.END))f=1}return!f});this.attachListener(a, -"key",function(b){if(a.readOnly)return false;var c=b.data.keyCode,d;if(c in{8:1,46:1}){var b=a.getSelection().getRanges()[0],f=b.startPath(),g,c=c==8;for(b.checkEndOfBlock()&&b.moveToPosition(f.block,CKEDITOR.POSITION_BEFORE_END);(g=b[c?"getPreviousNode":"getNextNode"]())&&g.type==CKEDITOR.NODE_ELEMENT&&g.isReadOnly();){d=1;b.moveToPosition(g,c?CKEDITOR.POSITION_BEFORE_START:CKEDITOR.POSITION_AFTER_END)}if(d){b[c?"moveToElementEditEnd":"moveToElementEditStart"](g);b.select();b.scrollIntoView()}}return!d}); -CKEDITOR.env.ie&&this.attachListener(this,"click",e);!CKEDITOR.env.ie&&!CKEDITOR.env.opera&&this.attachListener(this,"mousedown",function(b){var c=b.data.getTarget();if(c.is("img","hr","input","textarea","select")){a.getSelection().selectElement(c);c.is("input","textarea","select")&&b.data.preventDefault()}});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(b.data.$.button==2){b=b.data.getTarget();if(!b.getOuterHtml().replace(h,"")){var c=a.createRange();c.moveToElementEditStart(b); -c.select(true)}}});if(CKEDITOR.env.webkit){this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()});this.attachListener(this,"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()})}}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());a=this.getDocument();var b=a.getHead(); -if(b.getCustomData("stylesheet")){var c=a.getCustomData("stylesheet_ref");if(--c)a.setCustomData("stylesheet_ref",c);else{a.removeCustomData("stylesheet_ref");b.removeCustomData("stylesheet").remove()}}delete this.editor}}});CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;if(arguments.length)b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null);return b};var h=/(^|]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi, -g=CKEDITOR.dom.walker.whitespaces(true),j=CKEDITOR.dom.walker.bookmark(false,true);CKEDITOR.on("instanceLoaded",function(a){var b=a.editor;b.on("insertElement",function(a){a=a.data;if(a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))){a.getAttribute("contentEditable")!="false"&&a.data("cke-editable",a.hasAttribute("contenteditable")?"true":"1");a.setAttribute("contentEditable",false)}});b.on("selectionChange",function(a){if(!b.readOnly){var c=b.getSelection();if(c&&!c.isLocked){c=b.checkDirty(); -b.fire("lockSnapshot");d(a);b.fire("unlockSnapshot");!c&&b.resetDirty()}}})});CKEDITOR.on("instanceCreated",function(a){var b=a.editor;b.on("mode",function(){var a=b.editable();if(a&&a.isInline()){var c=b.title;a.changeAttr("role","textbox");a.changeAttr("aria-label",c);c&&a.changeAttr("title",c);if(c=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents")){var d=CKEDITOR.tools.getNextId(),f=CKEDITOR.dom.element.createFromHtml(''+this.lang.common.editorHelp+ -"");c.append(f);a.changeAttr("aria-describedby",d)}}})});CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");var k=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function b(c,d){var f,g,e,i,j=[],k=d.range.startContainer;f=d.range.startPath();for(var k=h[k.getName()],m=0,v=c.getChildren(),l=v.count(),y=-1,t=-1,q=0,s=f.contains(h.$list);m-1)j[y].firstNotAllowed=1;if(t>-1)j[t].lastNotAllowed=1;return j}function d(b,c){var f=[],g=b.getChildren(),e=g.count(),i,j=0,k=h[c],m=!b.is(h.$inline)|| -b.is("br");for(m&&f.push(" ");j ",r.document);r.insertNode(B); -r.setStartAfter(B)}D=new CKEDITOR.dom.elementPath(r.startContainer);u.endPath=F=new CKEDITOR.dom.elementPath(r.endContainer);if(!r.collapsed){var A=F.block||F.blockLimit,K=r.getCommonAncestor();A&&(!A.equals(K)&&!A.contains(K)&&r.checkEndOfBlock())&&u.zombies.push(A);r.deleteContents()}for(;(E=a(r.startContainer)&&r.startContainer.getChild(r.startOffset-1))&&a(E)&&E.isBlockBoundary()&&D.contains(E);)r.moveToPosition(E,CKEDITOR.POSITION_BEFORE_END);g(r,u.blockLimit,D,F);if(B){r.setEndBefore(B);r.collapse(); -B.remove()}B=r.startPath();if(A=B.contains(f,false,1)){r.splitElement(A);u.inlineStylesRoot=A;u.inlineStylesPeak=B.lastElement}B=r.createBookmark();(A=B.startNode.getPrevious(c))&&a(A)&&f(A)&&C.push(A);(A=B.startNode.getNext(c))&&a(A)&&f(A)&&C.push(A);for(A=B.startNode;(A=A.getParent())&&f(A);)C.push(A);r.moveToBookmark(B);if(B=s){B=u.range;if(u.type=="text"&&u.inlineStylesRoot){E=u.inlineStylesPeak;r=E.getDocument().createText("{cke-peak}");for(C=u.inlineStylesRoot.getParent();!E.equals(C);){r=r.appendTo(E.clone()); -E=E.getParent()}s=r.getOuterHtml().split("{cke-peak}").join(s)}E=u.blockLimit.getName();if(/^\s+|\s+$/.test(s)&&"span"in CKEDITOR.dtd[E])var I=' ',s=I+s+I;s=u.editor.dataProcessor.toHtml(s,null,false,u.dontFilter);E=B.document.createElement("body");E.setHtml(s);if(I){E.getFirst().remove();E.getLast().remove()}if((I=B.startPath().block)&&!(I.getChildCount()==1&&I.getBogus()))a:{var G;if(E.getChildCount()==1&&a(G=E.getFirst())&&G.is(k)){I=G.getElementsByTag("*"); -B=0;for(C=I.count();B0;else{H=G.startPath();if(!F.isBlock&&(P=u.editor.config.enterMode!=CKEDITOR.ENTER_BR&&u.editor.config.autoParagraph!==false?u.editor.config.enterMode==CKEDITOR.ENTER_DIV?"div":"p":false)&&!H.block&&H.blockLimit&& -H.blockLimit.equals(G.root)){P=I.createElement(P);!CKEDITOR.env.ie&&P.appendBogus();G.insertNode(P);!CKEDITOR.env.ie&&(L=P.getBogus())&&L.remove();G.moveToPosition(P,CKEDITOR.POSITION_BEFORE_END)}if((H=G.startPath().block)&&!H.equals(J)){if(L=H.getBogus()){L.remove();E.push(H)}J=H}F.firstNotAllowed&&(r=1);if(r&&F.isElement){H=G.startContainer;for(M=null;H&&!h[H.getName()][F.name];){if(H.equals(s)){H=null;break}M=H;H=H.getParent()}if(H){if(M){R=G.splitElement(M);u.zombies.push(R);u.zombies.push(M)}}else{M= -s.getName();Q=!B;H=B==D.length-1;M=d(F.node,M);for(var N=[],S=M.length,V=0,T=void 0,U=0,W=-1;V1&&g&&g.intersectsNode(c.$)){d=[f.anchorOffset,f.focusOffset];g=f.focusNode==c.$&&f.focusOffset>0;f.anchorNode==c.$&&f.anchorOffset>0&&d[0]--;g&&d[1]--;var e;g=f; -if(!g.isCollapsed){e=g.getRangeAt(0);e.setStart(g.anchorNode,g.anchorOffset);e.setEnd(g.focusNode,g.focusOffset);e=e.collapsed}e&&d.unshift(d.pop())}}c.setText(i(c.getText()));if(d){c=f.getRangeAt(0);c.setStart(c.startContainer,d[0]);c.setEnd(c.startContainer,d[1]);f.removeAllRanges();f.addRange(c)}}}function i(a){return a.replace(/\u200B( )?/g,function(a){return a[1]?" ":""})}function h(a,b,c){var d=a.on("focus",function(a){a.cancel()},null,null,-100);if(CKEDITOR.env.ie)var f=a.getDocument().on("selectionchange", -function(a){a.cancel()},null,null,-100);else{var g=new CKEDITOR.dom.range(a);g.moveToElementEditStart(a);var e=a.getDocument().$.createRange();e.setStart(g.startContainer.$,g.startOffset);e.collapse(1);b.removeAllRanges();b.addRange(e)}c&&a.focus();d.removeListener();f&&f.removeListener()}function g(a){var b=CKEDITOR.dom.element.createFromHtml('
 
');a.fire("lockSnapshot");a.editable().append(b);var c=a.getSelection(), -d=a.createRange(),f=c.root.on("selectionchange",function(a){a.cancel()},null,null,0);d.setStartAt(b,CKEDITOR.POSITION_AFTER_START);d.setEndAt(b,CKEDITOR.POSITION_BEFORE_END);c.selectRanges([d]);f.removeListener();a.fire("unlockSnapshot");a._.hiddenSelectionContainer=b}function j(a,b,c,d){d=CKEDITOR.tools.extend(m(),d||{},true);b._.fakeSelectionKeyListener=b.editable().attachListener(b.editable(),"keydown",function(f){var g=d[f.data.getKeystroke()],e;g&&(e=g({editor:b,selected:c,selection:a,keyEvent:f})); -e||f.data.preventDefault()},null,null,-100)}function k(a){var b={37:1,39:1,8:1,46:1};return function(c){var d=c.data.getKeystroke();if(b[d]){var f=a.getSelection().getRanges(),g=f[0];if(f.length==1&&g.collapsed)if((d=g[d<38?"getPreviousNode":"getNextNode"](l))&&d.type==CKEDITOR.NODE_ELEMENT&&d.getAttribute("contenteditable")=="false"){a.getSelection().fake(d);c.data.preventDefault();c.cancel()}}}}var n,p,q=CKEDITOR.dom.walker.invisible(1),m=function(){function a(b){return function(a){var c=a.editor.createRange(); -c.moveToClosestEditablePosition(a.selected,b)&&a.editor.getSelection().selectRanges([c])}}function b(a){return function(b){var c=b.editor,d=c.createRange(),f;if(!(f=d.moveToClosestEditablePosition(b.selected,a)))f=d.moveToClosestEditablePosition(b.selected,!a);f&&c.getSelection().selectRanges([d]);b.selected.remove();if(!f){d.moveToElementEditablePosition(c.editable());c.getSelection().selectRanges([d])}c.fire("saveSnapshot")}}var c=a(),d=a(1);return function(){return{37:c,38:c,39:d,40:d,8:b(),46:b(1)}}}(), -l=CKEDITOR.dom.walker.editable();CKEDITOR.addCss(".cke_hidden_sel{opacity:0;"+(CKEDITOR.env.ie?"margin-left:-1000px":"position:fixed;top:0;left:-1000px")+"}");CKEDITOR.on("instanceCreated",function(a){function b(){var a=c.getSelection();a&&a.removeAllRanges()}var c=a.editor;c.on("contentDom",function(){var a=c.document,b=CKEDITOR.document,g=c.editable(),h=a.getBody(),i=a.getDocumentElement(),j=g.isInline(),m;CKEDITOR.env.gecko&&g.attachListener(g,"focus",function(a){a.removeListener();if(m!==0)if((a= -c.getSelection().getNative())&&a.isCollapsed&&a.anchorNode==g.$){a=c.createRange();a.moveToElementEditStart(g);a.select()}},null,null,-2);g.attachListener(g,"focus",function(){c.unlockSelection(m);m=0},null,null,-1);g.attachListener(g,"mousedown",function(){m=0});if(CKEDITOR.env.ie||CKEDITOR.env.opera||j){var l,v=function(){l=new CKEDITOR.dom.selection(c.getSelection());l.lock()};t?g.attachListener(g,"beforedeactivate",v,null,null,-1):g.attachListener(c,"selectionCheck",v,null,null,-1);g.attachListener(g, -"blur",function(){c.lockSelection(l);m=1},null,null,-1);g.attachListener(g,"DOMFocusIn",function(){c.unlockSelection()});g.attachListener(g,"mousedown",function(){m=0})}if(CKEDITOR.env.ie&&!j){var E;g.attachListener(g,"mousedown",function(a){a.data.$.button==2&&c.document.$.selection.type=="None"&&(E=c.window.getScrollPosition())});g.attachListener(g,"mouseup",function(a){if(a.data.$.button==2&&E){c.document.$.documentElement.scrollLeft=E.x;c.document.$.documentElement.scrollTop=E.y}E=null});if(a.$.compatMode!= -"BackCompat"){if(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)i.on("mousedown",function(a){function c(a){a=a.data.$;if(f){var b=h.$.createTextRange();try{b.moveToPoint(a.x,a.y)}catch(d){}f.setEndPoint(e.compareEndPoints("StartToStart",b)<0?"EndToEnd":"StartToStart",b);f.select()}}function d(){i.removeListener("mousemove",c);b.removeListener("mouseup",d);i.removeListener("mouseup",d);f.select()}a=a.data;if(a.getTarget().is("html")&&a.$.y7){i.on("mousedown",function(a){if(a.data.getTarget().is("html")){b.on("mouseup",o);i.on("mouseup",o)}});var o=function(){b.removeListener("mouseup",o);i.removeListener("mouseup",o);var c=CKEDITOR.document.$.selection,d=c.createRange();c.type!="None"&&d.parentElement().ownerDocument==a.$&&d.select()}}}}g.attachListener(g,"selectionchange",d,c);g.attachListener(g, -"keyup",e,c);g.attachListener(g,"focus",function(){c.forceNextSelectionCheck();c.selectionChange(1)});if(j?CKEDITOR.env.webkit||CKEDITOR.env.gecko:CKEDITOR.env.opera){var y;g.attachListener(g,"mousedown",function(){y=1});g.attachListener(a.getDocumentElement(),"mouseup",function(){y&&e.call(c);y=0})}else g.attachListener(CKEDITOR.env.ie?g:a.getDocumentElement(),"mouseup",e,c);CKEDITOR.env.webkit&&g.attachListener(a,"keydown",function(a){switch(a.data.getKey()){case 13:case 33:case 34:case 35:case 36:case 37:case 39:case 8:case 45:case 46:f(g)}}, -null,null,-1);g.attachListener(g,"keydown",k(c),null,null,-1)});c.on("contentDomUnload",c.forceNextSelectionCheck,c);c.on("dataReady",function(){delete c._.fakeSelection;delete c._.hiddenSelectionContainer;var a=c._.fakeSelectionKeyListener;if(a){c.editable().isInline()&&a.removeListener();delete c._.fakeSelectionKeyListener}c.selectionChange(1)});c.on("loadSnapshot",function(){var a=c.editable().getLast(function(a){return a.type==CKEDITOR.NODE_ELEMENT});a&&a.hasAttribute("data-cke-hidden-sel")&& -a.remove()},null,null,100);CKEDITOR.env.ie9Compat&&c.on("beforeDestroy",b,null,null,9);CKEDITOR.env.webkit&&c.on("setData",b);c.on("contentDomUnload",function(){c.unlockSelection()})});CKEDITOR.on("instanceReady",function(a){var c=a.editor;if(CKEDITOR.env.webkit){c.on("selectionChange",function(){var a=c.editable(),d=b(a);d&&(d.getCustomData("ready")?f(a):d.setCustomData("ready",1))},null,null,-1);c.on("beforeSetMode",function(){f(c.editable())},null,null,-1);var d,g,a=function(){var a=c.editable(); -if(a)if(a=b(a)){var f=c.document.$.defaultView.getSelection();f.type=="Caret"&&f.anchorNode==a.$&&(g=1);d=a.getText();a.setText(i(d))}},e=function(){var a=c.editable();if(a)if(a=b(a)){a.setText(d);if(g){c.document.$.defaultView.getSelection().setPosition(a.$,a.getLength());g=0}}};c.on("beforeUndoImage",a);c.on("afterUndoImage",e);c.on("beforeGetData",a,null,null,0);c.on("getData",e)}});CKEDITOR.editor.prototype.selectionChange=function(a){(a?d:e).call(this)};CKEDITOR.editor.prototype.getSelection= -function(a){if((this._.savedSelection||this._.fakeSelection)&&!a)return this._.savedSelection||this._.fakeSelection;return(a=this.editable())&&this.mode=="wysiwyg"?new CKEDITOR.dom.selection(a):null};CKEDITOR.editor.prototype.lockSelection=function(a){a=a||this.getSelection(1);if(a.getType()!=CKEDITOR.SELECTION_NONE){!a.isLocked&&a.lock();this._.savedSelection=a;return true}return false};CKEDITOR.editor.prototype.unlockSelection=function(a){var b=this._.savedSelection;if(b){b.unlock(a);delete this._.savedSelection; -return true}return false};CKEDITOR.editor.prototype.forceNextSelectionCheck=function(){delete this._.selectionPreviousPath};CKEDITOR.dom.document.prototype.getSelection=function(){return new CKEDITOR.dom.selection(this)};CKEDITOR.dom.range.prototype.select=function(){var a=this.root instanceof CKEDITOR.editable?this.root.editor.getSelection():new CKEDITOR.dom.selection(this.root);a.selectRanges([this]);return a};CKEDITOR.SELECTION_NONE=1;CKEDITOR.SELECTION_TEXT=2;CKEDITOR.SELECTION_ELEMENT=3;var t= -typeof window.getSelection!="function",o=1;CKEDITOR.dom.selection=function(a){if(a instanceof CKEDITOR.dom.selection)var b=a,a=a.root;var c=a instanceof CKEDITOR.dom.element;this.rev=b?b.rev:o++;this.document=a instanceof CKEDITOR.dom.document?a:a.getDocument();this.root=a=c?a:this.document.getBody();this.isLocked=0;this._={cache:{}};if(b){CKEDITOR.tools.extend(this._.cache,b._.cache);this.isFake=b.isFake;this.isLocked=b.isLocked}else{b=t?this.document.$.selection:this.document.getWindow().$.getSelection(); -if(CKEDITOR.env.webkit)(b.type=="None"&&this.document.getActive().equals(a)||b.type=="Caret"&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&h(a,b);else if(CKEDITOR.env.gecko)b&&(this.document.getActive().equals(a)&&b.anchorNode&&b.anchorNode.nodeType==CKEDITOR.NODE_DOCUMENT)&&h(a,b,true);else if(CKEDITOR.env.ie){var d;try{d=this.document.getActive()}catch(f){}if(t)b.type=="None"&&(d&&d.equals(this.document.getDocumentElement()))&&h(a,null,true);else{(b=b&&b.anchorNode)&&(b=new CKEDITOR.dom.node(b)); -d&&(d.equals(this.document.getDocumentElement())&&b&&(a.equals(b)||a.contains(b)))&&h(a,null,true)}}d=this.getNative();var g,e;if(d)if(d.getRangeAt)g=(e=d.rangeCount&&d.getRangeAt(0))&&new CKEDITOR.dom.node(e.commonAncestorContainer);else{try{e=d.createRange()}catch(i){}g=e&&CKEDITOR.dom.element.get(e.item&&e.item(0)||e.parentElement())}if(!g||!(g.type==CKEDITOR.NODE_ELEMENT||g.type==CKEDITOR.NODE_TEXT)||!this.root.equals(g)&&!this.root.contains(g)){this._.cache.type=CKEDITOR.SELECTION_NONE;this._.cache.startElement= -null;this._.cache.selectedElement=null;this._.cache.selectedText="";this._.cache.ranges=new CKEDITOR.dom.rangeList}return this}};var s={img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1};CKEDITOR.dom.selection.prototype={getNative:function(){return this._.cache.nativeSel!==void 0?this._.cache.nativeSel:this._.cache.nativeSel=t?this.document.$.selection:this.document.getWindow().$.getSelection()},getType:t?function(){var a= -this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_NONE;try{var c=this.getNative(),d=c.type;if(d=="Text")b=CKEDITOR.SELECTION_TEXT;if(d=="Control")b=CKEDITOR.SELECTION_ELEMENT;if(c.createRange().parentElement())b=CKEDITOR.SELECTION_TEXT}catch(f){}return a.type=b}:function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_TEXT,c=this.getNative();if(!c||!c.rangeCount)b=CKEDITOR.SELECTION_NONE;else if(c.rangeCount==1){var c=c.getRangeAt(0),d=c.startContainer;if(d==c.endContainer&& -d.nodeType==1&&c.endOffset-c.startOffset==1&&s[d.childNodes[c.startOffset].nodeName.toLowerCase()])b=CKEDITOR.SELECTION_ELEMENT}return a.type=b},getRanges:function(){var a=t?function(){function a(b){return(new CKEDITOR.dom.node(b)).getIndex()}var b=function(b,c){b=b.duplicate();b.collapse(c);var d=b.parentElement(),f=d.ownerDocument;if(!d.hasChildNodes())return{container:d,offset:0};for(var g=d.children,e,h,i=b.duplicate(),j=0,k=g.length-1,m=-1,l,o;j<=k;){m=Math.floor((j+k)/2);e=g[m];i.moveToElementText(e); -l=i.compareEndPoints("StartToStart",b);if(l>0)k=m-1;else if(l<0)j=m+1;else{if(CKEDITOR.env.ie9Compat&&e.tagName=="BR"){g=f.defaultView.getSelection();return{container:g[c?"anchorNode":"focusNode"],offset:g[c?"anchorOffset":"focusOffset"]}}return{container:d,offset:a(e)}}}if(m==-1||m==g.length-1&&l<0){i.moveToElementText(d);i.setEndPoint("StartToStart",b);f=i.text.replace(/(\r\n|\r)/g,"\n").length;g=d.childNodes;if(!f){e=g[g.length-1];return e.nodeType!=CKEDITOR.NODE_TEXT?{container:d,offset:g.length}: -{container:e,offset:e.nodeValue.length}}for(d=g.length;f>0&&d>0;){h=g[--d];if(h.nodeType==CKEDITOR.NODE_TEXT){o=h;f=f-h.nodeValue.length}}return{container:o,offset:-f}}i.collapse(l>0?true:false);i.setEndPoint(l>0?"StartToStart":"EndToStart",b);f=i.text.replace(/(\r\n|\r)/g,"\n").length;if(!f)return{container:d,offset:a(e)+(l>0?0:1)};for(;f>0;)try{h=e[l>0?"previousSibling":"nextSibling"];if(h.nodeType==CKEDITOR.NODE_TEXT){f=f-h.nodeValue.length;o=h}e=h}catch(n){return{container:d,offset:a(e)}}return{container:o, -offset:l>0?-f:o.nodeValue.length+f}};return function(){var a=this.getNative(),c=a&&a.createRange(),d=this.getType();if(!a)return[];if(d==CKEDITOR.SELECTION_TEXT){a=new CKEDITOR.dom.range(this.root);d=b(c,true);a.setStart(new CKEDITOR.dom.node(d.container),d.offset);d=b(c);a.setEnd(new CKEDITOR.dom.node(d.container),d.offset);a.endContainer.getPosition(a.startContainer)&CKEDITOR.POSITION_PRECEDING&&a.endOffset<=a.startContainer.getIndex()&&a.collapse();return[a]}if(d==CKEDITOR.SELECTION_ELEMENT){for(var d= -[],f=0;f=b.getLength()?j.setStartAfter(b):j.setStartBefore(b));e&&e.type==CKEDITOR.NODE_TEXT&&(i?j.setEndAfter(e):j.setEndBefore(e));b=new CKEDITOR.dom.walker(j);b.evaluator=function(a){if(a.type==CKEDITOR.NODE_ELEMENT&&a.isReadOnly()){var b=g.clone();g.setEndBefore(a);g.collapsed&&d.splice(f--,1);if(!(a.getPosition(j.endContainer)&CKEDITOR.POSITION_CONTAINS)){b.setStartAfter(a);b.collapsed|| -d.splice(f+1,0,b)}return true}return false};b.next()}}return c.ranges}}(),getStartElement:function(){var a=this._.cache;if(a.startElement!==void 0)return a.startElement;var b;switch(this.getType()){case CKEDITOR.SELECTION_ELEMENT:return this.getSelectedElement();case CKEDITOR.SELECTION_TEXT:var c=this.getRanges()[0];if(c){if(c.collapsed){b=c.startContainer;b.type!=CKEDITOR.NODE_ELEMENT&&(b=b.getParent())}else{for(c.optimize();;){b=c.startContainer;if(c.startOffset==(b.getChildCount?b.getChildCount(): -b.getLength())&&!b.isBlockBoundary())c.setStartAfter(b);else break}b=c.startContainer;if(b.type!=CKEDITOR.NODE_ELEMENT)return b.getParent();b=b.getChild(c.startOffset);if(!b||b.type!=CKEDITOR.NODE_ELEMENT)b=c.startContainer;else for(c=b.getFirst();c&&c.type==CKEDITOR.NODE_ELEMENT;){b=c;c=c.getFirst()}}b=b.$}}return a.startElement=b?new CKEDITOR.dom.element(b):null},getSelectedElement:function(){var a=this._.cache;if(a.selectedElement!==void 0)return a.selectedElement;var b=this,c=CKEDITOR.tools.tryThese(function(){return b.getNative().createRange().item(0)}, -function(){for(var a=b.getRanges()[0],c,d,f=2;f&&(!(c=a.getEnclosedNode())||!(c.type==CKEDITOR.NODE_ELEMENT&&s[c.getName()]&&(d=c)));f--)a.shrink(CKEDITOR.SHRINK_ELEMENT);return d.$});return a.selectedElement=c?new CKEDITOR.dom.element(c):null},getSelectedText:function(){var a=this._.cache;if(a.selectedText!==void 0)return a.selectedText;var b=this.getNative(),b=t?b.type=="Control"?"":b.createRange().text:b.toString();return a.selectedText=b},lock:function(){this.getRanges();this.getStartElement(); -this.getSelectedElement();this.getSelectedText();this._.cache.nativeSel=null;this.isLocked=1},unlock:function(a){if(this.isLocked){if(a)var b=this.getSelectedElement(),c=!b&&this.getRanges(),d=this.isFake;this.isLocked=0;this.reset();if(a)(a=b||c[0]&&c[0].getCommonAncestor())&&a.getAscendant("body",1)&&(d?this.fake(b):b?this.selectElement(b):this.selectRanges(c))}},reset:function(){this._.cache={};this.isFake=0;var a=this.root.editor,b;if(a&&a._.fakeSelection)if(this.rev==a._.fakeSelection.rev){delete a._.fakeSelection; -if(b=a._.fakeSelectionKeyListener){b.removeListener();delete a._.fakeSelectionKeyListener}if(b=a._.hiddenSelectionContainer){a.fire("lockSnapshot");b.remove();a.fire("unlockSnapshot")}delete a._.hiddenSelectionContainer}else window.console&&console.log("Wrong selection instance resets fake selection.");this.rev=o++},selectElement:function(a){var b=new CKEDITOR.dom.range(this.root);b.setStartBefore(a);b.setEndAfter(a);this.selectRanges([b])},selectRanges:function(b){this.reset();if(b.length)if(this.isLocked){var c= -CKEDITOR.document.getActive();this.unlock();this.selectRanges(b);this.lock();!c.equals(this.root)&&c.focus()}else if(b.length==1&&!b[0].collapsed&&(c=b[0].getEnclosedNode())&&c.type==CKEDITOR.NODE_ELEMENT&&c.getAttribute("contenteditable")=="false")this.fake(c);else{if(t){var d=CKEDITOR.dom.walker.whitespaces(true),g=/\ufeff|\u00a0/,e={table:1,tbody:1,tr:1};if(b.length>1){c=b[b.length-1];b[0].setEnd(c.endContainer,c.endOffset)}var c=b[0],b=c.collapsed,h,i,j,k=c.getEnclosedNode();if(k&&k.type==CKEDITOR.NODE_ELEMENT&& -k.getName()in s&&(!k.is("a")||!k.getText()))try{j=k.$.createControlRange();j.addElement(k.$);j.select();return}catch(m){}(c.startContainer.type==CKEDITOR.NODE_ELEMENT&&c.startContainer.getName()in e||c.endContainer.type==CKEDITOR.NODE_ELEMENT&&c.endContainer.getName()in e)&&c.shrink(CKEDITOR.NODE_ELEMENT,true);j=c.createBookmark();var e=j.startNode,l;if(!b)l=j.endNode;j=c.document.$.body.createTextRange();j.moveToElementText(e.$);j.moveStart("character",1);if(l){g=c.document.$.body.createTextRange(); -g.moveToElementText(l.$);j.setEndPoint("EndToEnd",g);j.moveEnd("character",-1)}else{h=e.getNext(d);i=e.hasAscendant("pre");h=!(h&&h.getText&&h.getText().match(g))&&(i||!e.hasPrevious()||e.getPrevious().is&&e.getPrevious().is("br"));i=c.document.createElement("span");i.setHtml("");i.insertBefore(e);h&&c.document.createText("").insertBefore(e)}c.setStartBefore(e);e.remove();if(b){if(h){j.moveStart("character",-1);j.select();c.document.$.selection.clear()}else j.select();c.moveToPosition(i, -CKEDITOR.POSITION_BEFORE_START);i.remove()}else{c.setEndBefore(l);l.remove();j.select()}}else{l=this.getNative();if(!l)return;if(CKEDITOR.env.opera){c=this.document.$.createRange();c.selectNodeContents(this.root.$);l.addRange(c)}this.removeAllRanges();for(j=0;j=0){c.collapse(1);g.setEnd(c.endContainer.$, -c.endOffset)}else throw o;}l.addRange(g)}}this.reset();this.root.fire("selectionchange")}},fake:function(a,b){var c=this.root.editor;g(c);var d=this._.cache,f=new CKEDITOR.dom.range(a.getDocument());f.setStartBefore(a);f.setEndAfter(a);d.ranges=new CKEDITOR.dom.rangeList(f);d.selectedElement=d.startElement=a;d.type=CKEDITOR.SELECTION_ELEMENT;d.selectedText=d.nativeSel=null;this.isFake=1;this.rev=o++;c._.fakeSelection=this;j(this,c,a,b&&b.keystrokeHandlers);this.root.fire("selectionchange")},isHidden:function(){var a= -this.getCommonAncestor();a&&a.type==CKEDITOR.NODE_TEXT&&(a=a.getParent());return!(!a||!a.data("cke-hidden-sel"))},createBookmarks:function(a){a=this.getRanges().createBookmarks(a);this.isFake&&(a.isFake=1);return a},createBookmarks2:function(a){a=this.getRanges().createBookmarks2(a);this.isFake&&(a.isFake=1);return a},selectBookmarks:function(a){for(var b=[],c=0;c]*>)[ \t\r\n]*/gi,"$1");e=e.replace(/([ \t\n\r]+| )/g," ");e=e.replace(/]*>/gi,"\n");if(CKEDITOR.env.ie){var h=a.getDocument().createElement("div");h.append(f);f.$.outerHTML="
"+e+"
";f.copyAttributes(h.getFirst()); -f=h.getFirst().remove()}else f.setHtml(e);b=f}else e?b=k(c?[a.getHtml()]:g(a),b):a.moveChildren(b);b.replace(a);if(d){var c=b,i;if((i=c.getPrevious(C))&&i.is&&i.is("pre")){d=j(i.getHtml(),/\n$/,"")+"\n\n"+j(c.getHtml(),/^\n/,"");CKEDITOR.env.ie?c.$.outerHTML="
"+d+"
":c.setHtml(d);i.remove()}}else c&&m(b)}function g(a){a.getName();var b=[];j(a.getOuterHtml(),/(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,function(a,b,c){return b+"
"+c+"
"}).replace(/([\s\S]*?)<\/pre>/gi,
-function(a,c){b.push(c)});return b}function j(a,b,c){var d="",f="",a=a.replace(/(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi,function(a,b,c){b&&(d=b);c&&(f=c);return""});return d+a.replace(b,c)+f}function k(a,b){var c;a.length>1&&(c=new CKEDITOR.dom.documentFragment(b.getDocument()));for(var d=0;d"),f=f.replace(/[ \t]{2,}/g,function(a){return CKEDITOR.tools.repeat(" ",a.length-1)+" "});if(c){var g=b.clone();g.setHtml(f);c.append(g)}else b.setHtml(f)}return c||b}function n(a){var b=this._.definition,c=b.attributes,b=b.styles,d=s(this)[a.getName()],f=CKEDITOR.tools.isEmpty(c)&&CKEDITOR.tools.isEmpty(b),g;for(g in c)if(!((g=="class"||this._.definition.fullMatch)&&a.getAttribute(g)!=
-y(g,c[g]))){f=a.hasAttribute(g);a.removeAttribute(g)}for(var e in b)if(!(this._.definition.fullMatch&&a.getStyle(e)!=y(e,b[e],true))){f=f||!!a.getStyle(e);a.removeStyle(e)}q(a,d,z[a.getName()]);f&&(this._.definition.alwaysRemoveElement?m(a,1):!CKEDITOR.dtd.$block[a.getName()]||this._.enterMode==CKEDITOR.ENTER_BR&&!a.hasAttributes()?m(a):a.renameNode(this._.enterMode==CKEDITOR.ENTER_P?"p":"div"))}function p(a){for(var b=s(this),c=a.getElementsByTag(this.element),d=c.count();--d>=0;)n.call(this,c.getItem(d));
-for(var f in b)if(f!=this.element){c=a.getElementsByTag(f);for(d=c.count()-1;d>=0;d--){var g=c.getItem(d);q(g,b[f])}}}function q(a,b,c){if(b=b&&b.attributes)for(var d=0;d",a||b.name,"");return c.join("")},getDefinition:function(){return this._.definition}};CKEDITOR.style.getStyleText=function(a){var b=a._ST;if(b)return b;var b=a.styles,c=a.attributes&&a.attributes.style||"",d="";c.length&&(c=c.replace(w,";"));for(var f in b){var g=b[f],e=(f+":"+g).replace(w,";");g=="inherit"?d=d+e:c=c+e}c.length&&(c=CKEDITOR.tools.normalizeCssText(c,true));return a._ST=c+d}}(),CKEDITOR.styleCommand=function(d,e){this.requiredContent=this.allowedContent=
-this.style=d;CKEDITOR.tools.extend(this,e,true)},CKEDITOR.styleCommand.prototype.exec=function(d){d.focus();this.state==CKEDITOR.TRISTATE_OFF?d.applyStyle(this.style):this.state==CKEDITOR.TRISTATE_ON&&d.removeStyle(this.style)},CKEDITOR.stylesSet=new CKEDITOR.resourceManager("","stylesSet"),CKEDITOR.addStylesSet=CKEDITOR.tools.bind(CKEDITOR.stylesSet.add,CKEDITOR.stylesSet),CKEDITOR.loadStylesSet=function(d,e,c){CKEDITOR.stylesSet.addExternal(d,e,"");CKEDITOR.stylesSet.load(d,c)},CKEDITOR.editor.prototype.getStylesSet=
-function(d){if(this._.stylesDefinitions)d(this._.stylesDefinitions);else{var e=this,c=e.config.stylesCombo_stylesSet||e.config.stylesSet;if(c===false)d(null);else if(c instanceof Array){e._.stylesDefinitions=c;d(c)}else{c||(c="default");var c=c.split(":"),a=c[0];CKEDITOR.stylesSet.addExternal(a,c[1]?c.slice(1).join(":"):CKEDITOR.getUrl("styles.js"),"");CKEDITOR.stylesSet.load(a,function(b){e._.stylesDefinitions=b[a];d(e._.stylesDefinitions)})}}},CKEDITOR.dom.comment=function(d,e){typeof d=="string"&&
-(d=(e?e.$:document).createComment(d));CKEDITOR.dom.domObject.call(this,d)},CKEDITOR.dom.comment.prototype=new CKEDITOR.dom.node,CKEDITOR.tools.extend(CKEDITOR.dom.comment.prototype,{type:CKEDITOR.NODE_COMMENT,getOuterHtml:function(){return"<\!--"+this.$.nodeValue+"--\>"}}),function(){var d={},e;for(e in CKEDITOR.dtd.$blockLimit)e in CKEDITOR.dtd.$list||(d[e]=1);var c={};for(e in CKEDITOR.dtd.$block)e in CKEDITOR.dtd.$blockLimit||e in CKEDITOR.dtd.$empty||(c[e]=1);CKEDITOR.dom.elementPath=function(a,
-b){var f=null,e=null,h=[],b=b||a.getDocument().getBody(),g=a;do if(g.type==CKEDITOR.NODE_ELEMENT){h.push(g);if(!this.lastElement){this.lastElement=g;if(g.is(CKEDITOR.dtd.$object))continue}var j=g.getName();if(!e){!f&&c[j]&&(f=g);if(d[j]){var k;if(k=!f){if(j=j=="div"){a:{j=g.getChildren();k=0;for(var n=j.count();k
--1}:typeof d=="function"?a=d:typeof d=="object"&&(a=function(a){return a.getName()in d});var b=this.elements,f=b.length;e&&f--;if(c){b=Array.prototype.slice.call(b,0);b.reverse()}for(e=0;e=a){f=b.createText("");f.insertAfter(this)}else{d=b.createText("");d.insertAfter(f);d.remove()}return f},substring:function(d,e){return typeof e!="number"?this.$.nodeValue.substr(d):this.$.nodeValue.substring(d,e)}}),function(){function d(c,a,b){var d=c.serializable,e=a[b?"endContainer":"startContainer"],h=b?"endOffset":"startOffset",g=d?a.document.getById(c.startNode):c.startNode,c=d?a.document.getById(c.endNode):c.endNode;if(e.equals(g.getPrevious())){a.startOffset=
-a.startOffset-e.getLength()-c.getPrevious().getLength();e=c.getNext()}else if(e.equals(c.getPrevious())){a.startOffset=a.startOffset-e.getLength();e=c.getNext()}e.equals(g.getParent())&&a[h]++;e.equals(c.getParent())&&a[h]++;a[b?"endContainer":"startContainer"]=e;return a}CKEDITOR.dom.rangeList=function(c){if(c instanceof CKEDITOR.dom.rangeList)return c;c?c instanceof CKEDITOR.dom.range&&(c=[c]):c=[];return CKEDITOR.tools.extend(c,e)};var e={createIterator:function(){var c=this,a=CKEDITOR.dom.walker.bookmark(),
-b=[],d;return{getNextRange:function(e){d=d==void 0?0:d+1;var h=c[d];if(h&&c.length>1){if(!d)for(var g=c.length-1;g>=0;g--)b.unshift(c[g].createBookmark(true));if(e)for(var j=0;c[d+j+1];){for(var k=h.document,e=0,g=k.getById(b[j].endNode),k=k.getById(b[j+1].startNode);;){g=g.getNextSourceNode(false);if(k.equals(g))e=1;else if(a(g)||g.type==CKEDITOR.NODE_ELEMENT&&g.isBlockBoundary())continue;break}if(!e)break;j++}for(h.moveToBookmark(b.shift());j--;){g=c[++d];g.moveToBookmark(b.shift());h.setEnd(g.endContainer,
-g.endOffset)}}return h}}},createBookmarks:function(c){for(var a=[],b,f=0;fb?-1:1}),f=0,g;f
', -CKEDITOR.document);d.appendTo(CKEDITOR.document.getHead());try{CKEDITOR.env.hc=d.getComputedStyle("border-top-color")==d.getComputedStyle("border-right-color")}catch(e){CKEDITOR.env.hc=false}d.remove()}if(CKEDITOR.env.hc)CKEDITOR.env.cssClass=CKEDITOR.env.cssClass+" cke_hc";CKEDITOR.document.appendStyleText(".cke{visibility:hidden;}");CKEDITOR.status="loaded";CKEDITOR.fireOnce("loaded");if(d=CKEDITOR._.pending){delete CKEDITOR._.pending;for(var c=0;c=0;f--)if(l[f].priority<=i){l.splice(f+1,0,g);return{removeListener:k}}l.unshift(g)}return{removeListener:k}},once:function(){var c=arguments[1];arguments[1]=function(a){a.removeListener();return c.apply(this,arguments)};return this.on.apply(this, +arguments)},capture:function(){CKEDITOR.event.useCapture=1;var c=this.on.apply(this,arguments);CKEDITOR.event.useCapture=0;return c},fire:function(){var c=0,a=function(){c=1},e=0,b=function(){e=1};return function(d,g,k){var l=f(this)[d],d=c,n=e;c=e=0;if(l){var o=l.listeners;if(o.length)for(var o=o.slice(0),q,j=0;j=0&&e.listeners.splice(b,1)}},removeAllListeners:function(){var c=f(this),a;for(a in c)delete c[a]},hasListeners:function(c){return(c=f(this)[c])&&c.listeners.length>0}}}()),CKEDITOR.editor||(CKEDITOR.editor=function(){CKEDITOR._.pending.push([this,arguments]);CKEDITOR.event.call(this)},CKEDITOR.editor.prototype.fire= +function(b,f){b in{instanceReady:1,loaded:1}&&(this[b]=true);return CKEDITOR.event.prototype.fire.call(this,b,f,this)},CKEDITOR.editor.prototype.fireOnce=function(b,f){b in{instanceReady:1,loaded:1}&&(this[b]=true);return CKEDITOR.event.prototype.fireOnce.call(this,b,f,this)},CKEDITOR.event.implementOn(CKEDITOR.editor.prototype)),CKEDITOR.env||(CKEDITOR.env=function(){var b=navigator.userAgent.toLowerCase(),f={ie:b.indexOf("trident/")>-1,webkit:b.indexOf(" applewebkit/")>-1,air:b.indexOf(" adobeair/")> +-1,mac:b.indexOf("macintosh")>-1,quirks:document.compatMode=="BackCompat"&&(!document.documentMode||document.documentMode<10),mobile:b.indexOf("mobile")>-1,iOS:/(ipad|iphone|ipod)/.test(b),isCustomDomain:function(){if(!this.ie)return false;var a=document.domain,e=window.location.hostname;return a!=e&&a!="["+e+"]"},secure:location.protocol=="https:"};f.gecko=navigator.product=="Gecko"&&!f.webkit&&!f.ie;if(f.webkit)b.indexOf("chrome")>-1?f.chrome=true:f.safari=true;var d=0;if(f.ie){d=f.quirks||!document.documentMode? +parseFloat(b.match(/msie (\d+)/)[1]):document.documentMode;f.ie9Compat=d==9;f.ie8Compat=d==8;f.ie7Compat=d==7;f.ie6Compat=d<7||f.quirks}if(f.gecko){var c=b.match(/rv:([\d\.]+)/);if(c){c=c[1].split(".");d=c[0]*1E4+(c[1]||0)*100+(c[2]||0)*1}}f.air&&(d=parseFloat(b.match(/ adobeair\/(\d+)/)[1]));f.webkit&&(d=parseFloat(b.match(/ applewebkit\/(\d+)/)[1]));f.version=d;f.isCompatible=f.iOS&&d>=534||!f.mobile&&(f.ie&&d>6||f.gecko&&d>=2E4||f.air&&d>=1||f.webkit&&d>=522||false);f.hidpi=window.devicePixelRatio>= +2;f.needsBrFiller=f.gecko||f.webkit||f.ie&&d>10;f.needsNbspFiller=f.ie&&d<11;f.cssClass="cke_browser_"+(f.ie?"ie":f.gecko?"gecko":f.webkit?"webkit":"unknown");if(f.quirks)f.cssClass=f.cssClass+" cke_browser_quirks";if(f.ie)f.cssClass=f.cssClass+(" cke_browser_ie"+(f.quirks?"6 cke_browser_iequirks":f.version));if(f.air)f.cssClass=f.cssClass+" cke_browser_air";if(f.iOS)f.cssClass=f.cssClass+" cke_browser_ios";if(f.hidpi)f.cssClass=f.cssClass+" cke_hidpi";return f}()),"unloaded"==CKEDITOR.status&&function(){CKEDITOR.event.implementOn(CKEDITOR); +CKEDITOR.loadFullCore=function(){if(CKEDITOR.status!="basic_ready")CKEDITOR.loadFullCore._load=1;else{delete CKEDITOR.loadFullCore;var b=document.createElement("script");b.type="text/javascript";b.src=CKEDITOR.basePath+"ckeditor.js";document.getElementsByTagName("head")[0].appendChild(b)}};CKEDITOR.loadFullCoreTimeout=0;CKEDITOR.add=function(b){(this._.pending||(this._.pending=[])).push(b)};(function(){CKEDITOR.domReady(function(){var b=CKEDITOR.loadFullCore,f=CKEDITOR.loadFullCoreTimeout;if(b){CKEDITOR.status= +"basic_ready";b&&b._load?b():f&&setTimeout(function(){CKEDITOR.loadFullCore&&CKEDITOR.loadFullCore()},f*1E3)}})})();CKEDITOR.status="basic_loaded"}(),CKEDITOR.dom={},function(){var b=[],f=CKEDITOR.env.gecko?"-moz-":CKEDITOR.env.webkit?"-webkit-":CKEDITOR.env.ie?"-ms-":"",d=/&/g,c=/>/g,a=/"+ +e+""):c.push('');return c.join("")},htmlEncode:function(l){return(""+l).replace(d,"&").replace(c,">").replace(a,"<")},htmlDecode:function(a){return a.replace(h,"&").replace(i,">").replace(g,"<")},htmlEncodeAttr:function(l){return l.replace(e,""").replace(a,"<").replace(c,">")},htmlDecodeAttr:function(a){return a.replace(k,'"').replace(g,"<").replace(i,">")},getNextNumber:function(){var a=0;return function(){return++a}}(), +getNextId:function(){return"cke_"+this.getNextNumber()},override:function(a,e){var c=e(a);c.prototype=a.prototype;return c},setTimeout:function(a,e,c,b,j){j||(j=window);c||(c=j);return j.setTimeout(function(){b?a.apply(c,[].concat(b)):a.apply(c)},e||0)},trim:function(){var a=/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;return function(e){return e.replace(a,"")}}(),ltrim:function(){var a=/^[ \t\n\r]+/g;return function(e){return e.replace(a,"")}}(),rtrim:function(){var a=/[ \t\n\r]+$/g;return function(e){return e.replace(a, +"")}}(),indexOf:function(a,e){if(typeof e=="function")for(var c=0,b=a.length;c=0?a[c]:null},bind:function(a,e){return function(){return a.apply(e,arguments)}},createClass:function(a){var e=a.$,c=a.base,b=a.privates||a._,j=a.proto,a=a.statics;!e&&(e=function(){c&&this.base.apply(this,arguments)});if(b)var d=e,e=function(){var a= +this._||(this._={}),e;for(e in b){var c=b[e];a[e]=typeof c=="function"?CKEDITOR.tools.bind(c,this):c}d.apply(this,arguments)};if(c){e.prototype=this.prototypedCopy(c.prototype);e.prototype.constructor=e;e.base=c;e.baseProto=c.prototype;e.prototype.base=function(){this.base=c.prototype.base;c.apply(this,arguments);this.base=arguments.callee}}j&&this.extend(e.prototype,j,true);a&&this.extend(e,a,true);return e},addFunction:function(a,e){return b.push(function(){return a.apply(e||this,arguments)})-1}, +removeFunction:function(a){b[a]=null},callFunction:function(a){var e=b[a];return e&&e.apply(window,Array.prototype.slice.call(arguments,1))},cssLength:function(){var a=/^-?\d+\.?\d*px$/,e;return function(c){e=CKEDITOR.tools.trim(c+"")+"px";return a.test(e)?e:c||""}}(),convertToPx:function(){var a;return function(e){if(!a){a=CKEDITOR.dom.element.createFromHtml('
',CKEDITOR.document);CKEDITOR.document.getBody().append(a)}if(!/%$/.test(e)){a.setStyle("width", +e);return a.$.clientWidth}return e}}(),repeat:function(a,e){return Array(e+1).join(a)},tryThese:function(){for(var a,e=0,c=arguments.length;e8)&&f)b=f+":"+b;return new CKEDITOR.dom.nodeList(this.$.getElementsByTagName(b))},getHead:function(){var b=this.$.getElementsByTagName("head")[0];return b=b?new CKEDITOR.dom.element(b):this.getDocumentElement().append(new CKEDITOR.dom.element("head"),true)},getBody:function(){return new CKEDITOR.dom.element(this.$.body)},getDocumentElement:function(){return new CKEDITOR.dom.element(this.$.documentElement)},getWindow:function(){return new CKEDITOR.dom.window(this.$.parentWindow|| +this.$.defaultView)},write:function(b){this.$.open("text/html","replace");CKEDITOR.env.ie&&(b=b.replace(/(?:^\s*]*?>)|^/i,'$&\n' . "\n"); - $this->assertIdentical($command->render(), $ajax_commands[1], 'The append command contains the expected data.'); + $this->assertTrue(in_array('core/modules/quickedit/js/editors/formEditor.js', array_keys($ajax_commands[0]['settings']['ajaxPageState']['js'])), 'The quickedit.inPlaceEditor.form library is loaded.'); // Retrieving the form for this field should result in a 200 response, // containing only a quickeditFieldForm command. @@ -237,9 +237,10 @@ class QuickEditLoadingTest extends WebTestBase { $this->assertText('Fine thanks.'); // Ensure no new revision was created and the log message is unchanged. - $revisions = node_revision_list(node_load(1)); - $this->assertIdentical(1, count($revisions), 'The node has only one revision.'); - $this->assertIdentical($original_log, $revisions[1]->log, 'The revision log message is unchanged.'); + $node = node_load(1); + $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node); + $this->assertIdentical(1, count($vids), 'The node has only one revision.'); + $this->assertIdentical($original_log, $node->log->value, 'The revision log message is unchanged.'); // Now configure this node type to create new revisions automatically, // then again retrieve the field form, fill it, submit it (so it ends up @@ -285,10 +286,12 @@ class QuickEditLoadingTest extends WebTestBase { $this->assertResponse(200); // Test that a revision was created with the correct log message. - $revisions = node_revision_list(node_load(1)); - $this->assertIdentical(2, count($revisions), 'The node has two revisions.'); - $this->assertIdentical($original_log, $revisions[1]->log, 'The first revision log message is unchanged.'); - $this->assertIdentical('Updated the Body field through in-place editing.', $revisions[2]->log, 'The second revision log message was correctly generated by Quick Edit module.'); + $vids = \Drupal::entityManager()->getStorage('node')->revisionIds(node_load(1)); + $this->assertIdentical(2, count($vids), 'The node has two revisions.'); + $revision = node_revision_load($vids[0]); + $this->assertIdentical($original_log, $revision->log->value, 'The first revision log message is unchanged.'); + $revision = node_revision_load($vids[1]); + $this->assertIdentical('Updated the Body field through in-place editing.', $revision->log->value, 'The second revision log message was correctly generated by Quick Edit module.'); } } @@ -299,6 +302,11 @@ class QuickEditLoadingTest extends WebTestBase { $this->drupalLogin($this->editor_user); $this->drupalGet('node/1'); + // Ensure that the full page title is actually in-place editable + $node = entity_load('node', 1); + $elements = $this->xpath('//h1/span[@data-quickedit-field-id="node/1/title/und/full" and normalize-space(text())=:title]', array(':title' => $node->label())); + $this->assertTrue(!empty($elements), 'Title with data-quickedit-field-id attribute found.'); + // Retrieving the metadata should result in a 200 JSON response. $htmlPageDrupalSettings = $this->drupalSettings; $post = array('fields[0]' => 'node/1/title/und/full'); diff --git a/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php b/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php index 4fc19ed..c72899b 100644 --- a/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php +++ b/core/modules/quickedit/lib/Drupal/quickedit/Tests/QuickEditTestBase.php @@ -10,9 +10,9 @@ use Drupal\simpletest\DrupalUnitTestBase; /** - * Parent class for Quick Edit tests. + * Base class for testing Quick Edit functionality. */ -class QuickEditTestBase extends DrupalUnitTestBase { +abstract class QuickEditTestBase extends DrupalUnitTestBase { /** * Modules to enable. diff --git a/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityAccessCheckTest.php b/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityAccessCheckTest.php index c3f87da..6596ccb 100644 --- a/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityAccessCheckTest.php +++ b/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityAccessCheckTest.php @@ -8,7 +8,6 @@ namespace Drupal\quickedit\Tests\Access; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Route; use Drupal\Core\Access\AccessCheckInterface; use Drupal\quickedit\Access\EditEntityAccessCheck; use Drupal\Tests\UnitTestCase; @@ -103,7 +102,6 @@ class EditEntityAccessCheckTest extends UnitTestCase { * @dataProvider providerTestAccess */ public function testAccess(EntityInterface $entity, $expected_result) { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity' => 'TRUE')); $request = new Request(); // Prepare the request to be valid. @@ -111,7 +109,7 @@ class EditEntityAccessCheckTest extends UnitTestCase { $request->attributes->set('entity_type', 'test_entity'); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $access = $this->editAccessCheck->access($route, $request, $account); + $access = $this->editAccessCheck->access($request, $account); $this->assertSame($expected_result, $access); } @@ -119,7 +117,6 @@ class EditEntityAccessCheckTest extends UnitTestCase { * Tests the access method with an undefined entity type. */ public function testAccessWithUndefinedEntityType() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity' => 'TRUE')); $request = new Request(); $request->attributes->set('entity_type', 'non_valid'); @@ -129,14 +126,13 @@ class EditEntityAccessCheckTest extends UnitTestCase { ->will($this->returnValue(NULL)); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $account)); } /** * Tests the access method with a non existing entity. */ public function testAccessWithNotExistingEntity() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', 1); @@ -152,7 +148,7 @@ class EditEntityAccessCheckTest extends UnitTestCase { ->will($this->returnValue(NULL)); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $account)); } } diff --git a/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityFieldAccessCheckTest.php b/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityFieldAccessCheckTest.php index 6895422..d1b0228 100644 --- a/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityFieldAccessCheckTest.php +++ b/core/modules/quickedit/tests/Drupal/quickedit/Tests/Access/EditEntityFieldAccessCheckTest.php @@ -8,7 +8,6 @@ namespace Drupal\quickedit\Tests\Access; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Route; use Drupal\Core\Access\AccessCheckInterface; use Drupal\quickedit\Access\EditEntityFieldAccessCheck; use Drupal\Tests\UnitTestCase; @@ -118,13 +117,13 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { * @dataProvider providerTestAccess */ public function testAccess(EntityInterface $entity, FieldConfigInterface $field = NULL, $expected_result) { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); + $field_name = 'valid'; $entity_with_field = clone $entity; $entity_with_field->expects($this->any()) ->method('get') - ->with('valid') + ->with($field_name) ->will($this->returnValue($field)); $entity_with_field->expects($this->once()) ->method('hasTranslation') @@ -134,11 +133,11 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { // Prepare the request to be valid. $request->attributes->set('entity_type', 'test_entity'); $request->attributes->set('entity', $entity_with_field); - $request->attributes->set('field_name', 'valid'); + $request->attributes->set('field_name', $field_name); $request->attributes->set('langcode', Language::LANGCODE_NOT_SPECIFIED); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $access = $this->editAccessCheck->access($route, $request, $account); + $access = $this->editAccessCheck->access($request, $field_name, $account); $this->assertSame($expected_result, $access); } @@ -146,7 +145,6 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { * Tests the access method with an undefined entity type. */ public function testAccessWithUndefinedEntityType() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); $request->attributes->set('entity_type', 'non_valid'); @@ -156,14 +154,13 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { ->will($this->returnValue(NULL)); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account)); } /** * Tests the access method with a non existing entity. */ public function testAccessWithNotExistingEntity() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', 1); @@ -179,48 +176,47 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { ->will($this->returnValue(NULL)); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account)); } /** * Tests the access method with a forgotten passed field_name. */ public function testAccessWithNotPassedFieldName() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', $this->createMockEntity()); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account)); } /** * Tests the access method with a non existing field. */ public function testAccessWithNonExistingField() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); + $field_name = 'not_valid'; $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', $this->createMockEntity()); - $request->attributes->set('field_name', 'not_valid'); + $request->attributes->set('field_name', $field_name); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account)); } /** * Tests the access method with a forgotten passed language. */ public function testAccessWithNotPassedLanguage() { - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); + $field_name = 'valid'; $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', $this->createMockEntity()); - $request->attributes->set('field_name', 'valid'); + $request->attributes->set('field_name', $field_name); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account)); } /** @@ -233,15 +229,15 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase { ->with('xx-lolspeak') ->will($this->returnValue(FALSE)); - $route = new Route('/quickedit/form/test_entity/1/body/und/full', array(), array('_access_quickedit_entity_field' => 'TRUE')); $request = new Request(); + $field_name = 'valid'; $request->attributes->set('entity_type', 'entity_test'); $request->attributes->set('entity', $entity); - $request->attributes->set('field_name', 'valid'); + $request->attributes->set('field_name', $field_name); $request->attributes->set('langcode', 'xx-lolspeak'); $account = $this->getMock('Drupal\Core\Session\AccountInterface'); - $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($route, $request, $account)); + $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account)); } /** diff --git a/core/modules/quickedit/tests/modules/quickedit_test.info.yml b/core/modules/quickedit/tests/modules/quickedit_test.info.yml index 91c40bd..fe21dc5 100644 --- a/core/modules/quickedit/tests/modules/quickedit_test.info.yml +++ b/core/modules/quickedit/tests/modules/quickedit_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for the Quick Edit module tests.' core: 8.x package: Testing version: VERSION -hidden: true diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index bf8c60e..8ea735b 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -7,13 +7,14 @@ use Drupal\Core\Template\Attribute; use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function rdf_help($path, $arg) { - switch ($path) { - case 'admin/help#rdf': +function rdf_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.rdf': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The RDF module enriches your content with metadata to let other applications (e.g., search engines, aggregators, and so on) better understand its relationships and attributes. This semantically enriched, machine-readable output for Drupal sites uses the RDFa specification, which allows RDF data to be embedded in HTML markup. Other modules can define mappings of their data to RDF terms, and the RDF module makes this RDF data available to the theme. The core Drupal modules define RDF mappings for their data model, and the core Drupal themes output this RDF metadata information along with the human-readable visual information. For more information, see the online documentation for the RDF module.', array('!rdfa' => 'http://www.w3.org/TR/xhtml-rdfa-primer/', '!rdf' => 'https://drupal.org/documentation/modules/rdf')) . '

'; diff --git a/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php b/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php index abe8c07..6c2793d 100644 --- a/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php +++ b/core/modules/rdf/tests/Drupal/rdf/Tests/RdfMappingConfigEntityUnitTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains \Drupal\rdf\Tests\RdfMappingEntityUnitTest. + * Contains \Drupal\rdf\Tests\RdfMappingConfigEntityUnitTest. */ namespace Drupal\rdf\Tests; @@ -17,7 +17,7 @@ * @group Drupal * @group Config */ -class RdfMappingEntityUnitTest extends UnitTestCase { +class RdfMappingConfigEntityUnitTest extends UnitTestCase { /** * The entity type used for testing. diff --git a/core/modules/rdf/tests/rdf_conflicting_namespaces/rdf_conflicting_namespaces.info.yml b/core/modules/rdf/tests/rdf_conflicting_namespaces/rdf_conflicting_namespaces.info.yml index 33a5cd9..65d06db 100644 --- a/core/modules/rdf/tests/rdf_conflicting_namespaces/rdf_conflicting_namespaces.info.yml +++ b/core/modules/rdf/tests/rdf_conflicting_namespaces/rdf_conflicting_namespaces.info.yml @@ -4,6 +4,5 @@ description: 'Test conflicting namespace declaration.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - rdf diff --git a/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info.yml b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info.yml index 7ace964..84cd974 100644 --- a/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info.yml +++ b/core/modules/rdf/tests/rdf_test_namespaces/rdf_test_namespaces.info.yml @@ -4,6 +4,5 @@ description: 'Test namespace declaration.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - rdf diff --git a/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageAdminUITest.php b/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageAdminUITest.php index 41e152e..b752a16 100644 --- a/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageAdminUITest.php +++ b/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageAdminUITest.php @@ -16,6 +16,13 @@ class ResponsiveImageAdminUITest extends WebTestBase { /** + * The breakpoint group for testing. + * + * @var \Drupal\breakpoint\Entity\BreakpointGroupInterface + */ + protected $breakpointGroup; + + /** * Modules to enable. * * @var array @@ -47,8 +54,8 @@ class ResponsiveImageAdminUITest extends WebTestBase { $this->drupalLogin($this->admin_user); // Add breakpoint_group and breakpoints. - $breakpoint_group = entity_create('breakpoint_group', array( - 'id' => 'atestset', + $this->breakpointGroup = entity_create('breakpoint_group', array( + 'name' => 'atestset', 'label' => 'A test set', 'sourceType' => Breakpoint::SOURCE_TYPE_USER_DEFINED, )); @@ -67,9 +74,9 @@ class ResponsiveImageAdminUITest extends WebTestBase { ), )); $breakpoint->save(); - $breakpoint_group->addBreakpoints(array($breakpoint)); + $this->breakpointGroup->addBreakpoints(array($breakpoint)); } - $breakpoint_group->save(); + $this->breakpointGroup->save(); } @@ -83,13 +90,13 @@ class ResponsiveImageAdminUITest extends WebTestBase { // Add a new responsive image mapping, our breakpoint set should be selected. $this->drupalGet('admin/config/media/responsive-image-mapping/add'); - $this->assertFieldByName('breakpointGroup', 'atestset'); + $this->assertFieldByName('breakpointGroup', $this->breakpointGroup->id()); // Create a new group. $edit = array( 'label' => 'Mapping One', 'id' => 'mapping_one', - 'breakpointGroup' => 'atestset', + 'breakpointGroup' => $this->breakpointGroup->id(), ); $this->drupalPostForm('admin/config/media/responsive-image-mapping/add', $edit, t('Save')); @@ -103,7 +110,7 @@ class ResponsiveImageAdminUITest extends WebTestBase { // Edit the group. $this->drupalGet('admin/config/media/responsive-image-mapping/mapping_one'); $this->assertFieldByName('label', 'Mapping One'); - $this->assertFieldByName('breakpointGroup', 'atestset'); + $this->assertFieldByName('breakpointGroup', $this->breakpointGroup->id()); // Check if the dropdows are present for the mappings. $this->assertFieldByName('mappings[custom.user.small][1x]', ''); @@ -116,7 +123,7 @@ class ResponsiveImageAdminUITest extends WebTestBase { // Save mappings for 1x variant only. $edit = array( 'label' => 'Mapping One', - 'breakpointGroup' => 'atestset', + 'breakpointGroup' => $this->breakpointGroup->id(), 'mappings[custom.user.small][1x]' => 'thumbnail', 'mappings[custom.user.medium][1x]' => 'medium', 'mappings[custom.user.large][1x]' => 'large', diff --git a/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageFieldDisplayTest.php index e7e54a6..5d34845 100644 --- a/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageFieldDisplayTest.php +++ b/core/modules/responsive_image/lib/Drupal/responsive_image/Tests/ResponsiveImageFieldDisplayTest.php @@ -60,7 +60,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase { // Add breakpoint_group and breakpoints. $breakpoint_group = entity_create('breakpoint_group', array( - 'id' => 'atestset', + 'name' => 'atestset', 'label' => 'A test set', 'sourceType' => Breakpoint::SOURCE_TYPE_USER_DEFINED, )); @@ -87,7 +87,7 @@ class ResponsiveImageFieldDisplayTest extends ImageFieldTestBase { $responsive_image_mapping = entity_create('responsive_image_mapping', array( 'id' => 'mapping_one', 'label' => 'Mapping One', - 'breakpointGroup' => 'atestset', + 'breakpointGroup' => $breakpoint_group->id(), )); $responsive_image_mapping->save(); $mappings = array(); diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module index be888b4..82ecafe 100644 --- a/core/modules/responsive_image/responsive_image.module +++ b/core/modules/responsive_image/responsive_image.module @@ -7,14 +7,15 @@ use Drupal\responsive_image\Entity\ResponsiveImageMapping; use \Drupal\Core\Template\Attribute; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function responsive_image_help($path, $arg) { +function responsive_image_help($route_name, Request $request) { $output = ''; - switch ($path) { - case 'admin/help#responsive_image': + switch ($route_name) { + case 'help.page.responsive_image': $output .= '

' . t('About') . '

'; $output .= '

' . t('The Responsive Image module provides an image formatter and breakpoint mappings to output responsive images using the HTML5 picture tag. For more information, see the online documentation for the Responsive Image module.', array( '!responsive_image' => 'https://drupal.org/documentation/modules/responsive_image')) . '

'; $output .= '

' . t('Uses') . '

'; @@ -25,7 +26,8 @@ function responsive_image_help($path, $arg) { $output .= '
' . t('After defining responsive image mappings, you can use them in the display settings for your Image fields, so that the site displays responsive images using the HTML5 picture tag. Open the Manage display page for the entity type (content type, taxonomy vocabulary, etc.) that the Image field is attached to. Choose the format Responsive image, click the Edit icon, and select one of the responsive image mappings that you have created. For general information on how to manage fields and their display see the help page of the Field UI module. For information about entities see the help page of the Entity module.', array('!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')),'!entity_help' => \Drupal::url('help.page', array('name' => 'entity')))) . '
'; $output .= ''; break; - case 'admin/config/media/responsive-image-mapping': + + case 'responsive_image.mapping_page': $output .= '

' . t('A responsive image mapping associates an image style with each breakpoint defined by your theme.') . '

'; break; diff --git a/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/ResponsiveImageMappingEntityTest.php b/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/ResponsiveImageMappingEntityTest.php index 43ccb05..8565c3b 100644 --- a/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/ResponsiveImageMappingEntityTest.php +++ b/core/modules/responsive_image/tests/Drupal/responsive_image/Tests/ResponsiveImageMappingEntityTest.php @@ -99,7 +99,7 @@ class ResponsiveImageMappingEntityTest extends UnitTestCase { $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface'); $this->breakpointGroupId = $this->randomName(9); - $this->breakpointGroup = $this->getMock('Drupal\breakpoint\Entity\BreakpointGroup', array(), array(array('id' => $this->breakpointGroupId))); + $this->breakpointGroup = $this->getMock('Drupal\breakpoint\Entity\BreakpointGroup', array(), array(array('name' => 'test', 'id' => $this->breakpointGroupId))); $this->breakpointGroupStorage = $this->getMock('\Drupal\Core\Config\Entity\ConfigEntityStorageInterface'); $this->breakpointGroupStorage diff --git a/core/modules/rest/lib/Drupal/rest/Access/CSRFAccessCheck.php b/core/modules/rest/lib/Drupal/rest/Access/CSRFAccessCheck.php index 396b3a1..6d50ff0 100644 --- a/core/modules/rest/lib/Drupal/rest/Access/CSRFAccessCheck.php +++ b/core/modules/rest/lib/Drupal/rest/Access/CSRFAccessCheck.php @@ -41,9 +41,17 @@ class CSRFAccessCheck implements AccessCheckInterface { } /** - * Implements AccessCheckInterface::access(). + * Checks access. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Request $request, AccountInterface $account) { $method = $request->getMethod(); $cookie = $request->attributes->get('_authentication_provider') == 'cookie'; diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php b/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php index 8b31ed2..fc62ede 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/Derivative/EntityDerivative.php @@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface; +use Drupal\Core\Routing\RouteBuilder; +use Drupal\Core\Routing\RouteBuilderInterface; use Drupal\Core\Routing\RouteProviderInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\Exception\RouteNotFoundException; @@ -40,16 +42,26 @@ class EntityDerivative implements ContainerDerivativeInterface { protected $routeProvider; /** + * The route builder. + * + * @var \Drupal\Core\Routing\RouteBuilderInterface + */ + protected $routeBuilder; + + /** * Constructs an EntityDerivative object. * * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider * The route provider. + * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder + * The route builder. */ - public function __construct(EntityManagerInterface $entity_manager, RouteProviderInterface $route_provider) { + public function __construct(EntityManagerInterface $entity_manager, RouteProviderInterface $route_provider, RouteBuilderInterface $route_builder) { $this->entityManager = $entity_manager; $this->routeProvider = $route_provider; + $this->routeBuilder = $route_builder; } /** @@ -58,7 +70,8 @@ class EntityDerivative implements ContainerDerivativeInterface { public static function create(ContainerInterface $container, $base_plugin_id) { return new static( $container->get('entity.manager'), - $container->get('router.route_provider') + $container->get('router.route_provider'), + $container->get('router.builder') ); } @@ -104,12 +117,17 @@ class EntityDerivative implements ContainerDerivativeInterface { $this->derivatives[$entity_type_id]['uri_paths'][$link_relation] = $route->getPath(); } catch (RouteNotFoundException $e) { - // If the route does not exist it means we are in a brittle state - // of module enabling/disabling, so we simply exclude this entity - // type. - unset($this->derivatives[$entity_type_id]); - // Continue with the next entity type; - continue 2; + if (($collection = $this->routeBuilder->getCollectionDuringRebuild()) && $route = $collection->get($route_name)) { + $this->derivatives[$entity_type_id]['uri_paths'][$link_relation] = $route->getPath(); + } + else { + // If the route does not exist it means we are in a brittle state + // of module enabling/disabling, so we simply exclude this entity + // type. + unset($this->derivatives[$entity_type_id]); + // Continue with the next entity type; + continue 2; + } } } else { diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php index b28b477..263e441 100644 --- a/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php +++ b/core/modules/rest/lib/Drupal/rest/Plugin/views/display/RestExport.php @@ -7,7 +7,7 @@ namespace Drupal\rest\Plugin\views\display; - +use Drupal\Core\Form\FormErrorInterface; use Drupal\Core\State\StateInterface; use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\ContentNegotiation; @@ -99,13 +99,15 @@ class RestExport extends PathPluginBase { * The route provider * @param \Drupal\Core\State\StateInterface $state * The state key value store. + * @param \Drupal\Core\Form\FormErrorInterface $form_error + * The form error helper. * @param \Drupal\Core\ContentNegotiation $content_negotiation * The content negotiation library. * @param \Symfony\Component\HttpFoundation\Request $request * The request object. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, ContentNegotiation $content_negotiation, Request $request) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state); + public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, FormErrorInterface $form_error, ContentNegotiation $content_negotiation, Request $request) { + parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state, $form_error); $this->contentNegotiation = $content_negotiation; $this->request = $request; } @@ -120,6 +122,7 @@ class RestExport extends PathPluginBase { $plugin_definition, $container->get('router.route_provider'), $container->get('state'), + $container->get('form_validator'), $container->get('content_negotiation'), $container->get('request') ); diff --git a/core/modules/rest/lib/Drupal/rest/RequestHandler.php b/core/modules/rest/lib/Drupal/rest/RequestHandler.php index 20aaf5d..a5551b2 100644 --- a/core/modules/rest/lib/Drupal/rest/RequestHandler.php +++ b/core/modules/rest/lib/Drupal/rest/RequestHandler.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; diff --git a/core/modules/rest/lib/Drupal/rest/Routing/ResourceRoutes.php b/core/modules/rest/lib/Drupal/rest/Routing/ResourceRoutes.php index fcea617..5605048 100644 --- a/core/modules/rest/lib/Drupal/rest/Routing/ResourceRoutes.php +++ b/core/modules/rest/lib/Drupal/rest/Routing/ResourceRoutes.php @@ -9,13 +9,16 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Routing\RouteSubscriberBase; use Drupal\rest\Plugin\Type\ResourcePluginManager; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Routing\RouteCollection; /** * Subscriber for REST-style routes. */ -class ResourceRoutes implements ContainerInjectionInterface { +class ResourceRoutes extends RouteSubscriberBase{ /** * The plugin manager for REST plugins. @@ -45,22 +48,13 @@ class ResourceRoutes implements ContainerInjectionInterface { } /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.rest'), - $container->get('config.factory') - ); - } - - /** - * Returns an array of route objects. + * Alters existing routes for a specific collection. * - * @return \Symfony\Component\Routing\Route[] - * An array of route objects. + * @param \Symfony\Component\Routing\RouteCollection $collection + * The route collection for adding routes. + * @return array */ - public function routes() { + protected function alterRoutes(RouteCollection $collection) { $routes = array(); $enabled_resources = $this->config->get('rest.settings')->get('resources') ?: array(); @@ -97,10 +91,10 @@ class ResourceRoutes implements ContainerInjectionInterface { // authentication provider and add the route. $route->setOption('_auth', $enabled_methods[$method]['supported_auth']); $routes["rest.$name"] = $route; + $collection->add("rest.$name", $route); } } } - return $routes; } } diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php index fdf56d9..1e19be2 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php @@ -247,9 +247,6 @@ protected function rebuildCache() { // Rebuild routing cache, so that the REST API paths are available. $this->container->get('router.builder')->rebuild(); - // Reset the Simpletest permission cache, so that the new resource - // permissions get picked up. - drupal_static_reset('checkPermissions'); } /** diff --git a/core/modules/rest/rest.module b/core/modules/rest/rest.module index 086961a..a3cf32b 100644 --- a/core/modules/rest/rest.module +++ b/core/modules/rest/rest.module @@ -5,6 +5,8 @@ * RESTful web services module. */ +use Symfony\Component\HttpFoundation\Request; + /** * Implements hook_permission(). */ @@ -24,10 +26,9 @@ function rest_permission() { /** * Implements hook_help(). */ -function rest_help($path, $arg) { - switch ($path) { - // Main module help for the REST module. - case 'admin/help#rest': +function rest_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.rest': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The REST module provides a framework for exposing Drupal\'s data structures as RESTful web services. It can be used to read and write resources remotely, such as entity types like nodes or users. For more information, see the online handbook entry for the RESTful web services module.', array('@rest' => 'http://drupal.org/documentation/modules/rest')) . '

'; diff --git a/core/modules/rest/rest.routing.yml b/core/modules/rest/rest.routing.yml index 3017130..843dee9 100644 --- a/core/modules/rest/rest.routing.yml +++ b/core/modules/rest/rest.routing.yml @@ -4,6 +4,3 @@ rest.csrftoken: _controller: '\Drupal\rest\RequestHandler::csrfToken' requirements: _access: 'TRUE' - -route_callbacks: - - '\Drupal\rest\Routing\ResourceRoutes::routes' diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index bf4a19c..e7b43f3 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -22,3 +22,8 @@ services: rest.link_manager.relation: class: Drupal\rest\LinkManager\RelationLinkManager arguments: ['@cache.default', '@entity.manager'] + rest.resource_routes: + class: Drupal\rest\Routing\ResourceRoutes + arguments: ['@plugin.manager.rest', '@config.factory'] + tags: + - { name: 'event_subscriber' } diff --git a/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php b/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php index 5b78e76..f511192 100644 --- a/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php +++ b/core/modules/rest/tests/Drupal/rest/Tests/CollectRoutesTest.php @@ -90,6 +90,9 @@ class CollectRoutesTest extends UnitTestCase { ->getMock(); $container->set('plugin.manager.views.style', $style_manager); + $form_error = $this->getMock('Drupal\Core\Form\FormErrorInterface'); + $container->set('form_validator', $form_error); + \Drupal::setContainer($container); $this->restExport = RestExport::create($container, array(), "test_routes", array()); diff --git a/core/modules/rest/tests/modules/rest_test_views/rest_test_views.info.yml b/core/modules/rest/tests/modules/rest_test_views/rest_test_views.info.yml index debc5e2..662ad1b 100644 --- a/core/modules/rest/tests/modules/rest_test_views/rest_test_views.info.yml +++ b/core/modules/rest/tests/modules/rest_test_views/rest_test_views.info.yml @@ -7,4 +7,3 @@ core: 8.x dependencies: - rest - views -hidden: true diff --git a/core/modules/search/lib/Drupal/search/Controller/SearchController.php b/core/modules/search/lib/Drupal/search/Controller/SearchController.php index ec7d8bf..20ce39a 100644 --- a/core/modules/search/lib/Drupal/search/Controller/SearchController.php +++ b/core/modules/search/lib/Drupal/search/Controller/SearchController.php @@ -93,12 +93,17 @@ class SearchController extends ControllerBase { ); } + $no_results = t('
    +
  • Check if your spelling is correct.
  • +
  • Remove quotes around phrases to search for each word individually. bike shed will often show more results than "bike shed".
  • +
  • Consider loosening your query with OR. bike OR shed will often show more results than bike shed.
  • +
'); $build['search_results'] = array( '#theme' => array('item_list__search_results__' . $plugin->getPluginId(), 'item_list__search_results'), '#items' => $results, '#empty' => array( // @todo Revisit where this help text is added. - '#markup' => '

' . $this->t('Your search yielded no results.') . '

' . search_help('search#noresults', drupal_help_arg()), + '#markup' => '

' . $this->t('Your search yielded no results.') . '

' . $no_results, ), '#list_type' => 'ol', '#attributes' => array( @@ -107,6 +112,9 @@ class SearchController extends ControllerBase { $plugin->getPluginId() . '-results', ), ), + '#cache' => array( + 'tags' => $entity->getCacheTag(), + ), ); $build['pager'] = array( diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchCommentCountToggleTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchCommentCountToggleTest.php index adfb110..64c1171 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchCommentCountToggleTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchCommentCountToggleTest.php @@ -26,10 +26,7 @@ class SearchCommentCountToggleTest extends SearchTestBase { * * @var array */ - public static $modules = array('comment'); - - // Requires node types, comment config, filter formats. - protected $profile = 'standard'; + public static $modules = array('node', 'comment'); protected $searching_user; protected $searchable_nodes; @@ -46,7 +43,7 @@ function setUp() { parent::setUp(); // Create searching user. - $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); + $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'post comments', 'skip comment approval')); // Login with sufficient privileges. $this->drupalLogin($this->searching_user); @@ -84,9 +81,10 @@ function testSearchCommentCountToggle() { $edit = array( 'keys' => "'SearchCommentToggleTestCase'", ); + $this->drupalGet('search/node'); // Test comment count display for nodes with comment status set to Open - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm(NULL, $edit, t('Search')); $this->assertText(t('0 comments'), 'Empty comment count displays for nodes with comment status set to Open'); $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Open'); @@ -96,7 +94,7 @@ function testSearchCommentCountToggle() { $this->searchable_nodes['1 comment']->set('comment', CommentItemInterface::CLOSED); $this->searchable_nodes['1 comment']->save(); - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm(NULL, $edit, t('Search')); $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Closed'); $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Closed'); @@ -106,7 +104,7 @@ function testSearchCommentCountToggle() { $this->searchable_nodes['1 comment']->set('comment', CommentItemInterface::HIDDEN); $this->searchable_nodes['1 comment']->save(); - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm(NULL, $edit, t('Search')); $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Hidden'); $this->assertNoText(t('1 comment'), 'Non-empty comment count does not display for nodes with comment status set to Hidden'); } diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchCommentTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchCommentTest.php index 2957d11..896bf7b 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchCommentTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchCommentTest.php @@ -8,7 +8,7 @@ namespace Drupal\search\Tests; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; -use Drupal\field\Field; +use Drupal\field\Entity\FieldInstanceConfig; /** * Test integration searching comments. @@ -20,9 +20,7 @@ class SearchCommentTest extends SearchTestBase { * * @var array */ - public static $modules = array('comment'); - - protected $profile = 'standard'; + public static $modules = array('filter', 'node', 'comment'); protected $admin_user; @@ -37,14 +35,22 @@ class SearchCommentTest extends SearchTestBase { function setUp() { parent::setUp(); + $full_html_format = entity_create('filter_format', array( + 'format' => 'full_html', + 'name' => 'Full HTML', + 'weight' => 1, + 'filters' => array(), + )); + $full_html_format->save(); + // Create and log in an administrative user having access to the Full HTML // text format. - $full_html_format = entity_load('filter_format', 'full_html'); $permissions = array( 'administer filters', $full_html_format->getPermissionName(), 'administer permissions', 'create page content', + 'post comments', 'skip comment approval', 'access comments', ); @@ -58,18 +64,25 @@ function setUp() { * Verify that comments are rendered using proper format in search results. */ function testSearchResultsComment() { + // Create basic_html format that escapes all HTML. + $basic_html_format = entity_create('filter_format', array( + 'format' => 'basic_html', + 'name' => 'Basic HTML', + 'weight' => 1, + 'filters' => array( + 'filter_html_escape' => array('status' => 1), + ), + 'roles' => array(DRUPAL_AUTHENTICATED_RID), + )); + $basic_html_format->save(); + $comment_body = 'Test comment body'; // Make preview optional. - $instance = Field::fieldInfo()->getInstance('node', 'article', 'comment'); + $instance = FieldInstanceConfig::loadByName('node', 'article', 'comment'); $instance->settings['preview'] = DRUPAL_OPTIONAL; $instance->save(); - // Enable check_plain() for 'Basic HTML' text format. - $basic_html_format_id = 'basic_html'; - $edit = array( - 'filters[filter_html_escape][status]' => TRUE, - ); - $this->drupalPostForm('admin/config/content/formats/manage/' . $basic_html_format_id, $edit, t('Save configuration')); + // Allow anonymous users to search content. $edit = array( DRUPAL_ANONYMOUS_RID . '[search content]' => 1, @@ -96,7 +109,7 @@ function testSearchResultsComment() { $edit = array( 'keys' => "'" . $edit_comment['subject'] . "'", ); - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm('search/node', $edit, t('Search')); $node2 = node_load($node->id(), TRUE); $this->assertText($node2->label(), 'Node found in search results.'); $this->assertText($edit_comment['subject'], 'Comment subject found in search results.'); @@ -105,7 +118,7 @@ function testSearchResultsComment() { $edit = array( 'keys' => "'" . $comment_body . "'", ); - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm(NULL, $edit, t('Search')); $this->assertText($node2->label(), 'Node found in search results.'); // Verify that comment is rendered using proper format. @@ -123,7 +136,7 @@ function testSearchResultsComment() { $this->cronRun(); // Search for $title. - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm('search/node', $edit, t('Search')); $this->assertNoText($comment_body, 'Comment body text not found in search results.'); } @@ -138,7 +151,7 @@ function testSearchResultsCommentAccess() { // Create a node. // Make preview optional. - $instance = Field::fieldInfo()->getInstance('node', 'article', 'comment'); + $instance = FieldInstanceConfig::loadByName('node', 'article', 'comment'); $instance->settings['preview'] = DRUPAL_OPTIONAL; $instance->save(); $this->node = $this->drupalCreateNode(array('type' => 'article')); @@ -211,7 +224,7 @@ function assertCommentAccess($assume_access, $message) { $edit = array( 'keys' => "'" . $this->comment_subject . "'", ); - $this->submitGetForm('', $edit, t('Search')); + $this->drupalPostForm('search/node', $edit, t('Search')); if ($assume_access) { $expected_node_result = $this->assertText($this->node->label()); @@ -235,14 +248,20 @@ function testAddNewComment() { 'body' => array(array('value' => 'short body text')), ); - $user = $this->drupalCreateUser(array('search content', 'create article content', 'access content')); + $user = $this->drupalCreateUser(array( + 'search content', + 'create article content', + 'access content', + 'post comments', + 'access comments', + )); $this->drupalLogin($user); $node = $this->drupalCreateNode($settings); // Verify that if you view the node on its own page, 'add new comment' // is there. $this->drupalGet('node/' . $node->id()); - $this->assertText(t('Add new comment'), 'Add new comment appears on node page'); + $this->assertText(t('Add new comment')); // Run cron to index this page. $this->drupalLogout(); @@ -251,12 +270,12 @@ function testAddNewComment() { // Search for 'comment'. Should be no results. $this->drupalLogin($user); $this->drupalPostForm('search/node', array('keys' => 'comment'), t('Search')); - $this->assertText(t('Your search yielded no results'), 'No results searching for the word comment'); + $this->assertText(t('Your search yielded no results')); // Search for the node title. Should be found, and 'Add new comment' should // not be part of the search snippet. $this->drupalPostForm('search/node', array('keys' => 'short'), t('Search')); $this->assertText($node->label(), 'Search for keyword worked'); - $this->assertNoText(t('Add new comment'), 'Add new comment does not appear on search results page'); + $this->assertNoText(t('Add new comment')); } } diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchLanguageTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchLanguageTest.php index c7b51c8..ffcf33d 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchLanguageTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchLanguageTest.php @@ -8,7 +8,7 @@ namespace Drupal\search\Tests; use Drupal\Core\Language\Language; -use Drupal\field\Field; +use Drupal\field\Entity\FieldConfig; /** * Test node search with multiple languages. @@ -25,7 +25,7 @@ class SearchLanguageTest extends SearchTestBase { public static function getInfo() { return array( 'name' => 'Search language selection', - 'description' => 'Tests advanced search with different languages enabled.', + 'description' => 'Tests advanced search with different languages added.', 'group' => 'Search', ); } @@ -47,7 +47,7 @@ function setUp() { // Make the body field translatable. The title is already translatable by // definition. The parent class has already created the article and page // content types. - $field = Field::fieldInfo()->getField('node', 'body'); + $field = FieldConfig::loadByName('node', 'body'); $field->translatable = TRUE; $field->save(); diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php index 37e00e8..1b670bf 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchMultilingualEntityTest.php @@ -8,7 +8,7 @@ namespace Drupal\search\Tests; use Drupal\Core\Language\Language; -use Drupal\field\Field; +use Drupal\field\Entity\FieldConfig; /** * Tests entities with multilingual fields. @@ -58,7 +58,7 @@ function setUp() { // Make the body field translatable. The title is already translatable by // definition. The parent class has already created the article and page // content types. - $field = Field::fieldInfo()->getField('node', 'body'); + $field = FieldConfig::loadByName('node', 'body'); $field->translatable = TRUE; $field->save(); diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchPageCacheTagsTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchPageCacheTagsTest.php new file mode 100644 index 0000000..2478e66 --- /dev/null +++ b/core/modules/search/lib/Drupal/search/Tests/SearchPageCacheTagsTest.php @@ -0,0 +1,64 @@ + 'Search page cache tags', + 'description' => 'Tests the presence of the SearchPage entity\'s cache tags on the search results pages.', + 'group' => 'Search' + ); + } + + function setUp() { + parent::setUp(); + + // Create user. + $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles')); + } + + /** + * Tests the presence of the expected cache tag in various situations. + */ + function testSearchText() { + $this->drupalLogin($this->searching_user); + + // Initial page for searching nodes. + $this->drupalGet('search/node'); + $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); + $this->assertTrue(in_array('search_page:node_search', $cache_tags)); + + // Node search results. + $edit = array(); + $edit['keys'] = 'bike shed'; + $this->drupalPostForm('search/node', $edit, t('Search')); + $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); + $this->assertTrue(in_array('search_page:node_search', $cache_tags)); + + // Initial page for searching users. + $this->drupalGet('search/user'); + $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); + $this->assertTrue(in_array('search_page:user_search', $cache_tags)); + + // User search results. + $edit['keys'] = $this->searching_user->getUsername(); + $this->drupalPostForm('search/user', $edit, t('Search')); + $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags')); + $this->assertTrue(in_array('search_page:user_search', $cache_tags)); + } + +} diff --git a/core/modules/search/search.module b/core/modules/search/search.module index e7a725f..bbfed7d 100644 --- a/core/modules/search/search.module +++ b/core/modules/search/search.module @@ -6,6 +6,7 @@ */ use Drupal\Component\Utility\Unicode; +use Symfony\Component\HttpFoundation\Request; /** * Matches all 'N' Unicode character classes (numbers) @@ -65,9 +66,9 @@ /** * Implements hook_help(). */ -function search_help($path, $arg) { - switch ($path) { - case 'admin/help#search': +function search_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.search': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Search module provides the ability to index and search for content by exact keywords, and for users by username or e-mail. For more information, see the online handbook entry for Search module.', array('@search-module' => 'http://drupal.org/documentation/modules/search', '@search' => url('search'))) . '

'; @@ -87,14 +88,9 @@ function search_help($path, $arg) { $output .= '
' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as Porter Stemmer for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as Apache Solr or Sphinx. These and other search-related contributed modules can be downloaded by visiting Drupal.org.', array('@contrib-search' => 'http://drupal.org/project/modules?filters=tid%3A105', '@porterstemmer_url' => 'http://drupal.org/project/porterstemmer', '@solr_url' => 'http://drupal.org/project/apachesolr', '@sphinx_url' => 'http://drupal.org/project/sphinx')) . '
'; $output .= ''; return $output; - case 'admin/config/search/pages': + + case 'search.settings': return '

' . t('The search engine maintains an index of words found in your site\'s content. To build and maintain this index, a correctly configured cron maintenance task is required. Indexing behavior can be adjusted using the settings below.', array('@cron' => url('admin/reports/status'))) . '

'; - case 'search#noresults': - return t('
    -
  • Check if your spelling is correct.
  • -
  • Remove quotes around phrases to search for each word individually. bike shed will often show more results than "bike shed".
  • -
  • Consider loosening your query with OR. bike OR shed will often show more results than bike shed.
  • -
'); } } diff --git a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.info.yml b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.info.yml index 9c17197..0df41aa 100644 --- a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.info.yml +++ b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Search module testing of embedded forms.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/search/tests/modules/search_extra_type/search_extra_type.info.yml b/core/modules/search/tests/modules/search_extra_type/search_extra_type.info.yml index 21a99cf..caea321 100644 --- a/core/modules/search/tests/modules/search_extra_type/search_extra_type.info.yml +++ b/core/modules/search/tests/modules/search_extra_type/search_extra_type.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Search module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/search/tests/modules/search_langcode_test/search_langcode_test.info.yml b/core/modules/search/tests/modules/search_langcode_test/search_langcode_test.info.yml index 53d0a4b..da34c5f 100644 --- a/core/modules/search/tests/modules/search_langcode_test/search_langcode_test.info.yml +++ b/core/modules/search/tests/modules/search_langcode_test/search_langcode_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for search module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/search/tests/modules/search_query_alter/search_query_alter.info.yml b/core/modules/search/tests/modules/search_query_alter/search_query_alter.info.yml index 870ea25..8c6a3d8 100644 --- a/core/modules/search/tests/modules/search_query_alter/search_query_alter.info.yml +++ b/core/modules/search/tests/modules/search_query_alter/search_query_alter.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Search module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php index e11fa5b..3f4d0dd 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntityResolverTest.php @@ -6,6 +6,9 @@ namespace Drupal\serialization\Tests; +/** + * Tests the entity reference resolver. + */ class EntityResolverTest extends NormalizerTestBase { /** diff --git a/core/modules/serialization/serialization.module b/core/modules/serialization/serialization.module index 7aa66dd..5169f93 100644 --- a/core/modules/serialization/serialization.module +++ b/core/modules/serialization/serialization.module @@ -1,11 +1,18 @@ ' . t('About') . ''; $output .= '

' . t('The Serialization module provides a service for serializing and deserializing data to and from formats such as JSON and XML.') . '

'; diff --git a/core/modules/serialization/tests/serialization_test/serialization_test.info.yml b/core/modules/serialization/tests/serialization_test/serialization_test.info.yml index be64412..d6c0130 100644 --- a/core/modules/serialization/tests/serialization_test/serialization_test.info.yml +++ b/core/modules/serialization/tests/serialization_test/serialization_test.info.yml @@ -4,4 +4,3 @@ description: "Support module for serialization tests." package: Testing version: VERSION core: 8.x -hidden: TRUE diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index 870bd8a..3ad21f0 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -5,6 +5,7 @@ * Allows users to manage customizable lists of shortcut links. */ +use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Routing\UrlMatcher; use Drupal\Core\Url; @@ -15,11 +16,9 @@ /** * Implements hook_help(). */ -function shortcut_help($path, $arg) { - $user = \Drupal::currentUser(); - - switch ($path) { - case 'admin/help#shortcut': +function shortcut_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.shortcut': $output = '

' . t('About') . '

'; $output .= '

' . t('The Shortcut module allows users to create sets of shortcut links to commonly-visited pages of the site. Shortcuts are contained within sets. Each user with Select any shortcut set permission can select a shortcut set created by anyone at the site. For more information, see the online handbook entry for Shortcut module.', array('!shortcut' => 'http://drupal.org/documentation/modules/shortcut')) . '

'; $output .= '

' . t('Uses') . '

'; @@ -34,8 +33,10 @@ function shortcut_help($path, $arg) { $output .= ''; return $output; - case 'admin/config/user-interface/shortcut': - case 'admin/config/user-interface/shortcut/%': + case 'shortcut.set_admin': + case 'shortcut.set_add': + case 'shortcut.set_edit': + $user = \Drupal::currentUser(); if ($user->hasPermission('switch shortcut sets')) { $output = '

' . t('Define which shortcut set you are using on the Shortcuts tab of your account page.', array('@shortcut-link' => url("user/{$user->id()}/shortcuts"))) . '

'; return $output; @@ -273,7 +274,7 @@ function shortcut_set_title_exists($title) { */ function shortcut_valid_link($path) { // Do not use URL aliases. - $normal_path = \Drupal::service('path.alias_manager')->getSystemPath($path); + $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path); if ($path != $normal_path) { $path = $normal_path; } @@ -299,12 +300,15 @@ function shortcut_renderable_links($shortcut_set = NULL) { $shortcut_set = shortcut_current_displayed_set(); } + /** @var \Drupal\shortcut\ShortcutInterface[] $shortcuts */ $shortcuts = \Drupal::entityManager()->getStorage('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id())); + $all_cache_tags = array(); foreach ($shortcuts as $shortcut) { $links[] = array( 'title' => $shortcut->label(), 'href' => $shortcut->path->value, ); + $all_cache_tags[] = $shortcut->getCacheTag(); } if (!empty($links)) { @@ -314,6 +318,9 @@ function shortcut_renderable_links($shortcut_set = NULL) { '#attributes' => array( 'class' => array('menu'), ), + '#cache' => array( + 'tags' => NestedArray::mergeDeepArray($all_cache_tags), + ), ); } @@ -341,7 +348,6 @@ function shortcut_preprocess_page(&$variables) { $request = \Drupal::request(); $item = array(); if ($route = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) { - $item['href'] = $request->attributes->get('_system_path'); // @todo What should be done on a 404/403 page? $item['access'] = TRUE; } diff --git a/core/modules/shortcut/shortcut.services.yml b/core/modules/shortcut/shortcut.services.yml index a660594..dc8d30c 100644 --- a/core/modules/shortcut/shortcut.services.yml +++ b/core/modules/shortcut/shortcut.services.yml @@ -1,14 +1,4 @@ services: - access_check.shortcut.link: - class: Drupal\shortcut\Access\LinkAccessCheck - tags: - - { name: access_check, applies_to: _access_shortcut_link } - - access_check.shortcut.shortcut_set_edit: - class: Drupal\shortcut\Access\ShortcutSetEditAccessCheck - tags: - - { name: access_check, applies_to: _access_shortcut_set_edit } - access_check.shortcut.shortcut_set_switch: class: Drupal\shortcut\Access\ShortcutSetSwitchAccessCheck tags: diff --git a/core/modules/shortcut/src/Access/LinkAccessCheck.php b/core/modules/shortcut/src/Access/LinkAccessCheck.php deleted file mode 100644 index b85fb9f..0000000 --- a/core/modules/shortcut/src/Access/LinkAccessCheck.php +++ /dev/null @@ -1,32 +0,0 @@ -attributes->get('menu_link'); - $set_name = str_replace('shortcut-', '', $menu_link['menu_name']); - if ($shortcut_set = shortcut_set_load($set_name)) { - return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY; - } - return static::DENY; - } - -} diff --git a/core/modules/shortcut/src/Access/ShortcutSetEditAccessCheck.php b/core/modules/shortcut/src/Access/ShortcutSetEditAccessCheck.php deleted file mode 100644 index 21bbda8..0000000 --- a/core/modules/shortcut/src/Access/ShortcutSetEditAccessCheck.php +++ /dev/null @@ -1,37 +0,0 @@ -attributes->get('shortcut_set'); - // Sufficiently-privileged users can edit their currently displayed shortcut - // set, but not other sets. Shortcut administrators can edit any set. - if ($account->hasPermission('administer shortcuts')) { - return static::ALLOW; - } - if ($account->hasPermission('customize shortcut links')) { - return !isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set() ? static::ALLOW : static::DENY; - } - return static::DENY; - } - -} diff --git a/core/modules/shortcut/src/Access/ShortcutSetSwitchAccessCheck.php b/core/modules/shortcut/src/Access/ShortcutSetSwitchAccessCheck.php index 6d844ed..51463f8 100644 --- a/core/modules/shortcut/src/Access/ShortcutSetSwitchAccessCheck.php +++ b/core/modules/shortcut/src/Access/ShortcutSetSwitchAccessCheck.php @@ -9,18 +9,25 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; +use Drupal\user\UserInterface; /** - * Provides an access check for shortcut link delete routes. + * Checks access to switch a user's shortcut set. */ class ShortcutSetSwitchAccessCheck implements AccessInterface { /** - * {@inheritdoc} + * Checks access. + * + * @param \Drupal\user\UserInterface $user + * The owner of the shortcut set. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(UserInterface $user, AccountInterface $account) { if ($account->hasPermission('administer shortcuts')) { // Administrators can switch anyone's shortcut set. return static::ALLOW; @@ -31,8 +38,7 @@ class ShortcutSetSwitchAccessCheck implements AccessInterface { return static::DENY; } - $user = $request->attributes->get('account'); - if (!isset($user) || $user->id() == $account->id()) { + if ($user->id() == $account->id()) { // Users with the 'switch shortcut sets' permission can switch their own // shortcuts sets. return static::ALLOW; diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php index 71a33e4..5807c66 100644 --- a/core/modules/shortcut/src/Entity/Shortcut.php +++ b/core/modules/shortcut/src/Entity/Shortcut.php @@ -7,6 +7,7 @@ namespace Drupal\shortcut\Entity; +use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -25,6 +26,7 @@ * "form" = { * "default" = "Drupal\shortcut\ShortcutForm", * "add" = "Drupal\shortcut\ShortcutForm", + * "edit" = "Drupal\shortcut\ShortcutForm", * "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm" * }, * "translation" = "Drupal\content_translation\ContentTranslationHandler" @@ -39,9 +41,12 @@ * "label" = "title" * }, * links = { + * "canonical" = "shortcut.link_edit", * "delete-form" = "shortcut.link_delete", - * "edit-form" = "shortcut.link_edit" - * } + * "edit-form" = "shortcut.link_edit", + * "admin-form" = "shortcut.link_edit" + * }, + * bundle_entity_type = "shortcut_set" * ) */ class Shortcut extends ContentEntityBase implements ShortcutInterface { @@ -132,6 +137,21 @@ class Shortcut extends ContentEntityBase implements ShortcutInterface { /** * {@inheritdoc} */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + // Entity::postSave() calls Entity::invalidateTagsOnSave(), which only + // handles the regular cases. The Shortcut entity has one special case: a + // newly created shortcut is *also* added to a shortcut set, so we must + // invalidate the associated shortcut set's cache tag. + if (!$update) { + Cache::invalidateTags($this->getCacheTag()); + } + } + + /** + * {@inheritdoc} + */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['id'] = FieldDefinition::create('integer') ->setLabel(t('ID')) @@ -199,4 +219,18 @@ class Shortcut extends ContentEntityBase implements ShortcutInterface { return $fields; } + /** + * {@inheritdoc} + */ + public function getCacheTag() { + return $this->shortcut_set->entity->getCacheTag(); + } + + /** + * {@inheritdoc} + */ + public function getListCacheTags() { + return $this->shortcut_set->entity->getListCacheTags(); + } + } diff --git a/core/modules/shortcut/src/Entity/ShortcutSet.php b/core/modules/shortcut/src/Entity/ShortcutSet.php index f9338d9..32c1bb6 100644 --- a/core/modules/shortcut/src/Entity/ShortcutSet.php +++ b/core/modules/shortcut/src/Entity/ShortcutSet.php @@ -60,11 +60,11 @@ class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface { /** * {@inheritdoc} */ - public function postCreate(EntityStorageInterface $storage) { - parent::postCreate($storage); + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); // Generate menu-compatible set name. - if (!$this->getOriginalId()) { + if (!$update && !$this->getOriginalId()) { // Save a new shortcut set with links copied from the user's default set. $default_set = shortcut_default_set(); foreach ($default_set->getShortcuts() as $shortcut) { diff --git a/core/modules/shortcut/src/ShortcutPathValue.php b/core/modules/shortcut/src/ShortcutPathValue.php index 2c8b4e5..3d88d9b 100644 --- a/core/modules/shortcut/src/ShortcutPathValue.php +++ b/core/modules/shortcut/src/ShortcutPathValue.php @@ -40,7 +40,7 @@ class ShortcutPathValue extends TypedData { */ public function setValue($value, $notify = TRUE) { // Normalize the path in case it is an alias. - $value = \Drupal::service('path.alias_manager')->getSystemPath($value); + $value = \Drupal::service('path.alias_manager')->getPathByAlias($value); if (empty($value)) { $value = ''; } diff --git a/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php new file mode 100644 index 0000000..6a9c78a --- /dev/null +++ b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php @@ -0,0 +1,76 @@ +grantPermission('customize shortcut links'); + $user_role->save(); + } + + /** + * {@inheritdoc} + */ + protected function createEntity() { + // Create a "Llama" shortcut. + $shortcut = entity_create('shortcut', array( + 'set' => 'default', + 'title' => t('Llama'), + 'weight' => 0, + 'path' => 'admin', + )); + $shortcut->save(); + + return $shortcut; + } + + /** + * Tests that when creating a shortcut, the shortcut set tag is invalidated. + */ + public function testEntityCreation() { + // Create a cache entry that is tagged with a shortcut set cache tag. + $cache_tags = array('shortcut_set' => 'default'); + \Drupal::cache('render')->set('foo', 'bar', \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT, $cache_tags); + + // Verify a cache hit. + $this->verifyRenderCache('foo', array('shortcut_set:default')); + + // Now create a shortcut entity in that shortcut set. + $this->createEntity(); + + // Verify a cache miss. + $this->assertFalse(\Drupal::cache('render')->get('foo'), 'Creating a new shortcut invalidates the cache tag of the shortcut set.'); + } + +} diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php index 67f40ad..6607b7f 100644 --- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php +++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php @@ -62,7 +62,7 @@ class ShortcutLinksTest extends ShortcutTestBase { $this->assertResponse(200); $saved_set = shortcut_set_load($set->id()); $paths = $this->getShortcutInformation($saved_set, 'path'); - $this->assertTrue(in_array($this->container->get('path.alias_manager')->getSystemPath($test['path']), $paths), 'Shortcut created: ' . $test['path']); + $this->assertTrue(in_array($this->container->get('path.alias_manager')->getPathByAlias($test['path']), $paths), 'Shortcut created: ' . $test['path']); $this->assertLink($title, 0, 'Shortcut link found on the page.'); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/InstallerTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/InstallerTestBase.php index 899c7d8..e735ce3 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/InstallerTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/InstallerTestBase.php @@ -35,7 +35,7 @@ * * @var string */ - protected $profile = 'minimal'; + protected $profile = 'testing'; /** * Additional parameters to use for installer screens. diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index d3c3bf9..19dd044 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -958,7 +958,11 @@ $connection_info = Database::getConnectionInfo('default'); Database::renameConnection('default', 'simpletest_original_default'); foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = $value['prefix']['default'] . $this->databasePrefix; + // Replace the full table prefix definition to ensure that no table + // prefixes of the test runner leak into the test. + $connection_info[$target]['prefix'] = array( + 'default' => $value['prefix']['default'] . $this->databasePrefix, + ); } Database::addConnectionInfo('default', 'default', $connection_info['default']); } @@ -1058,7 +1062,7 @@ // way. // - WebTestBase re-initializes Drupal stream wrappers after installation. // The original stream wrappers are restored after the test run. - // @see TestBase::tearDown() + // @see TestBase::restoreEnvironment() $wrappers = file_get_stream_wrappers(); foreach ($wrappers as $scheme => $info) { stream_wrapper_unregister($scheme); @@ -1162,11 +1166,10 @@ usleep(50000); // Remove all prefixed tables. - // @todo Connection prefix info is not normalized into an array. $original_connection_info = Database::getConnectionInfo('simpletest_original_default'); - $original_prefix = is_array($original_connection_info['default']['prefix']) ? $original_connection_info['default']['prefix']['default'] : $original_connection_info['default']['prefix']; + $original_prefix = $original_connection_info['default']['prefix']['default']; $test_connection_info = Database::getConnectionInfo('default'); - $test_prefix = is_array($test_connection_info['default']['prefix']) ? $test_connection_info['default']['prefix']['default'] : $test_connection_info['default']['prefix']; + $test_prefix = $test_connection_info['default']['prefix']['default']; if ($original_prefix != $test_prefix) { $tables = Database::getConnection()->schema()->findTables($test_prefix . '%'); $prefix_length = strlen($test_prefix); @@ -1187,10 +1190,6 @@ // Restore original database connection. Database::removeConnection('default'); Database::renameConnection('simpletest_original_default', 'default'); - // @see TestBase::changeDatabasePrefix() - global $databases; - $connection_info = Database::getConnectionInfo('default'); - $databases['default']['default'] = $connection_info['default']; // Reset all static variables. // All destructors of statically cached objects have been invoked above; @@ -1222,6 +1221,9 @@ } conf_path(TRUE, TRUE); + // Restore stream wrappers of the test runner. + file_get_stream_wrappers(); + // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); $callbacks = $this->originalShutdownCallbacks; @@ -1479,7 +1481,8 @@ // Set up the ConfigImporter object for testing. $storage_comparer = new StorageComparer( $this->container->get('config.storage.staging'), - $this->container->get('config.storage') + $this->container->get('config.storage'), + $this->container->get('config.manager') ); $this->configImporter = new ConfigImporter( $storage_comparer, diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/MailCaptureTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/MailCaptureTest.php index 8cce823..a05e12f 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/MailCaptureTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/MailCaptureTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests Simpletest email capturing (TestMailCollector) and assertion methods. + */ class MailCaptureTest extends WebTestBase { /** * Implement getInfo(). diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php index 83f1048..e46051d 100755 --- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/SimpleTestTest.php @@ -10,6 +10,9 @@ use Drupal\Core\Database\Driver\pgsql\Select; use Drupal\simpletest\WebTestBase; +/** + * Tests the Simpletest UI test runner and internal browser. + */ class SimpleTestTest extends WebTestBase { /** diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 7cfbc58..340b555 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -15,6 +15,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\Database\ConnectionNotDefinedException; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AnonymousUserSession; @@ -334,14 +335,57 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { $this->assertEqual($status, SAVED_NEW, String::format('Created content type %type.', array('%type' => $type->id()))); - // Reset permissions so that permissions for this content type are - // available. - $this->checkPermissions(array(), TRUE); - return $type; } /** + * Builds the renderable view of an entity. + * + * Entities postpone the composition of their renderable arrays to #pre_render + * functions in order to maximize cache efficacy. This means that the full + * rendable array for an entity is constructed in drupal_render(). Some tests + * require the complete renderable array for an entity outside of the + * drupal_render process in order to verify the presence of specific values. + * This method isolates the steps in the render process that produce an + * entity's renderable array. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to prepare a renderable array for. + * @param string $view_mode + * (optional) The view mode that should be used to build the entity. + * @param null $langcode + * (optional) For which language the entity should be prepared, defaults to + * the current content language. + * @param bool $reset + * (optional) Whether to clear the cache for this entity. + * @return array + * + * @see drupal_render() + */ + protected function drupalBuildEntityView(EntityInterface $entity, $view_mode = 'full', $langcode = NULL, $reset = FALSE) { + $render_controller = $this->container->get('entity.manager')->getViewBuilder($entity->getEntityTypeId()); + if ($reset) { + $render_controller->resetCache(array($entity->id())); + } + $elements = $render_controller->view($entity, $view_mode, $langcode); + // If the default values for this element have not been loaded yet, populate + // them. + if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) { + $elements += element_info($elements['#type']); + } + + // Make any final changes to the element before it is rendered. This means + // that the $element or the children can be altered or corrected before the + // element is rendered into the final text. + if (isset($elements['#pre_render'])) { + foreach ($elements['#pre_render'] as $callable) { + $elements = call_user_func($callable, $elements); + } + } + return $elements; + } + + /** * Creates a block instance based on default settings. * * @param string $plugin_id @@ -615,23 +659,16 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { } /** - * Check to make sure that the array of permissions are valid. + * Checks whether a given list of permission names is valid. * - * @param $permissions - * Permissions to check. - * @param $reset - * Reset cached available permissions. + * @param array $permissions + * The permission names to check. * - * @return - * TRUE or FALSE depending on whether the permissions are valid. + * @return bool + * TRUE if the permissions are valid, FALSE otherwise. */ - protected function checkPermissions(array $permissions, $reset = FALSE) { - $available = &drupal_static(__FUNCTION__); - - if (!isset($available) || $reset) { - $available = array_keys(\Drupal::moduleHandler()->invokeAll('permission')); - } - + protected function checkPermissions(array $permissions) { + $available = array_keys(\Drupal::moduleHandler()->invokeAll('permission')); $valid = TRUE; foreach ($permissions as $permission) { if (!in_array($permission, $available)) { @@ -850,6 +887,18 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { ->set('interface.default', 'test_mail_collector') ->save(); + // By default, verbosely display all errors and disable all production + // environment optimizations for all tests to avoid needless overhead and + // ensure a sane default experience for test authors. + // @see https://drupal.org/node/2259167 + \Drupal::config('system.logging') + ->set('error_level', 'verbose') + ->save(); + \Drupal::config('system.performance') + ->set('css.preprocess', FALSE) + ->set('js.preprocess', FALSE) + ->save(); + // Restore the original Simpletest batch. $batch = &batch_get(); $batch = $this->originalBatch; @@ -899,6 +948,7 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { protected function installParameters() { $connection_info = Database::getConnectionInfo(); $driver = $connection_info['default']['driver']; + $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default']; unset($connection_info['default']['driver']); unset($connection_info['default']['namespace']); unset($connection_info['default']['pdo']); @@ -1084,7 +1134,6 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { // Reset static variables and reload permissions. $this->refreshVariables(); - $this->checkPermissions(array(), TRUE); } /** @@ -3092,7 +3141,7 @@ function drupalGetNodeByTitle($title, $reset = FALSE) { // Input element with correct value. $found = TRUE; } - elseif (isset($field->option)) { + elseif (isset($field->option) || isset($field->optgroup)) { // Select element found. $selected = $this->getSelectedItem($field); if ($selected === FALSE) { diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index dbf1dff..914228e 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -5,6 +5,7 @@ use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Render\Element; use Drupal\simpletest\TestBase; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Process\PhpExecutableFinder; /** @@ -15,9 +16,9 @@ /** * Implements hook_help(). */ -function simpletest_help($path, $arg) { - switch ($path) { - case 'admin/help#simpletest': +function simpletest_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.simpletest': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Testing module provides a framework for running automated tests. It can be used to verify a working state of Drupal before and after any code changes, or as a means for developers to write and execute tests for their modules. For more information, see the online documentation for the Testing module.', array('!simpletest' => 'https://drupal.org/documentation/modules/simpletest')) . '

'; @@ -29,7 +30,7 @@ function simpletest_help($path, $arg) { $output .= ''; return $output; - case 'admin/config/development/testing': + case 'simpletest.test_form': $output = t('Select the test(s) or test group(s) you would like to run, and click Run tests.'); return $output; } diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitAutoloaderTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitAutoloaderTest.php index 8e2b7f5..bcb940e 100644 --- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitAutoloaderTest.php +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitAutoloaderTest.php @@ -11,6 +11,9 @@ /** * Test PHPUnit autoloader works correctly. + * + * @group Drupal + * @group simpletest */ class PhpUnitAutoloaderTest extends UnitTestCase { diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php index afe2b52..fdd38fa 100644 --- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/PhpUnitErrorTest.php @@ -6,6 +6,9 @@ /** * Test PHPUnit errors are getting converted to Simpletest errors. + * + * @group Drupal + * @group simpletest */ class PhpUnitErrorTest extends UnitTestCase { @@ -20,6 +23,8 @@ class PhpUnitErrorTest extends UnitTestCase { /** * Test errors reported. + * + * @covers ::simpletest_phpunit_xml_to_rows */ public function testPhpUnitXmlParsing() { // This test class could be either in tests/Drupal/simpletest/Tests/, or in diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php index 3988603..79b0345 100644 --- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/TestBaseTest.php @@ -11,6 +11,10 @@ /** * Tests helper methods provided by the abstract TestBase class. + * + * @coversDefaultClass \Drupal\simpletest\TestBase + * @group Drupal + * @group simpletest */ class TestBaseTest extends UnitTestCase { @@ -64,6 +68,7 @@ class TestBaseTest extends UnitTestCase { * @see \Drupal\simpletest\TestBase::randomStringValidate(). * * @dataProvider randomStringValidateProvider + * @covers ::randomStringValidate */ public function testRandomStringValidate($string, $expected) { $actual = $this->stub->randomStringValidate($string); diff --git a/core/modules/simpletest/tests/Drupal/simpletest/Tests/WebTestBaseTest.php b/core/modules/simpletest/tests/Drupal/simpletest/Tests/WebTestBaseTest.php index bbfdadd..8bce846 100644 --- a/core/modules/simpletest/tests/Drupal/simpletest/Tests/WebTestBaseTest.php +++ b/core/modules/simpletest/tests/Drupal/simpletest/Tests/WebTestBaseTest.php @@ -13,7 +13,8 @@ * Tests helper methods provided by the abstract WebTestBase class. * * @group Drupal - * @group Simpletest + * @group simpletest + * @coversDefaultClass \Drupal\simpletest\WebTestBase */ class WebTestBaseTest extends UnitTestCase { @@ -60,6 +61,7 @@ class WebTestBaseTest extends UnitTestCase { * @see \Drupal\simpletest\WebTestBase::assertFieldByName() * * @dataProvider providerAssertFieldByName + * @covers ::assertFieldByName */ public function testAssertFieldByName($filename, $name, $value, $expected) { $content = file_get_contents(__DIR__ . '/Fixtures/' . $filename . '.html'); diff --git a/core/modules/simpletest/tests/modules/phpunit_test/phpunit_test.info.yml b/core/modules/simpletest/tests/modules/phpunit_test/phpunit_test.info.yml index 0fd89ae..e0a4c6e 100644 --- a/core/modules/simpletest/tests/modules/phpunit_test/phpunit_test.info.yml +++ b/core/modules/simpletest/tests/modules/phpunit_test/phpunit_test.info.yml @@ -4,4 +4,3 @@ description: 'Provides dummy classes for use by SimpleTest tests.' package: Testing version: VERSION core: 8.x -hidden: TRUE diff --git a/core/modules/statistics/lib/Drupal/statistics/StatisticsSettingsForm.php b/core/modules/statistics/lib/Drupal/statistics/StatisticsSettingsForm.php index 4b7f362..c002590 100644 --- a/core/modules/statistics/lib/Drupal/statistics/StatisticsSettingsForm.php +++ b/core/modules/statistics/lib/Drupal/statistics/StatisticsSettingsForm.php @@ -8,7 +8,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\ConfigFormBase; -use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\ConfigFactoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -26,12 +26,12 @@ class StatisticsSettingsForm extends ConfigFormBase { /** * Constructs a \Drupal\user\StatisticsSettingsForm object. * - * @param \Drupal\Core\Config\ConfigFactory $config_factory + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. */ - public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) { parent::__construct($config_factory); $this->moduleHandler = $module_handler; diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index 8877181..e5828c3 100644 --- a/core/modules/statistics/statistics.module +++ b/core/modules/statistics/statistics.module @@ -8,13 +8,14 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\node\NodeInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function statistics_help($path, $arg) { - switch ($path) { - case 'admin/help#statistics': +function statistics_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.statistics': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Statistics module shows you how often content is viewed. This is useful in determining which pages of your site are most popular. For more information, see the online documentation for the Statistics module.', array('!statistics_do' => 'https://drupal.org/documentation/modules/statistics/')) . '

'; @@ -26,7 +27,8 @@ function statistics_help($path, $arg) { $output .= '
' . t('The Statistics module includes a counter for each page that increases whenever the page is viewed. To use the counter, enable Count content views on the statistics settings page, and set the necessary permissions (View content hits) so that the counter is visible to the users.', array('!statistics-settings' => \Drupal::url('statistics.settings'), '!permissions' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-statistics')))) . '
'; $output .= ''; return $output; - case 'admin/config/system/statistics': + + case 'statistics.settings': return '

' . t('Settings for the statistical information that Drupal will keep about the site.') . '

'; } } @@ -48,11 +50,11 @@ function statistics_permission() { /** * Implements hook_node_view(). */ -function statistics_node_view(EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) { +function statistics_node_view(array &$build, EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) { if (!$node->isNew() && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview)) { - $node->content['statistics_content_counter']['#attached']['library'][] = 'statistics/drupal.statistics'; + $build['statistics_content_counter']['#attached']['library'][] = 'statistics/drupal.statistics'; $settings = array('data' => array('nid' => $node->id()), 'url' => url(drupal_get_path('module', 'statistics') . '/statistics.php')); - $node->content['statistics_content_counter']['#attached']['js'][] = array( + $build['statistics_content_counter']['#attached']['js'][] = array( 'data' => array('statistics' => $settings), 'type' => 'setting', ); @@ -169,7 +171,7 @@ function statistics_get($nid) { * A string as a link, truncated to the width, linked to the given $path. */ function _statistics_link($path, $width = 35) { - $title = \Drupal::service('path.alias_manager')->getPathAlias($path); + $title = \Drupal::service('path.alias_manager')->getAliasByPath($path); $title = truncate_utf8($title, $width, FALSE, TRUE); return l($title, $path); } diff --git a/core/modules/statistics/tests/modules/statistics_test_views/statistics_test_views.info.yml b/core/modules/statistics/tests/modules/statistics_test_views/statistics_test_views.info.yml index aff5a56..df9a43a 100644 --- a/core/modules/statistics/tests/modules/statistics_test_views/statistics_test_views.info.yml +++ b/core/modules/statistics/tests/modules/statistics_test_views/statistics_test_views.info.yml @@ -4,7 +4,6 @@ description: 'Provides default views for views statistics tests.' package: Testing version: VERSION core: 8.x -hidden: TRUE dependencies: - statistics - views diff --git a/core/modules/syslog/syslog.module b/core/modules/syslog/syslog.module index a93ee93..ba54118 100644 --- a/core/modules/syslog/syslog.module +++ b/core/modules/syslog/syslog.module @@ -5,12 +5,14 @@ * Redirects logging messages to syslog. */ +use Symfony\Component\HttpFoundation\Request; + /** * Implements hook_help(). */ -function syslog_help($path, $arg) { - switch ($path) { - case 'admin/help#syslog': +function syslog_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.syslog': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t("The Syslog module logs events by sending messages to the logging facility of your web server's operating system. Syslog is an operating system administrative logging tool that provides valuable information for use in system management and security auditing. Most suited to medium and large sites, Syslog provides filtering tools that allow messages to be routed by type and severity. For more information, see the online documentation for the Syslog module, as well as PHP's documentation pages for the openlog and syslog functions.", array('!syslog' => 'https://drupal.org/documentation/modules/syslog', '!php_openlog' => 'http://www.php.net/manual/function.openlog.php', '!php_syslog' => 'http://www.php.net/manual/function.syslog.php')) . '

'; diff --git a/core/modules/system/config/install/system.logging.yml b/core/modules/system/config/install/system.logging.yml index 3ecc76c..d836a56 100644 --- a/core/modules/system/config/install/system.logging.yml +++ b/core/modules/system/config/install/system.logging.yml @@ -1 +1 @@ -error_level: all +error_level: hide diff --git a/core/modules/system/config/install/system.performance.yml b/core/modules/system/config/install/system.performance.yml index 29b434b..1e75b4b 100644 --- a/core/modules/system/config/install/system.performance.yml +++ b/core/modules/system/config/install/system.performance.yml @@ -3,7 +3,7 @@ cache: use_internal: false max_age: 0 css: - preprocess: false + preprocess: true gzip: true fast_404: enabled: true @@ -11,7 +11,7 @@ fast_404: exclude_paths: '/\/(?:styles|imagecache)\//' html: '404 Not Found

Not Found

The requested URL "@path" was not found on this server.

' js: - preprocess: false + preprocess: true gzip: true response: gzip: false diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 2d9d11d..690a3ec 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -330,7 +330,7 @@ system.action.*: type: string label: 'Plugin' configuration: - type: action.configuration.[plugin] + type: action.configuration.[%parent.plugin] dependencies: type: config_dependencies label: 'Dependencies' diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php index c8a5ede..f2cc90e 100644 --- a/core/modules/system/core.api.php +++ b/core/modules/system/core.api.php @@ -68,13 +68,32 @@ * @{ * Information about the classes and interfaces that make up the Block API. * - * @todo write this - * - * Additional documentation paragraphs need to be written, and classes and - * interfaces need to be added to this topic. - * - * See https://drupal.org/node/2168137 - * @} + * Blocks are a combination of a configuration entity and a plugin. The + * configuration entity stores placement information (theme, region, weight) and + * any other configuration that is specific to the block. The block plugin does + * the work of rendering the block's content for display. + * + * To define a block in a module you need to: + * - Define a Block plugin by creating a new class that implements the + * \Drupal\block\BlockPluginInterface. For more information about how block + * plugins are discovered see the @link plugin_api Plugin API topic @endlink. + * - Usually you will want to extend the \Drupal\block\BlockBase class, which + * provides a common configuration form and utility methods for getting and + * setting configuration in the block configuration entity. + * - Block plugins use the annotations defined by + * \Drupal\block\Annotation\Block. See the + * @link annotation Annotations topic @endlink for more information about + * annotations. + * + * Further information and examples: + * - \Drupal\system\Plugin\Block\SystemPoweredByBlock provides a simple example + * of defining a block. + * - \Drupal\book\Plugin\Block\BookNavigationBlock is an example of a block with + * a custom configuration form. + * - For a more in-depth discussion of the Block API see + * https://drupal.org/developing/api/8/block_api + * - The examples project also provides a Block example in + * https://drupal.org/project/examples. */ /** diff --git a/core/modules/system/css/system.theme.css b/core/modules/system/css/system.theme.css index b8678a3..c8908b5 100644 --- a/core/modules/system/css/system.theme.css +++ b/core/modules/system/css/system.theme.css @@ -79,11 +79,20 @@ h4.label { .form-type-checkbox .description { margin-left: 2.4em; } -.marker, -.form-required { +.marker { color: #e00; } -abbr.form-required, abbr.tabledrag-changed, abbr.ajax-changed { + +.form-required:after { + color: #e00; + /* Use a Unicode symbol to prevent screen-readers from announcing the text. */ + content: " \204E "; + line-height: 1; + vertical-align: super; +} + +abbr.tabledrag-changed, +abbr.ajax-changed { border-bottom: none; } .form-item input.error, diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index 282e4e3..50b9f6a 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -436,6 +436,8 @@ function hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query /** * Act on entities being assembled before rendering. * + * @param &$build + * A renderable array representing the entity content. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity object. * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display @@ -446,8 +448,8 @@ function hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query * @param $langcode * The language code used for rendering. * - * The module may add elements to $entity->content prior to rendering. The - * structure of $entity->content is a renderable array as expected by + * The module may add elements to $build prior to rendering. The + * structure of $build is a renderable array as expected by * drupal_render(). * * @see hook_entity_view_alter() @@ -455,12 +457,12 @@ function hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query * @see hook_node_view() * @see hook_user_view() */ -function hook_entity_view(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { +function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { // Only do the extra work if the component is configured to be displayed. // This assumes a 'mymodule_addition' extra field has been defined for the // entity bundle in hook_entity_extra_field_info(). if ($display->getComponent('mymodule_addition')) { - $entity->content['mymodule_addition'] = array( + $build['mymodule_addition'] = array( '#markup' => mymodule_addition($entity), '#theme' => 'mymodule_my_additional_field', ); @@ -480,7 +482,7 @@ function hook_entity_view(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\C * the particular entity type template, if there is one (e.g., node.html.twig). * See drupal_render() and _theme() for details. * - * @param $build + * @param array &$build * A renderable array representing the entity content. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity object being rendered. @@ -494,7 +496,7 @@ function hook_entity_view(\Drupal\Core\Entity\EntityInterface $entity, \Drupal\C * @see hook_taxonomy_term_view_alter() * @see hook_user_view_alter() */ -function hook_entity_view_alter(&$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { +function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { // Change its weight. $build['an_additional_field']['#weight'] = -10; @@ -561,6 +563,54 @@ function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInter } /** + * Alters entity renderable values before cache checking in drupal_render(). + * + * Invoked for a specific entity type. + * + * The values in the #cache key of the renderable array are used to determine if + * a cache entry exists for the entity's rendered output. Ideally only values + * that pertain to caching should be altered in this hook. + * + * @param array &$build + * A renderable array containing the entity's caching and view mode values. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity that is being viewed. + * @param string $view_mode + * The view_mode that is to be used to display the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @see drupal_render() + * @see \Drupal\Core\Entity\EntityViewBuilder + */ +function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode, $langcode) { + +} + +/** + * Alters entity renderable values before cache checking in drupal_render(). + * + * The values in the #cache key of the renderable array are used to determine if + * a cache entry exists for the entity's rendered output. Ideally only values + * that pertain to caching should be altered in this hook. + * + * @param array &$build + * A renderable array containing the entity's caching and view mode values. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity that is being viewed. + * @param string $view_mode + * The view_mode that is to be used to display the entity. + * @param string $langcode + * The code of the language $entity is accessed in. + * + * @see drupal_render() + * @see \Drupal\Core\Entity\EntityViewBuilder + */ +function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode, $langcode) { + +} + +/** * Alters the settings used for displaying an entity. * * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display diff --git a/core/modules/system/lib/Drupal/system/Access/CronAccessCheck.php b/core/modules/system/lib/Drupal/system/Access/CronAccessCheck.php index ef094fd..7aa9a9d 100644 --- a/core/modules/system/lib/Drupal/system/Access/CronAccessCheck.php +++ b/core/modules/system/lib/Drupal/system/Access/CronAccessCheck.php @@ -8,9 +8,6 @@ namespace Drupal\system\Access; use Drupal\Core\Routing\Access\AccessInterface; -use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Access check for cron routes. @@ -18,10 +15,15 @@ class CronAccessCheck implements AccessInterface { /** - * Implements AccessCheckInterface::access(). + * Checks access. + * + * @param string $key + * The cron key. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { - $key = $request->attributes->get('key'); + public function access($key) { if ($key != \Drupal::state()->get('system.cron_key')) { watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE); return static::KILL; diff --git a/core/modules/system/lib/Drupal/system/Controller/SystemController.php b/core/modules/system/lib/Drupal/system/Controller/SystemController.php index 64049aa..6227296 100644 --- a/core/modules/system/lib/Drupal/system/Controller/SystemController.php +++ b/core/modules/system/lib/Drupal/system/Controller/SystemController.php @@ -302,10 +302,10 @@ class SystemController extends ControllerBase { // There are two possible theme groups. $theme_group_titles = array( - 'enabled' => $this->translationManager()->formatPlural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), + 'enabled' => $this->formatPlural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), ); if (!empty($theme_groups['disabled'])) { - $theme_group_titles['disabled'] = $this->translationManager()->formatPlural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'); + $theme_group_titles['disabled'] = $this->formatPlural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'); } uasort($theme_groups['enabled'], 'system_sort_themes'); diff --git a/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php b/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php index ef70321..eef05e1 100644 --- a/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php +++ b/core/modules/system/lib/Drupal/system/EventSubscriber/AdminRouteSubscriber.php @@ -19,7 +19,7 @@ class AdminRouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ - protected function alterRoutes(RouteCollection $collection, $provider) { + protected function alterRoutes(RouteCollection $collection) { foreach ($collection->all() as $route) { if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route')) { $route->setOption('_admin_route', TRUE); diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php index 1966bb5..d061495 100644 --- a/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ModulesListForm.php @@ -207,14 +207,10 @@ class ModulesListForm extends FormBase { $row['description']['#markup'] = $this->t($module->info['description']); $row['version']['#markup'] = $module->info['version']; - // Add links for each module. - // Used when checking if a module implements a help page. - $help = $this->moduleHandler->moduleExists('help') ? drupal_help_arg() : FALSE; - // Generate link for module's help page, if there is one. $row['links']['help'] = array(); - if ($help && $module->status && in_array($module->getName(), $this->moduleHandler->getImplementations('help'))) { - if ($this->moduleHandler->invoke($module->getName(), 'help', array('admin/help#' . $module->getName(), $help))) { + if ($this->moduleHandler->moduleExists('help') && $module->status && in_array($module->getName(), $this->moduleHandler->getImplementations('help'))) { + if ($this->moduleHandler->invoke($module->getName(), 'help', array('help.page.' . $module->getName(), $this->getRequest()))) { $row['links']['help'] = array( '#type' => 'link', '#title' => $this->t('Help'), diff --git a/core/modules/system/lib/Drupal/system/Form/PerformanceForm.php b/core/modules/system/lib/Drupal/system/Form/PerformanceForm.php index fbc60bc..7ab0ec1 100644 --- a/core/modules/system/lib/Drupal/system/Form/PerformanceForm.php +++ b/core/modules/system/lib/Drupal/system/Form/PerformanceForm.php @@ -84,7 +84,6 @@ class PerformanceForm extends ConfigFormBase { $period = array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400); $period = array_map('format_interval', array_combine($period, $period)); $period[0] = '<' . t('no caching') . '>'; - $period[\Drupal\Core\Cache\Cache::PERMANENT] = t('Forever'); $form['caching']['page_cache_maximum_age'] = array( '#type' => 'select', '#title' => t('Page cache maximum age'), diff --git a/core/modules/system/lib/Drupal/system/Form/SiteInformationForm.php b/core/modules/system/lib/Drupal/system/Form/SiteInformationForm.php index b5d3f5e..01e0d14 100644 --- a/core/modules/system/lib/Drupal/system/Form/SiteInformationForm.php +++ b/core/modules/system/lib/Drupal/system/Form/SiteInformationForm.php @@ -94,7 +94,7 @@ class SiteInformationForm extends ConfigFormBase { '#title' => t('Front page'), '#open' => TRUE, ); - $front_page = $site_config->get('page.front') != 'user' ? $this->aliasManager->getPathAlias($site_config->get('page.front')) : ''; + $front_page = $site_config->get('page.front') != 'user' ? $this->aliasManager->getAliasByPath($site_config->get('page.front')) : ''; $form['front_page']['site_frontpage'] = array( '#type' => 'textfield', '#title' => t('Default front page'), @@ -139,7 +139,7 @@ class SiteInformationForm extends ConfigFormBase { } else { // Get the normal path of the front page. - form_set_value($form['front_page']['site_frontpage'], $this->aliasManager->getSystemPath($form_state['values']['site_frontpage']), $form_state); + form_set_value($form['front_page']['site_frontpage'], $this->aliasManager->getPathByAlias($form_state['values']['site_frontpage']), $form_state); } // Validate front page path. if (!drupal_valid_path($form_state['values']['site_frontpage'])) { @@ -147,10 +147,10 @@ class SiteInformationForm extends ConfigFormBase { } // Get the normal paths of both error pages. if (!empty($form_state['values']['site_403'])) { - form_set_value($form['error_page']['site_403'], $this->aliasManager->getSystemPath($form_state['values']['site_403']), $form_state); + form_set_value($form['error_page']['site_403'], $this->aliasManager->getPathByAlias($form_state['values']['site_403']), $form_state); } if (!empty($form_state['values']['site_404'])) { - form_set_value($form['error_page']['site_404'], $this->aliasManager->getSystemPath($form_state['values']['site_404']), $form_state); + form_set_value($form['error_page']['site_404'], $this->aliasManager->getPathByAlias($form_state['values']['site_404']), $form_state); } // Validate 403 error path. if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) { diff --git a/core/modules/system/lib/Drupal/system/PathBasedBreadcrumbBuilder.php b/core/modules/system/lib/Drupal/system/PathBasedBreadcrumbBuilder.php index ccf3ca3..79d7788 100644 --- a/core/modules/system/lib/Drupal/system/PathBasedBreadcrumbBuilder.php +++ b/core/modules/system/lib/Drupal/system/PathBasedBreadcrumbBuilder.php @@ -150,7 +150,7 @@ class PathBasedBreadcrumbBuilder extends BreadcrumbBuilderBase { } // @todo Replace with a #type => link render element so that the alter // hook can work with the actual data. - $links[] = $this->l($title, $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME), $route_request->attributes->get('_raw_variables')->all()); + $links[] = $this->l($title, $route_request->attributes->get(RouteObjectInterface::ROUTE_NAME), $route_request->attributes->get('_raw_variables')->all(), array('html' => TRUE)); } } diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php index 7105a30..d15cfc6 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php +++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php @@ -11,6 +11,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -89,23 +90,14 @@ class SystemHelpBlock extends BlockBase implements ContainerFactoryPluginInterfa * The current request. */ protected function getActiveHelp(Request $request) { - $output = ''; - $router_path = $request->attributes->get('_system_path'); // Do not show on a 403 or 404 page. if ($request->attributes->has('exception')) { return ''; } - $arg = drupal_help_arg(explode('/', $router_path)); - - foreach ($this->moduleHandler->getImplementations('help') as $module) { - $function = $module . '_help'; - // Lookup help for this path. - if ($help = $function($router_path, $arg)) { - $output .= $help . "\n"; - } - } - return $output; + $route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME); + $help = $this->moduleHandler->invokeAll('help', array($route_name, $request)); + return $help ? implode("\n", $help) : ''; } /** diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php index e067014..fccff2b 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php @@ -320,17 +320,26 @@ class GDToolkit extends ImageToolkitBase { $res = imagecreatetruecolor($width, $height); if ($type == IMAGETYPE_GIF) { - // Grab transparent color index from image resource. + // Find out if a transparent color is set, will return -1 if no + // transparent color has been defined in the image. $transparent = imagecolortransparent($this->getResource()); - if ($transparent >= 0) { - // The original must have a transparent color, allocate to the new image. - $transparent_color = imagecolorsforindex($this->getResource(), $transparent); - $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); - - // Flood with our new transparent color. - imagefill($res, 0, 0, $transparent); - imagecolortransparent($res, $transparent); + // Find out the number of colors in the image palette. It will be 0 for + // truecolor images. + $palette_size = imagecolorstotal($this->getResource()); + if ($palette_size == 0 || $transparent < $palette_size) { + // Set the transparent color in the new resource, either if it is a + // truecolor image or if the transparent color is part of the palette. + // Since the index of the transparency color is a property of the + // image rather than of the palette, it is possible that an image + // could be created with this index set outside the palette size. + $transparent_color = imagecolorsforindex($this->getResource(), $transparent); + $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + + // Flood with our new transparent color. + imagefill($res, 0, 0, $transparent); + imagecolortransparent($res, $transparent); + } } } elseif ($type == IMAGETYPE_PNG) { diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkForm.php b/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkForm.php index 172162b..fc9898d 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkForm.php +++ b/core/modules/system/lib/Drupal/system/Plugin/views/field/BulkForm.php @@ -268,7 +268,7 @@ class BulkForm extends FieldPluginBase { $count = count(array_filter($form_state['values'][$this->options['id']])); if ($count) { - drupal_set_message($this->translationManager()->formatPlural($count, '%action was applied to @count item.', '%action was applied to @count items.', array( + drupal_set_message($this->formatPlural($count, '%action was applied to @count item.', '%action was applied to @count items.', array( '%action' => $action->label(), ))); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php index 5261187..e0952b8 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php @@ -143,12 +143,9 @@ class DialogTest extends AjaxTestBase { $ajax_result = $this->drupalPostAjaxForm('ajax-test/dialog', array(), 'button1'); // Check that CSS and JavaScript are "added" to the page dynamically. - $dialog_css_exists = strpos($ajax_result[1]['data'], 'jquery.ui.dialog.css') !== FALSE; - $this->assertTrue($dialog_css_exists, 'jQuery UI dialog CSS added to the page.'); - $dialog_js_exists = strpos($ajax_result[2]['data'], 'jquery.ui.dialog.js') !== FALSE; - $this->assertTrue($dialog_js_exists, 'jQuery UI dialog JS added to the page.'); - $dialog_js_exists = strpos($ajax_result[2]['data'], 'dialog.ajax.js') !== FALSE; - $this->assertTrue($dialog_js_exists, 'Drupal dialog JS added to the page.'); + $this->assertTrue(in_array('jquery.ui.dialog.css', array_keys($ajax_result[0]['settings']['ajaxPageState']['css'])), 'jQuery UI dialog CSS added to the page.'); + $this->assertTrue(in_array('core/assets/vendor/jquery.ui/ui/jquery.ui.dialog.js', array_keys($ajax_result[0]['settings']['ajaxPageState']['js'])), 'jQuery UI dialog JS added to the page.'); + $this->assertTrue(in_array('core/misc/dialog.ajax.js', array_keys($ajax_result[0]['settings']['ajaxPageState']['js'])), 'Drupal dialog JS added to the page.'); // Check that the response matches the expected value. $this->assertEqual($modal_expected_response, $ajax_result[3], 'POST request modal dialog JSON response matches.'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php index 7f6b6a0..fdf16b9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php @@ -196,15 +196,31 @@ function testPageCache() { $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.'); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.'); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.'); + } + + /** + * Tests the omit_vary_cookie setting. + */ + public function testPageCacheWithoutVaryCookie() { + $config = \Drupal::config('system.performance'); + $config->set('cache.page.use_internal', 1); + $config->set('cache.page.max_age', 300); + $config->save(); - // Check the omit_vary_cookie setting. - $this->drupalLogout(); $settings['settings']['omit_vary_cookie'] = (object) array( 'value' => TRUE, 'required' => TRUE, ); $this->writeSettings($settings); - $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); + + // Fill the cache. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); + $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.'); + + // Check cache. + $this->drupalGet(''); + $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.'); $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/ApcuBackendUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/ApcuBackendUnitTest.php new file mode 100644 index 0000000..2aae663 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/ApcuBackendUnitTest.php @@ -0,0 +1,52 @@ + 'APCu cache backend', + 'description' => 'Tests the APCu cache backend.', + 'group' => 'Cache', + ); + } + + protected function checkRequirements() { + $requirements = parent::checkRequirements(); + if (!extension_loaded('apc')) { + $requirements[] = 'APC extension not found.'; + } + else { + if (version_compare(phpversion('apc'), '3.1.1', '<')) { + $requirements[] = 'APC extension must be newer than 3.1.1 for APCIterator support.'; + } + if (PHP_SAPI === 'cli' && !ini_get('apc.enable_cli')) { + $requirements[] = 'apc.enable_cli must be enabled to run this test.'; + } + } + return $requirements; + } + + protected function createCacheBackend($bin) { + $this->backend = new ApcuBackend($bin, $this->databasePrefix); + return $this->backend; + } + + public function tearDown() { + $this->backend->removeBin(); + parent::tearDown(); + $this->backend = NULL; + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php index b390429..0a9108c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/GenericCacheBackendUnitTestBase.php @@ -161,6 +161,32 @@ $this->assertFalse($cached->valid, 'Item is marked as valid.'); $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); $this->assertEqual($cached->expire, REQUEST_TIME - 3, 'Expire time is correct.'); + + $this->assertIdentical(FALSE, $backend->get('test4'), "Backend does not contain data for cache id test4."); + $with_eof = array('foo' => "\nEOF\ndata"); + $backend->set('test4', $with_eof); + $cached = $backend->get('test4'); + $this->assert(is_object($cached), "Backend returned an object for cache id test4."); + $this->assertIdentical($with_eof, $cached->data); + $this->assertTrue($cached->valid, 'Item is marked as valid.'); + $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.'); + + $this->assertIdentical(FALSE, $backend->get('test5'), "Backend does not contain data for cache id test5."); + $with_eof_and_semicolon = array('foo' => "\nEOF;\ndata"); + $backend->set('test5', $with_eof_and_semicolon); + $cached = $backend->get('test5'); + $this->assert(is_object($cached), "Backend returned an object for cache id test5."); + $this->assertIdentical($with_eof_and_semicolon, $cached->data); + $this->assertTrue($cached->valid, 'Item is marked as valid.'); + $this->assertEqual($cached->created, REQUEST_TIME, 'Created time is correct.'); + $this->assertEqual($cached->expire, Cache::PERMANENT, 'Expire time is correct.'); + + $with_variable = array('foo' => '$bar'); + $backend->set('test6', $with_variable); + $cached = $backend->get('test6'); + $this->assert(is_object($cached), "Backend returned an object for cache id test6."); + $this->assertIdentical($with_variable, $cached->data); } /** @@ -335,21 +361,6 @@ } /** - * Tests Drupal\Core\Cache\CacheBackendInterface::isEmpty(). - */ - public function testIsEmpty() { - $backend = $this->getCacheBackend(); - - $this->assertTrue($backend->isEmpty(), "Backend is empty."); - - $backend->set('pony', "Shetland"); - $this->assertFalse($backend->isEmpty(), "Backend is not empty."); - - $backend->delete('pony'); - $this->assertTrue($backend->isEmpty(), "Backend is empty."); - } - - /** * Test Drupal\Core\Cache\CacheBackendInterface::delete() and * Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). */ @@ -456,17 +467,18 @@ function testDeleteTags() { */ public function testDeleteAll() { $backend = $this->getCacheBackend(); + $unrelated = $this->getCacheBackend('bootstrap'); // Set both expiring and permanent keys. $backend->set('test1', 1, Cache::PERMANENT); $backend->set('test2', 3, time() + 1000); + $unrelated->set('test3', 4, Cache::PERMANENT); $backend->deleteAll(); - $this->assertTrue($backend->isEmpty(), "Backend is empty after deleteAll()."); - $this->assertFalse($backend->get('test1'), 'First key has been deleted.'); $this->assertFalse($backend->get('test2'), 'Second key has been deleted.'); + $this->assertTrue($unrelated->get('test3'), 'Item in other bin is preserved.'); } /** @@ -563,15 +575,18 @@ function testInvalidateTags() { */ public function testInvalidateAll() { $backend = $this->getCacheBackend(); + $unrelated = $this->getCacheBackend('bootstrap'); // Set both expiring and permanent keys. $backend->set('test1', 1, Cache::PERMANENT); $backend->set('test2', 3, time() + 1000); + $unrelated->set('test3', 4, Cache::PERMANENT); $backend->invalidateAll(); $this->assertFalse($backend->get('test1'), 'First key has been invalidated.'); $this->assertFalse($backend->get('test2'), 'Second key has been invalidated.'); + $this->assertTrue($unrelated->get('test3'), 'Item in other bin is preserved.'); $this->assertTrue($backend->get('test1', TRUE), 'First key has not been deleted.'); $this->assertTrue($backend->get('test2', TRUE), 'Second key has not been deleted.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/AddFeedTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/AddFeedTest.php index b871710..2f4689b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/AddFeedTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/AddFeedTest.php @@ -7,6 +7,8 @@ namespace Drupal\system\Tests\Common; +use Drupal\Core\Page\FeedLinkElement; +use Drupal\Core\Page\HtmlPage; use Drupal\simpletest\WebTestBase; /** @@ -39,46 +41,41 @@ function testBasicFeedAddNoTitle() { // - 'title' == the title of the feed as passed into drupal_add_feed(). $urls = array( 'path without title' => array( - 'input_url' => $path, - 'output_url' => url($path, array('absolute' => TRUE)), + 'url' => url($path, array('absolute' => TRUE)), 'title' => '', ), 'external URL without title' => array( - 'input_url' => $external_url, - 'output_url' => $external_url, + 'url' => $external_url, 'title' => '', ), 'local URL without title' => array( - 'input_url' => $fully_qualified_local_url, - 'output_url' => $fully_qualified_local_url, + 'url' => $fully_qualified_local_url, 'title' => '', ), 'path with title' => array( - 'input_url' => $path_for_title, - 'output_url' => url($path_for_title, array('absolute' => TRUE)), + 'url' => url($path_for_title, array('absolute' => TRUE)), 'title' => $this->randomName(12), ), 'external URL with title' => array( - 'input_url' => $external_for_title, - 'output_url' => $external_for_title, + 'url' => $external_for_title, 'title' => $this->randomName(12), ), 'local URL with title' => array( - 'input_url' => $fully_qualified_for_title, - 'output_url' => $fully_qualified_for_title, + 'url' => $fully_qualified_for_title, 'title' => $this->randomName(12), ), ); + $html_page = new HtmlPage(); + foreach ($urls as $description => $feed_info) { - $build['#attached']['drupal_add_feed'][] = array($feed_info['input_url'], $feed_info['title']); + $feed_link = new FeedLinkElement($feed_info['title'], $feed_info['url']); + $html_page->addLinkElement($feed_link); } - drupal_render($build); - - $this->drupalSetContent(drupal_get_html_head()); + $this->drupalSetContent(\Drupal::service('html_page_renderer')->render($html_page)); foreach ($urls as $description => $feed_info) { - $this->assertPattern($this->urlToRSSLinkPattern($feed_info['output_url'], $feed_info['title']), format_string('Found correct feed header for %description', array('%description' => $description))); + $this->assertPattern($this->urlToRSSLinkPattern($feed_info['url'], $feed_info['title']), format_string('Found correct feed header for %description', array('%description' => $description))); } } @@ -88,7 +85,7 @@ function testBasicFeedAddNoTitle() { function urlToRSSLinkPattern($url, $title = '') { // Escape any regular expression characters in the URL ('?' is the worst). $url = preg_replace('/([+?.*])/', '[$0]', $url); - $generated_pattern = '%%'; + $generated_pattern = '%%'; return $generated_pattern; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php index 349a14e..caa80a2 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php @@ -472,7 +472,6 @@ function testDrupalRenderPostRenderCache() { $output = drupal_render($element); $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); @@ -490,7 +489,6 @@ function testDrupalRenderPostRenderCache() { $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); @@ -514,7 +512,6 @@ function testDrupalRenderPostRenderCache() { $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); @@ -532,7 +529,6 @@ function testDrupalRenderPostRenderCache() { $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); @@ -596,7 +592,6 @@ function testDrupalRenderChildrenPostRenderCache() { $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $expected_settings = $context_1 + $context_2 + $context_3; $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); @@ -642,7 +637,6 @@ function testDrupalRenderChildrenPostRenderCache() { $output = drupal_render($element); $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.'); @@ -655,7 +649,6 @@ function testDrupalRenderChildrenPostRenderCache() { $output = drupal_render($element); $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $expected_settings = $context_1 + $context_2 + $context_3; $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); @@ -677,7 +670,6 @@ function testDrupalRenderChildrenPostRenderCache() { $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], '

overridden

', '#markup is overridden.'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $expected_settings = $context_1 + $context_2 + $context_3; $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); @@ -752,7 +744,6 @@ function testDrupalRenderChildrenPostRenderCache() { $output = drupal_render($element); $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.'); $this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.'); @@ -765,7 +756,6 @@ function testDrupalRenderChildrenPostRenderCache() { $output = drupal_render($element); $this->assertIdentical($output, '

overridden

', 'Output is overridden.'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); - $this->assertTrue(!isset($element['#context_test']), '#context_test is not set: impossible to modify $element itself, only possible to modify its #markup and #attached properties.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $expected_settings = $context_2 + $context_3; $this->assertTrue(!isset($settings['foo']), 'Parent JavaScript setting is not added to the page.'); @@ -775,16 +765,22 @@ function testDrupalRenderChildrenPostRenderCache() { \Drupal::request()->setMethod($request_method); } - /** * Tests post-render cache-integrated 'render_cache_placeholder' element. */ function testDrupalRenderRenderCachePlaceholder() { - $context = array('bar' => $this->randomContextValue()); + $context = array( + 'bar' => $this->randomContextValue(), + 'token' => drupal_render_cache_generate_token(), + ); + $callback = 'common_test_post_render_cache_placeholder'; $test_element = array( - '#type' => 'render_cache_placeholder', - '#context' => $context, - '#callback' => 'common_test_post_render_cache_placeholder', + '#post_render_cache' => array( + $callback => array( + $context + ), + ), + '#markup' => drupal_render_cache_generate_placeholder($callback, $context, $context['token']), '#prefix' => '', '#suffix' => '' ); @@ -814,8 +810,7 @@ function testDrupalRenderRenderCachePlaceholder() { $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data. - $tokens = array_keys($element['#post_render_cache']['common_test_post_render_cache_placeholder']); - $expected_token = $tokens[0]; + $expected_token = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. @@ -830,10 +825,10 @@ function testDrupalRenderRenderCachePlaceholder() { $this->assertIdentical($token, $expected_token, 'The tokens are identical'); // Verify the token is in the cached element. $expected_element = array( - '#markup' => '', + '#markup' => '', '#post_render_cache' => array( 'common_test_post_render_cache_placeholder' => array( - $expected_token => $context, + $context ), ), '#cache' => array('tags' => array()), @@ -860,14 +855,21 @@ function testDrupalRenderRenderCachePlaceholder() { * element. */ function testDrupalRenderChildElementRenderCachePlaceholder() { - $context = array('bar' => $this->randomContextValue()); $container = array( '#type' => 'container', ); + $context = array( + 'bar' => $this->randomContextValue(), + 'token' => drupal_render_cache_generate_token(), + ); + $callback = 'common_test_post_render_cache_placeholder'; $test_element = array( - '#type' => 'render_cache_placeholder', - '#context' => $context, - '#callback' => 'common_test_post_render_cache_placeholder', + '#post_render_cache' => array( + $callback => array( + $context + ), + ), + '#markup' => drupal_render_cache_generate_placeholder($callback, $context, $context['token']), '#prefix' => '', '#suffix' => '' ); @@ -904,9 +906,9 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data for child element. - $child_tokens = array_keys($element['test_element']['#post_render_cache']['common_test_post_render_cache_placeholder']); - $parent_tokens = array_keys($element['#post_render_cache']['common_test_post_render_cache_placeholder']); - $expected_token = $child_tokens[0]; + $child_tokens = $element['test_element']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; + $parent_tokens = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; + $expected_token = $child_tokens; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. @@ -921,10 +923,10 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array( - '#markup' => '', + '#markup' => '', '#post_render_cache' => array( 'common_test_post_render_cache_placeholder' => array( - $expected_token => $context, + $context, ), ), '#cache' => array('tags' => array()), @@ -946,10 +948,10 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $this->assertIdentical($token, $expected_token, 'The tokens are identical for the parent element'); // Verify the token is in the cached element. $expected_element = array( - '#markup' => '
' . "\n", + '#markup' => '
' . "\n", '#post_render_cache' => array( 'common_test_post_render_cache_placeholder' => array( - $expected_token => $context, + $context, ), ), '#cache' => array('tags' => array()), @@ -963,7 +965,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Verify that the child element contains the correct // render_cache_placeholder markup. - $expected_token = $child_tokens[0]; + $expected_token = $child_tokens; $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); @@ -975,10 +977,10 @@ function testDrupalRenderChildElementRenderCachePlaceholder() { $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array( - '#markup' => '', + '#markup' => '', '#post_render_cache' => array( 'common_test_post_render_cache_placeholder' => array( - $expected_token => $context, + $context, ), ), '#cache' => array('tags' => array()), diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php index f3db246..7cda6a3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/UrlTest.php @@ -10,7 +10,6 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Language\Language; use Drupal\simpletest\WebTestBase; -use Symfony\Component\HttpFoundation\Request; /** * Tests for URL generation functions. @@ -26,7 +25,7 @@ class UrlTest extends WebTestBase { public static function getInfo() { return array( 'name' => 'URL generation tests', - 'description' => 'Confirm that url(), drupal_get_query_parameters(), \Drupal\Component\Utility\UrlHelper::buildQuery(), and l() work correctly with various input.', + 'description' => 'Confirm that url(), \Drupal\Component\Utility\UrlHelper::filterQueryParameters(), \Drupal\Component\Utility\UrlHelper::buildQuery(), and l() work correctly with various input.', 'group' => 'Common', ); } @@ -184,7 +183,7 @@ function testLinkRenderArrayText() { } /** - * Tests drupal_get_query_parameters(). + * Tests UrlHelper::filterQueryParameters(). */ function testDrupalGetQueryParameters() { $original = array( @@ -201,22 +200,22 @@ function testDrupalGetQueryParameters() { // First-level exclusion. $result = $original; unset($result['b']); - $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, "'b' was removed."); + $this->assertEqual(UrlHelper::filterQueryParameters($original, array('b')), $result, "'b' was removed."); // Second-level exclusion. $result = $original; unset($result['b']['d']); - $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, "'b[d]' was removed."); + $this->assertEqual(UrlHelper::filterQueryParameters($original, array('b[d]')), $result, "'b[d]' was removed."); // Third-level exclusion. $result = $original; unset($result['b']['e']['f']); - $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, "'b[e][f]' was removed."); + $this->assertEqual(UrlHelper::filterQueryParameters($original, array('b[e][f]')), $result, "'b[e][f]' was removed."); // Multiple exclusions. $result = $original; unset($result['a'], $result['b']['e'], $result['c']); - $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, "'a', 'b[e]', 'c' were removed."); + $this->assertEqual(UrlHelper::filterQueryParameters($original, array('a', 'b[e]', 'c')), $result, "'a', 'b[e]', 'c' were removed."); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php index 430a1b6..cb8b01f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/ConnectionUnitTest.php @@ -52,8 +52,6 @@ function setUp() { // and closed in this test. // @see TestBase::changeDatabasePrefix() Database::addConnectionInfo('default', 'monitor', $connection_info['default']); - global $databases; - $databases['default']['monitor'] = $connection_info['default']; $this->monitor = Database::getConnection('monitor'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Datetime/DrupalDateTimeTest.php b/core/modules/system/lib/Drupal/system/Tests/Datetime/DrupalDateTimeTest.php index 8484a82..781d6b0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Datetime/DrupalDateTimeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Datetime/DrupalDateTimeTest.php @@ -10,6 +10,9 @@ use Drupal\simpletest\WebTestBase; use Drupal\Core\Datetime\DrupalDateTime; +/** + * Tests DrupalDateTime functionality. + */ class DrupalDateTimeTest extends WebTestBase { /** diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelSiteTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelSiteTest.php new file mode 100644 index 0000000..d6f9fe8 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelSiteTest.php @@ -0,0 +1,57 @@ + 'DrupalKernel site service overrides', + 'description' => 'Tests site-specific service overrides.', + 'group' => 'DrupalKernel', + ); + } + + /** + * Tests services.yml in site directory. + */ + public function testServicesYml() { + $this->assertFalse($this->container->has('site.service.yml')); + // A service provider class always has precedence over services.yml files. + // DrupalUnitTestBase::buildContainer() swaps out many services with + // in-memory implementations already, so those cannot be tested. + $this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\DatabaseBackendFactory'); + + $class = __CLASS__; + $doc = <<siteDirectory . '/services.yml', $doc); + + // Rebuild the container. + $this->kernel->updateModules(array()); + + $this->assertTrue($this->container->has('site.service.yml')); + $this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\Core\Cache\MemoryBackendFactory'); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php index e4a6a52..2326794 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php @@ -7,8 +7,10 @@ namespace Drupal\system\Tests\Entity; -use Drupal\Component\Utility\String; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\EventSubscriber\HtmlViewSubscriber; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\system\Tests\Cache\PageCacheTagsTestBase; @@ -125,6 +127,20 @@ abstract protected function createEntity(); /** + * Returns the additional (non-standard) cache tags for the tested entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to be tested, as created by createEntity(). + * @return array + * An array of the additional cache tags. + * + * @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity() + */ + protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) { + return array(); + } + + /** * Creates a referencing and a non-referencing entity for testing purposes. * * @param \Drupal\Core\Entity\EntityInterface $referenced_entity @@ -167,8 +183,12 @@ ), ), ))->save(); + $formatter = 'entity_reference_entity_view'; + if (!$this->entity->getEntityType()->hasControllerClass('view_builder')) { + $formatter = 'entity_reference_label'; + } entity_get_display($entity_type, $bundle, 'full') - ->setComponent($field_name, array('type' => 'entity_reference_label')) + ->setComponent($field_name, array('type' => $formatter)) ->save(); // Create an entity that does reference the entity being tested. @@ -212,22 +232,27 @@ $theme_cache_tags = array('content:1', 'theme:stark', 'theme_global_settings:1'); - // Generate the standardized entity cache tags. - $cache_tag = $entity_type . ':' . $this->entity->id(); - $view_cache_tag = $entity_type . '_view:1'; + $view_cache_tag = array(); + if ($this->entity->getEntityType()->hasControllerClass('view_builder')) { + $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type) + ->getCacheTag(); + } // Generate the cache tags for the (non) referencing entities. - $referencing_entity_cache_tags = array( - 'entity_test_view:1', - 'entity_test:' . $this->referencing_entity->id(), + $referencing_entity_cache_tags = NestedArray::mergeDeep( + $this->referencing_entity->getCacheTag(), + \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag(), // Includes the main entity's cache tags, since this entity references it. - $cache_tag, - $view_cache_tag, + $this->entity->getCacheTag(), + $view_cache_tag ); - $non_referencing_entity_cache_tags = array( - 'entity_test_view:1', - 'entity_test:' . $this->non_referencing_entity->id(), + $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags)); + $referencing_entity_cache_tags = array_merge($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity)); + $non_referencing_entity_cache_tags = NestedArray::mergeDeep( + $this->non_referencing_entity->getCacheTag(), + \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag() ); + $non_referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($non_referencing_entity_cache_tags)); // Prime the page cache for the referencing entity. @@ -239,9 +264,7 @@ // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->referencing_entity->id() . ':full:stark:r.anonymous'; - $cache_entry = \Drupal::cache('render')->get($cid); - $this->assertIdentical($cache_entry->tags, $referencing_entity_cache_tags); - + $this->verifyRenderCache($cid, $referencing_entity_cache_tags); // Prime the page cache for the non-referencing entity. $this->verifyPageCache($non_referencing_entity_path, 'MISS'); @@ -252,8 +275,7 @@ // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->non_referencing_entity->id() . ':full:stark:r.anonymous'; - $cache_entry = \Drupal::cache('render')->get($cid); - $this->assertIdentical($cache_entry->tags, $non_referencing_entity_cache_tags); + $this->verifyRenderCache($cid, $non_referencing_entity_cache_tags); @@ -306,19 +328,21 @@ $this->verifyPageCache($non_referencing_entity_path, 'HIT'); - // Verify that after modifying the entity's "full" display, there is a cache - // miss for both the referencing entity, and the listing of referencing - // entities, but not for the non-referencing entity. - $this->pass("Test modification of referenced entity's 'full' display.", 'Debug'); - $entity_display = entity_get_display($entity_type, $this->entity->bundle(), 'full'); - $entity_display->save(); - $this->verifyPageCache($referencing_entity_path, 'MISS'); - $this->verifyPageCache($listing_path, 'MISS'); - $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + if ($this->entity->getEntityType()->hasControllerClass('view_builder')) { + // Verify that after modifying the entity's "full" display, there is a cache + // miss for both the referencing entity, and the listing of referencing + // entities, but not for the non-referencing entity. + $this->pass("Test modification of referenced entity's 'full' display.", 'Debug'); + $entity_display = entity_get_display($entity_type, $this->entity->bundle(), 'full'); + $entity_display->save(); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); - // Verify cache hits. - $this->verifyPageCache($referencing_entity_path, 'HIT'); - $this->verifyPageCache($listing_path, 'HIT'); + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); @@ -375,7 +399,7 @@ // a cache miss for both the referencing entity, and the listing of // referencing entities, but not for the non-referencing entity. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); - Cache::invalidateTags(array($entity_type => array($this->entity->id()))); + Cache::invalidateTags($this->entity->getCacheTag()); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); @@ -385,19 +409,20 @@ $this->verifyPageCache($listing_path, 'HIT'); - // Verify that after invalidating the generic entity type's view cache tag - // directly, there is a cache miss for both the referencing entity, and the - // listing of referencing entities, but not for the non-referencing entity. - $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); - Cache::invalidateTags(array($entity_type . '_view' => TRUE)); - $this->verifyPageCache($referencing_entity_path, 'MISS'); - $this->verifyPageCache($listing_path, 'MISS'); - $this->verifyPageCache($non_referencing_entity_path, 'HIT'); - - // Verify cache hits. - $this->verifyPageCache($referencing_entity_path, 'HIT'); - $this->verifyPageCache($listing_path, 'HIT'); + if (!empty($view_cache_tag)) { + // Verify that after invalidating the generic entity type's view cache tag + // directly, there is a cache miss for both the referencing entity, and the + // listing of referencing entities, but not for the non-referencing entity. + $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); + Cache::invalidateTags($view_cache_tag); + $this->verifyPageCache($referencing_entity_path, 'MISS'); + $this->verifyPageCache($listing_path, 'MISS'); + $this->verifyPageCache($non_referencing_entity_path, 'HIT'); + // Verify cache hits. + $this->verifyPageCache($referencing_entity_path, 'HIT'); + $this->verifyPageCache($listing_path, 'HIT'); + } // Verify that after deleting the entity, there is a cache miss for both the // referencing entity, and the listing of referencing entities, but not for @@ -409,12 +434,31 @@ $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. - $tags = array_merge($theme_cache_tags, array( - 'entity_test_view:1', - 'entity_test:' . $this->referencing_entity->id(), - )); + $referencing_entity_cache_tags = NestedArray::mergeDeep( + $this->referencing_entity->getCacheTag(), + \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag() + ); + $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags)); + $tags = array_merge($theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); $this->verifyPageCache($listing_path, 'HIT', $theme_cache_tags); } + /** + * Verify that a given render cache entry exists, with the correct cache tags. + * + * @param string $cid + * The render cache item ID. + * @param array $tags + * An array of expected cache tags. + */ + protected function verifyRenderCache($cid, array $tags) { + // Also verify the existence of an entity render cache entry. + $cache_entry = \Drupal::cache('render')->get($cid); + $this->assertTrue($cache_entry, 'A render cache entry exists.'); + sort($cache_entry->tags); + sort($tags); + $this->assertIdentical($cache_entry->tags, $tags); + } + } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 4a05431..a838d2f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -17,6 +17,7 @@ use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\TypedData\Type\StringInterface; use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\field\Entity\FieldInstanceConfig; /** * Tests Entity API base functionality. @@ -623,7 +624,7 @@ class EntityFieldTest extends EntityUnitTestBase { */ protected function assertComputedProperties($entity_type) { // Make the test text field processed. - $instance = field_info_instance($entity_type, 'field_test_text', $entity_type); + $instance = FieldInstanceConfig::loadByName($entity_type, $entity_type, 'field_test_text'); $instance->settings['text_processing'] = 1; $instance->save(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php index bd2ceb9..ada2fbd 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php @@ -8,7 +8,7 @@ namespace Drupal\system\Tests\Entity; use Drupal\Core\Language\Language; -use Drupal\field\Field as FieldService; +use Drupal\field\Entity\FieldConfig; /** * Base class for language-aware entity tests. @@ -133,12 +133,11 @@ function setUp() { protected function toggleFieldTranslatability($entity_type) { $fields = array($this->field_name, $this->untranslatable_field_name); foreach ($fields as $field_name) { - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $field = FieldConfig::loadByName($entity_type, $field_name); $translatable = !$field->isTranslatable(); $field->set('translatable', $translatable); $field->save(); - FieldService::fieldInfo()->flush(); - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $field = FieldConfig::loadByName($entity_type, $field_name); $this->assertEqual($field->isTranslatable(), $translatable, 'Field translatability changed.'); } \Drupal::cache('entity')->deleteAll(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php index 46b81bf..8c0288e 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationFormTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Entity; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; use Drupal\Core\Language\Language; @@ -102,10 +103,10 @@ function testEntityFormLanguage() { $this->assertTrue($node, 'Node found in database.'); // Make body translatable. - $field = field_info_field('node', 'body'); + $field = FieldConfig::loadByName('node', 'body'); $field->translatable = TRUE; $field->save(); - $field = field_info_field('node', 'body'); + $field = FieldConfig::loadByName('node', 'body'); $this->assertTrue($field->isTranslatable(), 'Field body is translatable.'); // Create a body translation and check the form language. diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index 58ad4c3..2086c14 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -490,19 +490,31 @@ function testLanguageFallback() { // Check that if the entity has no translation no fallback is applied. $entity2 = $controller->create(array('langcode' => $default_langcode)); + // Get an view builder. + $controller = $this->entityManager->getViewBuilder($entity_type); + $entity2_build = $controller->view($entity2); + $entity2_output = drupal_render($entity2_build); $translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode); - $this->assertIdentical($entity2, $translation, 'When the entity has no translation no fallback is applied.'); + $translation_build = $controller->view($translation); + $translation_output = drupal_render($translation_build); + $this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.'); // Checks that entity translations are rendered properly. $controller = $this->entityManager->getViewBuilder($entity_type); $build = $controller->view($entity); + drupal_render($build); $this->assertEqual($build['label']['#markup'], $values[$current_langcode]['name'], 'By default the entity is rendered in the current language.'); + $langcodes = array_combine($this->langcodes, $this->langcodes); // We have no translation for the $langcode2 langauge, hence the expected // result is the topmost existing translation, that is $langcode. $langcodes[$langcode2] = $langcode; foreach ($langcodes as $desired => $expected) { $build = $controller->view($entity, 'full', $desired); + // Unset the #cache key so that a fresh render is produced with each pass, + // making the renderable array keys available to compare. + unset($build['#cache']); + drupal_render($build); $this->assertEqual($build['label']['#markup'], $values[$expected]['name'], 'The entity is rendered in the expected language.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php index 102eeb7..4faec44 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php @@ -53,9 +53,19 @@ function setUp() { * Tests EntityViewController. */ function testEntityViewController() { + $get_label_markup = function($label) { + return '

+
+
' . $label . '
+
+
+

'; + }; + foreach ($this->entities as $entity) { $this->drupalGet('entity_test/' . $entity->id()); $this->assertRaw($entity->label()); + $this->assertRaw($get_label_markup($entity->label())); $this->assertRaw('full'); $this->drupalGet('entity_test_converter/' . $entity->id()); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php index 3d308a5..230e489 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityWithUriCacheTagsTestBase.php @@ -8,7 +8,6 @@ namespace Drupal\system\Tests\Entity; use Drupal\Core\Cache\Cache; -use Drupal\Core\Entity\EntityInterface; use Drupal\system\Tests\Entity\EntityCacheTagsTestBase; /** @@ -17,20 +16,6 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase { /** - * Returns the additional (non-standard) cache tags for the tested entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to be tested, as created by createEntity(). - * @return array - * An array of the additional cache tags. - * - * @see \Drupal\system\Tests\Entity\EntityCacheTagsTestBase::createEntity() - */ - protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) { - return array(); - } - - /** * Tests cache tags presence and invalidation of the entity at its URI. * * Tests the following cache tags: diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php index 82e63b2..8549d99 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php @@ -445,7 +445,7 @@ function testFieldSqlStorageForeignKeys() { $schema = $field->getSchema(); // Retrieve the field definition and check that the foreign key is in place. - $field = field_info_field('entity_test', $field_name); + $field = FieldConfig::loadByName('entity_test', $field_name); $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update'); $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php index 2e68569..b9d8cec 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\ContentEntityDatabaseStorage; use Drupal\Core\Language\Language; -use Drupal\field\Field as FieldService; +use Drupal\field\Entity\FieldConfig; /** * Tests entity translation. @@ -91,7 +91,7 @@ class FieldTranslationSqlStorageTest extends EntityLanguageTestBase { $fields = array($this->field_name, $this->untranslatable_field_name); foreach ($fields as $field_name) { - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $field = FieldConfig::loadByName($entity_type, $field_name); $tables = array( ContentEntityDatabaseStorage::_fieldTableName($field), ContentEntityDatabaseStorage::_fieldRevisionTableName($field), diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php index 6c51a04..b002635 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementsLabelsTest.php @@ -59,10 +59,10 @@ function testFormLabels() { // Exercise various defaults for textboxes and modifications to ensure // appropriate override and correct behavior. - $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required"]/child::span[@class="form-required"]/parent::*/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]'); + $elements = $this->xpath('//label[@for="edit-form-textfield-test-title-and-required" and @class="form-required"]/following-sibling::input[@id="edit-form-textfield-test-title-and-required"]'); $this->assertTrue(isset($elements[0]), 'Label precedes textfield, with required marker inside label.'); - $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required"]/span[@class="form-required"]'); + $elements = $this->xpath('//input[@id="edit-form-textfield-test-no-title-required"]/preceding-sibling::label[@for="edit-form-textfield-test-no-title-required" and @class="form-required"]'); $this->assertTrue(isset($elements[0]), 'Label tag with required marker precedes required textfield with no title.'); $elements = $this->xpath('//input[@id="edit-form-textfield-test-title-invisible"]/preceding-sibling::label[@for="edit-form-textfield-test-title-invisible" and @class="visually-hidden"]'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php index 634b004..e45defc 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php @@ -12,6 +12,9 @@ use Drupal\Core\Render\Element; use Drupal\simpletest\WebTestBase; +/** + * Tests form element validation. + */ class FormTest extends WebTestBase { /** @@ -97,8 +100,7 @@ function testRequiredFields() { $elements['file']['empty_values'] = $empty_strings; // Regular expression to find the expected marker on required elements. - $required_marker_preg = '@<(?:label|legend).*.*@'; - + $required_marker_preg = '@<.*?class=".*?form-required.*?">@'; // Go through all the elements and all the empty values for them. foreach ($elements as $type => $data) { foreach ($data['empty_values'] as $key => $empty) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php index 4c9a668..bf9219f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php @@ -274,7 +274,8 @@ function testManipulations() { $directory = $this->public_files_directory .'/imagetest'; file_prepare_directory($directory, FILE_CREATE_DIRECTORY); - $image->save($directory . '/' . $op . image_type_to_extension($image->getType())); + $file_path = $directory . '/' . $op . image_type_to_extension($image->getType()); + $image->save($file_path); $this->assertTrue($correct_dimensions_real, String::format('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op))); $this->assertTrue($correct_dimensions_object, String::format('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op))); @@ -307,6 +308,9 @@ function testManipulations() { $this->assertTrue($correct_colors, String::format('Image %file object after %action action has the correct color placement at corner %corner.', array('%file' => $file, '%action' => $op, '%corner' => $key))); } } + + // Check that saved image reloads without raising PHP errors. + $image_reloaded = $this->imageFactory->get($file_path); } } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageDirectionTest.php b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageDirectionTest.php new file mode 100644 index 0000000..7dfa719 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerLanguageDirectionTest.php @@ -0,0 +1,64 @@ + 'Installer language direction test', + 'description' => 'Verifies that the early installer uses the correct language direction.', + 'group' => 'Installer', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + parent::setUpLanguage(); + // After selecting a different language than English, all following screens + // should be translated already. + // @todo Instead of actually downloading random translations that cannot be + // asserted, write and supply a translation file. Until then, take + // over whichever string happens to be there, but ensure that the English + // string no longer appears. + $elements = $this->xpath('//input[@type="submit"]/@value'); + $string = (string) current($elements); + $this->assertNotEqual($string, 'Save and continue'); + $this->translations['Save and continue'] = $string; + + // Verify that language direction is right-to-left. + $direction = (string) current($this->xpath('/html/@dir')); + $this->assertEqual($direction, 'rtl'); + } + + /** + * Confirms that the installation succeeded. + */ + public function testInstalled() { + $this->assertUrl('user/1'); + $this->assertResponse(200); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php index 756d872..bb9202f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Installer/InstallerTranslationTest.php @@ -44,6 +44,10 @@ class InstallerTranslationTest extends InstallerTestBase { $string = (string) current($elements); $this->assertNotEqual($string, 'Save and continue'); $this->translations['Save and continue'] = $string; + + // Check the language direction. + $direction = (string) current($this->xpath('/html/@dir')); + $this->assertEqual($direction, 'ltr'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php index 8148c7a..d0db0de 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php @@ -31,6 +31,19 @@ class LinksTest extends WebTestBase { } /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + entity_create('menu', array( + 'id' => 'menu_test', + 'label' => 'Test menu', + 'description' => 'Description text', + ))->save(); + } + + /** * Create a simple hierarchy of links. */ function createLinkHierarchy($module = 'menu_test') { diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php index 3779f25..4383658 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php @@ -58,6 +58,7 @@ function setUp() { $this->doTestMenuOptionalPlaceholders(); $this->doTestMenuOnRoute(); $this->doTestMenuName(); + $this->doTestMenuLinkDefaultsAlter(); $this->doTestMenuItemTitlesCases(); $this->doTestMenuLinkMaintain(); $this->doTestMenuLinkOptions(); @@ -179,6 +180,22 @@ function setUp() { } /** + * Tests menu links added in hook_menu_link_defaults_alter(). + */ + protected function doTestMenuLinkDefaultsAlter() { + // Check that machine name does not need to be defined since it is already + // set as the key of each menu link. + $menu_links = entity_load_multiple_by_properties('menu_link', array('route_name' => 'menu_test.custom')); + $menu_link = reset($menu_links); + $this->assertEqual($menu_link->machine_name, 'menu_test.custom', 'Menu links added at hook_menu_link_defaults_alter() obtain the machine name from the $links key.'); + // Make sure that rebuilding the menu tree does not produce duplicates of + // links added by hook_menu_link_defaults_alter(). + \Drupal::service('router.builder')->rebuild(); + $this->drupalGet('menu-test'); + $this->assertUniqueText('Custom link', 'Menu links added by hook_menu_link_defaults_alter() do not duplicate after a menu rebuild.'); + } + + /** * Tests for menu hierarchy. */ protected function doTestMenuHierarchy() { diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleEnableTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleEnableTest.php deleted file mode 100644 index e69de29..0000000 diff --git a/core/modules/system/lib/Drupal/system/Tests/Page/DefaultMetatagsTest.php b/core/modules/system/lib/Drupal/system/Tests/Page/DefaultMetatagsTest.php new file mode 100644 index 0000000..baae991 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Page/DefaultMetatagsTest.php @@ -0,0 +1,41 @@ + 'Default HTML metatags', + 'description' => 'Tests the default HTML metatags on the page.', + 'group' => 'Page', + ); + } + + /** + * Tests meta tags. + */ + public function testMetaTag() { + $this->drupalGet(''); + // Ensures that the charset metatag is on the page. + $result = $this->xpath('//meta[@name="charset" and @charset="utf-8"]'); + $this->assertEqual(count($result), 1); + + // Ensure that the charset one is the first metatag. + $result = $this->xpath('//meta'); + $this->assertEqual((string) $result[0]->attributes()->name, 'charset'); + $this->assertEqual((string) $result[0]->attributes()->charset, 'utf-8'); + } + +} + diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php index 07bf006..0baae45 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php @@ -95,8 +95,8 @@ function testLookupPath() { ); $aliasStorage->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Basic alias lookup works.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Basic source lookup works.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Basic alias lookup works.'); + $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Basic source lookup works.'); // Create a language specific alias for the default language (English). $path = array( @@ -107,8 +107,8 @@ function testLookupPath() { $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); // Hook that clears cache is not executed with unit tests. \Drupal::service('path.alias_manager.cached')->cacheClear(); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'English alias overrides language-neutral alias.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'English source overrides language-neutral source.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'English alias overrides language-neutral alias.'); + $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'English source overrides language-neutral source.'); // Create a language-neutral alias for the same path, again. $path = array( @@ -116,7 +116,7 @@ function testLookupPath() { 'alias' => 'bar', ); $aliasStorage->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a language-neutral alias.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), "users/Dries", 'English alias still returned after entering a language-neutral alias.'); // Create a language-specific (xx-lolspeak) alias for the same path. $path = array( @@ -125,9 +125,9 @@ function testLookupPath() { 'langcode' => 'xx-lolspeak', ); $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a LOLspeak alias.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), "users/Dries", 'English alias still returned after entering a LOLspeak alias.'); // The LOLspeak alias should be returned if we really want LOLspeak. - $this->assertEqual($aliasManager->getPathAlias($path['source'], 'xx-lolspeak'), 'LOL', 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source'], 'xx-lolspeak'), 'LOL', 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.'); // Create a new alias for this path in English, which should override the // previous alias for "user/1". @@ -139,22 +139,22 @@ function testLookupPath() { $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); // Hook that clears cache is not executed with unit tests. $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Recently created English alias returned.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Recently created English source returned.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Recently created English alias returned.'); + $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Recently created English source returned.'); // Remove the English aliases, which should cause a fallback to the most // recently created language-neutral alias, 'bar'. $aliasStorage->delete(array('langcode' => 'en')); // Hook that clears cache is not executed with unit tests. $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getPathAlias($path['source']), 'bar', 'Path lookup falls back to recently created language-neutral alias.'); + $this->assertEqual($aliasManager->getAliasByPath($path['source']), 'bar', 'Path lookup falls back to recently created language-neutral alias.'); // Test the situation where the alias and language are the same, but // the source differs. The newer alias record should be returned. $aliasStorage->save('user/2', 'bar'); // Hook that clears cache is not executed with unit tests. $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getSystemPath('bar'), 'user/2', 'Newer alias record is returned when comparing two Language::LANGCODE_NOT_SPECIFIED paths with the same alias.'); + $this->assertEqual($aliasManager->getPathByAlias('bar'), 'user/2', 'Newer alias record is returned when comparing two Language::LANGCODE_NOT_SPECIFIED paths with the same alias.'); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php index 64a92ea..4807d47 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Path/PathUnitTestBase.php @@ -11,9 +11,9 @@ use Drupal\Core\Database\Database; /** - * Defines a base class for path unit testing. + * Base class for Path/URL alias integration tests. */ -class PathUnitTestBase extends DrupalUnitTestBase { +abstract class PathUnitTestBase extends DrupalUnitTestBase { public function setUp() { parent::setUp(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/UrlAlterFunctionalTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/UrlAlterFunctionalTest.php index 5772e0a..4924408 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Path/UrlAlterFunctionalTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Path/UrlAlterFunctionalTest.php @@ -119,7 +119,7 @@ function testCurrentUrlRequestedPath() { */ protected function assertUrlInboundAlter($original, $final) { // Test inbound altering. - $result = $this->container->get('path.alias_manager')->getSystemPath($original); + $result = $this->container->get('path.alias_manager')->getPathByAlias($original); $this->assertIdentical($result, $final, format_string('Altered inbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result))); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/DiscoveryTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/DiscoveryTestBase.php index 20a574a..aa5cf5b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/DiscoveryTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/DiscoveryTestBase.php @@ -10,9 +10,9 @@ use Drupal\simpletest\UnitTestBase; /** - * Tests that plugins are correctly discovered. + * Base class for plugin discovery tests. */ -class DiscoveryTestBase extends UnitTestBase { +abstract class DiscoveryTestBase extends UnitTestBase { /** * The discovery component to test. diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php index 5d88c6c..8831ec4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php @@ -181,52 +181,4 @@ function testAddAdditionalRoutes() { $this->assertEqual($this->state->get('routing.menu_masks.test_routes'), $expected); } - /** - * Tests that changing the provider of a route updates the dumped value. - */ - public function testDumpRouteProviderRename() { - $connection = Database::getConnection(); - $dumper = new MatcherDumper($connection, $this->state, 'test_routes'); - $this->fixtures->createTables($connection); - - $route = new Route('/test'); - $collection = new RouteCollection(); - $collection->add('test', $route); - - $dumper->addRoutes($collection); - $dumper->dump(array('provider' => 'module_provider')); - - $record = $connection->query("SELECT * FROM {test_routes} WHERE name = :name", array(':name' => 'test'))->fetchObject(); - $this->assertEqual($record->provider, 'module_provider'); - - // Dump the same route name again with a different provider. - $dumper->addRoutes($collection); - $dumper->dump(array('provider' => 'module_provider2')); - - // Ensure the route has the new provider. - $record = $connection->query("SELECT * FROM {test_routes} WHERE provider = :provider", array(':provider' => 'module_provider'))->fetchObject(); - $this->assertFalse($record); - - $record = $connection->query("SELECT * FROM {test_routes} WHERE provider = :provider", array(':provider' => 'module_provider2'))->fetchObject(); - $this->assertEqual($record->path, '/test'); - $this->assertEqual($record->name, 'test'); - - // Test dumping an empty route collection. - $dumper->addRoutes(new RouteCollection()); - $dumper->dump(array('provider' => 'module_provider2')); - - // Ensure the route of the provider no longer exists. - $record = $connection->query("SELECT * FROM {test_routes} WHERE provider = :provider", array(':provider' => 'module_provider2'))->fetchObject(); - $this->assertFalse($record); - - $dumper->addRoutes($collection); - $dumper->dump(array('provider' => 'module_provider2')); - - // Test with an unset $routes property. - $dumper->dump(array('provider' => 'module_provider2')); - // Ensure the route of the provider no longer exists. - $record = $connection->query("SELECT * FROM {test_routes} WHERE provider = :provider", array(':provider' => 'module_provider2'))->fetchObject(); - $this->assertFalse($record); - } - } diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php index 3d8d737..5337099 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MockAliasManager.php @@ -62,18 +62,21 @@ class MockAliasManager implements AliasManagerInterface { /** * {@inheritdoc} */ - public function getSystemPath($path, $path_language = NULL) { - $language = $path_language ?: $this->defaultLanguage; - return $this->systemPaths[$path][$language]; + public function getPathByAlias($alias, $langcode = NULL) { + $langcode = $langcode ?: $this->defaultLanguage; + return $this->systemPaths[$alias][$langcode]; } /** * {@inheritdoc} + * @param $path + * @param null $langcode + * @return */ - public function getPathAlias($path, $path_language = NULL) { - $language = $path_language ?: $this->defaultLanguage; + public function getAliasByPath($path, $langcode = NULL) { + $langcode = $langcode ?: $this->defaultLanguage; $this->lookedUp[$path] = 1; - return $this->aliases[$path][$language]; + return $this->aliases[$path][$langcode]; } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php index a9e4598..51c4063 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php @@ -197,7 +197,7 @@ class RouterTest extends WebTestBase { */ public function testRouterUninstall() { \Drupal::moduleHandler()->uninstall(array('router_test')); - $route_count = \Drupal::database()->query('SELECT COUNT(*) FROM {router} WHERE provider = :provider', array(':provider' => 'router_test'))->fetchField(); + $route_count = \Drupal::database()->query('SELECT COUNT(*) FROM {router} WHERE name = :route_name', array(':route_name' => 'router_test.1'))->fetchField(); $this->assertEqual(0, $route_count, 'All router_test routes have been removed on uninstall.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php b/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php index f1779f4..a9c709c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Session/SessionHttpsTest.php @@ -113,11 +113,20 @@ class SessionHttpsTest extends WebTestBase { // Clear browser cookie jar. $this->cookies = array(); + } + /** + * Tests sessions in SSL mixed mode. + */ + protected function testMixedModeSslSession() { if ($this->request->isSecure()) { // The functionality does not make sense when running on HTTPS. return; } + else { + $secure_session_name = 'S' . session_name(); + $insecure_session_name = session_name(); + } // Enable secure pages. $this->settingsSet('mixed_mode_sessions', TRUE); @@ -128,6 +137,8 @@ class SessionHttpsTest extends WebTestBase { ); $this->writeSettings($settings); + $user = $this->drupalCreateUser(array('access administration pages')); + $this->curlClose(); // Start an anonymous session on the insecure site. $session_data = $this->randomName(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php b/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php index 634ae1d..720f16f 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Session/SessionTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests session handling. + */ class SessionTest extends WebTestBase { /** diff --git a/core/modules/system/lib/Drupal/system/Tests/System/AdminMetaTagTest.php b/core/modules/system/lib/Drupal/system/Tests/System/AdminMetaTagTest.php index 508ac2d..928b1f6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/AdminMetaTagTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/AdminMetaTagTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests the fingerprinting "Generator" HTML meta tag. + */ class AdminMetaTagTest extends WebTestBase { /** * Implement getInfo(). diff --git a/core/modules/system/lib/Drupal/system/Tests/System/CronRunTest.php b/core/modules/system/lib/Drupal/system/Tests/System/CronRunTest.php index 6fe28f2..8aa4069 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/CronRunTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/CronRunTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests cron runs. + */ class CronRunTest extends WebTestBase { /** diff --git a/core/modules/system/lib/Drupal/system/Tests/System/DefaultMobileMetaTagsTest.php b/core/modules/system/lib/Drupal/system/Tests/System/DefaultMobileMetaTagsTest.php index ba0889f..b235aed 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/DefaultMobileMetaTagsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/DefaultMobileMetaTagsTest.php @@ -10,6 +10,9 @@ use Drupal\Component\Utility\String; use Drupal\simpletest\WebTestBase; +/** + * Tests default mobile meta tags on HTML pages. + */ class DefaultMobileMetaTagsTest extends WebTestBase { public static function getInfo() { return array( diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PageNotFoundTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PageNotFoundTest.php index b248ff7..b7a7c0a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/PageNotFoundTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/PageNotFoundTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests "404 Not found" pages and custom 404 pages. + */ class PageNotFoundTest extends WebTestBase { protected $admin_user; diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php index a944f2f..87c4edb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php @@ -11,6 +11,9 @@ use Drupal\Core\Utility\Title; use Drupal\simpletest\WebTestBase; +/** + * Tests HTML output escaping of page title, site name, and slogan. + */ class PageTitleTest extends WebTestBase { /** diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ScriptTest.php index b6314b9..9ba2e5c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ScriptTest.php @@ -7,12 +7,13 @@ namespace Drupal\system\Tests\System; -use Drupal\simpletest\UnitTestBase; +use Drupal\Component\Utility\String; +use Drupal\simpletest\DrupalUnitTestBase; /** * Tests core shell scripts. */ -class ScriptTest extends UnitTestBase { +class ScriptTest extends DrupalUnitTestBase { /** * {@inheritdoc} @@ -26,31 +27,45 @@ class ScriptTest extends UnitTestBase { } /** - * {@inheritdoc} - */ - public function setUp() { - parent::setUp(); - chdir(DRUPAL_ROOT); - } - - /** * Tests password-hash.sh. */ public function testPasswordHashSh() { - $cmd = 'core/scripts/password-hash.sh xyz'; - exec($cmd, $output, $exit_code); - $this->assertIdentical(0, $exit_code, 'Exit code'); - $this->assertTrue(strpos(implode(' ', $output), 'hash: $S$') !== FALSE); + $_SERVER['argv'] = array( + 'core/scripts/password-hash.sh', + 'xyz', + ); + ob_start(); + include DRUPAL_ROOT . '/core/scripts/password-hash.sh'; + $this->content = ob_get_contents(); + ob_end_clean(); + $this->assertRaw('hash: $S$'); } /** * Tests rebuild_token_calculator.sh. */ public function testRebuildTokenCalculatorSh() { - $cmd = 'core/scripts/rebuild_token_calculator.sh'; - exec($cmd, $output, $exit_code); - $this->assertIdentical(0, $exit_code, 'Exit code'); - $this->assertTrue(strpos(implode(' ', $output), 'token=') !== FALSE); + $_SERVER['argv'] = array( + 'core/scripts/rebuild_token_calculator.sh', + ); + ob_start(); + include DRUPAL_ROOT . '/core/scripts/rebuild_token_calculator.sh'; + $this->content = ob_get_contents(); + ob_end_clean(); + $this->assertRaw('token='); + } + + /** + * Asserts that a given string is found in $this->content. + * + * @param string $string + * The raw string to assert. + */ + protected function assertRaw($string) { + return $this->assert(strpos($this->content, $string) !== FALSE, String::format('Raw @value found in @output.', array( + '@value' => var_export($string, TRUE), + '@output' => var_export($this->content, TRUE), + ))); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/System/TokenReplaceUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/System/TokenReplaceUnitTestBase.php index 630cb83..c13bd4c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/TokenReplaceUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/TokenReplaceUnitTestBase.php @@ -11,9 +11,9 @@ use Drupal\system\Tests\Entity\EntityUnitTestBase; /** - * Test token replacement in strings. + * Base class for token replacement tests. */ -class TokenReplaceUnitTestBase extends EntityUnitTestBase { +abstract class TokenReplaceUnitTestBase extends EntityUnitTestBase { /** * The interface language. diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php index 26a0fd8..6b9c5ef 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TableTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Theme; +use Drupal\Component\Utility\String; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -41,10 +42,10 @@ function testThemeTableStickyHeaders() { '#rows' => $rows, '#sticky' => TRUE, ); - $this->content = drupal_render($table); + $this->render($table); $js = _drupal_add_js(); - $this->assertTrue(isset($js['core/misc/tableheader.js']), 'tableheader.js was included when $sticky = TRUE.'); - $this->assertRaw('sticky-enabled', 'Table has a class of sticky-enabled when $sticky = TRUE.'); + $this->assertTrue(isset($js['core/misc/tableheader.js']), 'tableheader.js found.'); + $this->assertRaw('sticky-enabled'); drupal_static_reset('_drupal_add_js'); } @@ -66,10 +67,10 @@ function testThemeTableNoStickyHeaders() { '#colgroups' => $colgroups, '#sticky' => FALSE, ); - $this->content = drupal_render($table); + $this->render($table); $js = _drupal_add_js(); - $this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js was not included because $sticky = FALSE.'); - $this->assertNoRaw('sticky-enabled', 'Table does not have a class of sticky-enabled because $sticky = FALSE.'); + $this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js not found.'); + $this->assertNoRaw('sticky-enabled'); drupal_static_reset('_drupal_add_js'); } @@ -79,9 +80,9 @@ function testThemeTableNoStickyHeaders() { */ function testThemeTableWithEmptyMessage() { $header = array( - t('Header 1'), + 'Header 1', array( - 'data' => t('Header 2'), + 'data' => 'Header 2', 'colspan' => 2, ), ); @@ -89,11 +90,12 @@ function testThemeTableWithEmptyMessage() { '#type' => 'table', '#header' => $header, '#rows' => array(), - '#empty' => t('No strings available.'), + '#empty' => 'Empty row.', ); - $this->content = drupal_render($table); - $this->assertRaw('No strings available.', 'Correct colspan was set on empty message.'); - $this->assertRaw('Header 1', 'Table header was printed.'); + $this->render($table); + $this->removeWhiteSpace(); + $this->assertRaw('Header 1Header 2', 'Table header found.'); + $this->assertRaw('Empty row.', 'Colspan on #empty row found.'); } /** @@ -110,7 +112,7 @@ function testThemeTableWithNoStriping() { '#type' => 'table', '#rows' => $rows, ); - $this->content = drupal_render($table); + $this->render($table); $this->assertNoRaw('class="odd"', 'Odd/even classes were not added because $no_striping = TRUE.'); $this->assertNoRaw('no_striping', 'No invalid no_striping HTML attribute was printed.'); } @@ -130,11 +132,33 @@ function testThemeTableHeaderCellOption() { '#type' => 'table', '#rows' => $rows, ); - $this->content = drupal_render($table); + $this->render($table); + $this->removeWhiteSpace(); $this->assertRaw('111', 'The th and td tags was printed correctly.'); } /** + * Renders a given render array. + * + * @param array $elements + * The render array elements to render. + * + * @return string + * The rendered HTML. + */ + protected function render(array $elements) { + $this->content = drupal_render($elements); + $this->verbose('
' . String::checkPlain($this->content));
+  }
+
+  /**
+   * Removes all white-space between HTML tags from $this->content.
+   */
+  protected function removeWhiteSpace() {
+    $this->content = preg_replace('@>\s+<@', '><', $this->content);
+  }
+
+  /**
    * Asserts that a raw string appears in $this->content.
    *
    * @param string $value
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
index 9c2105a..cd97dba 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigDebugMarkupTest.php
@@ -48,7 +48,8 @@ function testTwigDebugMarkup() {
 
     // Create a node and test different features of the debug markup.
     $node = $this->drupalCreateNode();
-    $output = _theme('node', node_view($node));
+    $build = node_view($node);
+    $output = drupal_render($build);
     $this->assertTrue(strpos($output, '') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
     $this->assertTrue(strpos($output, "CALL: _theme('node')") !== FALSE, 'Theme call information found.');
     $this->assertTrue(strpos($output, 'x node--1' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   * node' . $extension) !== FALSE, 'Suggested template files found in order and node ID specific template shown as current template.');
@@ -58,7 +59,8 @@ function testTwigDebugMarkup() {
     // Create another node and make sure the template suggestions shown in the
     // debug markup are correct.
     $node2 = $this->drupalCreateNode();
-    $output = _theme('node', node_view($node2));
+    $build = node_view($node2);
+    $output = drupal_render($build);
     $this->assertTrue(strpos($output, '* node--2' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
 
     // Create another node and make sure the template suggestions shown in the
@@ -75,7 +77,8 @@ function testTwigDebugMarkup() {
     $this->rebuildContainer();
     $this->resetAll();
 
-    $output = _theme('node', node_view($node));
+    $build = node_view($node);
+    $output = drupal_render($build);
     $this->assertFalse(strpos($output, '') !== FALSE, 'Twig debug markup not found in theme output when debug is disabled.');
   }
 
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 4447052..c69838a 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -2310,27 +2310,6 @@ function hook_install_tasks(&$install_state) {
 }
 
 /**
- * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head().
- *
- * Elements available to be altered are only those added using
- * drupal_add_html_head_link() or drupal_add_html_head(). CSS and JS files
- * are handled using _drupal_add_css() and _drupal_add_js(), so the head links
- * for those files will not appear in the $head_elements array.
- *
- * @param $head_elements
- *   An array of renderable elements. Generally the values of the #attributes
- *   array will be the most likely target for changes.
- */
-function hook_html_head_alter(&$head_elements) {
-  foreach ($head_elements as $key => $element) {
-    if (isset($element['#attributes']['rel']) && $element['#attributes']['rel'] == 'canonical') {
-      // I want a custom canonical URL.
-      $head_elements[$key]['#attributes']['href'] = mymodule_canonical_url();
-    }
-  }
-}
-
-/**
  * Alter the full list of installation tasks.
  *
  * You can use this hook to change or replace any part of the Drupal
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 47c0933..a14cd7b 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -790,13 +790,6 @@ function system_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
-      'provider' => array(
-        'description' => 'The provider grouping to which a route belongs.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
       'fit' => array(
         'description' => 'A numeric representation of how specific the path is.',
         'type' => 'int',
@@ -818,7 +811,6 @@ function system_schema() {
     ),
     'indexes' => array(
       'pattern_outline_fit' => array('pattern_outline', 'fit'),
-      'provider' => array('provider'),
     ),
     'primary key' => array('name'),
   );
@@ -964,25 +956,5 @@ function system_schema() {
     ),
   );
 
-  $schema['config_snapshot'] = array(
-    'description' => 'Stores a snapshot of the last imported configuration.',
-    'fields' => array(
-      'name' => array(
-        'description' => 'The identifier for the config object (the name of the file, minus the file extension).',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'data' => array(
-        'description' => 'The raw data for this configuration object.',
-        'type' => 'blob',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
-    ),
-    'primary key' => array('name'),
-  );
-
   return $schema;
 }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 8b4455b..8feb47b 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -13,6 +13,7 @@
 use Drupal\user\UserInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use GuzzleHttp\Exception\RequestException;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * New users will be set to the default time zone at registration.
@@ -73,11 +74,11 @@
 /**
  * Implements hook_help().
  */
-function system_help($path, $arg) {
+function system_help($route_name, Request $request) {
   global $base_url;
 
-  switch ($path) {
-    case 'admin/help#system':
+  switch ($route_name) {
+    case 'help.page.system':
       $output = '';
       $output .= '

' . t('About') . '

'; $output .= '

' . t('The System module is integral to the site, and provides basic but extensible functionality for use by other modules and themes. Some integral elements of Drupal are contained in and managed by the System module, including caching, enabling and disabling modules and themes, preparing and displaying the administrative page, and configuring fundamental site settings. A number of key system maintenance operations are also part of the System module. For more information, see the online handbook entry for System module.', array('@system' => 'http://drupal.org/documentation/modules/system')) . '

'; @@ -95,18 +96,23 @@ function system_help($path, $arg) { $output .= '
' . t('The System module also handles basic configuration options for your site, including Date and time settings, File system settings, Site name and other information, and a Maintenance mode for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '
'; $output .= ''; return $output; - case 'admin/index': + + case 'system.admin_index': return '

' . t('This page shows you all available administration tasks for each module.') . '

'; - case 'admin/appearance': + + case 'system.themes_page': $output = '

' . t('Set and configure the default theme for your website. Alternative themes are available.', array('@themes' => 'http://drupal.org/project/themes')) . '

'; return $output; - case 'admin/appearance/settings/' . $arg[3]: + + case 'system.theme_settings_theme': $theme_list = list_themes(); - $theme = $theme_list[$arg[3]]; + $theme = $theme_list[$request->attributes->get('theme')]; return '

' . t('These options control the display settings for the %name theme. When your site is displayed using this theme, these settings will be used.', array('%name' => $theme->info['name'])) . '

'; - case 'admin/appearance/settings': + + case 'system.theme_settings': return '

' . t('These options control the default display settings for your entire site, across all themes. Unless they have been overridden by a specific theme, these settings will be used.') . '

'; - case 'admin/modules': + + case 'system.modules_list': $output = '

' . t('Download additional contributed modules to extend Drupal\'s functionality.', array('@modules' => 'http://drupal.org/project/modules')) . '

'; if (\Drupal::moduleHandler()->moduleExists('update')) { if (update_manager_access()) { @@ -120,19 +126,29 @@ function system_help($path, $arg) { $output .= '

' . t('Regularly review available updates to maintain a secure and current site. Always run the update script each time a module is updated. Enable the Update Manager module to update and install modules and themes.', array('@update-php' => $base_url . '/core/update.php')) . '

'; } return $output; - case 'admin/modules/uninstall': + + case 'system.modules_uninstall': return '

' . t('The uninstall process removes all data related to a module.') . '

'; - case 'admin/structure/block/manage': - if ($arg[4] == 'system' && $arg[5] == 'powered-by') { + + case 'block.admin_edit': + if (($block = $request->attributes->get('block')) && $block->get('plugin') == 'system_powered_by_block') { + return '

' . t('The Powered by Drupal block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '

'; + } + break; + + case 'block.admin_add': + if ($request->attributes->get('plugin_id') == 'system_powered_by_block') { return '

' . t('The Powered by Drupal block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '

'; } break; - case 'admin/config/development/maintenance': + + case 'system.site_maintenance_mode': if (\Drupal::currentUser()->id() == 1) { return '

' . t('Use maintenance mode when making major updates, particularly if the updates could disrupt visitors or the update process. Examples include upgrading, importing or exporting content, modifying a theme, modifying content types, and making backups.') . '

'; } break; - case 'admin/reports/status': + + case 'system.status': return '

' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the system requirements.", array('@system-requirements' => 'http://drupal.org/requirements')) . '

'; } } @@ -235,6 +251,7 @@ function system_permission() { ), 'access site reports' => array( 'title' => t('View site reports'), + 'restrict access' => TRUE, ), ); } @@ -627,12 +644,6 @@ function system_element_info() { '#theme' => 'table', ); - // Other elements. - $types['render_cache_placeholder'] = array( - '#callback' => '', - '#context' => array(), - ); - return $types; } @@ -1296,7 +1307,7 @@ function system_rebuild_module_data() { * @return \Drupal\Core\Extension\Extension[] * Array of all available themes and their data. * - * @deprecated 8.x + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. * Use \Drupal::service('theme_handler')->rebuildThemeData(). */ function system_rebuild_theme_data() { diff --git a/core/modules/system/templates/fieldset.html.twig b/core/modules/system/templates/fieldset.html.twig index 9e4fe68..52d410f 100644 --- a/core/modules/system/templates/fieldset.html.twig +++ b/core/modules/system/templates/fieldset.html.twig @@ -25,7 +25,7 @@ {% if legend.title is not empty or required -%} {# Always wrap fieldset legends in a SPAN for CSS positioning. #} - {{ legend.title }}{{ required }} + {{ legend.title }}{{ required }} {%- endif %}
{% if prefix %} diff --git a/core/modules/system/templates/table.html.twig b/core/modules/system/templates/table.html.twig new file mode 100644 index 0000000..ce69286 --- /dev/null +++ b/core/modules/system/templates/table.html.twig @@ -0,0 +1,81 @@ +{# +/** + * @file + * Default theme implementation to display a table. + * + * Available variables: + * - attributes: HTML attributes to apply to the tag. + * - caption: A localized string for the tag. + * Note: Drupal currently supports only one table header row, see + * http://drupal.org/node/893530 and + * http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_table/7#comment-5109. + * - header: Table header cells. Each cell contains the following properties: + * - tag: The HTML tag name to use; either TH or TD. + * - attributes: HTML attributes to apply to the tag. + * - content: A localized string for the title of the column. + * - field: Field name (required for column sorting). + * - sort: Default sort order for this column ("asc" or "desc"). + * - sticky: A flag indicating whether to use a "sticky" table header. + * - rows: Table rows. Each row contains the following properties: + * - attributes: HTML attributes to apply to the tag. + * - data: Table cells. + * - no_striping: A flag indicating that the row should receive no + * 'even / odd' styling. Defaults to FALSE. + * - cells: Table cells of the row. Each cell contains the following keys: + * - tag: The HTML tag name to use; either TH or TD. + * - attributes: Any HTML attributes, such as "colspan", to apply to the + * table cell. + * - content: The string to display in the table cell. + * - empty: The message to display in an extra row if table does not have + * any rows. + * + * @see template_preprocess_table() + * + * @ingroup themeable + */ +#} + + {% if caption %} + + {% endif %} + + {% for colgroup in colgroups %} + {% if colgroup.cols %} + + {% for col in colgroup.cols %} + + {% endfor %} + + {% else %} + + {% endif %} + {% endfor %} + + {% if header %} + + + {% for cell in header %} + <{{ cell.tag }}{{ cell.attributes }}> + {{- cell.content -}} + + {% endfor %} + + + {% endif %} + + {% if rows %} + + {% for row in rows %} + + {% for cell in row.cells %} + <{{ cell.tag }}{{ cell.attributes }}> + {{- cell.content -}} + + {% endfor %} + + {% endfor %} + + {% endif %} +
tag. + * - colgroups: Column groups. Each group contains the following properties: + * - attributes: HTML attributes to apply to the
{{ caption }}
diff --git a/core/modules/system/tests/Drupal/system/Tests/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/Drupal/system/Tests/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php index ef7cb95..65f2a62 100644 --- a/core/modules/system/tests/Drupal/system/Tests/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php +++ b/core/modules/system/tests/Drupal/system/Tests/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php @@ -124,7 +124,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { $this->currentUser ); - $this->builder->setTranslationManager($this->getStringTranslationStub()); + $this->builder->setStringTranslation($this->getStringTranslationStub()); $this->linkGenerator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface'); $this->builder->setLinkGenerator($this->linkGenerator); @@ -190,7 +190,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { $link_front = 'Home'; $this->linkGenerator->expects($this->at(0)) ->method('generate') - ->with('Example', 'example', array(), array()) + ->with('Example', 'example', array(), array('html' => TRUE)) ->will($this->returnValue($link_example)); $this->linkGenerator->expects($this->at(1)) ->method('generate') @@ -241,12 +241,12 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { $link_front = 'Home'; $this->linkGenerator->expects($this->at(0)) ->method('generate') - ->with('Bar', 'example_bar', array(), array()) + ->with('Bar', 'example_bar', array(), array('html' => TRUE)) ->will($this->returnValue($link_example_bar)); $this->linkGenerator->expects($this->at(1)) ->method('generate') - ->with('Example', 'example', array(), array()) + ->with('Example', 'example', array(), array('html' => TRUE)) ->will($this->returnValue($link_example)); $this->linkGenerator->expects($this->at(2)) ->method('generate') @@ -364,7 +364,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { $link_front = 'Home'; $this->linkGenerator->expects($this->at(0)) ->method('generate') - ->with('Admin', 'user_page', array(), array()) + ->with('Admin', 'user_page', array(), array('html' => TRUE)) ->will($this->returnValue($link_user)); $this->linkGenerator->expects($this->at(1)) @@ -415,8 +415,8 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase { */ class TestPathBasedBreadcrumbBuilder extends PathBasedBreadcrumbBuilder { - public function setTranslationManager(TranslationInterface $translation_manager) { - $this->translationManager = $translation_manager; + public function setStringTranslation(TranslationInterface $string_translation) { + $this->stringTranslation = $string_translation; } public function setLinkGenerator(LinkGeneratorInterface $link_generator) { diff --git a/core/modules/system/tests/modules/action_test/action_test.info.yml b/core/modules/system/tests/modules/action_test/action_test.info.yml index c639b27..7cefe06 100644 --- a/core/modules/system/tests/modules/action_test/action_test.info.yml +++ b/core/modules/system/tests/modules/action_test/action_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for action testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.info.yml b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.info.yml index 054ebff..ba31a29 100644 --- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.info.yml +++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.info.yml @@ -4,4 +4,3 @@ description: 'Test for AJAX form calls.' core: 8.x package: Testing version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml index 153eee9..f151748 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.info.yml @@ -6,4 +6,3 @@ version: VERSION core: 8.x dependencies: - contact -hidden: true diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module index 356f7d6..eee24f3 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.module +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module @@ -95,6 +95,7 @@ function ajax_test_error() { function ajax_test_dialog_contents() { // This is a regular render array; the keys do not have special meaning. $content = array( + '#title' => 'AJAX Dialog contents', 'content' => array( '#markup' => 'Example message', ), diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml index 8be4f0a..e3c7cf5 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml @@ -1,7 +1,7 @@ ajax_test.dialog_contents: path: '/ajax-test/dialog-contents' defaults: - _title: 'AJAX Dialog contents' + _title: 'AJAX Dialog contents routing' _content: '\Drupal\ajax_test\Controller\AjaxTestController::dialogContents' requirements: _access: 'TRUE' diff --git a/core/modules/system/tests/modules/batch_test/batch_test.info.yml b/core/modules/system/tests/modules/batch_test/batch_test.info.yml index b45766d..072017e 100644 --- a/core/modules/system/tests/modules/batch_test/batch_test.info.yml +++ b/core/modules/system/tests/modules/batch_test/batch_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Batch API tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/cache_test/cache_test.info.yml b/core/modules/system/tests/modules/cache_test/cache_test.info.yml index b191556..329e0d6 100644 --- a/core/modules/system/tests/modules/cache_test/cache_test.info.yml +++ b/core/modules/system/tests/modules/cache_test/cache_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for cache system testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/common_test/common_test.info.yml b/core/modules/system/tests/modules/common_test/common_test.info.yml index 1a0bc0c..2efb155 100644 --- a/core/modules/system/tests/modules/common_test/common_test.info.yml +++ b/core/modules/system/tests/modules/common_test/common_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Common tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module index 1a081df..2785be9 100644 --- a/core/modules/system/tests/modules/common_test/common_test.module +++ b/core/modules/system/tests/modules/common_test/common_test.module @@ -197,6 +197,8 @@ function common_test_post_render_cache(array $element, array $context) { /** * #post_render_cache callback; replaces placeholder, extends #attached. * + * @param array $element + * The renderable array that contains the to be replaced placeholder. * @param array $context * An array with the following keys: * - bar: contains a random string. @@ -204,8 +206,9 @@ function common_test_post_render_cache(array $element, array $context) { * @return array * A render array. */ -function common_test_post_render_cache_placeholder(array $context) { - $element = array( +function common_test_post_render_cache_placeholder(array $element, array $context) { + $placeholder = drupal_render_cache_generate_placeholder(__FUNCTION__, $context, $context['token']); + $replace_element = array( '#markup' => '' . $context['bar'] . '', '#attached' => array( 'js' => array( @@ -218,6 +221,8 @@ function common_test_post_render_cache_placeholder(array $context) { ), ), ); + $markup = drupal_render($replace_element); + $element['#markup'] = str_replace($placeholder, $markup, $element['#markup']); return $element; } diff --git a/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.info.yml b/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.info.yml index ee46ca8..5d1ab7f 100644 --- a/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.info.yml +++ b/core/modules/system/tests/modules/common_test_cron_helper/common_test_cron_helper.info.yml @@ -4,4 +4,3 @@ description: 'Helper module for CronRunTestCase::testCronExceptions().' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/condition_test/condition_test.info.yml b/core/modules/system/tests/modules/condition_test/condition_test.info.yml index e51633b..b664502 100644 --- a/core/modules/system/tests/modules/condition_test/condition_test.info.yml +++ b/core/modules/system/tests/modules/condition_test/condition_test.info.yml @@ -4,4 +4,3 @@ description: "Test general form component for condition plugins." package: Testing version: VERSION core: 8.x -hidden: TRUE diff --git a/core/modules/system/tests/modules/cron_queue_test/cron_queue_test.info.yml b/core/modules/system/tests/modules/cron_queue_test/cron_queue_test.info.yml index 2d6dfa3..014e918 100644 --- a/core/modules/system/tests/modules/cron_queue_test/cron_queue_test.info.yml +++ b/core/modules/system/tests/modules/cron_queue_test/cron_queue_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for the cron queue runner.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/database_test/database_test.info.yml b/core/modules/system/tests/modules/database_test/database_test.info.yml index 914ba1f..92cea84 100644 --- a/core/modules/system/tests/modules/database_test/database_test.info.yml +++ b/core/modules/system/tests/modules/database_test/database_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Database layer tests.' core: 8.x package: Testing version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml b/core/modules/system/tests/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml index c419771..30bccf7 100644 --- a/core/modules/system/tests/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml +++ b/core/modules/system/tests/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing the drupal_system_listing function.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml b/core/modules/system/tests/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml index 0515956..a04828b 100644 --- a/core/modules/system/tests/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml +++ b/core/modules/system/tests/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing the drupal_system_listing function.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.info.yml b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.info.yml index e87d44f..fa4c0fe 100644 --- a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.info.yml +++ b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.info.yml @@ -6,4 +6,3 @@ version: VERSION core: 8.x dependencies: - entity_cache_test_dependency -hidden: true diff --git a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.info.yml b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.info.yml index 6adf6e3..282fcb7 100644 --- a/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.info.yml +++ b/core/modules/system/tests/modules/entity_cache_test_dependency/entity_cache_test_dependency.info.yml @@ -4,4 +4,3 @@ description: 'Support dependency module for testing entity cache.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.info.yml b/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.info.yml index c46c26b..7d35503 100644 --- a/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.info.yml +++ b/core/modules/system/tests/modules/entity_crud_hook_test/entity_crud_hook_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for CRUD hook tests.' core: 8.x package: Testing version: VERSION -hidden: true diff --git a/core/modules/system/tests/modules/entity_test/entity_test.info.yml b/core/modules/system/tests/modules/entity_test/entity_test.info.yml index 5bea26a..bd4382e 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.info.yml +++ b/core/modules/system/tests/modules/entity_test/entity_test.info.yml @@ -7,4 +7,3 @@ core: 8.x dependencies: - field - text -hidden: true diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml index 8b99660..38b0c44 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml +++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml @@ -2,6 +2,7 @@ entity_test.render: path: '/entity_test/{entity_test}' defaults: _entity_view: 'entity_test.full' + _title: 'Test full view mode' requirements: _access: 'TRUE' diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php index a2f2d2c..01e6872 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php @@ -82,7 +82,12 @@ class EntityTest extends ContentEntityBase implements EntityOwnerInterface { ->setLabel(t('Name')) ->setDescription(t('The name of the test entity.')) ->setTranslatable(TRUE) - ->setSetting('max_length', 32); + ->setSetting('max_length', 32) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + )); // @todo: Add allowed values validation. $fields['type'] = FieldDefinition::create('string') diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestViewBuilder.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestViewBuilder.php index 6805765..e45ddc0 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestViewBuilder.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestViewBuilder.php @@ -19,17 +19,20 @@ class EntityTestViewBuilder extends EntityViewBuilder { /** * {@inheritdoc} */ - public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) { - parent::buildContent($entities, $displays, $view_mode, $langcode); + public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { + parent::buildComponents($build, $entities, $displays, $view_mode, $langcode); - foreach ($entities as $entity) { - $entity->content['label'] = array( + foreach ($entities as $id => $entity) { + $build[$id]['label'] = array( + '#weight' => -100, '#markup' => check_plain($entity->label()), ); - $entity->content['separator'] = array( + $build[$id]['separator'] = array( + '#weight' => -150, '#markup' => ' | ', ); - $entity->content['view_mode'] = array( + $build[$id]['view_mode'] = array( + '#weight' => -200, '#markup' => check_plain($view_mode), ); } diff --git a/core/modules/system/tests/modules/error_test/error_test.info.yml b/core/modules/system/tests/modules/error_test/error_test.info.yml index 1b49c4f..f91c7b3 100644 --- a/core/modules/system/tests/modules/error_test/error_test.info.yml +++ b/core/modules/system/tests/modules/error_test/error_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for error and exception testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/form_test/form_test.info.yml b/core/modules/system/tests/modules/form_test/form_test.info.yml index 3d4280e..39d45ea 100644 --- a/core/modules/system/tests/modules/form_test/form_test.info.yml +++ b/core/modules/system/tests/modules/form_test/form_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Form API tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/image_test/image_test.info.yml b/core/modules/system/tests/modules/image_test/image_test.info.yml index e3ed9c5..2e54287 100644 --- a/core/modules/system/tests/modules/image_test/image_test.info.yml +++ b/core/modules/system/tests/modules/image_test/image_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for image toolkit tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/invalid_module_name_over_the_maximum_allowed_character_length/invalid_module_name_over_the_maximum_allowed_character_length.info.yml b/core/modules/system/tests/modules/invalid_module_name_over_the_maximum_allowed_character_length/invalid_module_name_over_the_maximum_allowed_character_length.info.yml index 16d9c78..db5cdcc 100644 --- a/core/modules/system/tests/modules/invalid_module_name_over_the_maximum_allowed_character_length/invalid_module_name_over_the_maximum_allowed_character_length.info.yml +++ b/core/modules/system/tests/modules/invalid_module_name_over_the_maximum_allowed_character_length/invalid_module_name_over_the_maximum_allowed_character_length.info.yml @@ -4,4 +4,3 @@ description: 'Test module with a name over the maximum allowed characters.' package: Testing version: VERSION core: 8.x -hidden: TRUE diff --git a/core/modules/system/tests/modules/menu_test/config/install/system.menu.changed.yml b/core/modules/system/tests/modules/menu_test/config/install/system.menu.changed.yml new file mode 100644 index 0000000..4530fee --- /dev/null +++ b/core/modules/system/tests/modules/menu_test/config/install/system.menu.changed.yml @@ -0,0 +1,5 @@ +id: changed +label: Changed test menu +description: 'The changed menu for menu_test tests.' +langcode: en +locked: true diff --git a/core/modules/system/tests/modules/menu_test/config/install/system.menu.original.yml b/core/modules/system/tests/modules/menu_test/config/install/system.menu.original.yml new file mode 100644 index 0000000..c3443fd --- /dev/null +++ b/core/modules/system/tests/modules/menu_test/config/install/system.menu.original.yml @@ -0,0 +1,5 @@ +id: original +label: Original test menu +description: 'The original menu for menu_test tests.' +langcode: en +locked: true diff --git a/core/modules/system/tests/modules/menu_test/menu_test.info.yml b/core/modules/system/tests/modules/menu_test/menu_test.info.yml index c927d5f..dbba9b8 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.info.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.info.yml @@ -4,7 +4,6 @@ description: 'Support module for menu hook testing.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - test_page_test - menu_ui diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module index a4212bc..f8aff5d 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -9,14 +9,21 @@ /** * Implements hook_menu_link_defaults_alter(). - * - * Many of the machine names here are slightly different from the route name. - * Since the machine name is arbitrary, this helps ensure that core does not - * add mistaken assumptions about the correlation. */ function menu_test_menu_link_defaults_alter(&$links) { + // Many of the machine names here are slightly different from the route name. + // Since the machine name is arbitrary, this helps ensure that core does not + // add mistaken assumptions about the correlation. $links['menu_test.menu_name_test']['menu_name'] = menu_test_menu_name(); $links['menu_test.context']['title'] = \Drupal::config('menu_test.menu_item')->get('title'); + + // Adds a custom menu link. + $links['menu_test.custom'] = array( + 'title' => 'Custom link', + 'route_name' => 'menu_test.custom', + 'description' => 'Custom link used to check the integrity of manually added menu links.', + 'parent' => 'menu_test', + ); } /** diff --git a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml index 04fcb65..6c3c106 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml @@ -358,3 +358,10 @@ menu_test.context: _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback' requirements: _access: 'TRUE' + +menu_test.custom: + path: '/menu-test/custom' + defaults: + _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback' + requirements: + _access: 'TRUE' diff --git a/core/modules/system/tests/modules/module_autoload_test/module_autoload_test.info.yml b/core/modules/system/tests/modules/module_autoload_test/module_autoload_test.info.yml index 4437ef6..1805185 100644 --- a/core/modules/system/tests/modules/module_autoload_test/module_autoload_test.info.yml +++ b/core/modules/system/tests/modules/module_autoload_test/module_autoload_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for module system tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/module_test/module_test.info.yml b/core/modules/system/tests/modules/module_test/module_test.info.yml index 390706d..025525c 100644 --- a/core/modules/system/tests/modules/module_test/module_test.info.yml +++ b/core/modules/system/tests/modules/module_test/module_test.info.yml @@ -4,7 +4,6 @@ description: 'Support module for module system testing.' package: Testing version: VERSION core: 8.x -hidden: true # Depends on the Node module to test making a module required using # hook_system_info_alter() and ensuring that its dependencies also become # required. diff --git a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml index 4aa2455..1953d92 100644 --- a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml +++ b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml @@ -4,4 +4,3 @@ description: "Support module for paramconverter testing." package: Testing version: VERSION core: 8.x -hidden: TRUE diff --git a/core/modules/system/tests/modules/path_test/path_test.info.yml b/core/modules/system/tests/modules/path_test/path_test.info.yml index 57a5893..ec7e405 100644 --- a/core/modules/system/tests/modules/path_test/path_test.info.yml +++ b/core/modules/system/tests/modules/path_test/path_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for path hook testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/plugin_test/plugin_test.info.yml b/core/modules/system/tests/modules/plugin_test/plugin_test.info.yml index 087ce08..950a1d0 100644 --- a/core/modules/system/tests/modules/plugin_test/plugin_test.info.yml +++ b/core/modules/system/tests/modules/plugin_test/plugin_test.info.yml @@ -4,4 +4,3 @@ description: 'Test that plugins can provide plugins and provide namespace discov package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/requirements1_test/requirements1_test.info.yml b/core/modules/system/tests/modules/requirements1_test/requirements1_test.info.yml index 789cf27..6288bf3 100644 --- a/core/modules/system/tests/modules/requirements1_test/requirements1_test.info.yml +++ b/core/modules/system/tests/modules/requirements1_test/requirements1_test.info.yml @@ -4,4 +4,3 @@ description: 'Tests that a module is not installed when it fails hook_requiremen package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml b/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml index ed093d8..670df80 100644 --- a/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml +++ b/core/modules/system/tests/modules/requirements2_test/requirements2_test.info.yml @@ -7,4 +7,3 @@ dependencies: package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/DefinedTestAccessCheck.php b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/DefinedTestAccessCheck.php index 7aa50dc..1469d20 100644 --- a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/DefinedTestAccessCheck.php +++ b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/DefinedTestAccessCheck.php @@ -8,8 +8,6 @@ namespace Drupal\router_test\Access; use Drupal\Core\Routing\Access\AccessInterface; -use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -18,9 +16,15 @@ class DefinedTestAccessCheck implements AccessInterface { /** - * {@inheritdoc} + * Checks access. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route) { if ($route->getRequirement('_test_access') === 'TRUE') { return static::ALLOW; } diff --git a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/TestAccessCheck.php b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/TestAccessCheck.php index eda7560..d8af2c6 100644 --- a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/TestAccessCheck.php +++ b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/Access/TestAccessCheck.php @@ -8,9 +8,6 @@ namespace Drupal\router_test\Access; use Drupal\Core\Routing\Access\AccessInterface; -use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Access check for test routes. @@ -18,9 +15,12 @@ class TestAccessCheck implements AccessInterface { /** - * Implements AccessCheckInterface::access(). + * Checks access. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access() { // No opinion, so other access checks should decide if access should be // allowed or not. return static::DENY; diff --git a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php index 39fa645..a2fcad0 100644 --- a/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php +++ b/core/modules/system/tests/modules/router_test_directory/lib/Drupal/router_test/RouteTestSubscriber.php @@ -17,12 +17,10 @@ class RouteTestSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ - protected function alterRoutes(RouteCollection $collection, $provider) { - if ($provider == 'router_test') { - $route = $collection->get('router_test.6'); - // Change controller method from test1 to test5. - $route->setDefault('_content', '\Drupal\router_test\TestControllers::test5'); - } + protected function alterRoutes(RouteCollection $collection) { + $route = $collection->get('router_test.6'); + // Change controller method from test1 to test5. + $route->setDefault('_content', '\Drupal\router_test\TestControllers::test5'); } } diff --git a/core/modules/system/tests/modules/router_test_directory/router_test.info.yml b/core/modules/system/tests/modules/router_test_directory/router_test.info.yml index 915f0ff..27a0827 100644 --- a/core/modules/system/tests/modules/router_test_directory/router_test.info.yml +++ b/core/modules/system/tests/modules/router_test_directory/router_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for routing testing. In a directory that does not m package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/service_provider_test/service_provider_test.info.yml b/core/modules/system/tests/modules/service_provider_test/service_provider_test.info.yml index 620c897..ba84020 100644 --- a/core/modules/system/tests/modules/service_provider_test/service_provider_test.info.yml +++ b/core/modules/system/tests/modules/service_provider_test/service_provider_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for service provider testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/session_test/session_test.info.yml b/core/modules/system/tests/modules/session_test/session_test.info.yml index 9e80a1c..b9abfef 100644 --- a/core/modules/system/tests/modules/session_test/session_test.info.yml +++ b/core/modules/system/tests/modules/session_test/session_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for session data testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml b/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml index e3ac2dc..e99d48e 100644 --- a/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml +++ b/core/modules/system/tests/modules/system_dependencies_test/system_dependencies_test.info.yml @@ -4,6 +4,5 @@ description: 'Support module for testing system dependencies.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - _missing_dependency diff --git a/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml b/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml index 4f6a8c0..148bd3b 100644 --- a/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml +++ b/core/modules/system/tests/modules/system_incompatible_core_version_dependencies_test/system_incompatible_core_version_dependencies_test.info.yml @@ -4,6 +4,5 @@ description: 'Support module for testing system dependencies.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - system_incompatible_core_version_test diff --git a/core/modules/system/tests/modules/system_incompatible_core_version_test/system_incompatible_core_version_test.info.yml b/core/modules/system/tests/modules/system_incompatible_core_version_test/system_incompatible_core_version_test.info.yml index 47dce2b..d99300b 100644 --- a/core/modules/system/tests/modules/system_incompatible_core_version_test/system_incompatible_core_version_test.info.yml +++ b/core/modules/system/tests/modules/system_incompatible_core_version_test/system_incompatible_core_version_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing system dependencies.' package: Testing version: VERSION core: 5.x -hidden: true diff --git a/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml b/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml index 244235f..55e004b 100644 --- a/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml +++ b/core/modules/system/tests/modules/system_incompatible_module_version_dependencies_test/system_incompatible_module_version_dependencies_test.info.yml @@ -4,6 +4,5 @@ description: 'Support module for testing system dependencies.' package: Testing version: VERSION core: 8.x -hidden: true dependencies: - 'system_incompatible_module_version_test (>2.0)' diff --git a/core/modules/system/tests/modules/system_incompatible_module_version_test/system_incompatible_module_version_test.info.yml b/core/modules/system/tests/modules/system_incompatible_module_version_test/system_incompatible_module_version_test.info.yml index 78c1a90..f0076b1 100644 --- a/core/modules/system/tests/modules/system_incompatible_module_version_test/system_incompatible_module_version_test.info.yml +++ b/core/modules/system/tests/modules/system_incompatible_module_version_test/system_incompatible_module_version_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing system dependencies.' package: Testing version: '1.0' core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml index 3561097..12ad6e2 100644 --- a/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml +++ b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml @@ -4,4 +4,3 @@ description: 'Provides a malfunctioning mail service for testing purposes.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/system_module_test/lib/Drupal/system_module_test/EventSubscriber/HtmlPageSubscriber.php b/core/modules/system/tests/modules/system_module_test/lib/Drupal/system_module_test/EventSubscriber/HtmlPageSubscriber.php new file mode 100644 index 0000000..f64cd54 --- /dev/null +++ b/core/modules/system/tests/modules/system_module_test/lib/Drupal/system_module_test/EventSubscriber/HtmlPageSubscriber.php @@ -0,0 +1,45 @@ +getControllerResult()) && $page instanceof HtmlPage) { + $metatags =& $page->getMetaElements(); + foreach ($metatags as $key => $tag) { + // Remove the HTML5 mobile meta-tags. + if (in_array($tag->getName(), array('MobileOptimized', 'HandheldFriendly', 'viewport', 'cleartype'))) { + unset($metatags[$key]); + } + } + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + // Execute between + // \Drupal\Core\EventSubscriber\HtmlViewSubscriber::onHtmlFragment and + // \Drupal\Core\EventSubscriber\HtmlViewSubscriber::onHtmlPage and + $events[KernelEvents::VIEW][] = array('onHtmlPage', 60); + return $events; + } + +} diff --git a/core/modules/system/tests/modules/system_module_test/system_module_test.info.yml b/core/modules/system/tests/modules/system_module_test/system_module_test.info.yml index c4403d3..6b787f5 100644 --- a/core/modules/system/tests/modules/system_module_test/system_module_test.info.yml +++ b/core/modules/system/tests/modules/system_module_test/system_module_test.info.yml @@ -4,4 +4,3 @@ description: 'Provides hook implementations for testing System module functional package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/system_module_test/system_module_test.module b/core/modules/system/tests/modules/system_module_test/system_module_test.module index 44dd782..8d67e7a 100644 --- a/core/modules/system/tests/modules/system_module_test/system_module_test.module +++ b/core/modules/system/tests/modules/system_module_test/system_module_test.module @@ -5,13 +5,3 @@ * Provides System module hook implementations for testing purposes. */ -/** - * Implements hook_html_head_alter(). - */ -function system_module_test_html_head_alter(&$head_elements) { - // Remove the HTML5 mobile meta-tags. - unset($head_elements['MobileOptimized']); - unset($head_elements['HandheldFriendly']); - unset($head_elements['viewport']); - unset($head_elements['cleartype']); -} diff --git a/core/modules/system/tests/modules/system_module_test/system_module_test.services.yml b/core/modules/system/tests/modules/system_module_test/system_module_test.services.yml new file mode 100644 index 0000000..3abeb5e --- /dev/null +++ b/core/modules/system/tests/modules/system_module_test/system_module_test.services.yml @@ -0,0 +1,5 @@ +services: + system_module_test.html_page_subscriber: + class: Drupal\system_module_test\EventSubscriber\HtmlPageSubscriber + tags: + - { name: event_subscriber } diff --git a/core/modules/system/tests/modules/system_test/system_test.info.yml b/core/modules/system/tests/modules/system_test/system_test.info.yml index eff3630..7228709 100644 --- a/core/modules/system/tests/modules/system_test/system_test.info.yml +++ b/core/modules/system/tests/modules/system_test/system_test.info.yml @@ -4,7 +4,6 @@ description: 'Support module for system testing.' package: Testing version: VERSION core: 8.x -hidden: true configure: system_test.configure configure_parameters: foo: bar diff --git a/core/modules/system/tests/modules/test_page_test/test_page_test.info.yml b/core/modules/system/tests/modules/test_page_test/test_page_test.info.yml index 053deb2..f01c597 100644 --- a/core/modules/system/tests/modules/test_page_test/test_page_test.info.yml +++ b/core/modules/system/tests/modules/test_page_test/test_page_test.info.yml @@ -4,4 +4,3 @@ description: 'Provides a test page for automated tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/theme_page_test/theme_page_test.info.yml b/core/modules/system/tests/modules/theme_page_test/theme_page_test.info.yml index 587e8a1..da50bb1 100644 --- a/core/modules/system/tests/modules/theme_page_test/theme_page_test.info.yml +++ b/core/modules/system/tests/modules/theme_page_test/theme_page_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for theme system testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml index 95f429f..fe61d0d 100644 --- a/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml +++ b/core/modules/system/tests/modules/theme_suggestions_test/theme_suggestions_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing theme suggestions.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/theme_test/theme_test.info.yml b/core/modules/system/tests/modules/theme_test/theme_test.info.yml index 863e112..0fe8c10 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.info.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for theme system testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/transliterate_test/transliterate_test.info.yml b/core/modules/system/tests/modules/transliterate_test/transliterate_test.info.yml index 15a724e..9eeddd5 100644 --- a/core/modules/system/tests/modules/transliterate_test/transliterate_test.info.yml +++ b/core/modules/system/tests/modules/transliterate_test/transliterate_test.info.yml @@ -4,4 +4,3 @@ description: 'Helper module for Transliteration system tests.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml index 270352f..5aee1e2 100644 --- a/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml +++ b/core/modules/system/tests/modules/twig_extension_test/twig_extension_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing Twig extensions.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_a/twig_namespace_a.info.yml b/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_a/twig_namespace_a.info.yml index d007f21..7584b57 100644 --- a/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_a/twig_namespace_a.info.yml +++ b/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_a/twig_namespace_a.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Twig namespace testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_b/twig_namespace_b.info.yml b/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_b/twig_namespace_b.info.yml index d007f21..7584b57 100644 --- a/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_b/twig_namespace_b.info.yml +++ b/core/modules/system/tests/modules/twig_theme_test/modules/twig_namespace_b/twig_namespace_b.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Twig namespace testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.info.yml b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.info.yml index f340341..7515315 100644 --- a/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.info.yml +++ b/core/modules/system/tests/modules/twig_theme_test/twig_theme_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for Twig theme system testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.info.yml b/core/modules/system/tests/modules/update_script_test/update_script_test.info.yml index cd04f16..b582c1a 100644 --- a/core/modules/system/tests/modules/update_script_test/update_script_test.info.yml +++ b/core/modules/system/tests/modules/update_script_test/update_script_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update script testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml b/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml index d9f07cd..4162fd0 100644 --- a/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml +++ b/core/modules/system/tests/modules/update_test_0/update_test_0.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_1/update_test_1.info.yml b/core/modules/system/tests/modules/update_test_1/update_test_1.info.yml index 8ac616f..5a7e2e1 100644 --- a/core/modules/system/tests/modules/update_test_1/update_test_1.info.yml +++ b/core/modules/system/tests/modules/update_test_1/update_test_1.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_2/update_test_2.info.yml b/core/modules/system/tests/modules/update_test_2/update_test_2.info.yml index 8ac616f..5a7e2e1 100644 --- a/core/modules/system/tests/modules/update_test_2/update_test_2.info.yml +++ b/core/modules/system/tests/modules/update_test_2/update_test_2.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_3/update_test_3.info.yml b/core/modules/system/tests/modules/update_test_3/update_test_3.info.yml index 8ac616f..5a7e2e1 100644 --- a/core/modules/system/tests/modules/update_test_3/update_test_3.info.yml +++ b/core/modules/system/tests/modules/update_test_3/update_test_3.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml index a41ace9..b9aefba 100644 --- a/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml +++ b/core/modules/system/tests/modules/update_test_invalid_hook/update_test_invalid_hook.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml index 3ac4a35..2b47a04 100644 --- a/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml +++ b/core/modules/system/tests/modules/update_test_with_7x/update_test_with_7x.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/system/tests/modules/url_alter_test/url_alter_test.info.yml b/core/modules/system/tests/modules/url_alter_test/url_alter_test.info.yml index 699683e..95aba30 100644 --- a/core/modules/system/tests/modules/url_alter_test/url_alter_test.info.yml +++ b/core/modules/system/tests/modules/url_alter_test/url_alter_test.info.yml @@ -4,4 +4,3 @@ description: 'A support module to test altering the inbound and outbound path.' core: 8.x package: Testing version: VERSION -hidden: true diff --git a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml index 48c5b2b..2edc823 100644 --- a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml +++ b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml @@ -3,7 +3,6 @@ type: theme description: 'Test theme which acts as a base theme for other test subthemes.' version: VERSION core: 8.x -hidden: true stylesheets: all: - base-add.css diff --git a/core/modules/system/tests/themes/test_invalid_basetheme/test_invalid_basetheme.info.yml b/core/modules/system/tests/themes/test_invalid_basetheme/test_invalid_basetheme.info.yml index a8d2bfe..f29a4d4 100644 --- a/core/modules/system/tests/themes/test_invalid_basetheme/test_invalid_basetheme.info.yml +++ b/core/modules/system/tests/themes/test_invalid_basetheme/test_invalid_basetheme.info.yml @@ -4,4 +4,3 @@ description: 'Test theme which has a non-existent base theme.' version: VERSION core: 8.x 'base theme': not_real_test_basetheme -hidden: true diff --git a/core/modules/system/tests/themes/test_invalid_engine/test_invalid_engine.info.yml b/core/modules/system/tests/themes/test_invalid_engine/test_invalid_engine.info.yml index 3f9ed1e..229ede4 100644 --- a/core/modules/system/tests/themes/test_invalid_engine/test_invalid_engine.info.yml +++ b/core/modules/system/tests/themes/test_invalid_engine/test_invalid_engine.info.yml @@ -3,5 +3,4 @@ type: theme description: 'Test theme which has a non-existent theme engine.' version: VERSION core: 8.x -hidden: true engine: not_real_engine diff --git a/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml b/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml index 6123684..6adfee4 100644 --- a/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml +++ b/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml @@ -4,7 +4,6 @@ description: 'Test theme which uses test_basetheme as the base theme.' version: VERSION core: 8.x 'base theme': test_basetheme -hidden: true stylesheets: all: - css/sub-add.css diff --git a/core/modules/system/tests/themes/test_theme/test_theme.info.yml b/core/modules/system/tests/themes/test_theme/test_theme.info.yml index f41fafc..d845875 100644 --- a/core/modules/system/tests/themes/test_theme/test_theme.info.yml +++ b/core/modules/system/tests/themes/test_theme/test_theme.info.yml @@ -13,7 +13,6 @@ type: theme description: 'Theme for testing the theme system' version: VERSION core: 8.x -hidden: true stylesheets-remove: - system.module.css settings: diff --git a/core/modules/system/tests/themes/test_theme_having_veery_long_name_which_is_too_long/test_theme_having_veery_long_name_which_is_too_long.info.yml b/core/modules/system/tests/themes/test_theme_having_veery_long_name_which_is_too_long/test_theme_having_veery_long_name_which_is_too_long.info.yml index fa0a207..46432b4 100644 --- a/core/modules/system/tests/themes/test_theme_having_veery_long_name_which_is_too_long/test_theme_having_veery_long_name_which_is_too_long.info.yml +++ b/core/modules/system/tests/themes/test_theme_having_veery_long_name_which_is_too_long/test_theme_having_veery_long_name_which_is_too_long.info.yml @@ -2,4 +2,3 @@ type: theme core: 8.x name: 'Test theme with a too long name' version: VERSION -hidden: true diff --git a/core/modules/system/tests/themes/test_theme_phptemplate/test_theme_phptemplate.info.yml b/core/modules/system/tests/themes/test_theme_phptemplate/test_theme_phptemplate.info.yml index e1a876a..da076b1 100644 --- a/core/modules/system/tests/themes/test_theme_phptemplate/test_theme_phptemplate.info.yml +++ b/core/modules/system/tests/themes/test_theme_phptemplate/test_theme_phptemplate.info.yml @@ -4,4 +4,3 @@ description: 'Theme for testing the theme system with the PHPTemplate engine' version: VERSION core: 8.x engine: phptemplate -hidden: true diff --git a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml index 6477894..c39264f 100644 --- a/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml +++ b/core/modules/taxonomy/config/install/views.view.taxonomy_term.yml @@ -165,7 +165,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true uses_fields: false provider: views row: diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php index ab1db7d..b1450b5 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php @@ -11,9 +11,8 @@ use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\String; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\Query\QueryInterface; -use Drupal\field\FieldInfo; -use Drupal\taxonomy\TermStorageInterface; use Drupal\taxonomy\VocabularyInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -33,33 +32,23 @@ class TermAutocompleteController implements ContainerInjectionInterface { protected $termEntityQuery; /** - * Field info service. + * Entity manager. * - * @var \Drupal\field\FieldInfo + * @var \Drupal\Core\Entity\EntityManagerInterface */ - protected $fieldInfo; - - /** - * Term storage. - * - * @var \Drupal\taxonomy\TermStorageInterface - */ - protected $termStorage; + protected $entityManager; /** * Constructs a new \Drupal\taxonomy\Controller\TermAutocompleteController object. * * @param \Drupal\Core\Entity\Query\QueryInterface $term_entity_query * The entity query service. - * @param \Drupal\field\FieldInfo $field_info - * The field info service. - * @param \Drupal\taxonomy\TermStorageInterface $term_storage - * The term storage. + * @param \Drupal\Core\Entity\EntityManagerInterface + * The entity manager. */ - public function __construct(QueryInterface $term_entity_query, FieldInfo $field_info, TermStorageInterface $term_storage) { + public function __construct(QueryInterface $term_entity_query, EntityManagerInterface $entity_manager) { $this->termEntityQuery = $term_entity_query; - $this->fieldInfo = $field_info; - $this->termStorage = $term_storage; + $this->entityManager = $entity_manager; } /** @@ -68,8 +57,7 @@ class TermAutocompleteController implements ContainerInjectionInterface { public static function create(ContainerInterface $container) { return new static( $container->get('entity.query')->get('taxonomy_term'), - $container->get('field.info'), - $container->get('entity.manager')->getStorage('taxonomy_term') + $container->get('entity.manager') ); } @@ -110,11 +98,14 @@ class TermAutocompleteController implements ContainerInjectionInterface { $tags_typed = $request->query->get('q'); // Make sure the field exists and is a taxonomy field. - if (!($field = $this->fieldInfo->getField($entity_type, $field_name)) || $field->getType() !== 'taxonomy_term_reference') { + $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type); + + if (!isset($field_storage_definitions[$field_name]) || $field_storage_definitions[$field_name]->getType() !== 'taxonomy_term_reference') { // Error string. The JavaScript handler will realize this is not JSON and // will display it as debugging information. return new Response(t('Taxonomy field @field_name not found.', array('@field_name' => $field_name)), 403); } + $field = $field_storage_definitions[$field_name]; // The user enters a comma-separated list of tags. We only autocomplete the // last tag. @@ -193,7 +184,7 @@ class TermAutocompleteController implements ContainerInjectionInterface { $prefix = count($tags_typed) ? Tags::implode($tags_typed) . ', ' : ''; if (!empty($tids)) { - $terms = $this->termStorage->loadMultiple(array_keys($tids)); + $terms = $this->entityManager->getStorage('taxonomy_term')->loadMultiple(array_keys($tids)); foreach ($terms as $term) { // Term names containing commas or quotes must be wrapped in quotes. $name = Tags::encode($term->getName()); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Vocabulary.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Vocabulary.php index 00ae49c..d492d7a 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Vocabulary.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Vocabulary.php @@ -7,9 +7,8 @@ namespace Drupal\taxonomy\Entity; -use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\ConfigEntityBundleBase; use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\field\Field; use Drupal\taxonomy\VocabularyInterface; /** @@ -44,7 +43,7 @@ * } * ) */ -class Vocabulary extends ConfigEntityBase implements VocabularyInterface { +class Vocabulary extends ConfigEntityBundleBase implements VocabularyInterface { /** * The taxonomy vocabulary ID. @@ -99,14 +98,11 @@ class Vocabulary extends ConfigEntityBase implements VocabularyInterface { public function postSave(EntityStorageInterface $storage, $update = TRUE) { parent::postSave($storage, $update); - if (!$update) { - entity_invoke_bundle_hook('create', 'taxonomy_term', $this->id()); - } - elseif ($this->getOriginalId() != $this->id() && !$this->isSyncing()) { + if ($update && $this->getOriginalId() != $this->id() && !$this->isSyncing()) { // Reflect machine name changes in the definitions of existing 'taxonomy' // fields. $field_ids = array(); - $field_map = Field::fieldInfo()->getFieldMap(); + $field_map = \Drupal::entityManager()->getFieldMap(); foreach ($field_map as $entity_type => $fields) { foreach ($fields as $field => $info) { if ($info['type'] == 'taxonomy_term_reference') { @@ -131,8 +127,6 @@ class Vocabulary extends ConfigEntityBase implements VocabularyInterface { $field->save(); } } - // Update bundles. - entity_invoke_bundle_hook('rename', 'taxonomy_term', $this->getOriginalId(), $this->id()); } $storage->resetCache($update ? array($this->getOriginalId()) : array()); } @@ -187,8 +181,6 @@ class Vocabulary extends ConfigEntityBase implements VocabularyInterface { } } } - // Reset caches. - $storage->resetCache(array_keys($vocabularies)); } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/LinkFormatter.php index eb0515c..856b73e 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/LinkFormatter.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldFormatter/LinkFormatter.php @@ -51,6 +51,8 @@ class LinkFormatter extends TaxonomyFormatterBase { // formatter output and should not be rendered in the field template. unset($item->_attributes); } + + $elements[$delta]['#cache']['tags'] = $item->entity->getCacheTag(); } } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php index a893063..cc8ef7e 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Field/FieldType/TaxonomyTermReferenceItem.php @@ -9,6 +9,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; +use Drupal\Core\Form\OptGroup; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\AllowedValuesInterface; @@ -48,7 +49,7 @@ class TaxonomyTermReferenceItem extends EntityReferenceItem implements AllowedVa public function getPossibleValues(AccountInterface $account = NULL) { // Flatten options firstly, because Possible Options may contain group // arrays. - $flatten_options = \Drupal::formBuilder()->flattenOptions($this->getPossibleOptions($account)); + $flatten_options = OptGroup::flattenOptions($this->getPossibleOptions($account)); return array_keys($flatten_options); } @@ -65,7 +66,7 @@ class TaxonomyTermReferenceItem extends EntityReferenceItem implements AllowedVa public function getSettableValues(AccountInterface $account = NULL) { // Flatten options firstly, because Settable Options may contain group // arrays. - $flatten_options = \Drupal::formBuilder()->flattenOptions($this->getSettableOptions($account)); + $flatten_options = OptGroup::flattenOptions($this->getSettableOptions($account)); return array_keys($flatten_options); } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php index cddfefd..c7035c9 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php @@ -8,13 +8,19 @@ namespace Drupal\taxonomy\Tests; use Drupal\Core\Datetime\DrupalDateTime; +use Drupal\Core\Field\FieldDefinitionInterface; /** * Test for legacy node bug. */ class LegacyTest extends TaxonomyTestBase { - protected $profile = 'standard'; + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('node', 'datetime'); public static function getInfo() { return array( @@ -26,6 +32,42 @@ class LegacyTest extends TaxonomyTestBase { function setUp() { parent::setUp(); + + // Create a tags vocabulary for the 'article' content type. + $vocabulary = entity_create('taxonomy_vocabulary', array( + 'name' => 'Tags', + 'vid' => 'tags', + )); + $vocabulary->save(); + $field_name = 'field_' . $vocabulary->id(); + + entity_create('field_config', array( + 'name' => $field_name, + 'entity_type' => 'node', + 'type' => 'taxonomy_term_reference', + 'cardinality' => FieldDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => $vocabulary->id(), + 'parent' => 0, + ), + ), + ), + ))->save(); + entity_create('field_instance_config', array( + 'entity_type' => 'node', + 'field_name' => $field_name, + 'bundle' => 'article', + 'label' => 'Tags', + ))->save(); + + entity_get_form_display('node', 'article', 'default') + ->setComponent($field_name, array( + 'type' => 'taxonomy_autocomplete', + )) + ->save(); + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'administer nodes', 'bypass node access')); $this->drupalLogin($this->admin_user); } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php index afac0db..8ec55e7 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldMultipleVocabularyTest.php @@ -8,6 +8,7 @@ namespace Drupal\taxonomy\Tests; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\field\Entity\FieldConfig; /** * Tests a taxonomy term reference field that allows multiple vocabularies. @@ -122,7 +123,7 @@ function testTaxonomyTermFieldMultipleVocabularies() { $this->assertNoText($term2->getName(), 'Term 2 name is not displayed.'); // Verify that field and instance settings are correct. - $field = field_info_field('entity_test', $this->field_name); + $field = FieldConfig::loadByName('entity_test', $this->field_name); $this->assertEqual(count($field->getSetting('allowed_values')), 1, 'Only one vocabulary is allowed for the field.'); // The widget should still be displayed. diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php index 1dd0aab..f61b91e 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermFieldTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\taxonomy\Tests; +use Drupal\field\Entity\FieldConfig; /** * Tests for taxonomy term field and formatter. @@ -170,7 +171,7 @@ function testTaxonomyTermFieldChangeMachineName() { $this->vocabulary->save(); // Check that the field instance is still attached to the vocabulary. - $field = field_info_field('entity_test', $this->field_name); + $field = FieldConfig::loadByName('entity_test', $this->field_name); $allowed_values = $field->getSetting('allowed_values'); $this->assertEqual($allowed_values[0]['vocabulary'], $new_name, 'Index 0: Machine name was updated correctly.'); $this->assertEqual($allowed_values[1]['vocabulary'], $new_name, 'Index 1: Machine name was updated correctly.'); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index 04bbf6c..d309e30 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -11,6 +11,7 @@ use Drupal\Component\Utility\Tags; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Component\Utility\String; +use Drupal\field\Entity\FieldConfig; /** * Tests for taxonomy term functions. @@ -237,7 +238,7 @@ function testNodeTermCreationAndDeletion() { $field_name = $this->randomName(); $tag = $this->randomName(); $message = t("Taxonomy field @field_name not found.", array('@field_name' => $field_name)); - $this->assertFalse(field_info_field('node', $field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name))); + $this->assertFalse(FieldConfig::loadByName('node', $field_name), format_string('Field %field_name does not exist.', array('%field_name' => $field_name))); $this->drupalGet('taxonomy/autocomplete/node/' . $field_name, array('query' => array('q' => $tag))); $this->assertRaw($message, 'Autocomplete returns correct error message when the taxonomy field does not exist.'); } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyCrudTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyCrudTest.php new file mode 100644 index 0000000..2bb922c --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyCrudTest.php @@ -0,0 +1,209 @@ + 'Taxonomy vocabularies', + 'description' => 'Test loading, saving and deleting vocabularies.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp(); + + $admin_user = $this->drupalCreateUser(array('create article content', 'administer taxonomy')); + $this->drupalLogin($admin_user); + $this->vocabulary = $this->createVocabulary(); + } + + /** + * Test deleting a taxonomy that contains terms. + */ + function testTaxonomyVocabularyDeleteWithTerms() { + // Delete any existing vocabularies. + foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) { + $vocabulary->delete(); + } + + // Assert that there are no terms left. + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'There are no terms remaining.'); + + // Create a new vocabulary and add a few terms to it. + $vocabulary = $this->createVocabulary(); + $terms = array(); + for ($i = 0; $i < 5; $i++) { + $terms[$i] = $this->createTerm($vocabulary); + } + + // Set up hierarchy. term 2 is a child of 1 and 4 a child of 1 and 2. + $terms[2]->parent = array($terms[1]->id()); + $terms[2]->save(); + $terms[4]->parent = array($terms[1]->id(), $terms[2]->id()); + $terms[4]->save(); + + // Assert that there are now 5 terms. + $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'There are 5 terms found.'); + + $vocabulary->delete(); + + // Assert that there are no terms left. + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'All terms are deleted.'); + } + + /** + * Ensure that the vocabulary static reset works correctly. + */ + function testTaxonomyVocabularyLoadStaticReset() { + $original_vocabulary = entity_load('taxonomy_vocabulary', $this->vocabulary->id()); + $this->assertTrue(is_object($original_vocabulary), 'Vocabulary loaded successfully.'); + $this->assertEqual($this->vocabulary->name, $original_vocabulary->name, 'Vocabulary loaded successfully.'); + + // Change the name and description. + $vocabulary = $original_vocabulary; + $vocabulary->name = $this->randomName(); + $vocabulary->description = $this->randomName(); + $vocabulary->save(); + + // Load the vocabulary. + $new_vocabulary = entity_load('taxonomy_vocabulary', $original_vocabulary->id()); + $this->assertEqual($new_vocabulary->name, $vocabulary->name, 'The vocabulary was loaded.'); + + // Delete the vocabulary. + $this->vocabulary->delete(); + $vocabularies = entity_load_multiple('taxonomy_vocabulary'); + $this->assertTrue(!isset($vocabularies[$this->vocabulary->id()]), 'The vocabulary was deleted.'); + } + + /** + * Tests for loading multiple vocabularies. + */ + function testTaxonomyVocabularyLoadMultiple() { + + // Delete any existing vocabularies. + foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) { + $vocabulary->delete(); + } + + // Create some vocabularies and assign weights. + $vocabulary1 = $this->createVocabulary(); + $vocabulary1->weight = 0; + $vocabulary1->save(); + $vocabulary2 = $this->createVocabulary(); + $vocabulary2->weight = 1; + $vocabulary2->save(); + $vocabulary3 = $this->createVocabulary(); + $vocabulary3->weight = 2; + $vocabulary3->save(); + + // Fetch the names for all vocabularies, confirm that they are keyed by + // machine name. + $names = taxonomy_vocabulary_get_names(); + $this->assertEqual($names[$vocabulary1->id()], $vocabulary1->id(), 'Vocabulary 1 name found.'); + + // Fetch the vocabularies with entity_load_multiple(), specifying IDs. + // Ensure they are returned in the same order as the original array. + $vocabularies = entity_load_multiple('taxonomy_vocabulary', array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id())); + $loaded_order = array_keys($vocabularies); + $expected_order = array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id()); + $this->assertIdentical($loaded_order, $expected_order); + + // Test loading vocabularies by their properties. + $controller = $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary'); + // Fetch vocabulary 1 by name. + $vocabulary = current($controller->loadByProperties(array('name' => $vocabulary1->name))); + $this->assertEqual($vocabulary->id(), $vocabulary1->id(), 'Vocabulary loaded successfully by name.'); + + // Fetch vocabulary 2 by name and ID. + $vocabulary = current($controller->loadByProperties(array( + 'name' => $vocabulary2->name, + 'vid' => $vocabulary2->id(), + ))); + $this->assertEqual($vocabulary->id(), $vocabulary2->id(), 'Vocabulary loaded successfully by name and ID.'); + } + + /** + * Tests that machine name changes are properly reflected. + */ + function testTaxonomyVocabularyChangeMachineName() { + // Add a field instance to the vocabulary. + entity_create('field_config', array( + 'name' => 'field_test', + 'entity_type' => 'taxonomy_term', + 'type' => 'test_field', + ))->save(); + entity_create('field_instance_config', array( + 'field_name' => 'field_test', + 'entity_type' => 'taxonomy_term', + 'bundle' => $this->vocabulary->id(), + ))->save(); + + // Change the machine name. + $old_name = $this->vocabulary->id(); + $new_name = drupal_strtolower($this->randomName()); + $this->vocabulary->vid = $new_name; + $this->vocabulary->save(); + + // Check that entity bundles are properly updated. + $info = entity_get_bundles('taxonomy_term'); + $this->assertFalse(isset($info[$old_name]), 'The old bundle name does not appear in entity_get_bundles().'); + $this->assertTrue(isset($info[$new_name]), 'The new bundle name appears in entity_get_bundles().'); + + // Check that the field instance is still attached to the vocabulary. + $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), 'The bundle name was updated correctly.'); + } + + /** + * Test uninstall and reinstall of the taxonomy module. + */ + function testUninstallReinstall() { + // Fields and field instances attached to taxonomy term bundles should be + // removed when the module is uninstalled. + $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); + $this->field_definition = array( + 'name' => $this->field_name, + 'entity_type' => 'taxonomy_term', + 'type' => 'text', + 'cardinality' => 4 + ); + entity_create('field_config', $this->field_definition)->save(); + $this->instance_definition = array( + 'field_name' => $this->field_name, + 'entity_type' => 'taxonomy_term', + 'bundle' => $this->vocabulary->id(), + 'label' => $this->randomName() . '_label', + ); + entity_create('field_instance_config', $this->instance_definition)->save(); + + require_once DRUPAL_ROOT . '/core/includes/install.inc'; + module_uninstall(array('taxonomy')); + \Drupal::moduleHandler()->install(array('taxonomy')); + + // Now create a vocabulary with the same name. All field instances + // connected to this vocabulary name should have been removed when the + // module was uninstalled. Creating a new field with the same name and + // an instance of this field on the same bundle name should be successful. + $this->vocabulary->enforceIsNew(); + $this->vocabulary->save(); + entity_create('field_config', $this->field_definition)->save(); + entity_create('field_instance_config', $this->instance_definition)->save(); + } +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php index 7b89802..6ed3e09 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php @@ -27,9 +27,6 @@ function testVocabularyPermissionsTaxonomyTerm() { // Vocabulary used for creating, removing and editing terms. $vocabulary = $this->createVocabulary(); - // Reset to permission static cache to get proper permissions. - $this->checkPermissions(array(), TRUE); - // Test as admin user. $user = $this->drupalCreateUser(array('administer taxonomy')); $this->drupalLogin($user); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php deleted file mode 100644 index de7338f..0000000 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyTest.php +++ /dev/null @@ -1,154 +0,0 @@ - 'Taxonomy vocabulary interface', - 'description' => 'Test the taxonomy vocabulary interface.', - 'group' => 'Taxonomy', - ); - } - - function setUp() { - parent::setUp(); - $this->admin_user = $this->drupalCreateUser(array('administer taxonomy')); - $this->drupalLogin($this->admin_user); - $this->vocabulary = $this->createVocabulary(); - } - - /** - * Create, edit and delete a vocabulary via the user interface. - */ - function testVocabularyInterface() { - // Visit the main taxonomy administration page. - $this->drupalGet('admin/structure/taxonomy'); - - // Create a new vocabulary. - $this->clickLink(t('Add vocabulary')); - $edit = array(); - $vid = drupal_strtolower($this->randomName()); - $edit['name'] = $this->randomName(); - $edit['description'] = $this->randomName(); - $edit['vid'] = $vid; - $this->drupalPostForm(NULL, $edit, t('Save')); - $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.'); - - // Edit the vocabulary. - $this->drupalGet('admin/structure/taxonomy'); - $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); - $this->clickLink(t('Edit vocabulary')); - $edit = array(); - $edit['name'] = $this->randomName(); - $this->drupalPostForm(NULL, $edit, t('Save')); - $this->drupalGet('admin/structure/taxonomy'); - $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); - - // Try to submit a vocabulary with a duplicate machine name. - $edit['vid'] = $vid; - $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); - $this->assertText(t('The machine-readable name is already in use. It must be unique.')); - - // Try to submit an invalid machine name. - $edit['vid'] = '!&^%'; - $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); - $this->assertText(t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); - - // Ensure that vocabulary titles are escaped properly. - $edit = array(); - $edit['name'] = 'Don\'t Panic'; - $edit['description'] = $this->randomName(); - $edit['vid'] = 'don_t_panic'; - $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); - - $site_name = \Drupal::config('system.site')->get('name'); - $this->assertTitle(t('Don\'t Panic | @site-name', array('@site-name' => $site_name)), 'The page title contains the escaped character.'); - $this->assertNoTitle(t('Don't Panic | @site-name', array('@site-name' => $site_name)), 'The page title does not contain an encoded character.'); - } - - /** - * Changing weights on the vocabulary overview with two or more vocabularies. - */ - function testTaxonomyAdminChangingWeights() { - // Create some vocabularies. - for ($i = 0; $i < 10; $i++) { - $this->createVocabulary(); - } - // Get all vocabularies and change their weights. - $vocabularies = entity_load_multiple('taxonomy_vocabulary'); - $edit = array(); - foreach ($vocabularies as $key => $vocabulary) { - $weight = -$vocabulary->weight; - $vocabularies[$key]->weight = $weight; - $edit['vocabularies[' . $key . '][weight]'] = $weight; - } - // Saving the new weights via the interface. - $this->drupalPostForm('admin/structure/taxonomy', $edit, t('Save')); - - // Load the vocabularies from the database. - $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); - $new_vocabularies = entity_load_multiple('taxonomy_vocabulary'); - - // Check that the weights are saved in the database correctly. - foreach ($vocabularies as $key => $vocabulary) { - $this->assertEqual($new_vocabularies[$key]->weight, $vocabularies[$key]->weight, 'The vocabulary weight was changed.'); - } - } - - /** - * Test the vocabulary overview with no vocabularies. - */ - function testTaxonomyAdminNoVocabularies() { - // Delete all vocabularies. - $vocabularies = entity_load_multiple('taxonomy_vocabulary'); - foreach ($vocabularies as $key => $vocabulary) { - $vocabulary->delete(); - } - // Confirm that no vocabularies are found in the database. - $this->assertFalse(entity_load_multiple('taxonomy_vocabulary'), 'No vocabularies found.'); - $this->drupalGet('admin/structure/taxonomy'); - // Check the default message for no vocabularies. - $this->assertText(t('No vocabularies available.')); - } - - /** - * Deleting a vocabulary. - */ - function testTaxonomyAdminDeletingVocabulary() { - // Create a vocabulary. - $vid = drupal_strtolower($this->randomName()); - $edit = array( - 'name' => $this->randomName(), - 'vid' => $vid, - ); - $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); - $this->assertText(t('Created new vocabulary'), 'New vocabulary was created.'); - - // Check the created vocabulary. - $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); - $vocabulary = entity_load('taxonomy_vocabulary', $vid); - $this->assertTrue($vocabulary, 'Vocabulary found.'); - - // Delete the vocabulary. - $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id()); - $this->clickLink(t('Delete')); - $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), '[confirm deletion] Asks for confirmation.'); - $this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), '[confirm deletion] Inform that all terms will be deleted.'); - - // Confirm deletion. - $this->drupalPostForm(NULL, NULL, t('Delete')); - $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), 'Vocabulary deleted.'); - $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); - $this->assertFalse(entity_load('taxonomy_vocabulary', $vid), 'Vocabulary not found.'); - } -} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUiTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUiTest.php new file mode 100644 index 0000000..112a597 --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUiTest.php @@ -0,0 +1,154 @@ + 'Taxonomy vocabulary interface', + 'description' => 'Test the taxonomy vocabulary interface.', + 'group' => 'Taxonomy', + ); + } + + function setUp() { + parent::setUp(); + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy')); + $this->drupalLogin($this->admin_user); + $this->vocabulary = $this->createVocabulary(); + } + + /** + * Create, edit and delete a vocabulary via the user interface. + */ + function testVocabularyInterface() { + // Visit the main taxonomy administration page. + $this->drupalGet('admin/structure/taxonomy'); + + // Create a new vocabulary. + $this->clickLink(t('Add vocabulary')); + $edit = array(); + $vid = drupal_strtolower($this->randomName()); + $edit['name'] = $this->randomName(); + $edit['description'] = $this->randomName(); + $edit['vid'] = $vid; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertRaw(t('Created new vocabulary %name.', array('%name' => $edit['name'])), 'Vocabulary created successfully.'); + + // Edit the vocabulary. + $this->drupalGet('admin/structure/taxonomy'); + $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); + $this->clickLink(t('Edit vocabulary')); + $edit = array(); + $edit['name'] = $this->randomName(); + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->drupalGet('admin/structure/taxonomy'); + $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.'); + + // Try to submit a vocabulary with a duplicate machine name. + $edit['vid'] = $vid; + $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); + $this->assertText(t('The machine-readable name is already in use. It must be unique.')); + + // Try to submit an invalid machine name. + $edit['vid'] = '!&^%'; + $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); + $this->assertText(t('The machine-readable name must contain only lowercase letters, numbers, and underscores.')); + + // Ensure that vocabulary titles are escaped properly. + $edit = array(); + $edit['name'] = 'Don\'t Panic'; + $edit['description'] = $this->randomName(); + $edit['vid'] = 'don_t_panic'; + $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); + + $site_name = \Drupal::config('system.site')->get('name'); + $this->assertTitle(t('Don\'t Panic | @site-name', array('@site-name' => $site_name)), 'The page title contains the escaped character.'); + $this->assertNoTitle(t('Don't Panic | @site-name', array('@site-name' => $site_name)), 'The page title does not contain an encoded character.'); + } + + /** + * Changing weights on the vocabulary overview with two or more vocabularies. + */ + function testTaxonomyAdminChangingWeights() { + // Create some vocabularies. + for ($i = 0; $i < 10; $i++) { + $this->createVocabulary(); + } + // Get all vocabularies and change their weights. + $vocabularies = entity_load_multiple('taxonomy_vocabulary'); + $edit = array(); + foreach ($vocabularies as $key => $vocabulary) { + $weight = -$vocabulary->weight; + $vocabularies[$key]->weight = $weight; + $edit['vocabularies[' . $key . '][weight]'] = $weight; + } + // Saving the new weights via the interface. + $this->drupalPostForm('admin/structure/taxonomy', $edit, t('Save')); + + // Load the vocabularies from the database. + $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); + $new_vocabularies = entity_load_multiple('taxonomy_vocabulary'); + + // Check that the weights are saved in the database correctly. + foreach ($vocabularies as $key => $vocabulary) { + $this->assertEqual($new_vocabularies[$key]->weight, $vocabularies[$key]->weight, 'The vocabulary weight was changed.'); + } + } + + /** + * Test the vocabulary overview with no vocabularies. + */ + function testTaxonomyAdminNoVocabularies() { + // Delete all vocabularies. + $vocabularies = entity_load_multiple('taxonomy_vocabulary'); + foreach ($vocabularies as $key => $vocabulary) { + $vocabulary->delete(); + } + // Confirm that no vocabularies are found in the database. + $this->assertFalse(entity_load_multiple('taxonomy_vocabulary'), 'No vocabularies found.'); + $this->drupalGet('admin/structure/taxonomy'); + // Check the default message for no vocabularies. + $this->assertText(t('No vocabularies available.')); + } + + /** + * Deleting a vocabulary. + */ + function testTaxonomyAdminDeletingVocabulary() { + // Create a vocabulary. + $vid = drupal_strtolower($this->randomName()); + $edit = array( + 'name' => $this->randomName(), + 'vid' => $vid, + ); + $this->drupalPostForm('admin/structure/taxonomy/add', $edit, t('Save')); + $this->assertText(t('Created new vocabulary'), 'New vocabulary was created.'); + + // Check the created vocabulary. + $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); + $vocabulary = entity_load('taxonomy_vocabulary', $vid); + $this->assertTrue($vocabulary, 'Vocabulary found.'); + + // Delete the vocabulary. + $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id()); + $this->clickLink(t('Delete')); + $this->assertRaw(t('Are you sure you want to delete the vocabulary %name?', array('%name' => $vocabulary->name)), '[confirm deletion] Asks for confirmation.'); + $this->assertText(t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'), '[confirm deletion] Inform that all terms will be deleted.'); + + // Confirm deletion. + $this->drupalPostForm(NULL, NULL, t('Delete')); + $this->assertRaw(t('Deleted vocabulary %name.', array('%name' => $vocabulary->name)), 'Vocabulary deleted.'); + $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary')->resetCache(); + $this->assertFalse(entity_load('taxonomy_vocabulary', $vid), 'Vocabulary not found.'); + } +} diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php deleted file mode 100644 index ae757f3..0000000 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyUnitTest.php +++ /dev/null @@ -1,209 +0,0 @@ - 'Taxonomy vocabularies', - 'description' => 'Test loading, saving and deleting vocabularies.', - 'group' => 'Taxonomy', - ); - } - - function setUp() { - parent::setUp(); - - $admin_user = $this->drupalCreateUser(array('create article content', 'administer taxonomy')); - $this->drupalLogin($admin_user); - $this->vocabulary = $this->createVocabulary(); - } - - /** - * Test deleting a taxonomy that contains terms. - */ - function testTaxonomyVocabularyDeleteWithTerms() { - // Delete any existing vocabularies. - foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) { - $vocabulary->delete(); - } - - // Assert that there are no terms left. - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'There are no terms remaining.'); - - // Create a new vocabulary and add a few terms to it. - $vocabulary = $this->createVocabulary(); - $terms = array(); - for ($i = 0; $i < 5; $i++) { - $terms[$i] = $this->createTerm($vocabulary); - } - - // Set up hierarchy. term 2 is a child of 1 and 4 a child of 1 and 2. - $terms[2]->parent = array($terms[1]->id()); - $terms[2]->save(); - $terms[4]->parent = array($terms[1]->id(), $terms[2]->id()); - $terms[4]->save(); - - // Assert that there are now 5 terms. - $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'There are 5 terms found.'); - - $vocabulary->delete(); - - // Assert that there are no terms left. - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {taxonomy_term_data}')->fetchField(), 'All terms are deleted.'); - } - - /** - * Ensure that the vocabulary static reset works correctly. - */ - function testTaxonomyVocabularyLoadStaticReset() { - $original_vocabulary = entity_load('taxonomy_vocabulary', $this->vocabulary->id()); - $this->assertTrue(is_object($original_vocabulary), 'Vocabulary loaded successfully.'); - $this->assertEqual($this->vocabulary->name, $original_vocabulary->name, 'Vocabulary loaded successfully.'); - - // Change the name and description. - $vocabulary = $original_vocabulary; - $vocabulary->name = $this->randomName(); - $vocabulary->description = $this->randomName(); - $vocabulary->save(); - - // Load the vocabulary. - $new_vocabulary = entity_load('taxonomy_vocabulary', $original_vocabulary->id()); - $this->assertEqual($new_vocabulary->name, $vocabulary->name, 'The vocabulary was loaded.'); - - // Delete the vocabulary. - $this->vocabulary->delete(); - $vocabularies = entity_load_multiple('taxonomy_vocabulary'); - $this->assertTrue(!isset($vocabularies[$this->vocabulary->id()]), 'The vocabulary was deleted.'); - } - - /** - * Tests for loading multiple vocabularies. - */ - function testTaxonomyVocabularyLoadMultiple() { - - // Delete any existing vocabularies. - foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) { - $vocabulary->delete(); - } - - // Create some vocabularies and assign weights. - $vocabulary1 = $this->createVocabulary(); - $vocabulary1->weight = 0; - $vocabulary1->save(); - $vocabulary2 = $this->createVocabulary(); - $vocabulary2->weight = 1; - $vocabulary2->save(); - $vocabulary3 = $this->createVocabulary(); - $vocabulary3->weight = 2; - $vocabulary3->save(); - - // Fetch the names for all vocabularies, confirm that they are keyed by - // machine name. - $names = taxonomy_vocabulary_get_names(); - $this->assertEqual($names[$vocabulary1->id()], $vocabulary1->id(), 'Vocabulary 1 name found.'); - - // Fetch the vocabularies with entity_load_multiple(), specifying IDs. - // Ensure they are returned in the same order as the original array. - $vocabularies = entity_load_multiple('taxonomy_vocabulary', array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id())); - $loaded_order = array_keys($vocabularies); - $expected_order = array($vocabulary3->id(), $vocabulary2->id(), $vocabulary1->id()); - $this->assertIdentical($loaded_order, $expected_order); - - // Test loading vocabularies by their properties. - $controller = $this->container->get('entity.manager')->getStorage('taxonomy_vocabulary'); - // Fetch vocabulary 1 by name. - $vocabulary = current($controller->loadByProperties(array('name' => $vocabulary1->name))); - $this->assertEqual($vocabulary->id(), $vocabulary1->id(), 'Vocabulary loaded successfully by name.'); - - // Fetch vocabulary 2 by name and ID. - $vocabulary = current($controller->loadByProperties(array( - 'name' => $vocabulary2->name, - 'vid' => $vocabulary2->id(), - ))); - $this->assertEqual($vocabulary->id(), $vocabulary2->id(), 'Vocabulary loaded successfully by name and ID.'); - } - - /** - * Tests that machine name changes are properly reflected. - */ - function testTaxonomyVocabularyChangeMachineName() { - // Add a field instance to the vocabulary. - entity_create('field_config', array( - 'name' => 'field_test', - 'entity_type' => 'taxonomy_term', - 'type' => 'test_field', - ))->save(); - entity_create('field_instance_config', array( - 'field_name' => 'field_test', - 'entity_type' => 'taxonomy_term', - 'bundle' => $this->vocabulary->id(), - ))->save(); - - // Change the machine name. - $old_name = $this->vocabulary->id(); - $new_name = drupal_strtolower($this->randomName()); - $this->vocabulary->vid = $new_name; - $this->vocabulary->save(); - - // Check that entity bundles are properly updated. - $info = entity_get_bundles('taxonomy_term'); - $this->assertFalse(isset($info[$old_name]), 'The old bundle name does not appear in entity_get_bundles().'); - $this->assertTrue(isset($info[$new_name]), 'The new bundle name appears in entity_get_bundles().'); - - // Check that the field instance is still attached to the vocabulary. - $this->assertTrue(field_info_instance('taxonomy_term', 'field_test', $new_name), 'The bundle name was updated correctly.'); - } - - /** - * Test uninstall and reinstall of the taxonomy module. - */ - function testUninstallReinstall() { - // Fields and field instances attached to taxonomy term bundles should be - // removed when the module is uninstalled. - $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); - $this->field_definition = array( - 'name' => $this->field_name, - 'entity_type' => 'taxonomy_term', - 'type' => 'text', - 'cardinality' => 4 - ); - entity_create('field_config', $this->field_definition)->save(); - $this->instance_definition = array( - 'field_name' => $this->field_name, - 'entity_type' => 'taxonomy_term', - 'bundle' => $this->vocabulary->id(), - 'label' => $this->randomName() . '_label', - ); - entity_create('field_instance_config', $this->instance_definition)->save(); - - require_once DRUPAL_ROOT . '/core/includes/install.inc'; - module_uninstall(array('taxonomy')); - \Drupal::moduleHandler()->install(array('taxonomy')); - - // Now create a vocabulary with the same name. All field instances - // connected to this vocabulary name should have been removed when the - // module was uninstalled. Creating a new field with the same name and - // an instance of this field on the same bundle name should be successful. - $this->vocabulary->enforceIsNew(); - $this->vocabulary->save(); - entity_create('field_config', $this->field_definition)->save(); - entity_create('field_instance_config', $this->instance_definition)->save(); - } -} diff --git a/core/modules/taxonomy/taxonomy.api.php b/core/modules/taxonomy/taxonomy.api.php index 960cff2..9fa84c1 100644 --- a/core/modules/taxonomy/taxonomy.api.php +++ b/core/modules/taxonomy/taxonomy.api.php @@ -243,10 +243,11 @@ function hook_taxonomy_term_delete(Drupal\taxonomy\Term $term) { /** * Act on a taxonomy term that is being assembled before rendering. * - * The module may add elements to $term->content prior to rendering. The - * structure of $term->content is a renderable array as expected by - * drupal_render(). - * + * The module may add elements to a taxonomy term's renderable array array prior + * to rendering. + + * @param array &$build + * A renderable array representing the taxonomy term content. * @param \Drupal\taxonomy\Entity\Term $term * The term that is being assembled for rendering. * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display @@ -259,12 +260,12 @@ function hook_taxonomy_term_delete(Drupal\taxonomy\Term $term) { * * @see hook_entity_view() */ -function hook_taxonomy_term_view(\Drupal\taxonomy\Entity\Term $term, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { +function hook_taxonomy_term_view(array &$build, \Drupal\taxonomy\Entity\Term $term, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { // Only do the extra work if the component is configured to be displayed. // This assumes a 'mymodule_addition' extra field has been defined for the // vocabulary in hook_entity_extra_field_info(). if ($display->getComponent('mymodule_addition')) { - $term->content['mymodule_addition'] = array( + $build['mymodule_addition'] = array( '#markup' => mymodule_addition($term), '#theme' => 'mymodule_my_additional_field', ); @@ -284,7 +285,7 @@ function hook_taxonomy_term_view(\Drupal\taxonomy\Entity\Term $term, \Drupal\Cor * hook_preprocess_HOOK() for taxonomy-term.html.twig. See drupal_render() and * _theme() documentation respectively for details. * - * @param $build + * @param array &$build * A renderable array representing the taxonomy term content. * @param \Drupal\taxonomy\Entity\Term $term * The taxonomy term being rendered. @@ -294,7 +295,7 @@ function hook_taxonomy_term_view(\Drupal\taxonomy\Entity\Term $term, \Drupal\Cor * * @see hook_entity_view_alter() */ -function hook_taxonomy_term_view_alter(&$build, \Drupal\taxonomy\Entity\Term $term, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { +function hook_taxonomy_term_view_alter(array &$build, \Drupal\taxonomy\Entity\Term $term, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { // Change its weight. $build['an_additional_field']['#weight'] = -10; diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 69bb530..4577e7e 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -16,6 +16,7 @@ use Drupal\taxonomy\Entity\Vocabulary; use Drupal\taxonomy\VocabularyInterface; use Drupal\Component\Utility\String; +use Symfony\Component\HttpFoundation\Request; /** * Denotes that no term in the vocabulary has a parent. @@ -43,9 +44,9 @@ /** * Implements hook_help(). */ -function taxonomy_help($path, $arg) { - switch ($path) { - case 'admin/help#taxonomy': +function taxonomy_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.taxonomy': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Taxonomy module allows you to classify the content of your website. To classify content, you define vocabularies that contain related terms, and then assign the vocabularies to content types. For more information, see the online handbook entry for the Taxonomy module.', array('@taxonomy' => 'http://drupal.org/documentation/modules/taxonomy')) . '

'; @@ -72,11 +73,13 @@ function taxonomy_help($path, $arg) { $output .= '
' . t('There are many contributed modules that extend the behavior of the Taxonomy module for both display and organization of terms.', array('@taxcontrib' => 'http://drupal.org/project/modules?filters=tid:71&solrsort=sis_project_release_usage%20desc')); $output .= ''; return $output; - case 'admin/structure/taxonomy': + + case 'taxonomy.vocabulary_list': $output = '

' . t('Taxonomy is for categorizing content. Terms are grouped into vocabularies. For example, a vocabulary called "Fruit" would contain the terms "Apple" and "Banana".') . '

'; return $output; - case 'admin/structure/taxonomy/manage/%': - $vocabulary = entity_load('taxonomy_vocabulary', $arg[4]); + + case 'taxonomy.overview_terms': + $vocabulary = $request->attributes->get('taxonomy_vocabulary'); switch ($vocabulary->hierarchy) { case TAXONOMY_HIERARCHY_DISABLED: return '

' . t('You can reorganize the terms in %capital_name using their drag-and-drop handles, and group terms under a parent term by sliding them under and to the right of the parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) . '

'; @@ -113,18 +116,6 @@ function taxonomy_permission() { } /** - * Implements hook_entity_bundle_info(). - */ -function taxonomy_entity_bundle_info() { - $bundles = array(); - foreach (taxonomy_vocabulary_get_names() as $id) { - $config = \Drupal::config('taxonomy.vocabulary.' . $id); - $bundles['taxonomy_term'][$id]['label'] = $config->get('name'); - } - return $bundles; -} - -/** * Entity URI callback. */ function taxonomy_term_uri($term) { diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml index 864ce98..be87906 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/taxonomy_test_views.info.yml @@ -7,4 +7,3 @@ core: 8.x dependencies: - taxonomy - views -hidden: true diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml index b44debb..716ccdc 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_filter_taxonomy_index_tid.yml @@ -62,7 +62,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true uses_fields: false row: type: fields diff --git a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml index 23594d9..129c719 100644 --- a/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml +++ b/core/modules/taxonomy/tests/modules/taxonomy_test_views/test_views/views.view.test_taxonomy_parent.yml @@ -68,7 +68,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true uses_fields: false provider: views row: diff --git a/core/modules/telephone/telephone.module b/core/modules/telephone/telephone.module index eee9213..f7dbc4e 100644 --- a/core/modules/telephone/telephone.module +++ b/core/modules/telephone/telephone.module @@ -5,12 +5,14 @@ * Defines a simple telephone number field type. */ +use Symfony\Component\HttpFoundation\Request; + /** * Implements hook_help(). */ -function telephone_help($path, $arg) { - switch ($path) { - case 'admin/help#telephone': +function telephone_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.telephone': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Telephone module allows you to create fields that contain telephone numbers. See the Field module help and the Field UI help pages for general information on fields and how to create and manage them. For more information, see the online documentation for the Telephone module.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!telephone_documentation' => 'https://drupal.org/documentation/modules/telephone')) . '

'; diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php index 6f33c16..5e0f80b 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php +++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php @@ -35,7 +35,16 @@ class TextDefaultFormatter extends FormatterBase { $elements = array(); foreach ($items as $delta => $item) { - $elements[$delta] = array('#markup' => $item->processed); + $elements[$delta] = array( + '#markup' => $item->processed, + '#cache' => array( + 'tags' => array( + 'filter_format' => array( + $item->format, + ), + ), + ), + ); } return $elements; diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php index 0ebd904..d4b3c34 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php +++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php @@ -79,7 +79,16 @@ class TextTrimmedFormatter extends FormatterBase { $output = $item->processed; $output = text_summary($output, $text_processing ? $item->format : NULL, $this->getSetting('trim_length')); } - $elements[$delta] = array('#markup' => $output); + $elements[$delta] = array( + '#markup' => $output, + '#cache' => array( + 'tags' => array( + 'filter_format' => array( + $item->format, + ), + ), + ), + ); } return $elements; diff --git a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php index aecf257..62a2ed4 100644 --- a/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/TextFieldTest.php @@ -226,7 +226,6 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { ); $this->drupalPostForm('admin/config/content/formats/add', $edit, t('Save configuration')); filter_formats_reset(); - $this->checkPermissions(array(), TRUE); $format = entity_load('filter_format', $edit['format']); $format_id = $format->format; $permission = $format->getPermissionName(); diff --git a/core/modules/text/text.module b/core/modules/text/text.module index 339d9da..37db348 100644 --- a/core/modules/text/text.module +++ b/core/modules/text/text.module @@ -7,13 +7,14 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Entity\EntityInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function text_help($path, $arg) { - switch ($path) { - case 'admin/help#text': +function text_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.text': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Text module allows you to create short and long text fields with optional summaries. See the Field module help and the Field UI help pages for general information on fields and how to create and manage them. For more information, see the online documentation for the Text module.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!text_documentation' => 'https://drupal.org/documentation/modules/text')) . '

'; diff --git a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.info.yml b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.info.yml index 961d81e..98fc607 100644 --- a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.info.yml +++ b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for toolbar testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 0f58cb2..3599c59 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -10,6 +10,7 @@ use Drupal\Core\Render\Element; use Drupal\Core\Template\Attribute; use Drupal\Component\Utility\Crypt; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Drupal\menu_link\MenuLinkInterface; use Drupal\user\RoleInterface; @@ -18,9 +19,9 @@ /** * Implements hook_help(). */ -function toolbar_help($path, $arg) { - switch ($path) { - case 'admin/help#toolbar': +function toolbar_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.toolbar': $output = '

' . t('About') . '

'; $output .= '

' . t('The Toolbar module displays links to top-level administration menu items and links from other modules at the top of the screen. For more information, see the online handbook entry for Toolbar module.', array('@toolbar' => 'http://drupal.org/documentation/modules/toolbar')) . '

'; $output .= '

' . t('Uses') . '

'; @@ -120,13 +121,11 @@ function _toolbar_initialize_page_cache() { // If we have a cache, serve it. // @see _drupal_bootstrap_page_cache() $request = \Drupal::request(); - $cache = drupal_page_get_cache($request); - if (is_object($cache)) { - $response = new Response(); + $response = drupal_page_get_cache($request); + if ($response) { $response->headers->set('X-Drupal-Cache', 'HIT'); - date_default_timezone_set(drupal_get_user_timezone()); - drupal_serve_page_from_cache($cache, $response, $request); + drupal_serve_page_from_cache($response, $request); $response->prepare($request); $response->send(); @@ -134,9 +133,6 @@ function _toolbar_initialize_page_cache() { exit; } - // Otherwise, create a new page response (that will be cached). - drupal_add_http_header('X-Drupal-Cache', 'MISS'); - // The Expires HTTP header is the heart of the client-side HTTP caching. The // additional server-side page cache only takes effect when the client // accesses the callback URL again (e.g., after clearing the browser cache or @@ -588,14 +584,11 @@ function _toolbar_get_user_cid($uid, $langcode) { * (optional) The user ID whose toolbar cache entry to clear. */ function _toolbar_clear_user_cache($uid = NULL) { - $cache = \Drupal::cache('toolbar'); - if (!$cache->isEmpty()) { - // Clear by the 'user' tag in order to delete all caches, in any language, - // associated with this user. - if (isset($uid)) { - Cache::deleteTags(array('user' => array($uid))); - } else { - $cache->deleteAll(); - } + // Clear by the 'user' tag in order to delete all caches, in any language, + // associated with this user. + if (isset($uid)) { + Cache::deleteTags(array('user' => array($uid))); + } else { + \Drupal::cache('toolbar')->deleteAll(); } } diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTestBase.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTestBase.php index 0beb7f0..c6372cd 100644 --- a/core/modules/tour/lib/Drupal/tour/Tests/TourTestBase.php +++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTestBase.php @@ -10,9 +10,9 @@ use Drupal\simpletest\WebTestBase; /** - * Tests tour functionality. + * Base class for testing Tour functionality. */ -class TourTestBase extends WebTestBase { +abstract class TourTestBase extends WebTestBase { /** * Assert function to determine if tips rendered to the page diff --git a/core/modules/tour/tests/Drupal/tour/Tests/Entity/TourTest.php b/core/modules/tour/tests/Drupal/tour/Tests/Entity/TourTest.php index 6693cec..5335fc8 100644 --- a/core/modules/tour/tests/Drupal/tour/Tests/Entity/TourTest.php +++ b/core/modules/tour/tests/Drupal/tour/Tests/Entity/TourTest.php @@ -4,7 +4,7 @@ * Contains \Drupal\tour\Tests\Entity\TourTest. */ -namespace Drupal\tour\Tests\Entity\TourTest; +namespace Drupal\tour\Tests\Entity; use Drupal\Tests\UnitTestCase; diff --git a/core/modules/tour/tests/tour_test/tour_test.info.yml b/core/modules/tour/tests/tour_test/tour_test.info.yml index 0617983..e55953a 100644 --- a/core/modules/tour/tests/tour_test/tour_test.info.yml +++ b/core/modules/tour/tests/tour_test/tour_test.info.yml @@ -4,6 +4,5 @@ description: Tests module for tour module. package: Core version: VERSION core: 8.x -hidden: TRUE dependencies: - tour diff --git a/core/modules/tour/tour.module b/core/modules/tour/tour.module index 610c025..ffe79c2 100644 --- a/core/modules/tour/tour.module +++ b/core/modules/tour/tour.module @@ -6,13 +6,14 @@ */ use Drupal\Core\Cache\CacheBackendInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function tour_help($path, $arg) { - switch ($path) { - case 'admin/help#tour': +function tour_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.tour': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t("The Tour module provides users with guided tours of the site interface. Each tour consists of several tips that highlight elements of the user interface, guide the user through a workflow, or explain key concepts of the website. For more information, see the online documentation for the Tour module.", array('!tour' => 'https://drupal.org/documentation/modules/tour')) . '

'; diff --git a/core/modules/tracker/lib/Drupal/tracker/Access/ViewOwnTrackerAccessCheck.php b/core/modules/tracker/lib/Drupal/tracker/Access/ViewOwnTrackerAccessCheck.php index a4c594e..4421b77 100644 --- a/core/modules/tracker/lib/Drupal/tracker/Access/ViewOwnTrackerAccessCheck.php +++ b/core/modules/tracker/lib/Drupal/tracker/Access/ViewOwnTrackerAccessCheck.php @@ -9,8 +9,7 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; +use Drupal\user\UserInterface; /** * Access check for user tracker routes. @@ -18,12 +17,17 @@ class ViewOwnTrackerAccessCheck implements AccessInterface { /** - * {@inheritdoc} + * Checks access. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * @param \Drupal\user\UserInterface $user + * The user whose tracker page is being accessed. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { - // The user object from the User ID in the path. - $user = $request->attributes->get('user'); + public function access(AccountInterface $account, UserInterface $user) { return ($user && $account->isAuthenticated() && ($user->id() == $account->id())) ? static::ALLOW : static::DENY; } } - diff --git a/core/modules/tracker/tests/modules/tracker_test_views/test_views/views.view.test_tracker_user_uid.yml b/core/modules/tracker/tests/modules/tracker_test_views/test_views/views.view.test_tracker_user_uid.yml index 1182ff5..32cfffe 100644 --- a/core/modules/tracker/tests/modules/tracker_test_views/test_views/views.view.test_tracker_user_uid.yml +++ b/core/modules/tracker/tests/modules/tracker_test_views/test_views/views.view.test_tracker_user_uid.yml @@ -26,7 +26,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true override: true sticky: false summary: '' diff --git a/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml b/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml index 9eb2c61..45a64a1 100644 --- a/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml +++ b/core/modules/tracker/tests/modules/tracker_test_views/tracker_test_views.info.yml @@ -7,4 +7,3 @@ core: 8.x dependencies: - tracker - views -hidden: true diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module index 96aecbf..98042f5 100644 --- a/core/modules/tracker/tracker.module +++ b/core/modules/tracker/tracker.module @@ -9,13 +9,14 @@ use Drupal\comment\CommentInterface; use Drupal\node\NodeInterface; use Drupal\Core\Session\AccountInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function tracker_help($path, $arg) { - switch ($path) { - case 'admin/help#tracker': +function tracker_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.tracker': $output = '

' . t('About') . '

'; $output .= '

' . t('The Tracker module displays the most recently added and updated content on your site, and allows you to follow new content created by each user. This module has no configuration options. For more information, see the online handbook entry for Tracker module.', array('@tracker' => 'http://drupal.org/documentation/modules/tracker')) . '

'; $output .= '

' . t('Uses') . '

'; diff --git a/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php b/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php index 5fba57f..5fc20b0 100644 --- a/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php +++ b/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php @@ -8,10 +8,7 @@ namespace Drupal\update\Access; use Drupal\Core\Routing\Access\AccessInterface; -use Drupal\Core\Session\AccountInterface; use Drupal\Core\Site\Settings; -use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Determines whether allow authorized operations is set. @@ -36,9 +33,12 @@ class UpdateManagerAccessCheck implements AccessInterface { } /** - * {@inheritdoc} + * Checks access. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access() { return $this->settings->get('allow_authorize_operations', TRUE) ? static::ALLOW : static::DENY; } diff --git a/core/modules/update/lib/Drupal/update/UpdateManager.php b/core/modules/update/lib/Drupal/update/UpdateManager.php index 8c7c48a..a1801fe 100644 --- a/core/modules/update/lib/Drupal/update/UpdateManager.php +++ b/core/modules/update/lib/Drupal/update/UpdateManager.php @@ -10,12 +10,14 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Utility\ProjectInfo; /** * Default implementation of UpdateManagerInterface. */ class UpdateManager implements UpdateManagerInterface { + use StringTranslationTrait; /** * The update settings @@ -60,13 +62,6 @@ class UpdateManager implements UpdateManagerInterface { protected $availableReleasesTempStore; /** - * The translation service. - * - * @var \Drupal\Core\StringTranslation\TranslationInterface - */ - protected $translation; - - /** * Constructs a UpdateManager. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory @@ -84,7 +79,7 @@ class UpdateManager implements UpdateManagerInterface { $this->updateSettings = $config_factory->get('update.settings'); $this->moduleHandler = $module_handler; $this->updateProcessor = $update_processor; - $this->translation = $translation; + $this->stringTranslation = $translation; $this->keyValueStore = $key_value_expirable_factory->get('update'); $this->availableReleasesTempStore = $key_value_expirable_factory->get('update_available_releases'); $this->projects = array(); @@ -215,13 +210,4 @@ class UpdateManager implements UpdateManagerInterface { } } - /** - * Translates a string to the current language or to a given language. - * - * See the t() documentation for details. - */ - protected function t($string, array $args = array(), array $options = array()) { - return $this->translation->translate($string, $args, $options); - } - } diff --git a/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml b/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml index 0bfea75..c424e74 100644 --- a/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml +++ b/core/modules/update/tests/aaa_update_test/aaa_update_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/modules/aaa_update_test/aaa_update_test.info.yml b/core/modules/update/tests/modules/aaa_update_test/aaa_update_test.info.yml index 0bfea75..c424e74 100644 --- a/core/modules/update/tests/modules/aaa_update_test/aaa_update_test.info.yml +++ b/core/modules/update/tests/modules/aaa_update_test/aaa_update_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/modules/bbb_update_test/bbb_update_test.info.yml b/core/modules/update/tests/modules/bbb_update_test/bbb_update_test.info.yml index 25540c3..0c2226b 100644 --- a/core/modules/update/tests/modules/bbb_update_test/bbb_update_test.info.yml +++ b/core/modules/update/tests/modules/bbb_update_test/bbb_update_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/modules/ccc_update_test/ccc_update_test.info.yml b/core/modules/update/tests/modules/ccc_update_test/ccc_update_test.info.yml index 919adcd..6fcd4ec 100644 --- a/core/modules/update/tests/modules/ccc_update_test/ccc_update_test.info.yml +++ b/core/modules/update/tests/modules/ccc_update_test/ccc_update_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/modules/update_test/config/schema/update_test.schema.yml b/core/modules/update/tests/modules/update_test/config/schema/update_test.schema.yml index cbe32d4..2235f2b 100644 --- a/core/modules/update/tests/modules/update_test/config/schema/update_test.schema.yml +++ b/core/modules/update/tests/modules/update_test/config/schema/update_test.schema.yml @@ -5,19 +5,19 @@ update_test.settings: label: 'Update test settings' mapping: system_info: - type: seqeuence + type: sequence label: 'System info' sequence: - type: string label: 'Value' update_status: - type: seqeuence + type: sequence label: 'Update status' sequence: - type: string label: 'Value' xml_map: - type: seqeuence + type: sequence label: 'XML map' sequence: - type: string diff --git a/core/modules/update/tests/modules/update_test/update_test.info.yml b/core/modules/update/tests/modules/update_test/update_test.info.yml index f6e38a3..053aafc 100644 --- a/core/modules/update/tests/modules/update_test/update_test.info.yml +++ b/core/modules/update/tests/modules/update_test/update_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for update module testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info.yml b/core/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info.yml index cdb01bb..9162147 100644 --- a/core/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info.yml +++ b/core/modules/update/tests/themes/update_test_basetheme/update_test_basetheme.info.yml @@ -3,4 +3,3 @@ type: theme description: 'Test theme which acts as a base theme for other test subthemes.' version: VERSION core: 8.x -hidden: true diff --git a/core/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info.yml b/core/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info.yml index d844542..2580fd6 100644 --- a/core/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info.yml +++ b/core/modules/update/tests/themes/update_test_subtheme/update_test_subtheme.info.yml @@ -4,4 +4,3 @@ description: 'Test theme which uses update_test_basetheme as the base theme.' version: VERSION core: 8.x 'base theme': update_test_basetheme -hidden: true diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 548ee63..6341857 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -12,6 +12,7 @@ */ use Drupal\Core\Site\Settings; +use Symfony\Component\HttpFoundation\Request; // These are internally used constants for this code, do not modify. @@ -63,12 +64,9 @@ /** * Implements hook_help(). */ -function update_help($path, $arg) { - switch ($path) { - case 'admin/reports/updates': - return '

' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '

'; - - case 'admin/help#update': +function update_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.update': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t("The Update Manager module periodically checks for new versions of your site's software (including contributed modules and themes), and alerts administrators to available updates. In order to provide update information, anonymous usage statistics are sent to Drupal.org. If desired, you may disable the Update Manager module from the Module administration page. For more information, see the online handbook entry for Update Manager module.", array('@update' => 'http://drupal.org/documentation/modules/update', '@modules' => url('admin/modules'))) . '

'; @@ -89,6 +87,9 @@ function update_help($path, $arg) { } $output .= ''; return $output; + + case 'update.status': + return '

' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '

'; } } diff --git a/core/modules/user/config/install/views.view.user_admin_people.yml b/core/modules/user/config/install/views.view.user_admin_people.yml index 89d1e4e..6ab676d 100644 --- a/core/modules/user/config/install/views.view.user_admin_people.yml +++ b/core/modules/user/config/install/views.view.user_admin_people.yml @@ -66,7 +66,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true override: true sticky: false summary: '' diff --git a/core/modules/user/config/install/views.view.who_s_online.yml b/core/modules/user/config/install/views.view.who_s_online.yml index 323825b..911f40f 100644 --- a/core/modules/user/config/install/views.view.who_s_online.yml +++ b/core/modules/user/config/install/views.view.who_s_online.yml @@ -51,7 +51,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true type: ul wrapper_class: item-list class: '' diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 23a688a..7361704 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -145,8 +145,12 @@ user.role.*: label: 'Default language' action.configuration.user_add_role_action: - type: action_configuration_default - label: 'Add a role to the selected users configuration' + type: mapping + label: 'Configuration for the add role action' + mapping: + rid: + type: string + label: 'The ID of the role to add' action.configuration.user_block_user_action: type: action_configuration_default @@ -158,15 +162,18 @@ action.configuration.user_cancel_user_action: action.configuration.user_remove_role_action: type: mapping - label: 'Remove a role from the selected users configuration' + label: 'Configuration for the remove role action' mapping: rid: - type: sequence - label: 'Roles' - sequence: - - type: sequence - label: 'Role' + type: string + label: 'The ID of the role to remove' action.configuration.user_unblock_user_action: type: action_configuration_default label: 'Unblock the selected users configuration' + +search.plugin.user_search: + type: sequence + label: 'User search' + sequence: + - type: undefined diff --git a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php index 01d7a6c..fbceacd 100644 --- a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php +++ b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php @@ -9,7 +9,6 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; /** @@ -18,9 +17,17 @@ class LoginStatusCheck implements AccessInterface { /** - * {@inheritdoc} + * Checks access. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Request $request, AccountInterface $account) { return ($request->attributes->get('_menu_admin') || $account->isAuthenticated()) ? static::ALLOW : static::DENY; } diff --git a/core/modules/user/lib/Drupal/user/Access/PermissionAccessCheck.php b/core/modules/user/lib/Drupal/user/Access/PermissionAccessCheck.php index a159b1c..2672a4e 100644 --- a/core/modules/user/lib/Drupal/user/Access/PermissionAccessCheck.php +++ b/core/modules/user/lib/Drupal/user/Access/PermissionAccessCheck.php @@ -10,7 +10,6 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; -use Symfony\Component\HttpFoundation\Request; /** * Determines access to routes based on permissions defined via hook_permission(). @@ -18,11 +17,18 @@ class PermissionAccessCheck implements AccessInterface { /** - * Implements AccessCheckInterface::access(). + * Checks access. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, AccountInterface $account) { $permission = $route->getRequirement('_permission'); - // If the access check fails, return NULL to give other checks a chance. return $account->hasPermission($permission) ? static::ALLOW : static::DENY; } } diff --git a/core/modules/user/lib/Drupal/user/Access/RegisterAccessCheck.php b/core/modules/user/lib/Drupal/user/Access/RegisterAccessCheck.php index eff984b..aec179b 100644 --- a/core/modules/user/lib/Drupal/user/Access/RegisterAccessCheck.php +++ b/core/modules/user/lib/Drupal/user/Access/RegisterAccessCheck.php @@ -9,7 +9,6 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; /** @@ -18,9 +17,17 @@ class RegisterAccessCheck implements AccessInterface { /** - * Implements AccessCheckInterface::access(). + * Checks access. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Request $request, AccountInterface $account) { return ($request->attributes->get('_menu_admin') || $account->isAnonymous()) && (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) ? static::ALLOW : static::DENY; } } diff --git a/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php b/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php index e3ace16..e8cfda4 100644 --- a/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php +++ b/core/modules/user/lib/Drupal/user/Access/RoleAccessCheck.php @@ -9,7 +9,6 @@ use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -22,9 +21,17 @@ class RoleAccessCheck implements AccessInterface { /** - * {@inheritdoc} + * Checks access. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { + public function access(Route $route, AccountInterface $account) { // Requirements just allow strings, so this might be a comma separated list. $rid_string = $route->getRequirement('_role'); diff --git a/core/modules/user/lib/Drupal/user/AccountForm.php b/core/modules/user/lib/Drupal/user/AccountForm.php index f85fca2..42a2292 100644 --- a/core/modules/user/lib/Drupal/user/AccountForm.php +++ b/core/modules/user/lib/Drupal/user/AccountForm.php @@ -82,20 +82,6 @@ '#weight' => -10, ); - // Only show name field on registration form or user can change own username. - $form['account']['name'] = array( - '#type' => 'textfield', - '#title' => $this->t('Username'), - '#maxlength' => USERNAME_MAX_LENGTH, - '#description' => $this->t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'), - '#required' => TRUE, - '#attributes' => array('class' => array('username'), 'autocorrect' => 'off', 'autocomplete' => 'off', 'autocapitalize' => 'off', - 'spellcheck' => 'false'), - '#default_value' => (!$register ? $account->getUsername() : ''), - '#access' => ($register || ($user->id() == $account->id() && $user->hasPermission('change own username')) || $admin), - '#weight' => -10, - ); - // The mail field is NOT required if account originally had no mail set // and the user performing the edit has 'administer users' permission. // This allows users without e-mail address to be edited and deleted. @@ -105,7 +91,23 @@ '#description' => $this->t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'), '#required' => !(!$account->getEmail() && $user->hasPermission('administer users')), '#default_value' => (!$register ? $account->getEmail() : ''), - '#attributes' => array('autocomplete' => 'off'), + ); + + // Only show name field on registration form or user can change own username. + $form['account']['name'] = array( + '#type' => 'textfield', + '#title' => $this->t('Username'), + '#maxlength' => USERNAME_MAX_LENGTH, + '#description' => $this->t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'), + '#required' => TRUE, + '#attributes' => array( + 'class' => array('username'), + 'autocorrect' => 'off', + 'autocapitalize' => 'off', + 'spellcheck' => 'false', + ), + '#default_value' => (!$register ? $account->getUsername() : ''), + '#access' => ($register || ($user->id() == $account->id() && $user->hasPermission('change own username')) || $admin), ); // Display password field only for existing users or when user is allowed to @@ -166,6 +168,16 @@ ); } + // When not building the user registration form, prevent web browsers from + // autofilling/prefilling the email, username, and password fields. + if ($this->getOperation() != 'register') { + foreach (array('mail', 'name', 'pass') as $key) { + if (isset($form['account'][$key])) { + $form['account'][$key]['#attributes']['autocomplete'] = 'off'; + } + } + } + if ($admin) { $status = $account->isActive(); } @@ -237,11 +249,11 @@ $user_preferred_admin_langcode = $register ? $language_interface->id : $account->getPreferredAdminLangcode(); - // Is the user preferred language enabled? - $user_language_enabled = FALSE; + // Is the user preferred language added? + $user_language_added = FALSE; if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) { $negotiator = $this->languageManager->getNegotiator(); - $user_language_enabled = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUser::METHOD_ID, Language::TYPE_INTERFACE); + $user_language_added = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUser::METHOD_ID, Language::TYPE_INTERFACE); } $form['language'] = array( '#type' => $this->languageManager->isMultilingual() ? 'details' : 'container', @@ -257,7 +269,7 @@ '#title' => $this->t('Site language'), '#languages' => Language::STATE_CONFIGURABLE, '#default_value' => $user_preferred_langcode, - '#description' => $user_language_enabled ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."), + '#description' => $user_language_added ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."), ); // Only show the account setting for Administration pages language to users diff --git a/core/modules/user/lib/Drupal/user/Controller/UserController.php b/core/modules/user/lib/Drupal/user/Controller/UserController.php index 6710afb..c27e3c9 100644 --- a/core/modules/user/lib/Drupal/user/Controller/UserController.php +++ b/core/modules/user/lib/Drupal/user/Controller/UserController.php @@ -36,10 +36,7 @@ class UserController extends ControllerBase { $response = $this->redirect('user.view', array('user' => $user->id())); } else { - // Sets the proper request. - // @todo Remove when the request object is synchronized. $form_builder = $this->formBuilder(); - $form_builder->setRequest($request); $response = $form_builder->getForm('Drupal\user\Form\UserLoginForm'); } return $response; diff --git a/core/modules/user/lib/Drupal/user/Plugin/Action/ChangeUserRoleBase.php b/core/modules/user/lib/Drupal/user/Plugin/Action/ChangeUserRoleBase.php index 758d84c..f844743 100644 --- a/core/modules/user/lib/Drupal/user/Plugin/Action/ChangeUserRoleBase.php +++ b/core/modules/user/lib/Drupal/user/Plugin/Action/ChangeUserRoleBase.php @@ -8,6 +8,7 @@ namespace Drupal\user\Plugin\Action; use Drupal\Core\Action\ConfigurableActionBase; +use Drupal\Core\Entity\DependencyTrait; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -17,6 +18,8 @@ */ abstract class ChangeUserRoleBase extends ConfigurableActionBase implements ContainerFactoryPluginInterface { + use DependencyTrait; + /** * The user role entity type. * @@ -80,12 +83,11 @@ * {@inheritdoc} */ public function calculateDependencies() { - $dependencies = array(); if (!empty($this->configuration['rid'])) { $prefix = $this->entityType->getConfigPrefix() . '.'; - $dependencies['entity'][] = $prefix . $this->configuration['rid']; + $this->addDependency('entity', $prefix . $this->configuration['rid']); } - return $dependencies; + return $this->dependencies; } } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAccountFormFieldsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAccountFormFieldsTest.php new file mode 100644 index 0000000..7785ccb --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/UserAccountFormFieldsTest.php @@ -0,0 +1,152 @@ + 'User account form fields', + 'description' => 'Verifies that the field order in user account forms is compatible with password managers of web browsers.', + 'group' => 'User', + ); + } + + /** + * Tests the root user account form section in the "Configure site" form. + */ + function testInstallConfigureForm() { + require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; + $install_state = install_state_defaults(); + $form_state = array(); + $form_state['build_info']['args'][] = &$install_state; + $form = $this->container->get('form_builder') + ->buildForm('Drupal\Core\Installer\Form\SiteConfigureForm', $form_state); + + // Verify name and pass field order. + $this->assertFieldOrder($form['admin_account']['account']); + + // Verify that web browsers may autocomplete the email value and + // autofill/prefill the name and pass values. + foreach (array('mail', 'name', 'pass') as $key) { + $this->assertFalse(isset($form['account'][$key]['#attributes']['autocomplete']), "'$key' field: 'autocomplete' attribute not found."); + } + } + + /** + * Tests the user registration form. + */ + function testUserRegistrationForm() { + // Install default configuration; required for AccountFormController. + $this->installConfig(array('user')); + + // Disable email confirmation to unlock the password field. + $this->container->get('config.factory') + ->get('user.settings') + ->set('verify_mail', FALSE) + ->save(); + + $form = $this->buildAccountForm('register'); + + // Verify name and pass field order. + $this->assertFieldOrder($form['account']); + + // Verify that web browsers may autocomplete the email value and + // autofill/prefill the name and pass values. + foreach (array('mail', 'name', 'pass') as $key) { + $this->assertFalse(isset($form['account'][$key]['#attributes']['autocomplete']), "'$key' field: 'autocomplete' attribute not found."); + } + } + + /** + * Tests the user edit form. + */ + function testUserEditForm() { + // Install default configuration; required for AccountFormController. + $this->installConfig(array('user')); + + $form = $this->buildAccountForm('default'); + + // Verify name and pass field order. + $this->assertFieldOrder($form['account']); + + // Verify that autocomplete is off on all account fields. + foreach (array('mail', 'name', 'pass') as $key) { + $this->assertIdentical($form['account'][$key]['#attributes']['autocomplete'], 'off', "'$key' field: 'autocomplete' attribute is 'off'."); + } + } + + /** + * Asserts that the 'name' form element is directly before the 'pass' element. + * + * @param array $elements + * A form array section that contains the user account form elements. + */ + protected function assertFieldOrder(array $elements) { + $name_index = 0; + $name_weight = 0; + $pass_index = 0; + $pass_weight = 0; + $index = 0; + foreach ($elements as $key => $element) { + if ($key === 'name') { + $name_index = $index; + $name_weight = $element['#weight']; + $this->assertTrue($element['#sorted'], "'name' field is #sorted."); + } + elseif ($key === 'pass') { + $pass_index = $index; + $pass_weight = $element['#weight']; + $this->assertTrue($element['#sorted'], "'pass' field is #sorted."); + } + $index++; + } + $this->assertEqual($name_index, $pass_index - 1, "'name' field ($name_index) appears before 'pass' field ($pass_index)."); + $this->assertTrue($name_weight < $pass_weight, "'name' field weight ($name_weight) is smaller than 'pass' field weight ($pass_weight)."); + } + + /** + * Builds the user account form for a given operation. + * + * @param string $operation + * The entity operation; one of 'register' or 'default'. + * + * @return array + * The form array. + */ + protected function buildAccountForm($operation) { + // @see HtmlEntityFormController::getFormObject() + $entity_type = 'user'; + $fields = array(); + if ($operation != 'register') { + $fields['uid'] = 2; + } + $entity = $this->container->get('entity.manager') + ->getStorage($entity_type) + ->create($fields); + $form_object = $this->container->get('entity.manager') + ->getFormObject($entity_type, $operation) + ->setEntity($entity); + + // @see EntityFormBuilder::getForm() + return $this->container->get('entity.form_builder')->getForm($entity, $operation); + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php b/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php new file mode 100644 index 0000000..3e616d5 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php @@ -0,0 +1,56 @@ + 'User action config schema', + 'description' => 'Ensures the user action for adding and removing roles have valid config schema.', + 'group' => 'User', + ); + } + + /** + * Tests whether the user action config schema are valid. + */ + function testValidUserActionConfigSchema() { + $rid = $this->drupalCreateRole(array()); + + // Test user_add_role_action configuration. + $config = \Drupal::config('system.action.user_add_role_action.' . $rid); + $this->assertEqual($config->get('id'), 'user_add_role_action.' . $rid); + $this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get()); + + // Test user_remove_role_action configuration. + $config = \Drupal::config('system.action.user_remove_role_action.' . $rid); + $this->assertEqual($config->get('id'), 'user_remove_role_action.' . $rid); + $this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get()); + } + +} diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminSettingsFormTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminSettingsFormTest.php index a340353..94fd2d2 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserAdminSettingsFormTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminSettingsFormTest.php @@ -10,6 +10,9 @@ use Drupal\system\Tests\System\SystemConfigFormTestBase; use Drupal\user\AccountSettingsForm; +/** + * Tests the administrative user settings configuration form. + */ class UserAdminSettingsFormTest extends SystemConfigFormTestBase { public static function getInfo() { diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php index fb58bc1..d8d384d 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserAdminTest.php @@ -9,6 +9,9 @@ use Drupal\simpletest\WebTestBase; +/** + * Tests the user administration UI. + */ class UserAdminTest extends WebTestBase { /** diff --git a/core/modules/user/lib/Drupal/user/Tests/UserEditedOwnAccountTest.php b/core/modules/user/lib/Drupal/user/Tests/UserEditedOwnAccountTest.php index 151ba88..2437aec 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserEditedOwnAccountTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserEditedOwnAccountTest.php @@ -9,8 +9,8 @@ use Drupal\simpletest\WebTestBase; -/* - * Test that a user, having editing their own account, can still log in. +/** + * Tests user login after editing own user account. */ class UserEditedOwnAccountTest extends WebTestBase { diff --git a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php index 4e54ff8..7014a03 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php @@ -51,11 +51,15 @@ class UserInstallTest extends DrupalUnitTestBase { $this->assertFalse(empty($anon->uuid), 'Anon user has a UUID'); $this->assertFalse(empty($admin->uuid), 'Admin user has a UUID'); - $this->assertEqual($anon->langcode, \Drupal::languageManager()->getDefaultLanguage()->id, 'Anon user language is the default.'); - $this->assertEqual($admin->langcode, \Drupal::languageManager()->getDefaultLanguage()->id, 'Admin user language is the default.'); + // Test that the anonymous and administrators languages are equal to the + // site's default language. + $this->assertEqual($anon->langcode, \Drupal::languageManager()->getDefaultLanguage()->id); + $this->assertEqual($admin->langcode, \Drupal::languageManager()->getDefaultLanguage()->id); - $this->assertEqual($admin->status, 1, 'Admin user is active.'); - $this->assertEqual($anon->status, 0, 'Anon user is blocked.'); + // Test that the administrator is active. + $this->assertEqual($admin->status, 1); + // Test that the anonymous user is blocked. + $this->assertEqual($anon->status, 0); } } diff --git a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php index 9ac9de1..03ad191 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserPermissionsTest.php @@ -10,6 +10,9 @@ use Drupal\simpletest\WebTestBase; use Drupal\user\RoleStorage; +/** + * Tests the user role permission UI. + */ class UserPermissionsTest extends WebTestBase { protected $admin_user; protected $rid; diff --git a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php index 15e75bf..a0b205c 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserRegistrationTest.php @@ -11,6 +11,9 @@ use Drupal\Core\Language\Language; use Drupal\simpletest\WebTestBase; +/** + * Tests user registration. + */ class UserRegistrationTest extends WebTestBase { /** diff --git a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php index 74a1712..83f5973 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php @@ -64,7 +64,6 @@ function setUp() { $this->full_html_format->save(); user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array($this->filtered_html_format->getPermissionName())); - $this->checkPermissions(array(), TRUE); // Create regular and administrative users. $this->web_user = $this->drupalCreateUser(array('post comments')); diff --git a/core/modules/user/lib/Drupal/user/UserStorage.php b/core/modules/user/lib/Drupal/user/UserStorage.php index afd53b3..d5d91ca 100644 --- a/core/modules/user/lib/Drupal/user/UserStorage.php +++ b/core/modules/user/lib/Drupal/user/UserStorage.php @@ -9,11 +9,10 @@ use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Password\PasswordInterface; use Drupal\Core\Database\Connection; -use Drupal\field\FieldInfo; -use Drupal\user\UserDataInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Entity\ContentEntityDatabaseStorage; @@ -46,8 +45,8 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt * The entity type definition. * @param \Drupal\Core\Database\Connection $database * The database connection to be used. - * @param \Drupal\field\FieldInfo $field_info - * The field info service. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. * @param \Drupal\Component\Uuid\UuidInterface $uuid_service * The UUID Service. * @param \Drupal\Core\Password\PasswordInterface $password @@ -55,8 +54,8 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt * @param \Drupal\user\UserDataInterface $user_data * The user data service. */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $database, $field_info, $uuid_service); + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) { + parent::__construct($entity_type, $database, $entity_manager, $uuid_service); $this->password = $password; $this->userData = $user_data; @@ -69,7 +68,7 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt return new static( $entity_type, $container->get('database'), - $container->get('field.info'), + $container->get('entity.manager'), $container->get('uuid'), $container->get('password'), $container->get('user.data') @@ -99,7 +98,9 @@ function mapFromStorageRecords(array $records) { * {@inheritdoc} */ public function save(EntityInterface $entity) { - if (!$entity->id()) { + // The anonymous user account is saved with the fixed user ID of 0. + // Therefore we need to check for NULL explicitly. + if ($entity->id() === NULL) { $entity->uid->value = $this->database->nextId($this->database->query('SELECT MAX(uid) FROM {users}')->fetchField()); $entity->enforceIsNew(); } diff --git a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml b/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml index f227a34..68b8ff9 100644 --- a/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml +++ b/core/modules/user/tests/modules/user_custom_phpass_params_test/user_custom_phpass_params_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing custom phpass password algorithm parame package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/user/tests/modules/user_form_test/user_form_test.info.yml b/core/modules/user/tests/modules/user_form_test/user_form_test.info.yml index ceea331..e77ebf2 100644 --- a/core/modules/user/tests/modules/user_form_test/user_form_test.info.yml +++ b/core/modules/user/tests/modules/user_form_test/user_form_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for user form testing.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml b/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml index e13b350..2554769 100644 --- a/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml +++ b/core/modules/user/tests/modules/user_test_views/user_test_views.info.yml @@ -7,4 +7,3 @@ core: 8.x dependencies: - user - views -hidden: true diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php index b71a65f..ab6e2b6 100644 --- a/core/modules/user/user.api.php +++ b/core/modules/user/user.api.php @@ -306,8 +306,10 @@ function hook_user_logout($account) { * The user's account information is being displayed. * * The module should format its custom additions for display and add them to the - * $account->content array. + * $build array. * + * @param array &$build + * A renderable array representing the user content. * @param \Drupal\user\UserInterface $account * The user object on which the operation is being performed. * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display @@ -321,12 +323,12 @@ function hook_user_logout($account) { * @see hook_user_view_alter() * @see hook_entity_view() */ -function hook_user_view(\Drupal\user\UserInterface $account, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { +function hook_user_view(array &$build, \Drupal\user\UserInterface $account, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) { // Only do the extra work if the component is configured to be displayed. // This assumes a 'mymodule_addition' extra field has been defined for the // user entity type in hook_entity_extra_field_info(). if ($display->getComponent('mymodule_addition')) { - $account->content['mymodule_addition'] = array( + $build['mymodule_addition'] = array( '#markup' => mymodule_addition($account), '#theme' => 'mymodule_my_additional_field', ); @@ -346,7 +348,7 @@ function hook_user_view(\Drupal\user\UserInterface $account, \Drupal\Core\Entity * user.html.twig. See drupal_render() and _theme() documentation * respectively for details. * - * @param $build + * @param array &$build * A renderable array representing the user. * @param \Drupal\user\UserInterface $account * The user account being rendered. @@ -357,7 +359,7 @@ function hook_user_view(\Drupal\user\UserInterface $account, \Drupal\Core\Entity * @see user_view() * @see hook_entity_view_alter() */ -function hook_user_view_alter(&$build, \Drupal\user\UserInterface $account, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { +function hook_user_view_alter(array &$build, \Drupal\user\UserInterface $account, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { // Check for the existence of a field added by another module. if (isset($build['an_additional_field'])) { // Change its weight. diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 97515a2..086a5a0 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -5,9 +5,6 @@ * Install, update and uninstall functions for the user module. */ -use Drupal\Core\Language\Language; -use Drupal\field\Field; - /** * Implements hook_schema(). */ @@ -220,28 +217,27 @@ function user_schema() { * Implements hook_install(). */ function user_install() { + $storage = \Drupal::entityManager()->getStorage('user'); + // @todo Rely on the default value for langcode in + // https://drupal.org/node/1966436 + $langcode = \Drupal::languageManager()->getDefaultLanguage()->id; // Insert a row for the anonymous user. - db_insert('users') - ->fields(array( + $storage + ->create(array( 'uid' => 0, - 'uuid' => \Drupal::service('uuid')->generate(), - 'name' => '', - 'mail' => '', - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + 'status' => 0, + 'langcode' => $langcode, )) - ->execute(); + ->save(); // We need some placeholders here as name and mail are uniques. // This will be changed by the settings form in the installer. - db_insert('users') - ->fields(array( + $storage + ->create(array( 'uid' => 1, - 'uuid' => \Drupal::service('uuid')->generate(), 'name' => 'placeholder-for-uid-1', 'mail' => 'placeholder-for-uid-1', - 'created' => REQUEST_TIME, - 'status' => 1, - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + 'langcode' => $langcode, )) - ->execute(); + ->save(); } diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 2418ee7..3fe1f1c 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -14,6 +14,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\TypedData\DataDefinition; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Drupal\menu_link\Entity\MenuLink; @@ -46,9 +47,9 @@ /** * Implement hook_help(). */ -function user_help($path, $arg) { - switch ($path) { - case 'admin/help#user': +function user_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.user': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The User module allows users to register, log in, and log out. It also allows users with proper permissions to manage user roles and permissions. For more information, see the online documentation for the User module.', array('!user_docs' => 'https://drupal.org/documentation/modules/user')) . '

'; @@ -66,11 +67,14 @@ function user_help($path, $arg) { $output .= '
' . t('Because User accounts are an entity type, you can extend them by adding fields through the Manage fields tab on the Account settings page. By adding fields for e.g., a picture, a biography, or address, you can a create a custom profile for the users of the website.', array('!entity_help' => \Drupal::url('help.page', array('name' => 'entity')),'!field_help'=>\Drupal::url('help.page', array('name' => 'field')), '!accounts' => \Drupal::url('user.account_settings'))) . '
'; $output .= ''; return $output; - case 'admin/people/create': + + case 'user.admin_create': return '

' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '

'; - case 'admin/people/permissions': + + case 'user.admin_permissions': return '

' . t('Permissions let you control what users can do and see on your site. You can define a specific set of permissions for each role. (See the Roles page to create a role). Two important roles to consider are Authenticated Users and Administrators. Any permissions granted to the Authenticated Users role will be given to any user who can log into your site. You can make any role the Administrator role for the site, meaning this will be granted all new permissions automatically. You can do this on the User Settings page. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('@role' => url('admin/people/roles'), '@settings' => url('admin/config/people/accounts'))) . '

'; - case 'admin/people/roles': + + case 'user.role_list': $output = '

' . t('Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined on the permissions page. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the names and order of the roles on your site. It is recommended to order your roles from least permissive (anonymous user) to most permissive (administrator). To delete a role choose "edit role".', array('@permissions' => url('admin/people/permissions'))) . '

'; $output .= '

' . t('Drupal has three special user roles:') . '

'; $output .= '
    '; @@ -79,14 +83,15 @@ function user_help($path, $arg) { $output .= '
  • ' . t('Administrator role: this role is automatically granted all new permissions when you install a new module. Configure which role is the administrator role on the Account settings page.', array('@account_settings' => url('admin/config/people/accounts'))) . '
  • '; $output .= '
'; return $output; - case 'admin/config/people/accounts/fields': + + case 'field_ui.overview_user': return '

' . t('This form lets administrators add and edit fields for storing user data.') . '

'; - case 'admin/config/people/accounts/form-display': + + case 'field_ui.form_display_overview_user': return '

' . t('This form lets administrators configure how form fields should be displayed when editing a user profile.') . '

'; - case 'admin/config/people/accounts/display': + + case 'field_ui.display_overview_user': return '

' . t('This form lets administrators configure how fields should be displayed when rendering a user profile page.') . '

'; - case 'admin/people/search': - return '

' . t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') . '

'; } } @@ -146,14 +151,6 @@ function user_js_alter(&$javascript) { } /** - * Implements hook_entity_bundle_info(). - */ -function user_entity_bundle_info() { - $bundles['user']['user']['label'] = t('User'); - return $bundles; -} - -/** * Entity URI callback. */ function user_uri($user) { @@ -165,13 +162,15 @@ function user_uri($user) { /** * Populates $entity->account for each prepared entity. * - * Called by Drupal\Core\Entity\EntityViewBuilderInterface::buildContent() + * Called by Drupal\Core\Entity\EntityViewBuilderInterface::buildComponents() * implementations. * + * @param array &$build + * A renderable array representing the entity content. * @param \Drupal\user\EntityOwnerInterface[] $entities * The entities keyed by entity ID. */ -function user_attach_accounts(array $entities) { +function user_attach_accounts(array &$build, array $entities) { $uids = array(); foreach ($entities as $entity) { $uids[] = $entity->getOwnerId(); @@ -199,7 +198,8 @@ function user_attach_accounts(array $entities) { * preprocess stage. */ function user_picture_enabled() { - return (bool) field_info_instance('user', 'user_picture', 'user'); + $field_definitions = \Drupal::entityManager()->getFieldDefinitions('user', 'user'); + return isset($field_definitions['user_picture']); } /** @@ -503,9 +503,9 @@ function user_permission() { /** * Implements hook_user_view(). */ -function user_user_view(UserInterface $account, EntityViewDisplayInterface $display) { +function user_user_view(array &$build, UserInterface $account, EntityViewDisplayInterface $display) { if ($display->getComponent('member_for')) { - $account->content['member_for'] = array( + $build['member_for'] = array( '#type' => 'item', '#title' => t('Member for'), '#markup' => format_interval(REQUEST_TIME - $account->getCreatedTime()), diff --git a/core/modules/views/config/schema/views.argument_validator.schema.yml b/core/modules/views/config/schema/views.argument_validator.schema.yml index 3dbc9d7..6e3cb44 100644 --- a/core/modules/views/config/schema/views.argument_validator.schema.yml +++ b/core/modules/views/config/schema/views.argument_validator.schema.yml @@ -22,8 +22,11 @@ views.argument_validator_entity: type: mapping mapping: bundles: - type: boolean + type: sequence label: 'Bundles' + sequence: + - type: string + label: 'Bundle' access: type: boolean label: 'Access' diff --git a/core/modules/views/config/schema/views.cache.schema.yml b/core/modules/views/config/schema/views.cache.schema.yml index 1139df6..3e90332 100644 --- a/core/modules/views/config/schema/views.cache.schema.yml +++ b/core/modules/views/config/schema/views.cache.schema.yml @@ -1,20 +1,29 @@ # Schema for the views cache. views.cache.none: - type: mapping - label: 'None' + type: views_cache + label: 'No caching' mapping: - type: - type: string - label: 'Cache type' + options: + type: sequence + label: 'Options' + sequence: + - type: undefined + +views.cache.tag: + type: views_cache + label: 'Tag based caching' + mapping: + options: + type: sequence + label: 'Options' + sequence: + - type: undefined views.cache.time: - type: mapping - label: 'None' + type: views_cache + label: 'Time based caching' mapping: - type: - type: string - label: 'Time-based' options: type: mapping label: 'Cache options' diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml index 53af089..ae143bb 100644 --- a/core/modules/views/config/schema/views.data_types.schema.yml +++ b/core/modules/views/config/schema/views.data_types.schema.yml @@ -20,21 +20,25 @@ views_display: label: 'Pager' mapping: type: + type: string label: 'Pager type' options: type: views.pager.[%parent.type] provider: + type: string label: 'Provider' exposed_form: type: mapping label: 'Exposed form' mapping: type: + type: string label: 'Exposed form type' options: label: 'Options' type: views.exposed_form.[%parent.type] provider: + type: string label: 'Provider' access: type: mapping @@ -46,15 +50,10 @@ views_display: options: type: views.access.[%parent.type] provider: + type: string label: 'Provider' cache: type: views.cache.[type] - label: 'Caching' - mapping: - type: - label: 'Cache type' - provider: - label: 'Provider' empty: type: sequence label: 'No results behavior' @@ -93,20 +92,24 @@ views_display: label: 'Format' mapping: type: + type: string label: 'Type' options: type: views.style.[%parent.type] provider: + type: string label: 'Provider' row: type: mapping label: 'Row' mapping: type: + type: string label: 'Row type' options: type: views.row.[%parent.type] provider: + type: string label: 'Provider' query: type: mapping @@ -118,6 +121,7 @@ views_display: options: type: views.query.[%parent.type] provider: + type: string label: 'Provider' defaults: type: mapping @@ -665,9 +669,6 @@ views_style: default_row_class: type: boolean label: 'Add views row classes' - row_class_special: - type: boolean - label: 'Add striping (odd/even), first/last row classes' uses_fields: type: boolean label: 'Force using fields' @@ -855,3 +856,14 @@ views_entity_row: view_mode: type: string label: 'View mode' + +views_cache: + type: mapping + label: 'Cache configuration' + mapping: + type: + type: string + label: 'Cache type' + provider: + type: string + label: 'Provider' diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml index a2ad5c0..9a165cc 100644 --- a/core/modules/views/config/schema/views.schema.yml +++ b/core/modules/views/config/schema/views.schema.yml @@ -79,24 +79,31 @@ views.view.*: type: boolean label: 'Status' module: + type: string label: 'Module' id: + type: string label: 'Machine name' description: type: text label: 'Administrative description' tag: + type: string label: 'Tag' base_table: + type: string label: 'Base table' base_field: + type: string label: 'Base field' label: type: label label: 'Human readable name' core: + type: string label: 'Drupal version' uuid: + type: string label: 'UUID' display: type: sequence @@ -106,11 +113,13 @@ views.view.*: label: 'Display settings' mapping: id: + type: string label: 'Machine name' display_title: type: text label: 'Title' display_plugin: + type: string label: 'Display plugin' position: type: integer diff --git a/core/modules/views/lib/Drupal/views/Entity/Render/CurrentLanguageRenderer.php b/core/modules/views/lib/Drupal/views/Entity/Render/CurrentLanguageRenderer.php index 8090ce6..fb91787 100644 --- a/core/modules/views/lib/Drupal/views/Entity/Render/CurrentLanguageRenderer.php +++ b/core/modules/views/lib/Drupal/views/Entity/Render/CurrentLanguageRenderer.php @@ -7,8 +7,20 @@ namespace Drupal\views\Entity\Render; +use Drupal\views\ResultRow; + /** * Renders entities in the current language. */ class CurrentLanguageRenderer extends RendererBase { + + /** + * Returns NULL so that the current language is used. + * + * @param \Drupal\views\ResultRow $row + * The result row. + */ + protected function getLangcode(ResultRow $row) { + } + } diff --git a/core/modules/views/lib/Drupal/views/Entity/Render/DefaultLanguageRenderer.php b/core/modules/views/lib/Drupal/views/Entity/Render/DefaultLanguageRenderer.php index 8bd9e94..18875f5 100644 --- a/core/modules/views/lib/Drupal/views/Entity/Render/DefaultLanguageRenderer.php +++ b/core/modules/views/lib/Drupal/views/Entity/Render/DefaultLanguageRenderer.php @@ -15,61 +15,6 @@ class DefaultLanguageRenderer extends RendererBase { /** - * {@inheritdoc} - */ - public function preRender(array $result) { - /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ - $entities = array(); - $langcodes = array(); - - /** @var \Drupal\views\ResultRow $row */ - foreach ($result as $row) { - $entity = $row->_entity; - $entity->view = $this->view; - $langcodes[] = $langcode = $this->getLangcode($row); - $entities[$entity->id()][$langcode] = $entity; - } - $count_langcodes = count(array_unique($langcodes)); - - $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id()); - - if ($count_langcodes > 1) { - // Render each entity separate if we do have more than one translation. - // @todo It should be possible to use viewMultiple even if you get - // more than one language. See https://drupal.org/node/2073217. - foreach ($entities as $entity_translation) { - foreach ($entity_translation as $langcode => $entity) { - $entity = $entity->getTranslation($langcode); - $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $langcode); - } - } - } - else { - $langcode = reset($langcodes); - $entity_translations = array(); - foreach ($entities as $entity_translation) { - $entity = $entity_translation[$langcode]; - $entity_translations[$entity->id()] = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity; - } - $this->build = $view_builder->viewMultiple($entity_translations, $this->view->rowPlugin->options['view_mode'], $langcode); - } - } - - /** - * {@inheritdoc} - */ - public function render(ResultRow $row) { - $entity_id = $row->_entity->id(); - $langcode = $this->getLangcode($row); - if (isset($this->build[$entity_id][$langcode])) { - return $this->build[$entity_id][$langcode]; - } - else { - return $this->build[$entity_id]; - } - } - - /** * Returns the language code associated to the given row. * * @param \Drupal\views\ResultRow $row diff --git a/core/modules/views/lib/Drupal/views/Entity/Render/RendererBase.php b/core/modules/views/lib/Drupal/views/Entity/Render/RendererBase.php index a3b211f..b3590c2 100644 --- a/core/modules/views/lib/Drupal/views/Entity/Render/RendererBase.php +++ b/core/modules/views/lib/Drupal/views/Entity/Render/RendererBase.php @@ -67,18 +67,14 @@ * The full array of results from the query. */ public function preRender(array $result) { - /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ - $entities = array(); + $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id()); /** @var \Drupal\views\ResultRow $row */ foreach ($result as $row) { $entity = $row->_entity; $entity->view = $this->view; - $entities[$entity->id()] = $entity; + $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row)); } - - $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id()); - $this->build = $view_builder->viewMultiple($entities, $this->view->rowPlugin->options['view_mode']); } /** diff --git a/core/modules/views/lib/Drupal/views/Entity/Render/TranslationLanguageRenderer.php b/core/modules/views/lib/Drupal/views/Entity/Render/TranslationLanguageRenderer.php index 362b142..e65190a 100644 --- a/core/modules/views/lib/Drupal/views/Entity/Render/TranslationLanguageRenderer.php +++ b/core/modules/views/lib/Drupal/views/Entity/Render/TranslationLanguageRenderer.php @@ -42,6 +42,30 @@ class TranslationLanguageRenderer extends DefaultLanguageRenderer { /** * {@inheritdoc} */ + public function preRender(array $result) { + $view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id()); + + /** @var \Drupal\views\ResultRow $row */ + foreach ($result as $row) { + $entity = $row->_entity; + $entity->view = $this->view; + $langcode = $this->getLangcode($row); + $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row)); + } + } + + /** + * {@inheritdoc} + */ + public function render(ResultRow $row) { + $entity_id = $row->_entity->id(); + $langcode = $this->getLangcode($row); + return $this->build[$entity_id][$langcode]; + } + + /** + * {@inheritdoc} + */ protected function getLangcode(ResultRow $row) { return $row->{$this->langcodeAlias}; } diff --git a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php index adccf25..9fec99b 100644 --- a/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php +++ b/core/modules/views/lib/Drupal/views/EventSubscriber/RouteSubscriber.php @@ -155,7 +155,7 @@ class RouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ - protected function alterRoutes(RouteCollection $collection, $provider) { + protected function alterRoutes(RouteCollection $collection) { foreach ($this->getViewsDisplayIDsWithRoute() as $pair) { list($view_id, $display_id) = explode('.', $pair); $view = $this->viewStorage->load($view_id); diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php index 9800e32..7c83546 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/ViewsEntityArgumentValidator.php @@ -10,6 +10,7 @@ use Drupal\Component\Plugin\Derivative\DerivativeBase; use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -21,6 +22,8 @@ * @see \Drupal\views\Plugin\views\argument_validator\Entity */ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDerivativeInterface { + use StringTranslationTrait; + /** * The base plugin ID this derivative is for. * @@ -36,13 +39,6 @@ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDe protected $entityManager; /** - * The string translation. - * - * @var \Drupal\Core\StringTranslation\TranslationInterface - */ - protected $translationManager; - - /** * List of derivative definitions. * * @var array @@ -56,13 +52,13 @@ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDe * The base plugin ID. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager + * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation. */ - public function __construct($base_plugin_id, EntityManagerInterface $entity_manager, TranslationInterface $translation_manager) { + public function __construct($base_plugin_id, EntityManagerInterface $entity_manager, TranslationInterface $string_translation) { $this->basePluginId = $base_plugin_id; $this->entityManager = $entity_manager; - $this->translationManager = $translation_manager; + $this->stringTranslation = $string_translation; } /** @@ -96,13 +92,4 @@ class ViewsEntityArgumentValidator extends DerivativeBase implements ContainerDe return $this->derivatives; } - /** - * Translates a string to the current language or to a given language. - * - * See the t() documentation for details. - */ - protected function t($string, array $args = array(), array $options = array()) { - return $this->translationManager->translate($string, $args, $options); - } - } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php index 5f8a349..3f144ae 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_default/Raw.php @@ -101,7 +101,7 @@ class Raw extends ArgumentDefaultPluginBase { public function getArgument() { $path = $this->request->attributes->get('_system_path'); if ($this->options['use_alias']) { - $path = $this->aliasManager->getPathAlias($path); + $path = $this->aliasManager->getAliasByPath($path); } $args = explode('/', $path); if (isset($args[$this->options['index']])) { diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php index bf4e008..44127ec 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php @@ -7,6 +7,7 @@ namespace Drupal\views\Plugin\views\display; +use Drupal\Core\Form\FormErrorInterface; use Drupal\Core\State\StateInterface; use Drupal\Core\Routing\RouteCompiler; use Drupal\Core\Routing\RouteProviderInterface; @@ -40,6 +41,13 @@ protected $state; /** + * The form error helper. + * + * @var \Drupal\Core\Form\FormErrorInterface + */ + protected $formError; + + /** * Constructs a PathPluginBase object. * * @param array $configuration @@ -52,12 +60,15 @@ * The route provider. * @param \Drupal\Core\State\StateInterface $state * The state key value store. + * @param \Drupal\Core\Form\FormErrorInterface $form_error + * The form error helper. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, FormErrorInterface $form_error) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->routeProvider = $route_provider; $this->state = $state; + $this->formError = $form_error; } /** @@ -69,7 +80,8 @@ $plugin_id, $plugin_definition, $container->get('router.route_provider'), - $container->get('state') + $container->get('state'), + $container->get('form_validator') ); } @@ -395,8 +407,9 @@ parent::validateOptionsForm($form, $form_state); if ($form_state['section'] == 'path') { - if (strpos($form_state['values']['path'], '%') === 0) { - form_error($form['path'], $form_state, t('"%" may not be used for the first segment of a path.')); + $errors = $this->validatePath($form_state['values']['path']); + foreach ($errors as $error) { + $this->formError->setError($form['path'], $form_state, $error); } // Automatically remove '/' and trailing whitespace from path. @@ -415,4 +428,44 @@ } } + /** + * Validates the path of the display. + * + * @param string $path + * The path to validate. + * + * @return array + * A list of error strings. + */ + protected function validatePath($path) { + $errors = array(); + if (strpos($path, '%') === 0) { + $errors[] = $this->t('"%" may not be used for the first segment of a path.'); + } + + $path_sections = explode('/', $path); + // Symfony routing does not allow to use numeric placeholders. + // @see \Symfony\Component\Routing\RouteCompiler + $numeric_placeholders = array_filter($path_sections, function ($section) { + return (preg_match('/^%(.*)/', $section, $matches) + && is_numeric($matches[1])); + }); + if (!empty($numeric_placeholders)) { + $errors[] = $this->t("Numeric placeholders may not be used. Please use plain placeholders (%)."); + } + return $errors; + } + + /** + * {@inheritdoc} + */ + public function validate() { + $errors = parent::validate(); + + $errors += $this->validatePath($this->getOption('path')); + + return $errors; + } + + } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php index 5508f3a..99f5ea4 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/exposed_form/InputRequired.php @@ -83,7 +83,7 @@ class InputRequired extends ExposedFormPluginBase { 'format' => $this->options['text_input_required_format'], ); $handler = Views::handlerManager('area')->getHandler($options); - $handler->init($this->view, $options); + $handler->init($this->view, $this->displayHandler, $options); $this->displayHandler->handlers['empty'] = array( 'area' => $handler, ); diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php index ee71ed0..64ebe6a 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php @@ -227,7 +227,6 @@ function usesFields() { if ($this->usesRowClass()) { $options['row_class'] = array('default' => ''); $options['default_row_class'] = array('default' => TRUE, 'bool' => TRUE); - $options['row_class_special'] = array('default' => TRUE, 'bool' => TRUE); } $options['uses_fields'] = array('default' => FALSE, 'bool' => TRUE); @@ -313,12 +312,6 @@ function usesFields() { '#type' => 'checkbox', '#default_value' => $this->options['default_row_class'], ); - $form['row_class_special'] = array( - '#title' => t('Add striping (odd/even), first/last row classes'), - '#description' => t('Add css classes to the first and last line, as well as odd/even classes for striping.'), - '#type' => 'checkbox', - '#default_value' => $this->options['row_class_special'], - ); } if (!$this->usesFields() || !empty($this->options['uses_fields'])) { diff --git a/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php index 1694fd0..869ad88 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Entity/RowEntityRenderersTest.php @@ -33,7 +33,7 @@ class RowEntityRenderersTest extends ViewUnitTestBase { public static $testViews = array('test_entity_row_renderers'); /** - * An array of enabled languages. + * An array of added languages. * * @var array */ @@ -60,6 +60,9 @@ class RowEntityRenderersTest extends ViewUnitTestBase { $this->installSchema('user', array('users')); $this->installConfig(array('node', 'language')); + // The node.view route must exist when nodes are rendered. + $this->container->get('router.builder')->rebuild(); + $this->langcodes = array(\Drupal::languageManager()->getDefaultLanguage()->id); for ($i = 0; $i < 2; $i++) { $langcode = 'l' . $i; @@ -168,7 +171,7 @@ class RowEntityRenderersTest extends ViewUnitTestBase { $result = TRUE; foreach ($view->result as $index => $row) { $build = $view->rowPlugin->render($row); - $output = drupal_render($build['title']); + $output = drupal_render($build); $result = strpos($output, $expected[$index]) !== FALSE; if (!$result) { break; diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php index 3a41e77..61c1568 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/ExposedFormTest.php @@ -45,7 +45,7 @@ class ExposedFormTest extends ViewTestBase { // Create some random nodes. for ($i = 0; $i < 5; $i++) { - $this->drupalCreateNode(); + $this->drupalCreateNode(array('type' => 'article')); } } @@ -162,6 +162,30 @@ class ExposedFormTest extends ViewTestBase { } /** + * Test the input required exposed form type. + */ + public function testInputRequired() { + $view = entity_load('view', 'test_exposed_form_buttons'); + $display = &$view->getDisplay('default'); + $display['display_options']['exposed_form']['type'] = 'input_required'; + $view->save(); + + $this->drupalGet('test_exposed_form_buttons'); + $this->assertResponse(200); + $this->helperButtonHasLabel('edit-submit-test-exposed-form-buttons', t('Apply')); + + // Ensure that no results are displayed. + $rows = $this->xpath("//div[contains(@class, 'views-row')]"); + $this->assertEqual(count($rows), 0, 'No rows are displayed by default when no input is provided.'); + + $this->drupalGet('test_exposed_form_buttons', array('query' => array('type' => 'article'))); + + // Ensure that results are displayed. + $rows = $this->xpath("//div[contains(@class, 'views-row')]"); + $this->assertEqual(count($rows), 5, 'All rows are displayed by default when input is provided.'); + } + + /** * Returns a views exposed form ID. * * @param \Drupal\views\ViewExecutable $view diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableTest.php index 5bcca66..62a784b 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTableTest.php @@ -7,6 +7,9 @@ namespace Drupal\views\Tests\Plugin; +/** + * Tests the table style views plugin. + */ class StyleTableTest extends PluginTestBase { /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php index b8a6735..0c367d2 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleUnformattedTest.php @@ -45,19 +45,6 @@ function testDefaultRowClasses() { $count++; $attributes = $row->attributes(); $class = (string) $attributes['class'][0]; - // Make sure that each row has a row css class. - $this->assertTrue(strpos($class, "views-row-$count") !== FALSE, 'Make sure that each row has a row css class.'); - // Make sure that the odd/even classes are set right. - $odd_even = $count % 2 == 0 ? 'even' : 'odd'; - $this->assertTrue(strpos($class, "views-row-$odd_even") !== FALSE, 'Make sure that the odd/even classes are set right.'); - - if ($count == 1) { - $this->assertTrue(strpos($class, "views-row-first") !== FALSE, 'Make sure that the first class is set right.'); - } - elseif ($count == $count_result) { - $this->assertTrue(strpos($class, "views-row-last") !== FALSE, 'Make sure that the last class is set right.'); - - } $this->assertTrue(strpos($class, 'views-row') !== FALSE, 'Make sure that the views row class is set right.'); } } diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewRenderTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewRenderTest.php index aee849b..db066b2 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewRenderTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewRenderTest.php @@ -10,6 +10,9 @@ use Drupal\views\Tests\ViewTestBase; use Drupal\views\Views; +/** + * Tests general rendering of a view. + */ class ViewRenderTest extends ViewTestBase { /** diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php index 4ed9064..b8a614d 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestBase.php @@ -33,6 +33,12 @@ protected function setUp() { parent::setUp(); + // Views' Page displays put menu links in the 'navigation' menu by default. + entity_create('menu', array( + 'id' => 'navigation', + 'label' => 'Navigation', + ))->save(); + // Ensure that the plugin definitions are cleared. foreach (ViewExecutable::getPluginTypes() as $plugin_type) { $this->container->get("plugin.manager.views.$plugin_type")->clearCachedDefinitions(); @@ -64,7 +70,6 @@ $query->values($record); } $query->execute(); - $this->checkPermissions(array(), TRUE); } /** diff --git a/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php index 8a81fb7..8a3b508 100644 --- a/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php +++ b/core/modules/views/lib/Drupal/views/ViewsAccessCheck.php @@ -9,7 +9,6 @@ use Drupal\Core\Access\AccessCheckInterface; use Drupal\Core\Session\AccountInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; /** @@ -27,12 +26,16 @@ class ViewsAccessCheck implements AccessCheckInterface { } /** - * Implements AccessCheckInterface::applies(). + * Checks access. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The currently logged in account. + * + * @return string + * A \Drupal\Core\Access\AccessInterface constant value. */ - public function access(Route $route, Request $request, AccountInterface $account) { - $access = $account->hasPermission('access all views'); - - return $access ? static::ALLOW : static::DENY; + public function access(AccountInterface $account) { + return $account->hasPermission('access all views') ? static::ALLOW : static::DENY; } } diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_default/RawTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_default/RawTest.php index af285f9..397cb36 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_default/RawTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_default/RawTest.php @@ -42,7 +42,7 @@ class RawTest extends UnitTestCase { $request = new Request(array(), array(), array('_system_path' => 'test/example')); $alias_manager = $this->getMock('Drupal\Core\Path\AliasManagerInterface'); $alias_manager->expects($this->never()) - ->method('getPathAlias'); + ->method('getAliasByPath'); // Don't use aliases. $raw = new Raw(array(), 'raw', array(), $request, $alias_manager); @@ -64,7 +64,7 @@ class RawTest extends UnitTestCase { // Setup an alias manager with a path alias. $alias_manager = $this->getMock('Drupal\Core\Path\AliasManagerInterface'); $alias_manager->expects($this->any()) - ->method('getPathAlias') + ->method('getAliasByPath') ->with($this->equalTo('test/example')) ->will($this->returnValue('other/example')); diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php index 95f140e..596b805 100644 --- a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php @@ -47,6 +47,13 @@ class PathPluginBaseTest extends UnitTestCase { */ protected $state; + /** + * The mocked form error. + * + * @var \Drupal\Core\Form\FormErrorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $formError; + public static function getInfo() { return array( 'name' => 'Display: Path plugin base.', @@ -63,8 +70,9 @@ class PathPluginBaseTest extends UnitTestCase { $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $this->state = $this->getMock('\Drupal\Core\State\StateInterface'); + $this->formError = $this->getMock('Drupal\Core\Form\FormErrorInterface'); $this->pathPlugin = $this->getMockBuilder('Drupal\views\Plugin\views\display\PathPluginBase') - ->setConstructorArgs(array(array(), 'path_base', array(), $this->routeProvider, $this->state)) + ->setConstructorArgs(array(array(), 'path_base', array(), $this->routeProvider, $this->state, $this->formError)) ->setMethods(NULL) ->getMock(); $this->setupContainer(); diff --git a/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php b/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php index 61c3850..1d95ff6 100644 --- a/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php +++ b/core/modules/views/tests/Drupal/views/Tests/ViewsTest.php @@ -13,6 +13,9 @@ use Drupal\views\ViewExecutableFactory; use Drupal\Core\DependencyInjection\ContainerBuilder; +/** + * @coversDefaultClass \Drupal\views\Views + */ class ViewsTest extends UnitTestCase { public static function getInfo() { @@ -55,6 +58,8 @@ class ViewsTest extends UnitTestCase { /** * Tests the getView() method. + * + * @covers ::getView */ public function testGetView() { $executable = Views::getView('test_view'); diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml index b23b62b..57d96a8 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml @@ -111,7 +111,6 @@ display: grouping: { } row_class: '' default_row_class: true - row_class_special: true row: type: fields fields: diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml index 159b4f6..7ea2f9f 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml @@ -52,7 +52,6 @@ display: grouping: { } row_class: '' default_row_class: '1' - row_class_special: '1' override: '1' sticky: '0' summary: summary-text diff --git a/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml b/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml index edbfd9c..3392573 100644 --- a/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml +++ b/core/modules/views/tests/modules/views_test_config/views_test_config.info.yml @@ -6,4 +6,3 @@ version: VERSION core: 8.x dependencies: - views -hidden: true diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml b/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml index 07084c0..39b72e4 100644 --- a/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml +++ b/core/modules/views/tests/modules/views_test_data/views_test_data.info.yml @@ -6,4 +6,3 @@ version: VERSION core: 8.x dependencies: - views -hidden: true diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 9d6e4af..216bc93 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -21,13 +21,14 @@ use Drupal\views\Entity\View; use Drupal\views\Views; use Drupal\field\FieldInstanceConfigInterface; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function views_help($path, $arg) { - switch($path) { - case 'admin/help#views': +function views_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.views': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Views module provides a back end to fetch information from content, user accounts, taxonomy terms, and other entities from the database and present it to the user as a grid, HTML list, table, unformatted list, etc. The resulting displays are known generally as views.') . '

'; diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 7308da8..ae967bd 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -499,8 +499,6 @@ function template_preprocess_views_view_table(&$variables) { $options = $view->style_plugin->options; $handler = $view->style_plugin; - $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : TRUE; - $fields = &$view->field; $columns = $handler->sanitizeColumns($options['columns'], $fields); @@ -676,18 +674,8 @@ function template_preprocess_views_view_table(&$variables) { $variables['header'] = array(); } - $count = 0; foreach ($variables['rows'] as $num => $row) { $variables['rows'][$num]['attributes'] = array(); - if ($row_class_special) { - $variables['rows'][$num]['attributes']['class'][] = ($count++ % 2 == 0) ? 'odd' : 'even'; - if ($num === 0) { - $variables['rows'][$num]['attributes']['class'][] = 'views-row-first'; - } - elseif ($num === (count($variables['rows']) - 1)) { - $variables['rows'][$num]['attributes']['class'][] = 'views-row-last'; - } - } if ($row_class = $handler->getRowClass($num)) { $variables['rows'][$num]['attributes']['class'][] = $row_class; } @@ -878,27 +866,12 @@ function template_preprocess_views_view_unformatted(&$variables) { $options = $style->options; $default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : FALSE; - $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : FALSE; - // Set up striping values. - $count = 0; - $max = count($rows); foreach ($rows as $id => $row) { $variables['rows'][$id] = array(); $variables['rows'][$id]['content'] = $row; $variables['rows'][$id]['attributes'] = array(); - $count++; if ($default_row_class) { $variables['rows'][$id]['attributes']['class'][] = 'views-row'; - $variables['rows'][$id]['attributes']['class'][] = 'views-row-' . $count; - } - if ($row_class_special) { - $variables['rows'][$id]['attributes']['class'][] = 'views-row-' . ($count % 2 ? 'odd' : 'even'); - if ($count == 1) { - $variables['rows'][$id]['attributes']['class'][] = 'views-row-first'; - } - if ($count == $max) { - $variables['rows'][$id]['attributes']['class'][] = 'views-row-last'; - } } if ($row_class = $view->style_plugin->getRowClass($id)) { $variables['rows'][$id]['attributes']['class'][] = $row_class; diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc index ca14056..e90ef35 100644 --- a/core/modules/views_ui/admin.inc +++ b/core/modules/views_ui/admin.inc @@ -225,7 +225,8 @@ function views_ui_taxonomy_autocomplete_validate($element, &$form_state) { if ($tags = $element['#value']) { // Get the machine names of the vocabularies we will search, keyed by the // vocabulary IDs. - $field = field_info_field($element['#entity_type'], $element['#field_name']); + $field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($element['#entity_type']); + $field = $field_storage_definitions[$element['#field_name']]; $vocabularies = array(); $allowed_values = $field->getSetting('allowed_values'); if (!empty($allowed_values)) { diff --git a/core/modules/views_ui/css/views_ui.admin.theme.css b/core/modules/views_ui/css/views_ui.admin.theme.css index 08bfd80..f81465d 100644 --- a/core/modules/views_ui/css/views_ui.admin.theme.css +++ b/core/modules/views_ui/css/views_ui.admin.theme.css @@ -470,6 +470,15 @@ td.group-title { margin: 0 5px 0 6px; } +.views-displays .tabs.secondary .tabs__tab + .tabs__tab { + border-top: 0; +} + +.views-displays .tabs.secondary li.tabs__tab:hover { + border: 0; + padding-left: 0; +} + .views-displays .tabs.secondary a { border: 1px solid #cbcbcb; border-radius: 7px; diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/AdvancedSettingsForm.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/AdvancedSettingsForm.php index 90dc49d..9ba103d 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Form/AdvancedSettingsForm.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/AdvancedSettingsForm.php @@ -97,6 +97,8 @@ class AdvancedSettingsForm extends ConfigFormBase { ->set('no_javascript', $form_state['values']['no_javascript']) ->set('display_extenders', isset($form_state['values']['display_extenders']) ? $form_state['values']['display_extenders'] : array()) ->save(); + + parent::submitForm($form, $form_state); } /** diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/BasicSettingsForm.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/BasicSettingsForm.php index a441805..f898f9a 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Form/BasicSettingsForm.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/BasicSettingsForm.php @@ -139,6 +139,8 @@ class BasicSettingsForm extends ConfigFormBase { ->set('ui.show.performance_statistics', $form_state['values']['ui_show_performance_statistics']) ->set('ui.show.additional_queries', $form_state['values']['ui_show_additional_queries']) ->save(); + + parent::submitForm($form, $form_state); } } diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Tests/DisplayPath.php b/core/modules/views_ui/lib/Drupal/views_ui/Tests/DisplayPath.php index 8435589..8eb3e6d 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Tests/DisplayPath.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/DisplayPath.php @@ -30,6 +30,14 @@ class DisplayPath extends UITestBase { } public function testPathUI() { + $this->doBasicPathUITest(); + $this->doAdvancedPathsValidationTest(); + } + + /** + * Tests basic functionality in configuring a view. + */ + protected function doBasicPathUITest() { $this->drupalGet('admin/structure/views/view/test_view'); // Add a new page display and check the appearing text. @@ -45,6 +53,21 @@ class DisplayPath extends UITestBase { } /** + * Tests a couple of invalid path patterns. + */ + protected function doAdvancedPathsValidationTest() { + $url = 'admin/structure/views/nojs/display/test_view/page_1/path'; + + $this->drupalPostForm($url, array('path' => '%/magrathea'), t('Apply')); + $this->assertUrl($url); + $this->assertText('"%" may not be used for the first segment of a path.'); + + $this->drupalPostForm($url, array('path' => 'user/%1/example'), t('Apply')); + $this->assertUrl($url); + $this->assertText("Numeric placeholders may not be used. Please use plain placeholders (%)."); + } + + /** * Tests deleting a page display that has no path. */ public function testDeleteWithNoPath() { diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Tests/SettingsTest.php b/core/modules/views_ui/lib/Drupal/views_ui/Tests/SettingsTest.php index 86029a9..a4e5f76 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Tests/SettingsTest.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/SettingsTest.php @@ -37,6 +37,10 @@ function testEditUI() { $this->drupalGet('admin/structure/views'); $this->assertLinkByHref('admin/structure/views/settings'); + // Test the confirmation message. + $this->drupalPostForm('admin/structure/views/settings', array(), t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + // Configure to always show the master display. $edit = array( 'ui_show_master_display' => TRUE, @@ -117,6 +121,27 @@ function testEditUI() { $this->assertEqual(count($xpath), 1, 'The views sql is shown.'); $this->assertFalse(strpos($xpath[0], 'db_condition_placeholder') !== FALSE, 'No placeholders are shown in the views sql.'); $this->assertTrue(strpos($xpath[0], "node_field_data.status = '1'") !== FALSE, 'The placeholders in the views sql is replace by the actual value.'); + + // Test the advanced settings form. + + // Test the confirmation message. + $this->drupalPostForm('admin/structure/views/settings/advanced', array(), t('Save configuration')); + $this->assertText(t('The configuration options have been saved.')); + + $edit = array( + 'skip_cache' => TRUE, + 'sql_signature' => TRUE, + 'no_javascript' => TRUE, + ); + $this->drupalPostForm('admin/structure/views/settings/advanced', $edit, t('Save configuration')); + + $this->assertFieldChecked('edit-skip-cache', 'The skip_cache option is checked.'); + $this->assertFieldChecked('edit-sql-signature', 'The sql_signature option is checked.'); + $this->assertFieldChecked('edit-no-javascript', 'The no_javascript option is checked.'); + + // Test the "Clear Views' cache" button. + $this->drupalPostForm('admin/structure/views/settings/advanced', array(), t("Clear Views' cache")); + $this->assertText(t('The cache has been cleared.')); } } diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Tests/ViewEditTest.php b/core/modules/views_ui/lib/Drupal/views_ui/Tests/ViewEditTest.php index 1693888..8e35fbc 100644 --- a/core/modules/views_ui/lib/Drupal/views_ui/Tests/ViewEditTest.php +++ b/core/modules/views_ui/lib/Drupal/views_ui/Tests/ViewEditTest.php @@ -101,7 +101,7 @@ class ViewEditTest extends UITestBase { $langcode_url = 'admin/structure/views/nojs/display/test_view/default/field_langcode'; $this->assertLinkByHref($langcode_url); $this->assertLink(t("Current user's language")); - // Click the link and check the form before language is enabled. + // Click the link and check the form before language is added. $this->drupalGet($langcode_url); $this->assertResponse(200); $this->assertText(t("You don't have translatable entity types.")); diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListBuilderTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListBuilderTest.php index 4837525..3ead6d9 100644 --- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListBuilderTest.php +++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListBuilderTest.php @@ -14,6 +14,9 @@ use Drupal\views\ViewExecutableFactory; use Drupal\views_ui\ViewListBuilder; +/** + * @coversDefaultClass \Drupal\views_ui\ViewListBuilder + */ class ViewListBuilderTest extends UnitTestCase { public static function getInfo() { @@ -28,6 +31,7 @@ class ViewListBuilderTest extends UnitTestCase { * Tests the listing of displays on a views list builder. * * @see \Drupal\views_ui\ViewListBuilder::getDisplaysList() + * @covers ::buildRow */ public function testBuildRowEntityList() { $storage = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityStorage') @@ -79,9 +83,10 @@ class ViewListBuilderTest extends UnitTestCase { ); $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); $state = $this->getMock('\Drupal\Core\State\StateInterface'); + $form_error = $this->getMock('Drupal\Core\Form\FormErrorInterface'); $page_display = $this->getMock('Drupal\views\Plugin\views\display\Page', array('initDisplay', 'getPath'), - array(array(), 'default', $display_manager->getDefinition('page'), $route_provider, $state) + array(array(), 'default', $display_manager->getDefinition('page'), $route_provider, $state, $form_error) ); $page_display->expects($this->any()) ->method('getPath') @@ -125,7 +130,7 @@ class ViewListBuilderTest extends UnitTestCase { // because t() is called on there. $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); $view_list_builder = new TestViewListBuilder($entity_type, $storage, $display_manager); - $view_list_builder->setTranslationManager($this->getStringTranslationStub()); + $view_list_builder->setStringTranslation($this->getStringTranslationStub()); $view = new View($values, 'view'); diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module index b1c3437..a39fa71 100644 --- a/core/modules/views_ui/views_ui.module +++ b/core/modules/views_ui/views_ui.module @@ -12,14 +12,14 @@ use Drupal\views\Analyzer; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; +use Symfony\Component\HttpFoundation\Request; /** * Implements hook_help(). */ -function views_ui_help($path, $arg) { - - switch ($path) { - case 'admin/help#views_ui': +function views_ui_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.views_ui': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The Views UI module provides an interface for managing views for the Views module. For more information, see the online documentation for the Views UI module.', array('@views' => \Drupal::url('help.page', array('name' => 'views')), '@handbook' => 'https://drupal.org/documentation/modules/views_ui')) . '

'; @@ -320,7 +320,7 @@ function views_ui_views_analyze(ViewExecutable $view) { continue; } if ($display->hasPath() && $path = $display->getOption('path')) { - $normal_path = \Drupal::service('path.alias_manager.cached')->getSystemPath($path); + $normal_path = \Drupal::service('path.alias_manager.cached')->getPathByAlias($path); if ($path != $normal_path) { $ret[] = Analyzer::formatMessage(t('You have configured display %display with a path which is an path alias as well. This might lead to unwanted effects so better use an internal path.', array('%display' => $display->display['display_title'])), 'warning'); } diff --git a/core/modules/xmlrpc/tests/modules/xmlrpc_test/xmlrpc_test.info.yml b/core/modules/xmlrpc/tests/modules/xmlrpc_test/xmlrpc_test.info.yml index 9c0fe2a..0359f92 100644 --- a/core/modules/xmlrpc/tests/modules/xmlrpc_test/xmlrpc_test.info.yml +++ b/core/modules/xmlrpc/tests/modules/xmlrpc_test/xmlrpc_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for XML-RPC tests according to the validator1 speci package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/modules/xmlrpc/xmlrpc.module b/core/modules/xmlrpc/xmlrpc.module index 187da85..bc82806 100644 --- a/core/modules/xmlrpc/xmlrpc.module +++ b/core/modules/xmlrpc/xmlrpc.module @@ -5,12 +5,14 @@ * Enables XML-RPC functionality. */ +use Symfony\Component\HttpFoundation\Request; + /** * Implements hook_help(). */ -function xmlrpc_help($path, $args) { - switch ($path) { - case 'admin/help#xmlrpc': +function xmlrpc_help($route_name, Request $request) { + switch ($route_name) { + case 'help.page.xmlrpc': $output = ''; $output .= '

' . t('About') . '

'; $output .= '

' . t('The XML-RPC module gives external systems the opportunity to communicate with the site through the XML-RPC protocol. An XML-RPC client can communicate with the site by making a request to xmlrpc.php. For more information, see the online documentation for the XML-RPC module.', array('!xrphp' => \Drupal::url('xmlrpc.php'),'!xrdocs' => 'https://drupal.org/documentation/modules/xmlrpc')) . '

'; diff --git a/core/profiles/standard/config/install/entity.form_display.user.user.default.yml b/core/profiles/standard/config/install/entity.form_display.user.user.default.yml index a9c9197..6f0f91f 100644 --- a/core/profiles/standard/config/install/entity.form_display.user.user.default.yml +++ b/core/profiles/standard/config/install/entity.form_display.user.user.default.yml @@ -13,3 +13,4 @@ status: true dependencies: module: - image + - user diff --git a/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml b/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml index 1c7e4e9..ef048ea 100644 --- a/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml +++ b/core/profiles/standard/config/install/entity.view_display.user.user.compact.yml @@ -18,3 +18,4 @@ dependencies: - entity.view_mode.user.compact module: - image + - user diff --git a/core/profiles/standard/config/install/entity.view_display.user.user.default.yml b/core/profiles/standard/config/install/entity.view_display.user.user.default.yml index f39700b..67df43e 100644 --- a/core/profiles/standard/config/install/entity.view_display.user.user.default.yml +++ b/core/profiles/standard/config/install/entity.view_display.user.user.default.yml @@ -14,3 +14,4 @@ status: true dependencies: module: - image + - user diff --git a/core/profiles/standard/config/install/field.instance.node.article.field_tags.yml b/core/profiles/standard/config/install/field.instance.node.article.field_tags.yml index 18b58c2..369306a 100644 --- a/core/profiles/standard/config/install/field.instance.node.article.field_tags.yml +++ b/core/profiles/standard/config/install/field.instance.node.article.field_tags.yml @@ -2,6 +2,7 @@ id: node.article.field_tags entity_type: node bundle: article field_name: field_tags +field_type: taxonomy_term_reference label: Tags description: 'Enter a comma-separated list. For example: Amsterdam, Mexico City, "Cleveland, Ohio"' required: false diff --git a/core/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml b/core/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml index c419771..30bccf7 100644 --- a/core/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml +++ b/core/profiles/testing/modules/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing the drupal_system_listing function.' package: Testing version: VERSION core: 8.x -hidden: true diff --git a/core/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml b/core/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml index 042257d..17bda6c 100644 --- a/core/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml +++ b/core/profiles/testing/modules/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info.yml @@ -4,4 +4,3 @@ description: 'Support module for testing the drupal_system_listing function.' package: Testing version: VERSION core: 6.x -hidden: true diff --git a/core/scripts/password-hash.sh b/core/scripts/password-hash.sh index 8c6ebb1..f80d75c 100755 --- a/core/scripts/password-hash.sh +++ b/core/scripts/password-hash.sh @@ -4,19 +4,24 @@ /** * Drupal hash script - to generate a hash from a plaintext password * - * Check for your PHP interpreter - on Windows you'll probably have to - * replace line 1 with - * #!c:/program files/php/php.exe - * * @param password1 [password2 [password3 ...]] * Plain-text passwords in quotes (or with spaces backslash escaped). */ -if (version_compare(PHP_VERSION, "5.2.0", "<")) { +use Drupal\Core\DrupalKernel; + +// Check for $_SERVER['argv'] instead of PHP_SAPI === 'cli' to allow this script +// to be tested with the Simpletest UI test runner. +// @see \Drupal\system\Tests\System\ScriptTest +if (!isset($_SERVER['argv']) || !is_array($_SERVER['argv'])) { + return; +} + +if (version_compare(PHP_VERSION, "5.4.2", "<")) { $version = PHP_VERSION; echo << - - Set the working directory for the script to the specified path. - To execute this script this has to be the root directory of your - Drupal installation, e.g. /home/www/foo/drupal (assuming Drupal - running on Unix). Use surrounding quotation marks on Windows. - "" ["" ["" ...]] One or more plan-text passwords enclosed by double quotes. The output hash may be manually entered into the {users}.pass field to change a password via SQL to a known value. -To run this script without the --root argument invoke it from the root directory -of your Drupal installation as - ./scripts/{$script} -\n EOF; exit; } -$passwords = array(); - -// Parse invocation arguments. -while ($param = array_shift($_SERVER['argv'])) { - switch ($param) { - case '--root': - // Change the working directory. - $path = array_shift($_SERVER['argv']); - if (is_dir($path)) { - chdir($path); - } - break; - default: - // Add a password to the list to be processed. - $passwords[] = $param; - break; - } -} +// Password list to be processed. +$passwords = $_SERVER['argv']; $core = dirname(__DIR__); require_once $core . '/vendor/autoload.php'; require_once $core . '/includes/bootstrap.inc'; // Bootstrap the code so we have the container. -drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + +$kernel = new DrupalKernel('prod', drupal_classloader(), FALSE); +$kernel->boot(); -$password_hasher = \Drupal::service('password'); +$password_hasher = $kernel->getContainer()->get('password'); foreach ($passwords as $password) { print("\npassword: $password \t\thash: ". $password_hasher->hash($password) ."\n"); diff --git a/core/scripts/rebuild_token_calculator.sh b/core/scripts/rebuild_token_calculator.sh index 729d34d..9e7dc03 100755 --- a/core/scripts/rebuild_token_calculator.sh +++ b/core/scripts/rebuild_token_calculator.sh @@ -1,5 +1,4 @@ #!/usr/bin/env php - boot(); $request = Request::createFromGlobals(); $container = $kernel->getContainer(); $container->enterScope('request'); $container->set('request', $request, 'request'); + $container->get('request_stack')->push($request); $module_handler = $container->get('module_handler'); - // @todo Remove System module. Only needed because \Drupal\Core\Datetime\Date - // has a (needless) dependency on the 'date_format' entity, so calls to - // format_date()/format_interval() cause a plugin not found exception. - $module_handler->addModule('system', 'core/modules/system'); - $module_handler->addModule('simpletest', 'core/modules/simpletest'); $module_handler->loadAll(); - $module_filenames = $module_handler->getModuleList(); - $kernel->updateModules($module_filenames, $module_filenames); simpletest_classloader_register(); } @@ -430,7 +423,7 @@ function simpletest_script_bootstrap() { * connections are prepared only. */ function simpletest_script_setup_database($new = FALSE) { - global $args, $databases; + global $args; // If there is an existing Drupal installation that contains a database // connection info in settings.php, then $databases['default']['default'] will @@ -442,7 +435,6 @@ function simpletest_script_setup_database($new = FALSE) { // connection can be set and/or overridden with the --dburl parameter. if (!empty($args['dburl'])) { // Remove a possibly existing default connection (from settings.php). - unset($databases['default']); Database::removeConnection('default'); $info = parse_url($args['dburl']); @@ -455,25 +447,26 @@ function simpletest_script_setup_database($new = FALSE) { 'pass' => '', 'fragment' => '', ); + if ($info['path'][0] === '/') { + $info['path'] = substr($info['path'], 1); + } + if ($info['scheme'] === 'sqlite' && $info['path'][0] !== '/') { + $info['path'] = DRUPAL_ROOT . '/' . $info['path']; + } $databases['default']['default'] = array( 'driver' => $info['scheme'], 'username' => $info['user'], 'password' => $info['pass'], 'host' => $info['host'], - 'database' => ltrim($info['path'], '/'), + 'database' => $info['path'], 'prefix' => array( 'default' => $info['fragment'], ), ); } - // Otherwise, ensure that database table prefix info is an array. - // @see https://drupal.org/node/2176621 - elseif (isset($databases['default']['default'])) { - if (!is_array($databases['default']['default']['prefix'])) { - $databases['default']['default']['prefix'] = array( - 'default' => $databases['default']['default']['prefix'], - ); - } + // Otherwise, use the default database connection from settings.php. + else { + $databases['default'] = Database::getConnectionInfo('default'); } // If there is no default database connection for tests, we cannot continue. diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php new file mode 100644 index 0000000..4d4bc0f --- /dev/null +++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageReadOnlyTest.php @@ -0,0 +1,123 @@ + 'Simple read only file storage', + 'description' => 'Tests the FileStorageReadOnly implementation.', + 'group' => 'PHP Storage', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + $dir_path = sys_get_temp_dir() . '/php'; + + $this->standardSettings = array( + 'directory' => $dir_path, + 'bin' => 'test', + ); + $this->readonlyStorage = array( + 'directory' => $dir_path, + // Let this read from the bin where the other instance is writing. + 'bin' => 'test', + ); + } + + /** + * Tests writing with one class and reading with another. + * + * @group Drupal + * @group PhpStorage + */ + public function testReadOnly() { + $php = new FileStorage($this->standardSettings); + $name = $this->randomName() . '/' . $this->randomName() . '.php'; + + // Find a global that doesn't exist. + do { + $random = mt_rand(10000, 100000); + } while (isset($GLOBALS[$random])); + + // Write out a PHP file and ensure it's successfully loaded. + $code = "save($name, $code); + $this->assertSame($success, TRUE); + $php_read = new FileReadOnlyStorage($this->readonlyStorage); + $php_read->load($name); + $this->assertTrue($GLOBALS[$random]); + + // If the file was successfully loaded, it must also exist, but ensure the + // exists() method returns that correctly. + $this->assertSame($php_read->exists($name), TRUE); + // Saving and deleting should always fail. + $this->assertFalse($php_read->save($name, $code)); + $this->assertFalse($php_read->delete($name)); + } + + /** + * Tests writeable() method. + * + * @group Drupal + * @group PhpStorage + */ + public function testWriteable() { + $php_read = new FileReadOnlyStorage($this->readonlyStorage); + $this->assertFalse($php_read->writeable()); + } + + /** + * Tests deleteAll() method. + * + * @group Drupal + * @group PhpStorage + */ + public function testDeleteAll() { + $php_read = new FileReadOnlyStorage($this->readonlyStorage); + $this->assertFalse($php_read->deleteAll()); + + // Make sure directory exists prior to removal. + $this->assertTrue(file_exists(sys_get_temp_dir() . '/php/test'), 'File storage directory does not exist.'); + } + +} diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php index 2645e51..93015a4 100644 --- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php +++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php @@ -8,7 +8,6 @@ namespace Drupal\Tests\Component\PhpStorage; use Drupal\Component\PhpStorage\FileStorage; -use Drupal\Component\PhpStorage\FileReadOnlyStorage; /** * Tests the simple file storage. @@ -16,7 +15,7 @@ * @group Drupal * @group PhpStorage * - * @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage|\Drupal\Component\PhpStorage\FileReadOnlyStorage + * @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage */ class FileStorageTest extends PhpStorageTestBase { @@ -28,13 +27,6 @@ class FileStorageTest extends PhpStorageTestBase { protected $standardSettings; /** - * Read only test settings to pass to storage instances. - * - * @var array - */ - protected $readonlyStorage; - - /** * {@inheritdoc} */ public static function getInfo() { @@ -57,11 +49,6 @@ class FileStorageTest extends PhpStorageTestBase { 'directory' => $dir_path, 'bin' => 'test', ); - $this->readonlyStorage = array( - 'directory' => $dir_path, - // Let this read from the bin where the other instance is writing. - 'bin' => 'test', - ); } /** @@ -76,46 +63,12 @@ class FileStorageTest extends PhpStorageTestBase { } /** - * Tests writing with one class and reading with another. - * - * @group Drupal - * @group PhpStorage - */ - public function testReadOnly() { - $php = new FileStorage($this->standardSettings); - $name = $this->randomName() . '/' . $this->randomName() . '.php'; - - // Find a global that doesn't exist. - do { - $random = mt_rand(10000, 100000); - } while (isset($GLOBALS[$random])); - - // Write out a PHP file and ensure it's successfully loaded. - $code = "save($name, $code); - $this->assertSame($success, TRUE); - $php_read = new FileReadOnlyStorage($this->readonlyStorage); - $php_read->load($name); - $this->assertTrue($GLOBALS[$random]); - - // If the file was successfully loaded, it must also exist, but ensure the - // exists() method returns that correctly. - $this->assertSame($php_read->exists($name), TRUE); - // Saving and deleting should always fail. - $this->assertFalse($php_read->save($name, $code)); - $this->assertFalse($php_read->delete($name)); - } - - /** * Tests writeable() method. * * @group Drupal * @group PhpStorage */ public function testWriteable() { - $php_read = new FileReadOnlyStorage($this->readonlyStorage); - $this->assertFalse($php_read->writeable()); - $php = new FileStorage($this->standardSettings); $this->assertTrue($php->writeable()); } @@ -127,8 +80,6 @@ class FileStorageTest extends PhpStorageTestBase { * @group PhpStorage */ public function testDeleteAll() { - $php_read = new FileReadOnlyStorage($this->readonlyStorage); - $this->assertFalse($php_read->deleteAll()); // Make sure directory exists prior to removal. $this->assertTrue(file_exists(sys_get_temp_dir() . '/php/test'), 'File storage directory does not exist.'); diff --git a/core/tests/Drupal/Tests/Core/Access/AccessArgumentsResolverTest.php b/core/tests/Drupal/Tests/Core/Access/AccessArgumentsResolverTest.php new file mode 100644 index 0000000..1aa8171 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Access/AccessArgumentsResolverTest.php @@ -0,0 +1,254 @@ + 'Access arguments resolver tests', + 'description' => 'Test for the AccessArgumentsResolver object.', + 'group' => 'Routing', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $this->route = new Route('/test'); + } + + /** + * Tests the getArgument() method. + * + * @dataProvider providerTestGetArgument + */ + public function testGetArgument($callable, $request, $expected) { + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame($expected, $arguments); + } + + /** + * Provides test data to testGetArgument(). + */ + public function providerTestGetArgument() { + $data = array(); + + // Test an optional parameter with no provided value. + $data[] = array( + function($foo = 'foo') {}, new Request(), array('foo'), + ); + + // Test an optional parameter with a provided value. + $request = new Request(); + $request->attributes->set('foo', 'bar'); + $data[] = array( + function($foo = 'foo') {}, $request, array('bar'), + ); + + // Test with a provided value. + $request = new Request(); + $request->attributes->set('foo', 'bar'); + $data[] = array( + function($foo) {}, $request, array('bar'), + ); + + // Test with an explicitly NULL value. + $request = new Request(); + $request->attributes->set('foo', NULL); + $data[] = array( + function($foo) {}, $request, array(NULL), + ); + + // Test with a raw value that overrides the provided upcast value, since + // it is not typehinted. + $request = new Request(); + $request->attributes->set('foo', 'bar'); + $request->attributes->set('_raw_variables', new ParameterBag(array('foo' => 'baz'))); + $data[] = array( + function($foo) {}, $request, array('baz'), + ); + + return $data; + } + + /** + * Tests getArgument() with a Route object. + */ + public function testGetArgumentRoute() { + $callable = function(Route $route) {}; + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array($this->route), $arguments); + } + + /** + * Tests getArgument() with a Route object for a parameter with a custom name. + */ + public function testGetArgumentRouteCustomName() { + $callable = function(Route $custom_name) {}; + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array($this->route), $arguments); + } + + /** + * Tests getArgument() with a Route, Request, and Account object. + */ + public function testGetArgumentRouteRequestAccount() { + $callable = function(Route $route, Request $request, AccountInterface $account) {}; + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array($this->route, $request, $this->account), $arguments); + + // Test again, but with the arguments in a different order. + $callable = function(AccountInterface $account, Request $request, Route $route) {}; + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array($this->account, $request, $this->route), $arguments); + } + + /** + * Tests getArgument() with a '$route' parameter with no typehint. + * + * Without the typehint, the Route object will not be passed to the callable. + * + * @expectedException \RuntimeException + * @expectedExceptionMessage requires a value for the "$route" argument. + */ + public function testGetArgumentRouteNoTypehint() { + $callable = function($route) {}; + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertNull($arguments); + } + + /** + * Tests getArgument() with a '$route' parameter with no typehint and a value. + * + * Without the typehint, passing a value to a parameter named '$route' will + * still receive the provided value. + */ + public function testGetArgumentRouteNoTypehintAndValue() { + $callable = function($route) {}; + $request = new Request(); + $request->attributes->set('route', 'foo'); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array('foo'), $arguments); + } + + /** + * Tests getArgument() when upcasting is bypassed. + */ + public function testGetArgumentBypassUpcasting() { + $callable = function(Route $route = NULL) {}; + + $request = new Request(); + $request->attributes->set('route', NULL); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertSame(array(NULL), $arguments); + } + + /** + * Tests handleUnresolvedArgument() for a non-upcast argument. + * + * @expectedException \RuntimeException + * @expectedExceptionMessage requires a value for the "$foo" argument. + */ + public function testHandleNotUpcastedArgument() { + $callable = function(\stdClass $foo) {}; + + $request = new Request(); + $request->attributes->set('foo', 'bar'); + $request->attributes->set('_raw_variables', new ParameterBag(array('foo' => 'baz'))); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertNull($arguments); + } + + /** + * Tests handleUnresolvedArgument() for missing arguments. + * + * @expectedException \RuntimeException + * @expectedExceptionMessage requires a value for the "$foo" argument. + * + * @dataProvider providerTestHandleUnresolvedArgument + */ + public function testHandleUnresolvedArgument($callable) { + $request = new Request(); + + $arguments = (new AccessArgumentsResolver())->getArguments($callable, $this->route, $request, $this->account); + $this->assertNull($arguments); + } + + /** + * Provides test data to testHandleUnresolvedArgument(). + */ + public function providerTestHandleUnresolvedArgument() { + $data = array(); + $data[] = array(function($foo) {}); + $data[] = array(array(new TestClass(), 'access')); + $data[] = array('test_access_arguments_resolver_access'); + return $data; + } + +} + +/** + * Provides a test class. + */ +class TestClass { + public function access($foo) { + } +} + +} + +namespace { + function test_access_arguments_resolver_access($foo) { + } +} diff --git a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php index 0e830e2..8221ea1 100644 --- a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php @@ -76,6 +76,13 @@ class AccessManagerTest extends UnitTestCase { */ protected $account; + /** + * The access arguments resolver. + * + * @var \Drupal\Core\Access\AccessArgumentsResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $argumentsResolver; + public static function getInfo() { return array( 'name' => 'Access manager tests', @@ -122,8 +129,9 @@ class AccessManagerTest extends UnitTestCase { $this->paramConverter = $this->getMock('Drupal\Core\ParamConverter\ParamConverterManagerInterface'); $this->account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $this->argumentsResolver = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverInterface'); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $this->accessManager->setContainer($this->container); } @@ -152,13 +160,13 @@ class AccessManagerTest extends UnitTestCase { */ public function testSetChecksWithDynamicAccessChecker() { // Setup the access manager. - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $this->accessManager->setContainer($this->container); // Setup the dynamic access checker. - $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); + $access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface'); $this->container->set('test_access', $access_check); - $this->accessManager->addCheckService('test_access'); + $this->accessManager->addCheckService('test_access', 'access'); $route = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '1')); $route2 = new Route('/test-path', array(), array('_foo' => '1', '_bar' => '2')); @@ -198,6 +206,11 @@ class AccessManagerTest extends UnitTestCase { } $this->accessManager->setChecks($this->routeCollection); + $this->argumentsResolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnCallback(function ($callable, $route, $request, $account) { + return array($route); + })); $this->assertFalse($this->accessManager->check($this->routeCollection->get('test_route_1'), $request, $this->account)); $this->assertTrue($this->accessManager->check($this->routeCollection->get('test_route_2'), $request, $this->account)); @@ -353,7 +366,7 @@ class AccessManagerTest extends UnitTestCase { $this->setupAccessChecker(); $access_check = new DefinedTestAccessCheck(); $this->container->register('test_access_defined', $access_check); - $this->accessManager->addCheckService('test_access_defined', array('_test_access')); + $this->accessManager->addCheckService('test_access_defined', 'access', array('_test_access')); $request = new Request(); @@ -366,6 +379,11 @@ class AccessManagerTest extends UnitTestCase { $options = $conjunction ? array('_access_mode' => $conjunction) : array(); $route = new Route($name, array(), $requirements, $options); $route_collection->add($name, $route); + $this->argumentsResolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnCallback(function ($callable, $route, $request, $account) { + return array($route, $request, $account); + })); $this->accessManager->setChecks($route_collection); $this->assertSame($this->accessManager->check($route, $request, $this->account), $expected_access); @@ -379,6 +397,11 @@ class AccessManagerTest extends UnitTestCase { public function testCheckNamedRoute() { $this->setupAccessChecker(); $this->accessManager->setChecks($this->routeCollection); + $this->argumentsResolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnCallback(function ($callable, $route, $request, $account) { + return array($route, $request, $account); + })); // Tests the access with routes without parameters. $request = new Request(); @@ -444,23 +467,22 @@ class AccessManagerTest extends UnitTestCase { ->with('/test-route-1/example') ->will($this->returnValue($subrequest)); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $this->accessManager->setContainer($this->container); $this->accessManager->setRequest(new Request()); - $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); + $access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface'); $access_check->expects($this->any()) ->method('applies') ->will($this->returnValue(TRUE)); $access_check->expects($this->any()) ->method('access') - ->with($route, $subrequest) ->will($this->returnValue(AccessInterface::KILL)); $subrequest->attributes->set('value', 'upcasted_value'); $this->container->register('test_access', $access_check); - $this->accessManager->addCheckService('test_access'); + $this->accessManager->addCheckService('test_access', 'access'); $this->accessManager->setChecks($this->routeCollection); $this->assertFalse($this->accessManager->checkNamedRoute('test_route_1', array('value' => 'example'), $this->account)); @@ -504,23 +526,22 @@ class AccessManagerTest extends UnitTestCase { ->with('/test-route-1/example') ->will($this->returnValue($subrequest)); - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $this->accessManager->setContainer($this->container); $this->accessManager->setRequest(new Request()); - $access_check = $this->getMock('Drupal\Core\Access\AccessCheckInterface'); + $access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface'); $access_check->expects($this->any()) ->method('applies') ->will($this->returnValue(TRUE)); $access_check->expects($this->any()) ->method('access') - ->with($route, $subrequest) ->will($this->returnValue(AccessInterface::KILL)); $subrequest->attributes->set('value', 'upcasted_value'); $this->container->register('test_access', $access_check); - $this->accessManager->addCheckService('test_access'); + $this->accessManager->addCheckService('test_access', 'access'); $this->accessManager->setChecks($this->routeCollection); $this->assertFalse($this->accessManager->checkNamedRoute('test_route_1', array(), $this->account)); @@ -566,21 +587,26 @@ class AccessManagerTest extends UnitTestCase { $route_provider->expects($this->any()) ->method('getRouteByName') ->will($this->returnValue($route)); + $this->argumentsResolver->expects($this->any()) + ->method('getArguments') + ->will($this->returnCallback(function ($callable, $route, $request, $account) { + return array($route); + })); $request = new Request(); $container = new ContainerBuilder(); // Register a service that will return an incorrect value. - $access_check = $this->getMock('Drupal\Core\Routing\Access\AccessInterface'); + $access_check = $this->getMock('Drupal\Tests\Core\Access\TestAccessCheckInterface'); $access_check->expects($this->any()) ->method('access') ->will($this->returnValue($return_value)); $container->set('test_incorrect_value', $access_check); - $access_manager = new AccessManager($route_provider, $this->urlGenerator, $this->paramConverter); + $access_manager = new AccessManager($route_provider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $access_manager->setContainer($container); - $access_manager->addCheckService('test_incorrect_value'); + $access_manager->addCheckService('test_incorrect_value', 'access'); $access_manager->checkNamedRoute('test_incorrect_value', array(), $this->account, $request); } @@ -633,11 +659,18 @@ class AccessManagerTest extends UnitTestCase { * Adds a default access check service to the container and the access manager. */ protected function setupAccessChecker() { - $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->account); + $this->accessManager = new AccessManager($this->routeProvider, $this->urlGenerator, $this->paramConverter, $this->argumentsResolver); $this->accessManager->setContainer($this->container); $access_check = new DefaultAccessCheck(); $this->container->register('test_access_default', $access_check); - $this->accessManager->addCheckService('test_access_default', array('_access')); + $this->accessManager->addCheckService('test_access_default', 'access', array('_access')); } } + +/** + * Defines an interface with a defined access() method for mocking. + */ +interface TestAccessCheckInterface extends AccessCheckInterface { + public function access(); +} diff --git a/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php b/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php index 81ac83d..5fe2570 100644 --- a/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Access/CsrfTokenGeneratorTest.php @@ -5,8 +5,9 @@ * Contains \Drupal\Tests\Core\Access\CsrfTokenGeneratorTest. */ -namespace Drupal\Tests\Core\Access { +namespace Drupal\Tests\Core\Access; +use Drupal\Core\Site\Settings; use Drupal\Tests\UnitTestCase; use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Component\Utility\Crypt; @@ -24,6 +25,13 @@ class CsrfTokenGeneratorTest extends UnitTestCase { */ protected $generator; + /** + * The mock private key instance. + * + * @var \Drupal\Core\PrivateKey|\PHPUnit_Framework_MockObject_MockObject + */ + protected $privateKey; + public static function getInfo() { return array( 'name' => 'CsrfTokenGenerator test', @@ -39,16 +47,22 @@ function setUp() { parent::setUp(); $this->key = Crypt::randomBytesBase64(55); - $private_key = $this->getMockBuilder('Drupal\Core\PrivateKey') + $this->privateKey = $this->getMockBuilder('Drupal\Core\PrivateKey') ->disableOriginalConstructor() ->setMethods(array('get')) ->getMock(); - $private_key->expects($this->any()) + $this->privateKey->expects($this->any()) ->method('get') ->will($this->returnValue($this->key)); - $this->generator = new CsrfTokenGenerator($private_key); + $settings = array( + 'hash_salt' => $this->randomName(), + ); + + new Settings($settings); + + $this->generator = new CsrfTokenGenerator($this->privateKey); } /** @@ -141,17 +155,16 @@ function setUp() { ); } -} - -} - -/** - * @todo Remove this when https://drupal.org/node/2036259 is resolved. - */ -namespace { - if (!function_exists('drupal_get_hash_salt')) { - function drupal_get_hash_salt() { - return hash('sha256', 'test_hash_salt'); - } + /** + * Tests the exception thrown when no 'hash_salt' is provided in settings. + * + * @expectedException \RuntimeException + */ + public function testGetWithNoHashSalt() { + // Update settings with no hash salt. + new Settings(array()); + $generator = new CsrfTokenGenerator($this->privateKey); + $generator->get(); } + } diff --git a/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php index 19a8bff..dca5620 100644 --- a/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php +++ b/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php @@ -34,6 +34,13 @@ class CustomAccessCheckTest extends UnitTestCase { */ protected $controllerResolver; + /** + * The mocked arguments resolver. + * + * @var \Drupal\Core\Access\AccessArgumentsResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $argumentsResolver; + public static function getInfo() { return array( 'name' => 'Custom access check', @@ -49,7 +56,8 @@ class CustomAccessCheckTest extends UnitTestCase { parent::setUp(); $this->controllerResolver = $this->getMock('Drupal\Core\Controller\ControllerResolverInterface'); - $this->accessChecker = new CustomAccessCheck($this->controllerResolver); + $this->argumentsResolver = $this->getMock('Drupal\Core\Access\AccessArgumentsResolverInterface'); + $this->accessChecker = new CustomAccessCheck($this->controllerResolver, $this->argumentsResolver); } /** @@ -63,25 +71,25 @@ class CustomAccessCheckTest extends UnitTestCase { ->with('\Drupal\Tests\Core\Access\TestController::accessDeny') ->will($this->returnValue(array(new TestController(), 'accessDeny'))); - $this->controllerResolver->expects($this->at(1)) + $this->argumentsResolver->expects($this->at(0)) ->method('getArguments') ->will($this->returnValue(array())); - $this->controllerResolver->expects($this->at(2)) + $this->controllerResolver->expects($this->at(1)) ->method('getControllerFromDefinition') ->with('\Drupal\Tests\Core\Access\TestController::accessAllow') ->will($this->returnValue(array(new TestController(), 'accessAllow'))); - $this->controllerResolver->expects($this->at(3)) + $this->argumentsResolver->expects($this->at(1)) ->method('getArguments') ->will($this->returnValue(array())); - $this->controllerResolver->expects($this->at(4)) + $this->controllerResolver->expects($this->at(2)) ->method('getControllerFromDefinition') ->with('\Drupal\Tests\Core\Access\TestController::accessParameter') ->will($this->returnValue(array(new TestController(), 'accessParameter'))); - $this->controllerResolver->expects($this->at(5)) + $this->argumentsResolver->expects($this->at(2)) ->method('getArguments') ->will($this->returnValue(array('parameter' => 'TRUE'))); diff --git a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php index 75c0a97..1cf617b 100644 --- a/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/BackendChainImplementationUnitTest.php @@ -204,20 +204,6 @@ class BackendChainImplementationUnitTest extends UnitTestCase { } /** - * Test that the chain is not empty when at least one backend has data. - */ - public function testNotEmptyIfOneBackendHasTheKey() { - $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty'); - - // This is the only test that needs to start with an empty chain. - $this->chain->deleteAll(); - $this->assertTrue($this->chain->isEmpty(), 'Chain have been emptied by the deleteAll() call'); - - $this->secondBackend->set('test', 5); - $this->assertFalse($this->chain->isEmpty(), 'Chain is not empty anymore now that the second backend has something'); - } - - /** * Test that the delete all operation is propagated to all backends in the chain. */ public function testDeleteAllPropagation() { @@ -226,9 +212,12 @@ class BackendChainImplementationUnitTest extends UnitTestCase { $this->chain->set('test2', 3, time() + 1000); $this->chain->deleteAll(); - $this->assertTrue($this->firstBackend->isEmpty(), 'First backend is empty after delete all.'); - $this->assertTrue($this->secondBackend->isEmpty(), 'Second backend is empty after delete all.'); - $this->assertTrue($this->thirdBackend->isEmpty(), 'Third backend is empty after delete all.'); + $this->assertFalse($this->firstBackend->get('test1'), 'First key has been deleted in first backend.'); + $this->assertFalse($this->firstBackend->get('test2'), 'Second key has been deleted in first backend.'); + $this->assertFalse($this->secondBackend->get('test1'), 'First key has been deleted in second backend.'); + $this->assertFalse($this->secondBackend->get('test2'), 'Second key has been deleted in second backend.'); + $this->assertFalse($this->thirdBackend->get('test1'), 'First key has been deleted in third backend.'); + $this->assertFalse($this->thirdBackend->get('test2'), 'Second key has been deleted in third backend.'); } /** diff --git a/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php b/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php index fc3a700..bb7e6ac 100644 --- a/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/NullBackendTest.php @@ -35,7 +35,6 @@ function testNullBackend() { $value = $this->randomName(); $null_cache->set($key, $value); - $this->assertTrue($null_cache->isEmpty()); $this->assertFalse($null_cache->get($key)); } } diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php index 6c60af4..a66d55b 100644 --- a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php @@ -6,7 +6,6 @@ use Drupal\Core\Config\CachedStorage; use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\Cache\NullBackend; -use Drupal\Core\Cache\CacheBackendInterface; /** * Tests the interaction of cache and file storage in CachedStorage. @@ -15,6 +14,11 @@ */ class CachedStorageTest extends UnitTestCase { + /** + * @var \Drupal\Core\Cache\CacheFactoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $cacheFactory; + public static function getInfo() { return array( 'name' => 'Config cached storage test', @@ -23,6 +27,10 @@ class CachedStorageTest extends UnitTestCase { ); } + public function setUp() { + $this->cacheFactory = $this->getMock('Drupal\Core\Cache\CacheFactoryInterface'); + } + /** * Test listAll static cache. */ @@ -37,7 +45,11 @@ class CachedStorageTest extends UnitTestCase { ->will($this->returnValue($response)); $cache = new NullBackend(__FUNCTION__); - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $this->assertEquals($response, $cachedStorage->listAll($prefix)); $this->assertEquals($response, $cachedStorage->listAll($prefix)); } @@ -53,7 +65,11 @@ class CachedStorageTest extends UnitTestCase { $response = array("$prefix." . $this->randomName(), "$prefix." . $this->randomName()); $cache = new MemoryBackend(__FUNCTION__); $cache->set('find:' . $prefix, $response); - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $this->assertEquals($response, $cachedStorage->listAll($prefix)); } @@ -79,7 +95,11 @@ class CachedStorageTest extends UnitTestCase { foreach ($configCacheValues as $key => $value) { $cache->set($key, $value); } - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $this->assertEquals($configCacheValues, $cachedStorage->readMultiple($configNames)); } @@ -119,7 +139,11 @@ class CachedStorageTest extends UnitTestCase { ->with(array(2 => $configNames[2], 4 => $configNames[4])) ->will($this->returnValue($response)); - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $expected_data = $configCacheValues + array($configNames[2] => $config_exists_not_cached_data); $this->assertEquals($expected_data, $cachedStorage->readMultiple($configNames)); @@ -143,7 +167,11 @@ class CachedStorageTest extends UnitTestCase { ->method('read') ->with($name) ->will($this->returnValue(FALSE)); - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $this->assertFalse($cachedStorage->read($name)); @@ -163,7 +191,11 @@ class CachedStorageTest extends UnitTestCase { $storage = $this->getMock('Drupal\Core\Config\StorageInterface'); $storage->expects($this->never()) ->method('read'); - $cachedStorage = new CachedStorage($storage, $cache); + $this->cacheFactory->expects($this->once()) + ->method('get') + ->with('config') + ->will($this->returnValue($cache)); + $cachedStorage = new CachedStorage($storage, $this->cacheFactory); $this->assertFalse($cachedStorage->read($name)); } diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php index 489fa28..b802c16 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigDependencyManagerTest.php @@ -1,6 +1,11 @@ sourceStorage = $this->getMock('Drupal\Core\Config\StorageInterface'); $this->targetStorage = $this->getMock('Drupal\Core\Config\StorageInterface'); - $this->storageComparer = new StorageComparer($this->sourceStorage, $this->targetStorage); + $this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface'); + $this->storageComparer = new StorageComparer($this->sourceStorage, $this->targetStorage, $this->configManager); } protected function getConfigData() { @@ -123,6 +129,15 @@ class StorageComparerTest extends UnitTestCase { $this->targetStorage->expects($this->once()) ->method('readMultiple') ->will($this->returnValue($config_data)); + $this->sourceStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->targetStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->configManager->expects($this->any()) + ->method('supportsConfigurationEntities') + ->will($this->returnValue(TRUE)); $this->storageComparer->createChangelist(); $this->assertEmpty($this->storageComparer->getChangelist('create')); @@ -151,6 +166,15 @@ class StorageComparerTest extends UnitTestCase { $this->targetStorage->expects($this->once()) ->method('readMultiple') ->will($this->returnValue($target_data)); + $this->sourceStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->targetStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->configManager->expects($this->any()) + ->method('supportsConfigurationEntities') + ->will($this->returnValue(TRUE)); $this->storageComparer->createChangelist(); $expected = array( @@ -184,6 +208,15 @@ class StorageComparerTest extends UnitTestCase { $this->targetStorage->expects($this->once()) ->method('readMultiple') ->will($this->returnValue($target_data)); + $this->sourceStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->targetStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->configManager->expects($this->any()) + ->method('supportsConfigurationEntities') + ->will($this->returnValue(TRUE)); $this->storageComparer->createChangelist(); $expected = array( @@ -217,6 +250,15 @@ class StorageComparerTest extends UnitTestCase { $this->targetStorage->expects($this->once()) ->method('readMultiple') ->will($this->returnValue($target_data)); + $this->sourceStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->targetStorage->expects($this->once()) + ->method('getAllCollectionNames') + ->will($this->returnValue(array())); + $this->configManager->expects($this->any()) + ->method('supportsConfigurationEntities') + ->will($this->returnValue(TRUE)); $this->storageComparer->createChangelist(); $expected = array( diff --git a/core/tests/Drupal/Tests/Core/Controller/ExceptionControllerTest.php b/core/tests/Drupal/Tests/Core/Controller/ExceptionControllerTest.php index 0c5f4e7..50d0172 100644 --- a/core/tests/Drupal/Tests/Core/Controller/ExceptionControllerTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/ExceptionControllerTest.php @@ -35,17 +35,18 @@ class ExceptionControllerTest extends UnitTestCase { public function test405HTML() { $exception = new \Exception('Test exception'); $flat_exception = FlattenException::create($exception, 405); - $translation_manager = $this->getStringTranslationStub(); $html_page_renderer = $this->getMock('Drupal\Core\Page\HtmlPageRendererInterface'); $html_fragment_renderer = $this->getMock('Drupal\Core\Page\HtmlFragmentRendererInterface'); $title_resolver = $this->getMock('Drupal\Core\Controller\TitleResolverInterface'); + $translation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); + $url_generator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); $content_negotiation = $this->getMock('Drupal\Core\ContentNegotiation'); $content_negotiation->expects($this->any()) ->method('getContentType') ->will($this->returnValue('html')); - $exception_controller = new ExceptionController($content_negotiation, $translation_manager, $title_resolver, $html_page_renderer, $html_fragment_renderer); + $exception_controller = new ExceptionController($content_negotiation, $title_resolver, $html_page_renderer, $html_fragment_renderer, $translation, $url_generator); $response = $exception_controller->execute($flat_exception, new Request()); $this->assertEquals($response->getStatusCode(), 405, 'HTTP status of response is correct.'); $this->assertEquals($response->getContent(), 'Method Not Allowed', 'HTTP response body is correct.'); diff --git a/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php new file mode 100644 index 0000000..20667ee --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/ConnectionTest.php @@ -0,0 +1,326 @@ + 'Connection Test', + 'description' => 'Test the Connection class.', + 'group' => 'Database', + ); + } + + /** + * Dataprovider for testPrefixRoundTrip(). + * + * @return array + * Array of arrays with the following elements: + * - Arguments to pass to Connection::setPrefix(). + * - Expected result from Connection::tablePrefix(). + */ + public function providerPrefixRoundTrip() { + return array( + array( + array('' => 'test_'), + 'test_', + ), + array( + array( + 'fooTable' => 'foo_', + 'barTable' => 'bar_', + ), + array( + 'fooTable' => 'foo_', + 'barTable' => 'bar_', + ), + ), + ); + } + + /** + * Exercise setPrefix() and tablePrefix(). + * + * @dataProvider providerPrefixRoundTrip + */ + public function testPrefixRoundTrip($expected, $prefix_info) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array()); + + // setPrefix() is protected, so we make it accessible with reflection. + $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\StubConnection'); + $set_prefix = $reflection->getMethod('setPrefix'); + $set_prefix->setAccessible(TRUE); + + // Set the prefix data. + $set_prefix->invokeArgs($connection, array($prefix_info)); + // Check the round-trip. + foreach ($expected as $table => $prefix) { + $this->assertEquals($prefix, $connection->tablePrefix($table)); + } + } + + /** + * Dataprovider for testPrefixTables(). + * + * @return array + * Array of arrays with the following elements: + * - Expected result. + * - Table prefix. + * - Query to be prefixed. + */ + public function providerTestPrefixTables() { + return array( + array( + 'SELECT * FROM test_table', + 'test_', + 'SELECT * FROM {table}', + ), + array( + 'SELECT * FROM first_table JOIN second_thingie', + array( + 'table' => 'first_', + 'thingie' => 'second_', + ), + 'SELECT * FROM {table} JOIN {thingie}', + ), + ); + } + + /** + * Exercise the prefixTables() method. + * + * @dataProvider providerTestPrefixTables + */ + public function testPrefixTables($expected, $prefix_info, $query) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array('prefix' => $prefix_info)); + $this->assertEquals($expected, $connection->prefixTables($query)); + } + + /** + * Dataprovider for testEscapeMethods(). + * + * @return array + * Array of arrays with the following elements: + * - Expected escaped string. + * - String to escape. + */ + public function providerEscapeMethods() { + return array( + array('thing', 'thing'), + array('_item', '_item'), + array('item_', 'item_'), + array('_item_', '_item_'), + array('', '!@#$%^&*()-=+'), + array('123', '!1@2#3'), + ); + } + + /** + * Test the various escaping methods. + * + * All tested together since they're basically the same method + * with different names. + * + * @dataProvider providerEscapeMethods + * @todo Separate test method for each escape method? + */ + public function testEscapeMethods($expected, $name) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array()); + $this->assertEquals($expected, $connection->escapeDatabase($name)); + $this->assertEquals($expected, $connection->escapeTable($name)); + $this->assertEquals($expected, $connection->escapeField($name)); + $this->assertEquals($expected, $connection->escapeAlias($name)); + } + + /** + * Dataprovider for testGetDriverClass(). + * + * @return array + * Array of arrays with the following elements: + * - Expected namespaced class name. + * - Driver. + * - Namespace. + * - Class name without namespace. + */ + public function providerGetDriverClass() { + return array( + array( + 'nonexistent_class', + 'stub', + '\\', + 'nonexistent_class', + ), + array( + 'Drupal\\Core\\Database\\Driver\\mysql\\Select', + 'mysql', + NULL, + 'Select', + ), + array( + 'Drupal\\Tests\\Core\\Database\\Stub\\Driver\\Schema', + 'stub', + 'Drupal\\Tests\\Core\\Database\\Stub\\Driver', + 'Schema', + ), + ); + } + + /** + * Test getDriverClass(). + * + * @dataProvider providerGetDriverClass + */ + public function testGetDriverClass($expected, $driver, $namespace, $class) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array('namespace' => $namespace)); + // Set the driver using our stub class' public property. + $connection->driver = $driver; + $this->assertEquals($expected, $connection->getDriverClass($class)); + } + + /** + * Dataprovider for testSchema(). + * + * @return array + * Array of arrays with the following elements: + * - Expected namespaced class of schema object. + * - Driver for PDO connection. + * - Namespace for connection. + */ + public function providerSchema() { + return array( + array( + 'Drupal\\Tests\\Core\\Database\\Stub\\Driver\\Schema', + 'stub', + 'Drupal\\Tests\\Core\\Database\\Stub\\Driver', + ), + ); + } + + /** + * Test Connection::schema(). + * + * @dataProvider providerSchema + */ + public function testSchema($expected, $driver, $namespace) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array('namespace' => $namespace)); + $connection->driver = $driver; + $this->assertInstanceOf($expected, $connection->schema()); + } + + /** + * Test Connection::destroy(). + */ + public function testDestroy() { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + // Mocking StubConnection gives us access to the $schema attribute. + $connection = $this->getMock( + 'Drupal\Tests\Core\Database\Stub\StubConnection', + NULL, + array($mock_pdo, array('namespace' => 'Drupal\\Tests\\Core\\Database\\Stub\\Driver')) + ); + // Generate a schema object in order to verify that we've NULLed it later. + $this->assertInstanceOf( + 'Drupal\\Tests\\Core\\Database\\Stub\\Driver\\Schema', + $connection->schema() + ); + $connection->destroy(); + $this->assertAttributeEquals(NULL, 'schema', $connection); + } + + /** + * Dataprovider for testMakeComments(). + * + * @return array + * Array of arrays with the following elements: + * - Expected filtered comment. + * - Arguments for Connection::makeComment(). + */ + public function providerMakeComments() { + return array( + array( + '/* */ ', + array(''), + ), + array( + '/* Exploit * / DROP TABLE node; -- */ ', + array('Exploit * / DROP TABLE node; --'), + ), + array( + '/* Exploit DROP TABLE node; --; another comment */ ', + array('Exploit */ DROP TABLE node; --', 'another comment'), + ), + ); + } + + /** + * Test Connection::makeComments(). + * + * @dataProvider providerMakeComments + */ + public function testMakeComments($expected, $comment_array) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array()); + $this->assertEquals($expected, $connection->makeComment($comment_array)); + } + + /** + * Dataprovider for testFilterComments(). + * + * @return array + * Array of arrays with the following elements: + * - Expected filtered comment. + * - Comment to filter. + */ + public function providerFilterComments() { + return array( + array('', ''), + array('Exploit * / DROP TABLE node; --', 'Exploit * / DROP TABLE node; --'), + array('Exploit DROP TABLE node; --', 'Exploit */ DROP TABLE node; --'), + ); + } + + /** + * Test Connection::filterComments(). + * + * @dataProvider providerFilterComments + */ + public function testFilterComments($expected, $comment) { + $mock_pdo = $this->getMock('Drupal\Tests\Core\Database\Stub\StubPDO'); + $connection = new StubConnection($mock_pdo, array()); + + // filterComment() is protected, so we make it accessible with reflection. + $reflection = new \ReflectionClass('Drupal\Tests\Core\Database\Stub\StubConnection'); + $filter_comment = $reflection->getMethod('filterComment'); + $filter_comment->setAccessible(TRUE); + + $this->assertEquals( + $expected, + $filter_comment->invokeArgs($connection, array($comment)) + ); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/Driver/Schema.php b/core/tests/Drupal/Tests/Core/Database/Stub/Driver/Schema.php new file mode 100644 index 0000000..eb7f773 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/Stub/Driver/Schema.php @@ -0,0 +1,18 @@ +driver; + } + + /** + * {@inheritdoc} + */ + public function databaseType() { + return 'stub'; + } + + /** + * {@inheritdoc} + */ + public function createDatabase($database) { + return; + } + + /** + * {@inheritdoc} + */ + public function mapConditionOperator($operator) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function nextId($existing_id = 0) { + return 0; + } + +} diff --git a/core/tests/Drupal/Tests/Core/Database/Stub/StubPDO.php b/core/tests/Drupal/Tests/Core/Database/Stub/StubPDO.php new file mode 100644 index 0000000..c0ea2be --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/Stub/StubPDO.php @@ -0,0 +1,36 @@ +method('getKeys') ->will($this->returnValue(array('id' => 'id'))); $entity_type->expects($this->atLeastOnce()) - ->method('hasKey') - ->will($this->returnCallback(function ($key) { - return $key == 'id'; - })); + ->method('isRevisionable') + ->will($this->returnValue(FALSE)); $entity_manager->expects($this->atLeastOnce()) ->method('getDefinition') ->with('test_entity_type') @@ -154,10 +152,7 @@ class ContentEntityDatabaseStorageTest extends UnitTestCase { $connection = $this->getMockBuilder('Drupal\Core\Database\Connection') ->disableOriginalConstructor() ->getMock(); - $field_info = $this->getMockBuilder('\Drupal\field\FieldInfo') - ->disableOriginalConstructor() - ->getMock(); - $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $field_info); + $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager); $entity = $entity_storage->create(); $entity->expects($this->atLeastOnce()) diff --git a/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php b/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php index 0bc6353..ac6c886 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Controller/EntityViewControllerTest.php @@ -49,10 +49,34 @@ class EntityViewControllerTest extends UnitTestCase{ ->method('getViewBuilder') ->will($this->returnValue($render_controller)); + // Mock the 'entity_test' entity type. + $entity_type = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityType') + ->disableOriginalConstructor() + ->getMock(); + $entity_type->expects($this->once()) + ->method('getKey') + ->with('label') + ->will($this->returnValue('name')); + + // Mock the 'name' field's definition. + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinition'); + $field_definition->expects($this->any()) + ->method('getDisplayOptions') + ->with('view') + ->will($this->returnValue(NULL)); + // Mock an 'entity_test' entity. $entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest') ->disableOriginalConstructor() ->getMock(); + $entity->expects($this->once()) + ->method('getEntityType') + ->will($this->returnValue($entity_type)); + $entity->expects($this->any()) + ->method('getFieldDefinition') + ->with('name') + ->will($this->returnValue($field_definition)); + // Initialize the controller to test. $controller = new EntityViewController($entity_manager); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php index f6d4129..121d745 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php @@ -132,7 +132,7 @@ class EntityListBuilderTest extends UnitTestCase { ->will($this->returnValue($url)); $list = new EntityListBuilder($this->entityType, $this->roleStorage, $this->moduleHandler); - $list->setTranslationManager($this->translationManager); + $list->setStringTranslation($this->translationManager); $operations = $list->getOperations($this->role); $this->assertInternalType('array', $operations); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index 080d79f..100ad29 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -348,7 +348,7 @@ class EntityManagerTest extends UnitTestCase { $apple_form = $this->entityManager->getFormObject('apple', 'default'); $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestEntityForm', $apple_form); $this->assertAttributeInstanceOf('Drupal\Core\Extension\ModuleHandlerInterface', 'moduleHandler', $apple_form); - $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'translationManager', $apple_form); + $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'stringTranslation', $apple_form); $banana_form = $this->entityManager->getFormObject('banana', 'default'); $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestEntityFormInjected', $banana_form); @@ -398,7 +398,7 @@ class EntityManagerTest extends UnitTestCase { $apple_controller = $this->entityManager->getController('apple', 'storage'); $this->assertInstanceOf($class, $apple_controller); $this->assertAttributeInstanceOf('Drupal\Core\Extension\ModuleHandlerInterface', 'moduleHandler', $apple_controller); - $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'translationManager', $apple_controller); + $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'stringTranslation', $apple_controller); $banana_controller = $this->entityManager->getController('banana', 'storage', 'getStorageClass'); $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestEntityControllerInjected', $banana_controller); @@ -456,6 +456,7 @@ class EntityManagerTest extends UnitTestCase { * Tests the getBaseFieldDefinitions() method. * * @covers ::getBaseFieldDefinitions() + * @covers ::buildBaseFieldDefinitions() */ public function testGetBaseFieldDefinitions() { $field_definition = $this->setUpEntityWithFieldDefinition(); @@ -468,6 +469,7 @@ class EntityManagerTest extends UnitTestCase { * Tests the getFieldDefinitions() method. * * @covers ::getFieldDefinitions() + * @covers ::buildBundleFieldDefinitions() */ public function testGetFieldDefinitions() { $field_definition = $this->setUpEntityWithFieldDefinition(); @@ -480,6 +482,7 @@ class EntityManagerTest extends UnitTestCase { * Tests the getFieldStorageDefinitions() method. * * @covers ::getFieldStorageDefinitions() + * @covers ::buildFieldStorageDefinitions() */ public function testGetFieldStorageDefinitions() { $field_definition = $this->setUpEntityWithFieldDefinition(TRUE); @@ -625,6 +628,7 @@ class EntityManagerTest extends UnitTestCase { * Tests the getBaseFieldDefinitions() method with an invalid definition. * * @covers ::getBaseFieldDefinitions() + * @covers ::buildBaseFieldDefinitions() * * @expectedException \LogicException */ @@ -641,6 +645,7 @@ class EntityManagerTest extends UnitTestCase { * Tests that getFieldDefinitions() method sets the 'provider' definition key. * * @covers ::getFieldDefinitions() + * @covers ::buildBundleFieldDefinitions() */ public function testGetFieldDefinitionsProvider() { $this->setUpEntityWithFieldDefinition(TRUE); @@ -958,6 +963,143 @@ function testgetExtraFields() { } /** + * @covers ::getFieldMap + */ + public function testGetFieldMap() { + // Set up a content entity type. + $entity_type = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface'); + $entity = $this->getMock('Drupal\Tests\Core\Entity\TestContentEntityInterface'); + $entity_class = get_class($entity); + $entity_type->expects($this->any()) + ->method('getClass') + ->will($this->returnValue($entity_class)); + $entity_type->expects($this->any()) + ->method('getKeys') + ->will($this->returnValue(array())); + $entity_type->expects($this->any()) + ->method('isSubclassOf') + ->with('\Drupal\Core\Entity\ContentEntityInterface') + ->will($this->returnValue(TRUE)); + + // Set up the module handler to return two bundles for the fieldable entity + // type. + $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $this->moduleHandler->expects($this->any()) + ->method('alter'); + $this->moduleHandler->expects($this->any()) + ->method('getImplementations') + ->will($this->returnValue(array())); + $module_implements_value_map = array( + array( + 'entity_bundle_info', array(), + array( + 'test_entity_type' => array( + 'first_bundle' => array(), + 'second_bundle' => array(), + ), + ), + ), + ); + $this->moduleHandler->expects($this->any()) + ->method('invokeAll') + ->will($this->returnValueMap($module_implements_value_map)); + + + // Define an ID field definition as a base field. + $id_definition = $this->getMockBuilder('Drupal\Core\Field\FieldDefinition') + ->disableOriginalConstructor() + ->getMock(); + $id_definition->expects($this->exactly(2)) + ->method('getType') + ->will($this->returnValue('integer')); + $base_field_definitions = array( + 'id' => $id_definition, + ); + $entity_class::staticExpects($this->once()) + ->method('baseFieldDefinitions') + ->will($this->returnValue($base_field_definitions)); + + // Set up a by bundle field definition that only exists on one bundle. + $bundle_definition = $this->getMockBuilder('Drupal\Core\Field\FieldDefinition') + ->disableOriginalConstructor() + ->getMock(); + $bundle_definition->expects($this->once()) + ->method('getType') + ->will($this->returnValue('string')); + $entity_class::staticExpects($this->any()) + ->method('bundleFieldDefinitions') + ->will($this->returnValueMap(array( + array( + $entity_type, + 'first_bundle', + $base_field_definitions, + array(), + ), + array( + $entity_type, + 'second_bundle', + $base_field_definitions, + array( + 'by_bundle' => $bundle_definition, + ), + ), + ))); + + // Set up a non-content entity type. + $non_content_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $entity_type->expects($this->any()) + ->method('isSubclassOf') + ->with('\Drupal\Core\Entity\ContentEntityInterface') + ->will($this->returnValue(FALSE)); + + $this->setUpEntityManager(array( + 'test_entity_type' => $entity_type, + 'non_fieldable' => $non_content_entity_type, + )); + + $expected = array( + 'test_entity_type' => array( + 'id' => array( + 'type' => 'integer', + 'bundles' => array('first_bundle', 'second_bundle'), + ), + 'by_bundle' => array( + 'type' => 'string', + 'bundles' => array('second_bundle'), + ), + ) + ); + $this->assertEquals($expected, $this->entityManager->getFieldMap()); + } + + /** + * @covers ::getFieldMap + */ + public function testGetFieldMapFromCache() { + $expected = array( + 'test_entity_type' => array( + 'id' => array( + 'type' => 'integer', + 'bundles' => array('first_bundle', 'second_bundle'), + ), + 'by_bundle' => array( + 'type' => 'string', + 'bundles' => array('second_bundle'), + ), + ) + ); + $this->setUpEntityManager(); + $this->cache->expects($this->once()) + ->method('get') + ->with('entity_field_map') + ->will($this->returnValue((object) array('data' => $expected))); + + // Call the field map twice to make sure the static cache works. + $this->assertEquals($expected, $this->entityManager->getFieldMap()); + $this->assertEquals($expected, $this->entityManager->getFieldMap()); + } + + /** * Gets a mock controller class name. * * @return string diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php index 1cb561c..ac29654 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php @@ -89,6 +89,18 @@ class EntityTypeTest extends UnitTestCase { } /** + * Tests the isRevisionable() method. + */ + public function testIsRevisionable() { + $entity_type = $this->setUpEntityType(array('entity_keys' => array('id' => 'id'))); + $this->assertFalse($entity_type->isRevisionable()); + $entity_type = $this->setUpEntityType(array('entity_keys' => array('id' => 'id', 'revision' => FALSE))); + $this->assertFalse($entity_type->isRevisionable()); + $entity_type = $this->setUpEntityType(array('entity_keys' => array('id' => 'id', 'revision' => TRUE))); + $this->assertTrue($entity_type->isRevisionable()); + } + + /** * Tests the getController() method. */ public function testGetController() { diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php index c25be20..2ff0bf7 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUrlTest.php @@ -251,9 +251,9 @@ class EntityUrlTest extends UnitTestCase { } /** - * Tests the getSystemPath() method. + * Tests the getPathByAlias() method. * - * @covers ::getSystemPath() + * @covers ::getPathByAlias() */ public function testGetSystemPath() { $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php index 37f1a40..c44e302 100644 --- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php +++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php @@ -64,9 +64,7 @@ class ExternalUrlTest extends UnitTestCase { $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); $this->urlGenerator->expects($this->any()) ->method('generateFromPath') - ->will($this->returnCallback(function ($path) { - return $path; - })); + ->will($this->returnArgument(0)); $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface'); $container = new ContainerBuilder(); @@ -143,7 +141,7 @@ class ExternalUrlTest extends UnitTestCase { public function testToArray(Url $url) { $expected = array( 'path' => $this->path, - 'options' => array(), + 'options' => array('external' => TRUE), ); $this->assertSame($expected, $url->toArray()); } diff --git a/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php b/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php new file mode 100644 index 0000000..49a3922 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php @@ -0,0 +1,86 @@ +getNamespacePath(); + // Suppport both PSR-0 and PSR-4 directory layouts. + $module_name = basename($namespace_path); + if ($module_name == 'src') { + $module_name = basename($module_name); + } + $namespaces = new \ArrayObject(array( + 'Drupal\\' . $module_name => $namespace_path, + )); + + $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); + $language_manager->expects($this->once()) + ->method('getCurrentLanguage') + ->will($this->returnValue(new Language(array('id' => 'en')))); + $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); + $module_handler->expects($this->once()) + ->method('moduleExists') + ->with($module_name) + ->will($this->returnValue(TRUE)); + $plugin_manager = new FieldTypePluginManager( + $namespaces, + $this->getMock('Drupal\Core\Cache\CacheBackendInterface'), + $language_manager, + $module_handler + ); + + $container = new ContainerBuilder(); + $container->set('plugin.manager.field.field_type', $plugin_manager); + // The 'string_translation' service is used by the @Translation annotation. + $container->set('string_translation', $this->getStringTranslationStub()); + \Drupal::setContainer($container); + + $this->definition = FieldDefinition::create($this->getPluginId()); + } + + /** + * Returns the plugin ID of the tested field type. + * + * @return string + * The plugin ID. + */ + abstract protected function getPluginId(); + + /** + * Returns the path to the module's classes. + * + * Depending on whether the module follows the PSR-0 or PSR-4 directory layout + * this should be either /path/to/module/lib/Drupal/mymodule or + * /path/to/module/src. + * + * @return string + * The path to the module's classes. + */ + abstract protected function getNamespacePath(); + +} diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 3bed3dc..8b09681 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -7,12 +7,9 @@ namespace Drupal\Tests\Core\Form { -use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Form\FormInterface; -use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\RedirectResponse; /** * Tests the form builder. @@ -36,17 +33,6 @@ class FormBuilderTest extends FormTestBase { } /** - * {@inheritdoc} - */ - public function setUp() { - parent::setUp(); - - $container = new ContainerBuilder(); - $container->set('url_generator', $this->urlGenerator); - \Drupal::setContainer($container); - } - - /** * Tests the getFormId() method with a string based form ID. */ public function testGetFormIdWithString() { @@ -219,168 +205,6 @@ class FormBuilderTest extends FormTestBase { } $this->assertSame($response, $form_state['response']); } - - /** - * Tests that form errors during submission throw an exception. - * - * @covers ::setErrorByName - * - * @expectedException \LogicException - * @expectedExceptionMessage Form errors cannot be set after form validation has finished. - */ - public function testFormErrorsDuringSubmission() { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - - $form_arg = $this->getMockForm($form_id, $expected_form); - $form_builder = $this->formBuilder; - $form_arg->expects($this->any()) - ->method('submitForm') - ->will($this->returnCallback(function ($form, &$form_state) use ($form_builder) { - $form_builder->setErrorByName('test', $form_state, 'Hello'); - })); - - $form_state = array(); - $this->formBuilder->getFormId($form_arg, $form_state); - - $form_state['values'] = array(); - $form_state['input']['form_id'] = $form_id; - $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE); - } - - /** - * Tests the redirectForm() method when a redirect is expected. - * - * @param array $form_state - * An array of form state data to use for the redirect. - * @param string $result - * The URL the redirect is targeting. - * @param int $status - * (optional) The HTTP status code for the redirect. - * - * @dataProvider providerTestRedirectWithResult - */ - public function testRedirectWithResult($form_state, $result, $status = 302) { - $this->urlGenerator->expects($this->once()) - ->method('generateFromPath') - ->will($this->returnValueMap(array( - array(NULL, array('query' => array(), 'absolute' => TRUE), ''), - array('foo', array('absolute' => TRUE), 'foo'), - array('bar', array('query' => array('foo' => 'baz'), 'absolute' => TRUE), 'bar'), - array('baz', array('absolute' => TRUE), 'baz'), - )) - ); - - $form_state += $this->formBuilder->getFormStateDefaults(); - $redirect = $this->formBuilder->redirectForm($form_state); - $this->assertSame($result, $redirect->getTargetUrl()); - $this->assertSame($status, $redirect->getStatusCode()); - } - - /** - * Tests the redirectForm() with redirect_route when a redirect is expected. - * - * @param array $form_state - * An array of form state data to use for the redirect. - * @param string $result - * The URL the redirect is targeting. - * @param int $status - * (optional) The HTTP status code for the redirect. - * - * @dataProvider providerTestRedirectWithRouteWithResult - */ - public function testRedirectWithRouteWithResult($form_state, $result, $status = 302) { - $this->urlGenerator->expects($this->once()) - ->method('generateFromRoute') - ->will($this->returnValueMap(array( - array('test_route_a', array(), array('absolute' => TRUE), 'test-route'), - array('test_route_b', array('key' => 'value'), array('absolute' => TRUE), 'test-route/value'), - )) - ); - - $form_state += $this->formBuilder->getFormStateDefaults(); - $redirect = $this->formBuilder->redirectForm($form_state); - $this->assertSame($result, $redirect->getTargetUrl()); - $this->assertSame($status, $redirect->getStatusCode()); - } - - /** - * Tests the redirectForm() method with a response object. - */ - public function testRedirectWithResponseObject() { - $redirect = new RedirectResponse('/example'); - $form_state['redirect'] = $redirect; - - $form_state += $this->formBuilder->getFormStateDefaults(); - $result_redirect = $this->formBuilder->redirectForm($form_state); - - $this->assertSame($redirect, $result_redirect); - } - - /** - * Tests the redirectForm() method when no redirect is expected. - * - * @param array $form_state - * An array of form state data to use for the redirect. - * - * @dataProvider providerTestRedirectWithoutResult - */ - public function testRedirectWithoutResult($form_state) { - $this->urlGenerator->expects($this->never()) - ->method('generateFromPath'); - $this->urlGenerator->expects($this->never()) - ->method('generateFromRoute'); - $form_state += $this->formBuilder->getFormStateDefaults(); - $redirect = $this->formBuilder->redirectForm($form_state); - $this->assertNull($redirect); - } - - /** - * Provides test data for testing the redirectForm() method with a redirect. - * - * @return array - * Returns some test data. - */ - public function providerTestRedirectWithResult() { - return array( - array(array(), ''), - array(array('redirect' => 'foo'), 'foo'), - array(array('redirect' => array('foo')), 'foo'), - array(array('redirect' => array('foo')), 'foo'), - array(array('redirect' => array('bar', array('query' => array('foo' => 'baz')))), 'bar'), - array(array('redirect' => array('baz', array(), 301)), 'baz', 301), - ); - } - - /** - * Provides test data for testing the redirectForm() method with a route name. - * - * @return array - * Returns some test data. - */ - public function providerTestRedirectWithRouteWithResult() { - return array( - array(array('redirect_route' => array('route_name' => 'test_route_a')), 'test-route'), - array(array('redirect_route' => array('route_name' => 'test_route_b', 'route_parameters' => array('key' => 'value'))), 'test-route/value'), - array(array('redirect_route' => new Url('test_route_b', array('key' => 'value'))), 'test-route/value'), - ); - } - - /** - * Provides test data for testing the redirectForm() method with no redirect. - * - * @return array - * Returns some test data. - */ - public function providerTestRedirectWithoutResult() { - return array( - array(array('programmed' => TRUE)), - array(array('rebuild' => TRUE)), - array(array('no_redirect' => TRUE)), - array(array('redirect' => FALSE)), - ); - } - /** * Tests the getForm() method with a string based form ID. */ @@ -500,229 +324,6 @@ class FormBuilderTest extends FormTestBase { } /** - * Tests the submitForm() method. - */ - public function testSubmitForm() { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - $expected_form['test']['#required'] = TRUE; - $expected_form['options']['#required'] = TRUE; - $expected_form['value']['#required'] = TRUE; - - $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); - $form_arg->expects($this->exactly(5)) - ->method('getFormId') - ->will($this->returnValue($form_id)); - $form_arg->expects($this->exactly(5)) - ->method('buildForm') - ->will($this->returnValue($expected_form)); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['options']); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['options'] = 'foo'; - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertEmpty($errors); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['options'] = array('foo'); - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertEmpty($errors); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['options'] = array('foo', 'baz'); - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['options']); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['options'] = $this->randomName(); - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['options']); - } - - /** - * Tests the 'must_validate' $form_state flag. - * - * @covers ::validateForm - */ - public function testMustValidate() { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - - $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); - $form_arg->expects($this->any()) - ->method('getFormId') - ->will($this->returnValue($form_id)); - $form_arg->expects($this->any()) - ->method('buildForm') - ->will($this->returnValue($expected_form)); - $form_builder = $this->formBuilder; - $form_arg->expects($this->exactly(2)) - ->method('validateForm') - ->will($this->returnCallback(function (&$form, &$form_state) use ($form_builder) { - $form_builder->setErrorByName('test', $form_state, 'foo'); - })); - - $form_state = array(); - // This submission will trigger validation. - $this->simulateFormSubmission($form_id, $form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['test']); - - // This submission will not re-trigger validation. - $this->simulateFormSubmission($form_id, $form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['test']); - - // The must_validate flag will re-trigger validation. - $form_state['must_validate'] = TRUE; - $this->simulateFormSubmission($form_id, $form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertNotEmpty($errors['test']); - } - - /** - * Tests the flattenOptions() method. - * - * @dataProvider providerTestFlattenOptions - */ - public function testFlattenOptions($options) { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - $expected_form['select']['#required'] = TRUE; - $expected_form['select']['#options'] = $options; - - $form_arg = $this->getMockForm($form_id, $expected_form); - - $form_state = array(); - $form_state['values']['select'] = 'foo'; - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertEmpty($errors); - } - - /** - * Provides test data for the flattenOptions() method. - * - * @return array - */ - public function providerTestFlattenOptions() { - $object = new \stdClass(); - $object->option = array('foo' => 'foo'); - return array( - array(array('foo' => 'foo')), - array(array(array('foo' => 'foo'))), - array(array($object)), - ); - } - - /** - * Tests the setErrorByName() method. - * - * @param array|null $limit_validation_errors - * The errors to limit validation for, NULL will run all validation. - * @param array $expected_errors - * The errors expected to be set. - * - * @dataProvider providerTestSetErrorByName - */ - public function testSetErrorByName($limit_validation_errors, $expected_errors) { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - $expected_form['actions']['submit']['#submit'][] = 'test_form_id_custom_submit'; - $expected_form['actions']['submit']['#limit_validation_errors'] = $limit_validation_errors; - - $form_arg = $this->getMockForm($form_id, $expected_form); - $form_builder = $this->formBuilder; - $form_arg->expects($this->once()) - ->method('validateForm') - ->will($this->returnCallback(function (array &$form, array &$form_state) use ($form_builder) { - $form_builder->setErrorByName('test', $form_state, 'Fail 1'); - $form_builder->setErrorByName('test', $form_state, 'Fail 2'); - $form_builder->setErrorByName('options', $form_state); - })); - - $form_state = array(); - $form_state['values']['test'] = $this->randomName(); - $form_state['values']['options'] = 'foo'; - $form_state['values']['op'] = 'Submit'; - $this->formBuilder->submitForm($form_arg, $form_state); - - $errors = $this->formBuilder->getErrors($form_state); - $this->assertSame($expected_errors, $errors); - } - - /** - * Provides test data for testing the setErrorByName() method. - * - * @return array - * Returns some test data. - */ - public function providerTestSetErrorByName() { - return array( - // Only validate the 'options' element. - array(array(array('options')), array('options' => '')), - // Do not limit an validation, and, ensuring the first error is returned - // for the 'test' element. - array(NULL, array('test' => 'Fail 1', 'options' => '')), - // Limit all validation. - array(array(), array()), - ); - } - - /** - * Tests the getError() method. - * - * @dataProvider providerTestGetError - */ - public function testGetError($parents, $expected = NULL) { - $form_state = array(); - // Set errors on a top level and a child element, and a nested element. - $this->formBuilder->setErrorByName('foo', $form_state, 'Fail 1'); - $this->formBuilder->setErrorByName('foo][bar', $form_state, 'Fail 2'); - $this->formBuilder->setErrorByName('baz][bim', $form_state, 'Fail 3'); - - $element['#parents'] = $parents; - $error = $this->formBuilder->getError($element, $form_state); - $this->assertSame($expected, $error); - } - - /** - * Provides test data for testing the getError() method. - * - * @return array - * Returns some test data. - */ - public function providerTestGetError() { - return array( - array(array('foo'), 'Fail 1'), - array(array('foo', 'bar'), 'Fail 1'), - array(array('baz')), - array(array('baz', 'bim'), 'Fail 3'), - array(array($this->randomName())), - array(array()), - ); - } - - /** * Tests the getCache() method. */ public function testGetCache() { @@ -774,8 +375,7 @@ class FormBuilderTest extends FormTestBase { $form_state['input']['form_id'] = $form_id; $form_state['input']['form_build_id'] = $form['#build_id']; $this->formBuilder->buildForm($form_id, $form_state); - $errors = $this->formBuilder->getErrors($form_state); - $this->assertEmpty($errors); + $this->assertEmpty($form_state['errors']); } /** @@ -799,6 +399,29 @@ class FormBuilderTest extends FormTestBase { $this->formBuilder->buildForm($form_arg, $form_state); } + /** + * Tests that HTML IDs are unique when rebuilding a form with errors. + */ + public function testUniqueHtmlId() { + $form_id = 'test_form_id'; + $expected_form = $form_id(); + $expected_form['test']['#required'] = TRUE; + + // Mock a form object that will be built two times. + $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); + $form_arg->expects($this->exactly(2)) + ->method('buildForm') + ->will($this->returnValue($expected_form)); + + $form_state = array(); + $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state); + $this->assertSame($form_id, $form['#id']); + + $form_state = array(); + $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state); + $this->assertSame("$form_id--2", $form['#id']); + } + } class TestForm implements FormInterface { diff --git a/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php new file mode 100644 index 0000000..5dcc668 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Form/FormSubmitterTest.php @@ -0,0 +1,291 @@ + 'Form submission test', + 'description' => 'Tests the form submission handler.', + 'group' => 'Form API', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); + } + + /** + * @covers ::doSubmitForm + */ + public function testHandleFormSubmissionNotSubmitted() { + $form_submitter = $this->getFormSubmitter(); + $form = array(); + $form_state = $this->getFormStateDefaults(); + + $return = $form_submitter->doSubmitForm($form, $form_state); + $this->assertFalse($form_state['executed']); + $this->assertNull($return); + } + + /** + * @covers ::doSubmitForm + */ + public function testHandleFormSubmissionNoRedirect() { + $form_submitter = $this->getFormSubmitter(); + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_state['submitted'] = TRUE; + $form_state['no_redirect'] = TRUE; + + $return = $form_submitter->doSubmitForm($form, $form_state); + $this->assertTrue($form_state['executed']); + $this->assertNull($return); + } + + /** + * @covers ::doSubmitForm + * + * @dataProvider providerTestHandleFormSubmissionWithResponses + */ + public function testHandleFormSubmissionWithResponses($class, $form_state_key) { + $response = $this->getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + $response->expects($this->any()) + ->method('prepare') + ->will($this->returnValue($response)); + + $form_state = $this->getFormStateDefaults(); + $form_state['submitted'] = TRUE; + $form_state[$form_state_key] = $response; + + $form_submitter = $this->getFormSubmitter(); + $form = array(); + $return = $form_submitter->doSubmitForm($form, $form_state); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $return); + } + + public function providerTestHandleFormSubmissionWithResponses() { + return array( + array('Symfony\Component\HttpFoundation\Response', 'response'), + array('Symfony\Component\HttpFoundation\RedirectResponse', 'redirect'), + ); + } + + /** + * Tests the redirectForm() method when a redirect is expected. + * + * @covers ::redirectForm + * + * @dataProvider providerTestRedirectWithResult + */ + public function testRedirectWithResult($form_state, $result, $status = 302) { + $form_submitter = $this->getFormSubmitter(); + $this->urlGenerator->expects($this->once()) + ->method('generateFromPath') + ->will($this->returnValueMap(array( + array(NULL, array('query' => array(), 'absolute' => TRUE), ''), + array('foo', array('absolute' => TRUE), 'foo'), + array('bar', array('query' => array('foo' => 'baz'), 'absolute' => TRUE), 'bar'), + array('baz', array('absolute' => TRUE), 'baz'), + )) + ); + + $form_state += $this->getFormStateDefaults(); + $redirect = $form_submitter->redirectForm($form_state); + $this->assertSame($result, $redirect->getTargetUrl()); + $this->assertSame($status, $redirect->getStatusCode()); + } + + /** + * Tests the redirectForm() with redirect_route when a redirect is expected. + * + * @covers ::redirectForm + * + * @dataProvider providerTestRedirectWithRouteWithResult + */ + public function testRedirectWithRouteWithResult($form_state, $result, $status = 302) { + $container = new ContainerBuilder(); + $container->set('url_generator', $this->urlGenerator); + \Drupal::setContainer($container); + $form_submitter = $this->getFormSubmitter(); + $this->urlGenerator->expects($this->once()) + ->method('generateFromRoute') + ->will($this->returnValueMap(array( + array('test_route_a', array(), array('absolute' => TRUE), 'test-route'), + array('test_route_b', array('key' => 'value'), array('absolute' => TRUE), 'test-route/value'), + )) + ); + + $form_state += $this->getFormStateDefaults(); + $redirect = $form_submitter->redirectForm($form_state); + $this->assertSame($result, $redirect->getTargetUrl()); + $this->assertSame($status, $redirect->getStatusCode()); + } + + /** + * Tests the redirectForm() method with a response object. + * + * @covers ::redirectForm + */ + public function testRedirectWithResponseObject() { + $form_submitter = $this->getFormSubmitter(); + $redirect = new RedirectResponse('/example'); + $form_state['redirect'] = $redirect; + + $form_state += $this->getFormStateDefaults(); + $result_redirect = $form_submitter->redirectForm($form_state); + + $this->assertSame($redirect, $result_redirect); + } + + /** + * Tests the redirectForm() method when no redirect is expected. + * + * @covers ::redirectForm + * + * @dataProvider providerTestRedirectWithoutResult + */ + public function testRedirectWithoutResult($form_state) { + $form_submitter = $this->getFormSubmitter(); + $this->urlGenerator->expects($this->never()) + ->method('generateFromPath'); + $this->urlGenerator->expects($this->never()) + ->method('generateFromRoute'); + $form_state += $this->getFormStateDefaults(); + $redirect = $form_submitter->redirectForm($form_state); + $this->assertNull($redirect); + } + + /** + * Provides test data for testing the redirectForm() method with a redirect. + * + * @return array + * Returns some test data. + */ + public function providerTestRedirectWithResult() { + return array( + array(array(), ''), + array(array('redirect' => 'foo'), 'foo'), + array(array('redirect' => array('foo')), 'foo'), + array(array('redirect' => array('foo')), 'foo'), + array(array('redirect' => array('bar', array('query' => array('foo' => 'baz')))), 'bar'), + array(array('redirect' => array('baz', array(), 301)), 'baz', 301), + ); + } + + /** + * Provides test data for testing the redirectForm() method with a route name. + * + * @return array + * Returns some test data. + */ + public function providerTestRedirectWithRouteWithResult() { + return array( + array(array('redirect_route' => array('route_name' => 'test_route_a')), 'test-route'), + array(array('redirect_route' => array('route_name' => 'test_route_b', 'route_parameters' => array('key' => 'value'))), 'test-route/value'), + array(array('redirect_route' => new Url('test_route_b', array('key' => 'value'))), 'test-route/value'), + ); + } + + /** + * Provides test data for testing the redirectForm() method with no redirect. + * + * @return array + * Returns some test data. + */ + public function providerTestRedirectWithoutResult() { + return array( + array(array('programmed' => TRUE)), + array(array('rebuild' => TRUE)), + array(array('no_redirect' => TRUE)), + array(array('redirect' => FALSE)), + ); + } + + /** + * @covers ::executeSubmitHandlers + */ + public function testExecuteSubmitHandlers() { + $form_submitter = $this->getFormSubmitter(); + $mock = $this->getMock('stdClass', array('submit_handler', 'hash_submit')); + $mock->expects($this->once()) + ->method('submit_handler') + ->with($this->isType('array'), $this->isType('array')); + $mock->expects($this->once()) + ->method('hash_submit') + ->with($this->isType('array'), $this->isType('array')); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_submitter->executeSubmitHandlers($form, $form_state); + + $form['#submit'][] = array($mock, 'hash_submit'); + $form_submitter->executeSubmitHandlers($form, $form_state); + + // $form_state submit handlers will supersede $form handlers. + $form_state['submit_handlers'][] = array($mock, 'submit_handler'); + $form_submitter->executeSubmitHandlers($form, $form_state); + } + + /** + * @return array() + */ + protected function getFormStateDefaults() { + $form_builder = $this->getMockBuilder('Drupal\Core\Form\FormBuilder') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + return $form_builder->getFormStateDefaults(); + } + + /** + * @return \Drupal\Core\Form\FormSubmitterInterface + */ + protected function getFormSubmitter() { + $request_stack = new RequestStack(); + $request_stack->push(new Request()); + return $this->getMockBuilder('Drupal\Core\Form\FormSubmitter') + ->setConstructorArgs(array($request_stack, $this->urlGenerator)) + ->setMethods(array('batchGet', 'drupalInstallationAttempted')) + ->getMock(); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php index 639b48f..280e54e 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php +++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php @@ -12,6 +12,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; /** @@ -29,6 +30,16 @@ protected $formBuilder; /** + * @var \Drupal\Core\Form\FormValidatorInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $formValidator; + + /** + * @var \Drupal\Core\Form\FormSubmitterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $formSubmitter; + + /** * The mocked URL generator. * * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\Routing\UrlGeneratorInterface @@ -78,6 +89,13 @@ protected $request; /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** * The event dispatcher. * * @var \PHPUnit_Framework_MockObject_MockObject|\Symfony\Component\EventDispatcher\EventDispatcherInterface @@ -92,11 +110,6 @@ protected $keyValueExpirableFactory; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\StringTranslation\TranslationInterface - */ - protected $translationManager; - - /** * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\HttpKernel */ protected $httpKernel; @@ -116,19 +129,29 @@ array('form_state', $this->formStateCache), ))); - $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface'); - $this->translationManager = $this->getStringTranslationStub(); $this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') ->disableOriginalConstructor() ->getMock(); $this->httpKernel = $this->getMockBuilder('Drupal\Core\HttpKernel') ->disableOriginalConstructor() ->getMock(); - $this->request = new Request(); $this->account = $this->getMock('Drupal\Core\Session\AccountInterface'); + $this->request = new Request(); + $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->requestStack = new RequestStack(); + $this->requestStack->push($this->request); + $this->formValidator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array($this->requestStack, $this->getStringTranslationStub(), $this->csrfToken)) + ->setMethods(array('drupalSetMessage')) + ->getMock(); + $this->formSubmitter = $this->getMockBuilder('Drupal\Core\Form\FormSubmitter') + ->setConstructorArgs(array($this->requestStack, $this->urlGenerator)) + ->setMethods(array('batchGet', 'drupalInstallationAttempted')) + ->getMock(); - $this->setupFormBuilder(); + $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->requestStack, $this->csrfToken, $this->httpKernel); + $this->formBuilder->setCurrentUser($this->account); } /** @@ -139,15 +162,6 @@ } /** - * Sets up a new form builder object to test. - */ - protected function setupFormBuilder() { - $this->formBuilder = new TestFormBuilder($this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->urlGenerator, $this->translationManager, $this->csrfToken, $this->httpKernel); - $this->formBuilder->setRequest($this->request); - $this->formBuilder->setCurrentUser($this->account); - } - - /** * Provides a mocked form object. * * @param string $form_id @@ -277,25 +291,6 @@ class TestFormBuilder extends FormBuilder { /** * {@inheritdoc} */ - protected function drupalInstallationAttempted() { - return FALSE; - } - - /** - * {@inheritdoc} - */ - protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) { - } - - /** - * {@inheritdoc} - */ - protected function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG_NOTICE, $link = NULL) { - } - - /** - * {@inheritdoc} - */ protected function drupalHtmlClass($class) { return $class; } @@ -320,14 +315,6 @@ class TestFormBuilder extends FormBuilder { static::$seenIds = array(); } - /** - * {@inheritdoc} - */ - protected function &batchGet() { - $batch = array(); - return $batch; - } - } } diff --git a/core/tests/Drupal/Tests/Core/Form/FormValidationTest.php b/core/tests/Drupal/Tests/Core/Form/FormValidationTest.php deleted file mode 100644 index cb5b52e..0000000 --- a/core/tests/Drupal/Tests/Core/Form/FormValidationTest.php +++ /dev/null @@ -1,47 +0,0 @@ - 'Form element validation', - 'description' => 'Tests various form element validation mechanisms.', - 'group' => 'Form API', - ); - } - - public function testUniqueHtmlId() { - $form_id = 'test_form_id'; - $expected_form = $form_id(); - $expected_form['test']['#required'] = TRUE; - - // Mock a form object that will be built three times. - $form_arg = $this->getMockForm($form_id, $expected_form, 2); - - $form_state = array(); - $this->formBuilder->getFormId($form_arg, $form_state); - $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state); - $this->assertSame($form_id, $form['#id']); - - $form_state = array(); - $form = $this->simulateFormSubmission($form_id, $form_arg, $form_state); - $this->assertSame("$form_id--2", $form['#id']); - } - -} diff --git a/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php b/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php new file mode 100644 index 0000000..efb06c4 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Form/FormValidatorTest.php @@ -0,0 +1,624 @@ + 'Form validator test', + 'description' => 'Tests the form validator.', + 'group' => 'Form API', + ); + } + + /** + * Tests that form errors during submission throw an exception. + * + * @covers ::setErrorByName + * + * @expectedException \LogicException + * @expectedExceptionMessage Form errors cannot be set after form validation has finished. + */ + public function testFormErrorsDuringSubmission() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + $form_state['validation_complete'] = TRUE; + $form_validator->setErrorByName('test', $form_state, 'message'); + } + + /** + * Tests the 'validation_complete' $form_state flag. + * + * @covers ::validateForm + * @covers ::finalizeValidation + */ + public function testValidationComplete() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $this->assertFalse($form_state['validation_complete']); + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertTrue($form_state['validation_complete']); + } + + /** + * Tests the 'must_validate' $form_state flag. + * + * @covers ::validateForm + */ + public function testPreventDuplicateValidation() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(array('doValidateForm')) + ->getMock(); + $form_validator->expects($this->never()) + ->method('doValidateForm'); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_state['validation_complete'] = TRUE; + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertArrayNotHasKey('#errors', $form); + } + + /** + * Tests the 'must_validate' $form_state flag. + * + * @covers ::validateForm + */ + public function testMustValidate() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(array('doValidateForm')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('doValidateForm'); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_state['validation_complete'] = TRUE; + $form_state['must_validate'] = TRUE; + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertArrayHasKey('#errors', $form); + } + + /** + * @covers ::validateForm + */ + public function testValidateInvalidFormToken() { + $request_stack = new RequestStack(); + $request = new Request(array(), array(), array(), array(), array(), array('REQUEST_URI' => '/test/example?foo=bar')); + $request_stack->push($request); + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $csrf_token->expects($this->once()) + ->method('validate') + ->will($this->returnValue(FALSE)); + + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('setErrorByName', 'doValidateForm')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('setErrorByName') + ->with('form_token', $this->isType('array'), 'The form has become outdated. Copy any unsaved work in the form below and then reload this page.'); + $form_validator->expects($this->never()) + ->method('doValidateForm'); + + $form['#token'] = 'test_form_id'; + $form_state = $this->getFormStateDefaults(); + $form_state['values']['form_token'] = 'some_random_token'; + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertTrue($form_state['validation_complete']); + } + + /** + * @covers ::validateForm + */ + public function testValidateValidFormToken() { + $request_stack = new RequestStack(); + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $csrf_token->expects($this->once()) + ->method('validate') + ->will($this->returnValue(TRUE)); + + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('setErrorByName', 'doValidateForm')) + ->getMock(); + $form_validator->expects($this->never()) + ->method('setErrorByName'); + $form_validator->expects($this->once()) + ->method('doValidateForm'); + + $form['#token'] = 'test_form_id'; + $form_state = $this->getFormStateDefaults(); + $form_state['values']['form_token'] = 'some_random_token'; + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertTrue($form_state['validation_complete']); + } + + /** + * Tests the setError() method. + * + * @covers ::setError + */ + public function testSetError() { + $form_state = $this->getFormStateDefaults(); + + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(array('setErrorByName')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('setErrorByName') + ->with('foo][bar', $form_state, 'Fail'); + + $element['#parents'] = array('foo', 'bar'); + $form_validator->setError($element, $form_state, 'Fail'); + } + + /** + * Tests the getError() method. + * + * @covers ::getError + * + * @dataProvider providerTestGetError + */ + public function testGetError($errors, $parents, $error = NULL) { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + + $element['#parents'] = $parents; + $form_state = $this->getFormStateDefaults(); + $form_state['errors'] = $errors; + $this->assertSame($error, $form_validator->getError($element, $form_state)); + } + + public function providerTestGetError() { + return array( + array(array(), array('foo')), + array(array('foo][bar' => 'Fail'), array()), + array(array('foo][bar' => 'Fail'), array('foo')), + array(array('foo][bar' => 'Fail'), array('bar')), + array(array('foo][bar' => 'Fail'), array('baz')), + array(array('foo][bar' => 'Fail'), array('foo', 'bar'), 'Fail'), + array(array('foo][bar' => 'Fail'), array('foo', 'bar', 'baz'), 'Fail'), + array(array('foo][bar' => 'Fail 2'), array('foo')), + array(array('foo' => 'Fail 1', 'foo][bar' => 'Fail 2'), array('foo'), 'Fail 1'), + array(array('foo' => 'Fail 1', 'foo][bar' => 'Fail 2'), array('foo', 'bar'), 'Fail 1'), + ); + } + + /** + * @covers ::setErrorByName + * + * @dataProvider providerTestSetErrorByName + */ + public function testSetErrorByName($limit_validation_errors, $expected_errors, $set_message = FALSE) { + $request_stack = new RequestStack(); + $request = new Request(); + $request_stack->push($request); + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('drupalSetMessage')) + ->getMock(); + $form_validator->expects($set_message ? $this->once() : $this->never()) + ->method('drupalSetMessage'); + + $form_state = $this->getFormStateDefaults(); + $form_state['limit_validation_errors'] = $limit_validation_errors; + $form_validator->setErrorByName('test', $form_state, 'Fail 1'); + $form_validator->setErrorByName('test', $form_state, 'Fail 2'); + $form_validator->setErrorByName('options', $form_state); + + $this->assertSame(!empty($expected_errors), $request->attributes->get('_form_errors', FALSE)); + $this->assertSame($expected_errors, $form_state['errors']); + } + + public function providerTestSetErrorByName() { + return array( + // Only validate the 'options' element. + array(array(array('options')), array('options' => '')), + // Do not limit an validation, and, ensuring the first error is returned + // for the 'test' element. + array(NULL, array('test' => 'Fail 1', 'options' => ''), TRUE), + // Limit all validation. + array(array(), array()), + ); + } + + /** + * @covers ::setElementErrorsFromFormState + */ + public function testSetElementErrorsFromFormState() { + $request_stack = new RequestStack(); + $request = new Request(); + $request_stack->push($request); + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array($request_stack, $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('drupalSetMessage')) + ->getMock(); + + $form = array( + '#parents' => array(), + ); + $form['test'] = array( + '#type' => 'textfield', + '#title' => 'Test', + '#parents' => array('test'), + ); + $form_state = $this->getFormStateDefaults(); + $form_validator->setErrorByName('test', $form_state, 'invalid'); + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertSame('invalid', $form['test']['#errors']); + } + + /** + * @covers ::handleErrorsWithLimitedValidation + * + * @dataProvider providerTestHandleErrorsWithLimitedValidation + */ + public function testHandleErrorsWithLimitedValidation($sections, $triggering_element, $values, $expected) { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_state['triggering_element'] = $triggering_element; + $form_state['triggering_element']['#limit_validation_errors'] = $sections; + + $form_state['values'] = $values; + $form_validator->validateForm('test_form_id', $form, $form_state); + $this->assertSame($expected, $form_state['values']); + } + + public function providerTestHandleErrorsWithLimitedValidation() { + return array( + // Test with a non-existent section. + array( + array(array('test1'), array('test3')), + array(), + array( + 'test1' => 'foo', + 'test2' => 'bar', + ), + array( + 'test1' => 'foo', + ), + ), + // Test with buttons in a non-validated section. + array( + array(array('test1')), + array( + '#is_button' => true, + '#value' => 'baz', + '#name' => 'op', + '#parents' => array('submit'), + ), + array( + 'test1' => 'foo', + 'test2' => 'bar', + 'op' => 'baz', + 'submit' => 'baz', + ), + array( + 'test1' => 'foo', + 'submit' => 'baz', + 'op' => 'baz', + ), + ), + // Test with a matching button #value and $form_state value. + array( + array(array('submit')), + array( + '#is_button' => TRUE, + '#value' => 'baz', + '#name' => 'op', + '#parents' => array('submit'), + ), + array( + 'test1' => 'foo', + 'test2' => 'bar', + 'op' => 'baz', + 'submit' => 'baz', + ), + array( + 'submit' => 'baz', + 'op' => 'baz', + ), + ), + // Test with a mismatched button #value and $form_state value. + array( + array(array('submit')), + array( + '#is_button' => TRUE, + '#value' => 'bar', + '#name' => 'op', + '#parents' => array('submit'), + ), + array( + 'test1' => 'foo', + 'test2' => 'bar', + 'op' => 'baz', + 'submit' => 'baz', + ), + array( + 'submit' => 'baz', + ), + ), + ); + } + + /** + * @covers ::executeValidateHandlers + */ + public function testExecuteValidateHandlers() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + $mock = $this->getMock('stdClass', array('validate_handler', 'hash_validate')); + $mock->expects($this->once()) + ->method('validate_handler') + ->with($this->isType('array'), $this->isType('array')); + $mock->expects($this->once()) + ->method('hash_validate') + ->with($this->isType('array'), $this->isType('array')); + + $form = array(); + $form_state = $this->getFormStateDefaults(); + $form_validator->executeValidateHandlers($form, $form_state); + + $form['#validate'][] = array($mock, 'hash_validate'); + $form_validator->executeValidateHandlers($form, $form_state); + + // $form_state validate handlers will supersede $form handlers. + $form_state['validate_handlers'][] = array($mock, 'validate_handler'); + $form_validator->executeValidateHandlers($form, $form_state); + } + + /** + * @covers ::doValidateForm + * + * @dataProvider providerTestRequiredErrorMessage + */ + public function testRequiredErrorMessage($element, $expected_message) { + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array(new RequestStack(), $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('executeValidateHandlers', 'setErrorByName')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('executeValidateHandlers'); + $form_validator->expects($this->once()) + ->method('setErrorByName') + ->with('test', $this->isType('array'), $expected_message); + + $form = array(); + $form['test'] = $element + array( + '#type' => 'textfield', + '#value' => '', + '#needs_validation' => TRUE, + '#required' => TRUE, + '#parents' => array('test'), + ); + $form_state = $this->getFormStateDefaults(); + $form_validator->validateForm('test_form_id', $form, $form_state); + } + + public function providerTestRequiredErrorMessage() { + return array( + array( + // Use the default message with a title. + array('#title' => 'Test'), + 'Test field is required.', + ), + // Use a custom message. + array( + array('#required_error' => 'FAIL'), + 'FAIL', + ), + // No title or custom message. + array( + array(), + '', + ), + ); + } + + /** + * @covers ::doValidateForm + */ + public function testElementValidate() { + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->disableOriginalConstructor() + ->setMethods(array('executeValidateHandlers', 'setErrorByName')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('executeValidateHandlers'); + $mock = $this->getMock('stdClass', array('element_validate')); + $mock->expects($this->once()) + ->method('element_validate') + ->with($this->isType('array'), $this->isType('array'), NULL); + + $form = array(); + $form['test'] = array( + '#type' => 'textfield', + '#title' => 'Test', + '#parents' => array('test'), + '#element_validate' => array(array($mock, 'element_validate')), + ); + $form_state = $this->getFormStateDefaults(); + $form_validator->validateForm('test_form_id', $form, $form_state); + } + + /** + * @covers ::performRequiredValidation + * + * @dataProvider providerTestPerformRequiredValidation + */ + public function testPerformRequiredValidation($element, $expected_message, $call_watchdog) { + $csrf_token = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator') + ->disableOriginalConstructor() + ->getMock(); + $form_validator = $this->getMockBuilder('Drupal\Core\Form\FormValidator') + ->setConstructorArgs(array(new RequestStack(), $this->getStringTranslationStub(), $csrf_token)) + ->setMethods(array('setErrorByName', 'watchdog')) + ->getMock(); + $form_validator->expects($this->once()) + ->method('setErrorByName') + ->with('test', $this->isType('array'), $expected_message); + + if ($call_watchdog) { + $form_validator->expects($this->once()) + ->method('watchdog') + ->with('form'); + } + + $form = array(); + $form['test'] = $element + array( + '#title' => 'Test', + '#needs_validation' => TRUE, + '#required' => FALSE, + '#parents' => array('test'), + ); + $form_state = $this->getFormStateDefaults(); + $form_state['values'] = array(); + $form_validator->validateForm('test_form_id', $form, $form_state); + } + + public function providerTestPerformRequiredValidation() { + return array( + array( + array( + '#type' => 'select', + '#options' => array( + 'foo' => 'Foo', + 'bar' => 'Bar', + ), + '#required' => TRUE, + '#value' => 'baz', + '#empty_value' => 'baz', + '#multiple' => FALSE, + ), + 'Test field is required.', + FALSE, + ), + array( + array( + '#type' => 'select', + '#options' => array( + 'foo' => 'Foo', + 'bar' => 'Bar', + ), + '#value' => 'baz', + '#multiple' => FALSE, + ), + 'An illegal choice has been detected. Please contact the site administrator.', + TRUE, + ), + array( + array( + '#type' => 'checkboxes', + '#options' => array( + 'foo' => 'Foo', + 'bar' => 'Bar', + ), + '#value' => array('baz'), + '#multiple' => TRUE, + ), + 'An illegal choice has been detected. Please contact the site administrator.', + TRUE, + ), + array( + array( + '#type' => 'select', + '#options' => array( + 'foo' => 'Foo', + 'bar' => 'Bar', + ), + '#value' => array('baz'), + '#multiple' => TRUE, + ), + 'An illegal choice has been detected. Please contact the site administrator.', + TRUE, + ), + array( + array( + '#type' => 'textfield', + '#maxlength' => 7, + '#value' => $this->randomName(8), + ), + String::format('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => 'Test', '%max' => '7', '%length' => 8)), + FALSE, + ), + ); + } + + /** + * @return array() + */ + protected function getFormStateDefaults() { + $form_builder = $this->getMockBuilder('Drupal\Core\Form\FormBuilder') + ->disableOriginalConstructor() + ->setMethods(NULL) + ->getMock(); + return $form_builder->getFormStateDefaults(); + } + +} + +} + +namespace { + if (!defined('WATCHDOG_ERROR')) { + define('WATCHDOG_ERROR', 3); + } + if (!defined('WATCHDOG_NOTICE')) { + define('WATCHDOG_NOTICE', 5); + } +} diff --git a/core/tests/Drupal/Tests/Core/Form/OptGroupTest.php b/core/tests/Drupal/Tests/Core/Form/OptGroupTest.php new file mode 100644 index 0000000..0807448 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Form/OptGroupTest.php @@ -0,0 +1,61 @@ + 'OptGroup test', + 'description' => 'Tests the OptGroup class.', + 'group' => 'Form API', + ); + } + + /** + * Tests the flattenOptions() method. + * + * @dataProvider providerTestFlattenOptions + */ + public function testFlattenOptions($options) { + $this->assertSame(array('foo' => 1), OptGroup::flattenOptions($options)); + } + + /** + * Provides test data for the flattenOptions() method. + * + * @return array + */ + public function providerTestFlattenOptions() { + $object1 = new \stdClass(); + $object1->option = array('foo' => 'foo'); + $object2 = new \stdClass(); + $object2->option = array(array('foo' => 'foo'), array('foo' => 'foo')); + return array( + array(array('foo' => 'foo')), + array(array(array('foo' => 'foo'))), + array(array($object1)), + array(array($object2)), + ); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php index 883b79e..9b1b4e7 100644 --- a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkDefaultTest.php @@ -73,7 +73,7 @@ class ContextualLinkDefaultTest extends UnitTestCase { protected function setupContextualLinkDefault() { $this->contextualLinkDefault = new ContextualLinkDefault($this->config, $this->pluginId, $this->pluginDefinition); - $this->contextualLinkDefault->setTranslationManager($this->stringTranslation); + $this->contextualLinkDefault->setStringTranslation($this->stringTranslation); } /** diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php index 4febf02..70bddbc 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php @@ -83,7 +83,7 @@ class LocalActionDefaultTest extends UnitTestCase { */ protected function setupLocalActionDefault() { $this->localActionDefault = new LocalActionDefault($this->config, $this->pluginId, $this->pluginDefinition, $this->routeProvider); - $this->localActionDefault->setTranslationManager($this->stringTranslation); + $this->localActionDefault->setStringTranslation($this->stringTranslation); } /** diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php index a0f8106..9cd7210 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php @@ -87,7 +87,7 @@ class LocalTaskDefaultTest extends UnitTestCase { $this->localTaskBase = new TestLocalTaskDefault($this->config, $this->pluginId, $this->pluginDefinition); $this->localTaskBase ->setRouteProvider($this->routeProvider) - ->setTranslationManager($this->stringTranslation); + ->setStringTranslation($this->stringTranslation); } diff --git a/core/tests/Drupal/Tests/Core/Page/HtmlPageTest.php b/core/tests/Drupal/Tests/Core/Page/HtmlPageTest.php new file mode 100644 index 0000000..ed73bad --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Page/HtmlPageTest.php @@ -0,0 +1,74 @@ + 'Tests \Drupal\Core\Page\HtmlPage', + 'description' => '', + 'group' => 'Page', + ); + } + + /** + * Ensures that a single metatags can be changed. + */ + public function testMetatagAlterability() { + $page = new HtmlPage(); + $page->addMetaElement(new MetaElement('', array('name' => 'example'))); + $page->addMetaElement(new MetaElement('', array('name' => 'example2'))); + $metatags = $page->getMetaElements(); + + foreach ($metatags as $tag) { + if ($tag->getName() == 'example') { + $tag->setContent('hello'); + } + } + + $metatags = $page->getMetaElements(); + $this->assertEquals('hello', $metatags[0]->getContent()); + $this->assertEquals('', $metatags[1]->getContent()); + } + + /** + * Ensures that a metatag can be removed. + */ + public function testMetatagRemovability() { + $page = new HtmlPage(); + $page->addMetaElement(new MetaElement('', array('name' => 'example'))); + $page->addMetaElement(new MetaElement('', array('name' => 'example2'))); + $metatags =& $page->getMetaElements(); + + foreach ($metatags as $key => $tag) { + if ($tag->getName() == 'example') { + unset($metatags[$key]); + } + } + + $metatags = $page->getMetaElements(); + reset($metatags); + $this->assertCount(1, $metatags); + $this->assertEquals('example2', current($metatags)->getName()); + } + +} + diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php index 40c8de0..d810d1c 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php +++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorAliasTest.php @@ -54,7 +54,7 @@ class PathProcessorAliasTest extends UnitTestCase { */ public function testProcessInbound() { $this->aliasManager->expects($this->exactly(2)) - ->method('getSystemPath') + ->method('getPathByAlias') ->will($this->returnValueMap(array( array('urlalias', NULL, 'internal-url'), array('url', NULL, 'url'), @@ -73,7 +73,7 @@ class PathProcessorAliasTest extends UnitTestCase { */ public function testProcessOutbound() { $this->aliasManager->expects($this->exactly(2)) - ->method('getPathAlias') + ->method('getAliasByPath') ->will($this->returnValueMap(array( array('internal-url', NULL, 'urlalias'), array('url', NULL, 'url'), diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php index 8f877f7..f58edc5 100644 --- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php @@ -110,7 +110,7 @@ function testProcessInbound() { ); $alias_manager->expects($this->any()) - ->method('getSystemPath') + ->method('getPathByAlias') ->will($this->returnValueMap($system_path_map)); // Create a stub config factory with all config settings that will be checked diff --git a/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php index 873ff0e..aec50c2 100644 --- a/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php +++ b/core/tests/Drupal/Tests/Core/Route/RoleAccessCheckTest.php @@ -8,13 +8,9 @@ namespace Drupal\Tests\Core\Route; use Drupal\Core\Access\AccessCheckInterface; -use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Session\UserSession; use Drupal\Tests\UnitTestCase; use Drupal\user\Access\RoleAccessCheck; -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; @@ -159,16 +155,14 @@ class RoleAccessCheckTest extends UnitTestCase { $collection = $this->getTestRouteCollection(); foreach ($grant_accounts as $account) { - $subrequest = Request::create($path, 'GET'); $message = sprintf('Access granted for user with the roles %s on path: %s', implode(', ', $account->getRoles()), $path); - $this->assertSame(AccessCheckInterface::ALLOW, $role_access_check->access($collection->get($path), $subrequest, $account), $message); + $this->assertSame(AccessCheckInterface::ALLOW, $role_access_check->access($collection->get($path), $account), $message); } // Check all users which don't have access. foreach ($deny_accounts as $account) { - $subrequest = Request::create($path, 'GET'); $message = sprintf('Access denied for user %s with the roles %s on path: %s', $account->id(), implode(', ', $account->getRoles()), $path); - $has_access = $role_access_check->access($collection->get($path), $subrequest, $account); + $has_access = $role_access_check->access($collection->get($path), $account); $this->assertSame(AccessCheckInterface::DENY, $has_access , $message); } } diff --git a/core/tests/Drupal/Tests/Core/Routing/NullRouteBuilder.php b/core/tests/Drupal/Tests/Core/Routing/NullRouteBuilder.php index 2631c48..55c00c8 100644 --- a/core/tests/Drupal/Tests/Core/Routing/NullRouteBuilder.php +++ b/core/tests/Drupal/Tests/Core/Routing/NullRouteBuilder.php @@ -15,4 +15,8 @@ class NullRouteBuilder implements RouteBuilderInterface { public function setRebuildNeeded() { } + public function getCollectionDuringRebuild() { + return FALSE; + } + } diff --git a/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php index 2aec9e0..fd777ce 100644 --- a/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/RouteBuilderTest.php @@ -169,15 +169,14 @@ class RouteBuilderTest extends UnitTestCase { ->method('findAll') ->will($this->returnValue(array('test_module' => $routes))); - // Ensure that the dispatch events for altering are fired. + $route_collection = $routing_fixtures->sampleRouteCollection(); + $route_build_event = new RouteBuildEvent($route_collection); + + // Ensure that the alter routes events are fired. $this->dispatcher->expects($this->at(0)) ->method('dispatch') - ->with($this->equalTo(RoutingEvents::ALTER), $this->isInstanceOf('Drupal\Core\Routing\RouteBuildEvent')); - - $empty_collection = new RouteCollection(); - $route_build_event = new RouteBuildEvent($empty_collection, 'dynamic_routes'); + ->with(RoutingEvents::DYNAMIC, $route_build_event); - // Ensure that the alter routes events are fired. $this->dispatcher->expects($this->at(1)) ->method('dispatch') ->with(RoutingEvents::ALTER, $route_build_event); @@ -185,16 +184,10 @@ class RouteBuilderTest extends UnitTestCase { // Ensure that the routes are set to the dumper and dumped. $this->dumper->expects($this->at(0)) ->method('addRoutes') - ->with($routing_fixtures->sampleRouteCollection()); + ->with($route_collection); $this->dumper->expects($this->at(1)) ->method('dump') - ->with(array('provider' => 'test_module')); - $this->dumper->expects($this->at(2)) - ->method('addRoutes') - ->with($empty_collection); - $this->dumper->expects($this->at(3)) - ->method('dump') - ->with(array('provider' => 'dynamic_routes')); + ->with(); $this->assertTrue($this->routeBuilder->rebuild()); } @@ -242,15 +235,13 @@ class RouteBuilderTest extends UnitTestCase { $route_collection_filled->add('test_route.1', new Route('/test-route/1')); $route_collection_filled->add('test_route.2', new Route('/test-route/2')); - // Ensure that the dispatch events for altering are fired. + $route_build_event = new RouteBuildEvent($route_collection_filled); + + // Ensure that the alter routes events are fired. $this->dispatcher->expects($this->at(0)) ->method('dispatch') - ->with($this->equalTo(RoutingEvents::ALTER), $this->isInstanceOf('Drupal\Core\Routing\RouteBuildEvent')); - - $empty_collection = new RouteCollection(); - $route_build_event = new RouteBuildEvent($empty_collection, 'dynamic_routes'); + ->with(RoutingEvents::DYNAMIC, $route_build_event); - // Ensure that the alter routes events are fired. $this->dispatcher->expects($this->at(1)) ->method('dispatch') ->with(RoutingEvents::ALTER, $route_build_event); @@ -260,8 +251,7 @@ class RouteBuilderTest extends UnitTestCase { ->method('addRoutes') ->with($route_collection_filled); $this->dumper->expects($this->at(1)) - ->method('dump') - ->with(array('provider' => 'test_module')); + ->method('dump'); $this->assertTrue($this->routeBuilder->rebuild()); } diff --git a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php index 151b749..5226261 100644 --- a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php @@ -115,7 +115,7 @@ function setUp() { ->getMock(); $alias_manager->expects($this->any()) - ->method('getPathAlias') + ->method('getAliasByPath') ->will($this->returnCallback(array($this, 'aliasManagerCallback'))); $this->aliasManager = $alias_manager; @@ -144,11 +144,12 @@ function setUp() { } /** - * Return value callback for the getPathAlias() method on the mock alias manager. + * Return value callback for the getAliasByPath() method on the mock alias + * manager. * - * Ensures that by default the call to getPathAlias() will return the first argument - * that was passed in. We special-case the paths for which we wish it to return an - * actual alias. + * Ensures that by default the call to getAliasByPath() will return the first + * argument that was passed in. We special-case the paths for which we wish it + * to return an actual alias. * * @return string */ diff --git a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php index 541cbe8..6038899 100644 --- a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php +++ b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php @@ -13,6 +13,8 @@ /** * Tests read-only settings. * + * @group Drupal + * * @coversDefaultClass \Drupal\Core\Site\Settings */ class SettingsTest extends UnitTestCase { @@ -49,6 +51,7 @@ class SettingsTest extends UnitTestCase { $this->config = array( 'one' => '1', 'two' => '2', + 'hash_salt' => $this->randomName(), ); $this->settings = new Settings($this->config); } @@ -58,7 +61,7 @@ class SettingsTest extends UnitTestCase { */ public function testGet() { // Test stored settings. - $this->assertEquals($this->config['one'], Settings::get('one'), 'The correect setting was not returned.'); + $this->assertEquals($this->config['one'], Settings::get('one'), 'The correct setting was not returned.'); $this->assertEquals($this->config['two'], Settings::get('two'), 'The correct setting was not returned.'); // Test setting that isn't stored with default. @@ -81,4 +84,41 @@ class SettingsTest extends UnitTestCase { $this->assertEquals($singleton, $this->settings); } + /** + * Tests Settings::getHashSalt(); + * + * @covers ::getHashSalt + */ + public function testGetHashSalt() { + $this->assertSame($this->config['hash_salt'], $this->settings->getHashSalt()); + } + + /** + * Tests Settings::getHashSalt() with no hash salt value. + * + * @covers ::getHashSalt + * + * @dataProvider providerTestGetHashSaltEmpty + * + * @expectedException \RuntimeException + */ + public function testGetHashSaltEmpty(array $config) { + // Re-create settings with no 'hash_salt' key. + $settings = new Settings($config); + $settings->getHashSalt(); + } + + /** + * Data provider for testGetHashSaltEmpty. + * + * @return array + */ + public function providerTestGetHashSaltEmpty() { + return array( + array(array()), + array(array('hash_salt' => '')), + array(array('hash_salt' => NULL)), + ); + } + } diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php new file mode 100644 index 0000000..69d2cea --- /dev/null +++ b/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php @@ -0,0 +1,82 @@ + 'String translation trait', + 'description' => 'Tests the string translation trait.', + 'group' => 'StringTranslation', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->translation = $this->getObjectForTrait('\Drupal\Core\StringTranslation\StringTranslationTrait'); + $stub = $this->getStringTranslationStub(); + $stub->expects($this->any()) + ->method('formatPlural') + ->will($this->returnArgument(2)); + $this->translation->setStringTranslation($stub); + $this->reflection = new \ReflectionClass(get_class($this->translation)); + } + + /** + * @covers ::t + */ + public function testT() { + $method = $this->reflection->getMethod('t'); + $method->setAccessible(TRUE); + + $this->assertEquals('something', $method->invoke($this->translation, 'something')); + } + + /** + * @covers ::formatPlural + */ + public function testFormatPlural() { + $method = $this->reflection->getMethod('formatPlural'); + $method->setAccessible(TRUE); + + $this->assertEquals('apples', $method->invoke($this->translation, 2, 'apple', 'apples')); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php index 55f4339..d862ec1 100644 --- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php @@ -17,6 +17,9 @@ * * @see \Drupal\Core\Utility\LinkGenerator * + * @group Drupal + * @group Utility + * * @coversDefaultClass \Drupal\Core\Utility\LinkGenerator */ class LinkGeneratorTest extends UnitTestCase { @@ -121,7 +124,7 @@ class LinkGeneratorTest extends UnitTestCase { } /** - * Tests the generateFromUrl() method. + * Tests the generateFromUrl() method with a route. * * @covers ::generateFromUrl() */ @@ -149,6 +152,38 @@ class LinkGeneratorTest extends UnitTestCase { } /** + * Tests the generateFromUrl() method with an external URL. + * + * The set_active_class option is set to TRUE to ensure this does not cause + * an error together with an external URL. + * + * @covers ::generateFromUrl() + */ + public function testGenerateFromUrlExternal() { + $this->urlGenerator->expects($this->once()) + ->method('generateFromPath') + ->with('http://drupal.org', array('set_active_class' => TRUE, 'external' => TRUE) + $this->defaultOptions) + ->will($this->returnArgument(0)); + + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with('link', $this->isType('array')); + + $url = Url::createFromPath('http://drupal.org'); + $url->setUrlGenerator($this->urlGenerator); + $url->setOption('set_active_class', TRUE); + + $result = $this->linkGenerator->generateFromUrl('Drupal', $url); + $this->assertTag(array( + 'tag' => 'a', + 'attributes' => array( + 'href' => 'http://drupal.org', + ), + 'content' => 'Drupal', + ), $result); + } + + /** * Tests the link method with additional attributes. * * @see \Drupal\Core\Utility\LinkGenerator::generate() diff --git a/core/tests/Drupal/Tests/Core/Utility/TokenTest.php b/core/tests/Drupal/Tests/Core/Utility/TokenTest.php new file mode 100644 index 0000000..f92502e --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Utility/TokenTest.php @@ -0,0 +1,118 @@ + '', + 'name' => '\Drupal\Tests\Core\Utility\Token unit test', + 'group' => 'System', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + $this->cache = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface'); + + $this->languageManager = $this->getMockBuilder('\Drupal\Core\Language\LanguageManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); + + $this->token = new Token($this->moduleHandler, $this->cache, $this->languageManager); + } + + /** + * @covers ::getInfo + */ + public function testGetInfo() { + $token_info = array( + 'types' => array( + 'foo' => array( + 'name' => $this->randomName(), + ), + ), + ); + + $language = $this->getMock('\Drupal\Core\Language\Language'); + $language->id = $this->randomName(); + + $this->languageManager->expects($this->once()) + ->method('getCurrentLanguage') + ->with(Language::TYPE_CONTENT) + ->will($this->returnValue($language)); + + // The persistent cache must only be hit once, after which the info is + // cached statically. + $this->cache->expects($this->once()) + ->method('get'); + $this->cache->expects($this->once()) + ->method('set') + ->with('token_info:' . $language->id, $token_info); + + $this->moduleHandler->expects($this->once()) + ->method('invokeAll') + ->with('token_info') + ->will($this->returnValue($token_info)); + $this->moduleHandler->expects($this->once()) + ->method('alter') + ->with('token_info', $token_info); + + // Get the information for the first time. The cache should be checked, the + // hooks invoked, and the info should be set to the cache should. + $this->token->getInfo(); + // Get the information for the second time. The data must be returned from + // the static cache, so the persistent cache must not be accessed and the + // hooks must not be invoked. + $this->token->getInfo(); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Utility/TokenUnitTest.php b/core/tests/Drupal/Tests/Core/Utility/TokenUnitTest.php deleted file mode 100644 index f2d0a2d..0000000 --- a/core/tests/Drupal/Tests/Core/Utility/TokenUnitTest.php +++ /dev/null @@ -1,118 +0,0 @@ - '', - 'name' => '\Drupal\Tests\Core\Utility\Token unit test', - 'group' => 'System', - ); - } - - /** - * {@inheritdoc} - */ - public function setUp() { - $this->cache = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface'); - - $this->languageManager = $this->getMockBuilder('\Drupal\Core\Language\LanguageManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface'); - - $this->token = new Token($this->moduleHandler, $this->cache, $this->languageManager); - } - - /** - * @covers getInfo - */ - public function testGetInfo() { - $token_info = array( - 'types' => array( - 'foo' => array( - 'name' => $this->randomName(), - ), - ), - ); - - $language = $this->getMock('\Drupal\Core\Language\Language'); - $language->id = $this->randomName(); - - $this->languageManager->expects($this->once()) - ->method('getCurrentLanguage') - ->with(Language::TYPE_CONTENT) - ->will($this->returnValue($language)); - - // The persistent cache must only be hit once, after which the info is - // cached statically. - $this->cache->expects($this->once()) - ->method('get'); - $this->cache->expects($this->once()) - ->method('set') - ->with('token_info:' . $language->id, $token_info); - - $this->moduleHandler->expects($this->once()) - ->method('invokeAll') - ->with('token_info') - ->will($this->returnValue($token_info)); - $this->moduleHandler->expects($this->once()) - ->method('alter') - ->with('token_info', $token_info); - - // Get the information for the first time. The cache should be checked, the - // hooks invoked, and the info should be set to the cache should. - $this->token->getInfo(); - // Get the information for the second time. The data must be returned from - // the static cache, so the persistent cache must not be accessed and the - // hooks must not be invoked. - $this->token->getInfo(); - } - -} diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css index 80ddc82..d8dff1a 100644 --- a/core/themes/bartik/css/style.css +++ b/core/themes/bartik/css/style.css @@ -440,7 +440,7 @@ h1.site-name { background: #fff; background: rgba(255, 255, 255, 0.8); } -.region-header .form-required { +.region-header .form-required:after { color: #eee; color: rgba(255, 255, 255, 0.7); } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index b2ce930..be30de6 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -276,59 +276,6 @@ $settings['update_free_access'] = FALSE; /** - * Twig debugging: - * - * When debugging is enabled: - * - The markup of each Twig template is surrounded by HTML comments that - * contain theming information, such as template file name suggestions. - * - Note that this debugging markup will cause automated tests that directly - * check rendered HTML to fail. When running automated tests, 'twig_debug' - * should be set to FALSE. - * - The dump() function can be used in Twig templates to output information - * about template variables. - * - Twig templates are automatically recompiled whenever the source code - * changes (see twig_auto_reload below). - * - * Note: changes to this setting will only take effect once the cache is - * cleared. - * - * For more information about debugging Twig templates, see - * http://drupal.org/node/1906392. - * - * Not recommended in production environments (Default: FALSE). - */ -# $settings['twig_debug'] = TRUE; - -/** - * Twig auto-reload: - * - * Automatically recompile Twig templates whenever the source code changes. If - * you don't provide a value for twig_auto_reload, it will be determined based - * on the value of twig_debug. - * - * Note: changes to this setting will only take effect once the cache is - * cleared. - * - * Not recommended in production environments (Default: NULL). - */ -# $settings['twig_auto_reload'] = TRUE; - -/** - * Twig cache: - * - * By default, Twig templates will be compiled and stored in the filesystem to - * increase performance. Disabling the Twig cache will recompile the templates - * from source each time they are used. In most cases the twig_auto_reload - * setting above should be enabled rather than disabling the Twig cache. - * - * Note: changes to this setting will only take effect once the cache is - * cleared. - * - * Not recommended in production environments (Default: TRUE). - */ -# $settings['twig_cache'] = FALSE; - -/** * External access proxy settings: * * If your site must access the Internet via a web proxy then you can enter @@ -491,7 +438,7 @@ * Remove the leading hash signs to enable. * * The "en" part of the variable name, is dynamic and can be any langcode of - * any enabled language. (eg locale_custom_strings_de for german). + * any added language. (eg locale_custom_strings_de for german). */ # $settings['locale_custom_strings_en'][''] = array( # 'forum' => 'Discussion board', @@ -511,16 +458,6 @@ # $settings['maintenance_theme'] = 'bartik'; /** - * Enable access to rebuild.php. - * - * This setting can be enabled to allow Drupal's php and database cached - * storage to be cleared via the rebuild.php page. Access to this page can also - * be gained by generating a query string from rebuild_token_calculator.sh and - * using these parameters in a request to rebuild.php. - */ -# $settings['rebuild_access'] = TRUE; - -/** * Base URL (optional). * * If Drupal is generating incorrect URLs on your site, which could @@ -611,7 +548,7 @@ * The 'bootstrap_config_storage' setting needs to be a callable that returns * core.services.yml. */ - # $settings['bootstrap_config_storage'] = array('Drupal\Core\Config\BootstrapConfigStorageFactory', 'getFileStorage'); +# $settings['bootstrap_config_storage'] = array('Drupal\Core\Config\BootstrapConfigStorageFactory', 'getFileStorage'); /** * Configuration overrides. diff --git a/sites/example.settings.local.php b/sites/example.settings.local.php new file mode 100644 index 0000000..1847f86 --- /dev/null +++ b/sites/example.settings.local.php @@ -0,0 +1,84 @@ + - +