Pathauto and dependencies
This commit is contained in:
parent
4b1a293d57
commit
24ffcb956b
257 changed files with 29510 additions and 0 deletions
339
web/modules/contrib/pathauto/LICENSE.txt
Normal file
339
web/modules/contrib/pathauto/LICENSE.txt
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
83
web/modules/contrib/pathauto/README.md
Normal file
83
web/modules/contrib/pathauto/README.md
Normal file
|
@ -0,0 +1,83 @@
|
|||
#Pathauto
|
||||
|
||||
If you are developing for this module, have a look at pathauto.api.php.
|
||||
|
||||
##Description
|
||||
|
||||
The Pathauto module provides support functions for other modules to
|
||||
automatically generate aliases based on appropriate criteria, with a
|
||||
central settings path for site administrators.
|
||||
|
||||
Implementations are provided for core entity types: content, taxonomy terms,
|
||||
and users (including blogs and forum pages).
|
||||
|
||||
Pathauto also provides a way to delete large numbers of aliases. This feature
|
||||
is available at Administer > Configuration > Search and metadata > URL aliases >
|
||||
Delete aliases.
|
||||
|
||||
##Benefits
|
||||
|
||||
Besides making the page address more reflective of its content than
|
||||
"node/138", it's important to know that modern search engines give
|
||||
heavy weight to search terms which appear in a page's URL. By
|
||||
automatically using keywords based directly on the page content in the URL,
|
||||
relevant search engine hits for your page can be significantly
|
||||
enhanced.
|
||||
|
||||
##Notices
|
||||
|
||||
Pathauto just adds URL aliases to content, users, and taxonomy terms.
|
||||
Because it's an alias, the standard Drupal URL (for example node/123 or
|
||||
taxonomy/term/1) will still function as normal. If you have external links
|
||||
to your site pointing to standard Drupal URLs, or hardcoded links in a module,
|
||||
template, content or menu which point to standard Drupal URLs it will bypass
|
||||
the alias set by Pathauto.
|
||||
|
||||
There are reasons you might not want two URLs for the same content on your
|
||||
site. If this applies to you, please note that you will need to update any
|
||||
hard coded links in your content or blocks.
|
||||
|
||||
If you use the "system path" (i.e. node/10) for menu items and settings like
|
||||
that, Drupal will replace it with the url_alias.
|
||||
|
||||
For external links, you might want to consider the Path Redirect or
|
||||
Global Redirect modules, which allow you to set forwarding either per item or
|
||||
across the site to your aliased URLs.
|
||||
|
||||
###URLs (not) Getting Replaced With Aliases:
|
||||
Please bear in mind that only URLs passed through Drupal's Drupal's URL and
|
||||
Link APIs will be replaced with their aliases during page output. If
|
||||
a module or your template contains hardcoded links, such as
|
||||
'href="node/$node->nid"', those won't get replaced with their corresponding
|
||||
aliases.
|
||||
|
||||
## Disabling Pathauto for a specific content type (or taxonomy)
|
||||
|
||||
When the pattern for a content type is left blank, the default pattern will be
|
||||
used. But if the default pattern is also blank, Pathauto will be disabled
|
||||
for that content type.
|
||||
|
||||
## Installing Pathauto
|
||||
1. Install the module as normal, note that there are two dependencies.
|
||||
2. Configure the module at admin/config/search/path/patterns - add a new pattern by creating by clicking "Add Pathauto pattern".
|
||||
3. Fill out "Path pattern" with fx [node:title], choose which content types this applies to, give it a label (the name) and save it.
|
||||
4. When you save new content from now on, it should automatically be assigned an alternative URL
|
||||
|
||||
##Credits:
|
||||
|
||||
The original module combined the functionality of Mike Ryan's autopath with
|
||||
Tommy Sundstrom's path_automatic.
|
||||
|
||||
Significant enhancements were contributed by jdmquin @ www.bcdems.net.
|
||||
|
||||
Matt England added the tracker support (tracker support has been removed in
|
||||
recent changes).
|
||||
|
||||
Other suggestions and patches contributed by the Drupal community.
|
||||
|
||||
Current maintainers:
|
||||
|
||||
- Dave Reid - http://www.davereid.net
|
||||
- Greg Knaddison - http://www.knaddison.com
|
||||
- Mike Ryan - http://mikeryan.name
|
||||
- Frederik 'Freso' S. Olesen - http://freso.dk
|
|
@ -0,0 +1,13 @@
|
|||
enabled_entity_types:
|
||||
- user
|
||||
punctuation:
|
||||
hyphen: 1
|
||||
verbose : FALSE
|
||||
separator : '-'
|
||||
max_length : 100
|
||||
max_component_length: 100
|
||||
transliterate : TRUE
|
||||
reduce_ascii : FALSE
|
||||
case : TRUE
|
||||
ignore_words : 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with'
|
||||
update_action : 2
|
|
@ -0,0 +1,9 @@
|
|||
id: pathauto_update_alias_node
|
||||
label: 'Update URL alias'
|
||||
status: true
|
||||
langcode: en
|
||||
type: node
|
||||
plugin: pathauto_update_alias
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
|
@ -0,0 +1,9 @@
|
|||
id: pathauto_update_alias_user
|
||||
label: 'Update URL alias'
|
||||
status: true
|
||||
langcode: en
|
||||
type: user
|
||||
plugin: pathauto_update_alias
|
||||
dependencies:
|
||||
module:
|
||||
- user
|
|
@ -0,0 +1,36 @@
|
|||
pathauto.settings:
|
||||
type: config_object
|
||||
mapping:
|
||||
enabled_entity_types:
|
||||
label: Enabled entity types
|
||||
type: sequence
|
||||
sequence:
|
||||
type: string
|
||||
punctuation:
|
||||
type: sequence
|
||||
sequence:
|
||||
type: integer
|
||||
verbose:
|
||||
type: boolean
|
||||
separator:
|
||||
type: string
|
||||
max_length:
|
||||
type: integer
|
||||
max_component_length:
|
||||
type: integer
|
||||
transliterate:
|
||||
type: boolean
|
||||
reduce_ascii:
|
||||
type: boolean
|
||||
ignore_words:
|
||||
type: string
|
||||
case:
|
||||
type: boolean
|
||||
ignore_words:
|
||||
type: string
|
||||
update_action:
|
||||
type: integer
|
||||
|
||||
action.configuration.pathauto_update_alias:
|
||||
type: action_configuration_default
|
||||
label: 'Update URL alias'
|
|
@ -0,0 +1,40 @@
|
|||
pathauto.pattern.*:
|
||||
type: config_entity
|
||||
label: 'Pathauto pattern config'
|
||||
mapping:
|
||||
id:
|
||||
type: string
|
||||
label: 'ID'
|
||||
label:
|
||||
type: label
|
||||
label: 'Label'
|
||||
uuid:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
label: 'Pattern type'
|
||||
pattern:
|
||||
type: string
|
||||
label: 'Pattern'
|
||||
selection_criteria:
|
||||
type: sequence
|
||||
label: 'Selection criteria'
|
||||
sequence:
|
||||
type: condition.plugin.[id]
|
||||
label: 'Selection condition'
|
||||
selection_logic:
|
||||
type: string
|
||||
label: 'Selection logic'
|
||||
weight:
|
||||
type: integer
|
||||
label: 'Weight'
|
||||
relationships:
|
||||
type: sequence
|
||||
label: 'Context definitions'
|
||||
sequence:
|
||||
- type: mapping
|
||||
label: 'Relationship'
|
||||
mapping:
|
||||
label:
|
||||
type: label
|
||||
label: 'Label'
|
160
web/modules/contrib/pathauto/pathauto.api.php
Normal file
160
web/modules/contrib/pathauto/pathauto.api.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Documentation for pathauto API.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
|
||||
/**
|
||||
* @todo Update for 8.x-1.x
|
||||
*
|
||||
* It may be helpful to review some examples of integration from
|
||||
* pathauto.pathauto.inc.
|
||||
*
|
||||
* Pathauto works by using tokens in path patterns. Thus the simplest
|
||||
* integration is just to provide tokens. Token support is provided by Drupal
|
||||
* core. To provide additional token from your module, implement the following
|
||||
* hooks:
|
||||
*
|
||||
* hook_tokens() - http://api.drupal.org/api/function/hook_tokens
|
||||
* hook_token_info() - http://api.drupal.org/api/function/hook_token_info
|
||||
*
|
||||
* If you wish to provide pathauto integration for custom paths provided by your
|
||||
* module, there are a few steps involved.
|
||||
*
|
||||
* 1. hook_pathauto()
|
||||
* Provide information required by pathauto for the settings form as well as
|
||||
* bulk generation. See the documentation for hook_pathauto() for more
|
||||
* details.
|
||||
*
|
||||
* 2. pathauto_create_alias()
|
||||
* At the appropriate time (usually when a new item is being created for
|
||||
* which a generated alias is desired), call pathauto_create_alias() with the
|
||||
* appropriate parameters to generate and create the alias. See the user,
|
||||
* taxonomy, and node hook implementations in pathauto.module for examples.
|
||||
* Also see the documentation for pathauto_create_alias().
|
||||
*
|
||||
* 3. pathauto_path_delete_all()
|
||||
* At the appropriate time (usually when an item is being deleted), call
|
||||
* pathauto_path_delete_all() to remove any aliases that were created for the
|
||||
* content being removed. See the documentation for
|
||||
* pathauto_path_delete_all() for more details.
|
||||
*
|
||||
* 4. hook_path_alias_types()
|
||||
* For modules that create new types of content that can be aliased with
|
||||
* pathauto, a hook implementation is needed to allow the user to delete them
|
||||
* all at once. See the documentation for hook_path_alias_types() below for
|
||||
* more information.
|
||||
*
|
||||
* There are other integration points with pathauto, namely alter hooks that
|
||||
* allow you to change the data used by pathauto at various points in the
|
||||
* process. See the below hook documentation for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Alter pathauto alias type definitions.
|
||||
*
|
||||
* @param array &$definitions
|
||||
* Alias type definitions.
|
||||
*/
|
||||
function hook_path_alias_types_alter(array &$definitions) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a possible URL alias would conflict with any existing paths.
|
||||
*
|
||||
* Returning TRUE from this function will trigger pathauto_alias_uniquify() to
|
||||
* generate a similar URL alias with a suffix to avoid conflicts.
|
||||
*
|
||||
* @param string $alias
|
||||
* The potential URL alias.
|
||||
* @param string $source
|
||||
* The source path for the alias (e.g. 'node/1').
|
||||
* @param string $langcode
|
||||
* The language code for the alias (e.g. 'en').
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if $alias conflicts with an existing, reserved path, or FALSE/NULL if
|
||||
* it does not match any reserved paths.
|
||||
*
|
||||
* @see pathauto_alias_uniquify()
|
||||
*/
|
||||
function hook_pathauto_is_alias_reserved($alias, $source, $langcode) {
|
||||
// Check our module's list of paths and return TRUE if $alias matches any of
|
||||
// them.
|
||||
return (bool) \Drupal::database()->query("SELECT 1 FROM {mytable} WHERE path = :path", [':path' => $alias])->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the pattern to be used before an alias is generated by Pathauto.
|
||||
*
|
||||
* This hook will only be called if a default pattern is configured (on
|
||||
* admin/config/search/path/patterns).
|
||||
*
|
||||
* @param string $pattern
|
||||
* The alias pattern for Pathauto to pass to token_replace() to generate the
|
||||
* URL alias.
|
||||
* @param array $context
|
||||
* An associative array of additional options, with the following elements:
|
||||
* - 'module': The module or entity type being aliased.
|
||||
* - 'op': A string with the operation being performed on the object being
|
||||
* aliased. Can be either 'insert', 'update', 'return', or 'bulkupdate'.
|
||||
* - 'source': A string of the source path for the alias (e.g. 'node/1').
|
||||
* - 'data': An array of keyed objects to pass to token_replace().
|
||||
* - 'type': The sub-type or bundle of the object being aliased.
|
||||
* - 'language': A string of the language code for the alias (e.g. 'en').
|
||||
* This can be altered by reference.
|
||||
*/
|
||||
function hook_pathauto_pattern_alter(&$pattern, array $context) {
|
||||
// Switch out any [node:created:*] tokens with [node:updated:*] on update.
|
||||
if ($context['module'] == 'node' && ($context['op'] == 'update')) {
|
||||
$pattern = preg_replace('/\[node:created(\:[^]]*)?\]/', '[node:updated$1]', $pattern);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter Pathauto-generated aliases before saving.
|
||||
*
|
||||
* @param string $alias
|
||||
* The automatic alias after token replacement and strings cleaned.
|
||||
* @param array $context
|
||||
* An associative array of additional options, with the following elements:
|
||||
* - 'module': The module or entity type being aliased.
|
||||
* - 'op': A string with the operation being performed on the object being
|
||||
* aliased. Can be either 'insert', 'update', 'return', or 'bulkupdate'.
|
||||
* - 'source': A string of the source path for the alias (e.g. 'node/1').
|
||||
* This can be altered by reference.
|
||||
* - 'data': An array of keyed objects to pass to token_replace().
|
||||
* - 'type': The sub-type or bundle of the object being aliased.
|
||||
* - 'language': A string of the language code for the alias (e.g. 'en').
|
||||
* This can be altered by reference.
|
||||
* - 'pattern': A string of the pattern used for aliasing the object.
|
||||
*/
|
||||
function hook_pathauto_alias_alter(&$alias, array &$context) {
|
||||
// Add a suffix so that all aliases get saved as 'content/my-title.html'
|
||||
$alias .= '.html';
|
||||
|
||||
// Force all aliases to be saved as language neutral.
|
||||
$context['language'] = Language::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the list of punctuation characters for Pathauto control.
|
||||
*
|
||||
* @param $punctuation
|
||||
* An array of punctuation to be controlled by Pathauto during replacement
|
||||
* keyed by punctuation name. Each punctuation record should be an array
|
||||
* with the following key/value pairs:
|
||||
* - value: The raw value of the punctuation mark.
|
||||
* - name: The human-readable name of the punctuation mark. This must be
|
||||
* translated using t() already.
|
||||
*/
|
||||
function hook_pathauto_punctuation_chars_alter(array &$punctuation) {
|
||||
// Add the trademark symbol.
|
||||
$punctuation['trademark'] = array('value' => '™', 'name' => t('Trademark symbol'));
|
||||
|
||||
// Remove the dollar sign.
|
||||
unset($punctuation['dollar']);
|
||||
}
|
20
web/modules/contrib/pathauto/pathauto.info.yml
Normal file
20
web/modules/contrib/pathauto/pathauto.info.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
name : 'Pathauto'
|
||||
description : 'Provides a mechanism for modules to automatically generate aliases for the content they manage.'
|
||||
# core: 8.x
|
||||
type: module
|
||||
|
||||
dependencies:
|
||||
- ctools:ctools
|
||||
- drupal:path
|
||||
- token:token
|
||||
|
||||
configure: entity.pathauto_pattern.collection
|
||||
|
||||
recommends:
|
||||
- redirect:redirect
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-04-29
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'pathauto'
|
||||
datestamp: 1493468049
|
301
web/modules/contrib/pathauto/pathauto.install
Normal file
301
web/modules/contrib/pathauto/pathauto.install
Normal file
|
@ -0,0 +1,301 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update, and uninstall functions for Pathauto.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\pathauto\Entity\PathautoPattern;
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function pathauto_install() {
|
||||
// Set the weight to 1
|
||||
module_set_weight('pathauto', 1);
|
||||
|
||||
// Ensure the url_alias table exists.
|
||||
_pathauto_ensure_url_alias_table_exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to ensure the url_alias table exists.
|
||||
*
|
||||
* Only necessary on Drupal 8.1.x.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2704821
|
||||
*/
|
||||
function _pathauto_ensure_url_alias_table_exists() {
|
||||
$alias_storage = \Drupal::service('path.alias_storage');
|
||||
if (method_exists($alias_storage, 'schemaDefinition')) {
|
||||
$database_schema = \Drupal::database()->schema();
|
||||
if (!$database_schema->tableExists($alias_storage::TABLE)) {
|
||||
$schema_definition = $alias_storage->schemaDefinition();
|
||||
$database_schema->createTable($alias_storage::TABLE, $schema_definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates pathauto widgets to use the path widget ID.
|
||||
*/
|
||||
function pathauto_update_8001() {
|
||||
|
||||
// Replace values in the 'entity.definitions.installed' keyvalue collection.
|
||||
$collection = \Drupal::service('keyvalue')->get('entity.definitions.installed');
|
||||
foreach ($collection->getAll() as $key => $definitions) {
|
||||
if (!is_array($definitions) || empty($definitions['path'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve and change path base field definition.
|
||||
$path_definition = $definitions['path'];
|
||||
if (($options = $path_definition->getDisplayOptions('form')) && $options['type'] = 'pathauto') {
|
||||
$options['type'] = 'path';
|
||||
$path_definition->setDisplayOptions('form', $options);
|
||||
// Save the new value.
|
||||
$collection->set($key, $definitions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach (EntityFormDisplay::loadMultiple() as $form_display) {
|
||||
if ($component = $form_display->getComponent('path')) {
|
||||
if (isset($component['type']) && $component['type'] == 'pathauto') {
|
||||
$component['type'] = 'path';
|
||||
$form_display->setComponent('path', $component);
|
||||
$form_display->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts patterns from configuration objects to configuration entities.
|
||||
*/
|
||||
function pathauto_update_8100() {
|
||||
\Drupal::service('module_installer')->install(['ctools']);
|
||||
|
||||
$messages = array();
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info */
|
||||
$entity_bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
$language_manager = \Drupal::languageManager();
|
||||
$entity_type_manager->clearCachedDefinitions();
|
||||
\Drupal::service('plugin.manager.alias_type')->clearCachedDefinitions();
|
||||
$entity_types = $entity_type_manager->getDefinitions();
|
||||
|
||||
// 1. Load all patterns.
|
||||
$config = \Drupal::configFactory()->getEditable('pathauto.pattern');
|
||||
$patterns = $config->get('patterns');
|
||||
|
||||
// 2. Create a configuration entity per pattern.
|
||||
foreach ($patterns as $entity_type => $entity_patterns) {
|
||||
if (!array_key_exists($entity_type, $entity_types)) {
|
||||
// We found an unknown entity type. Report it.
|
||||
$messages[] = t('Entity of type @type was not processed. It defines the following patterns: @patterns', array(
|
||||
'@type' => $entity_type,
|
||||
'@patterns' => print_r($entity_patterns, TRUE),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
$entity_label = $entity_types[$entity_type]->getLabel();
|
||||
|
||||
if (!empty($entity_patterns['default'])) {
|
||||
// This is a pattern for an entity type, such as "node".
|
||||
$pattern = PathautoPattern::create([
|
||||
'id' => $entity_type,
|
||||
'label' => $entity_label,
|
||||
'type' => 'canonical_entities:' . $entity_type,
|
||||
'pattern' => $entity_patterns['default'],
|
||||
'weight' => 0,
|
||||
]);
|
||||
$pattern->save();
|
||||
}
|
||||
|
||||
// Loop over bundles and create patterns if they have a value.
|
||||
// Bundle keys may have a language suffix for language-dependant patterns.
|
||||
if (isset($entity_patterns['bundles'])) {
|
||||
$bundle_info = $entity_bundle_info->getBundleInfo($entity_type);
|
||||
foreach ($entity_patterns['bundles'] as $bundle => $bundle_patterns) {
|
||||
if (empty($bundle_patterns['default'])) {
|
||||
// This bundle does not define a pattern. Move on to the next one.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($bundle_info[$bundle])) {
|
||||
// This is a pattern for a bundle, such as "node_article".
|
||||
$pattern = PathautoPattern::create([
|
||||
'id' => $entity_type . '_' . $bundle,
|
||||
'label' => $entity_label . ' ' . $bundle_info[$bundle]['label'],
|
||||
'type' => 'canonical_entities:' . $entity_type,
|
||||
'pattern' => $bundle_patterns['default'],
|
||||
'weight' => -5,
|
||||
]);
|
||||
|
||||
// Add the bundle condition.
|
||||
$pattern->addSelectionCondition([
|
||||
'id' => 'entity_bundle:' . $entity_type,
|
||||
'bundles' => array($bundle => $bundle),
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [ $entity_type => $entity_type ],
|
||||
]);
|
||||
|
||||
$pattern->save();
|
||||
}
|
||||
else {
|
||||
// This is either a language dependent pattern such as "article_es" or
|
||||
// an unknown bundle or langcode. Let's figure it out.
|
||||
$matches = NULL;
|
||||
$langcode = NULL;
|
||||
$extracted_bundle = NULL;
|
||||
$language = NULL;
|
||||
preg_match('/^(.*)_([a-z-]*)$/', $bundle, $matches);
|
||||
if (count($matches) == 3) {
|
||||
list(, $extracted_bundle, $langcode) = $matches;
|
||||
$language = $language_manager->getLanguage($langcode);
|
||||
}
|
||||
// Validate bundle, langcode and language.
|
||||
if (!isset($bundle_info[$extracted_bundle]) || ($langcode == NULL) || ($language == NULL)) {
|
||||
$messages[] = t('Unrecognized entity bundle @entity:@bundle was not processed. It defines the following patterns: @patterns', array(
|
||||
'@entity' => $entity_type,
|
||||
'@bundle' => $bundle,
|
||||
'@patterns' => print_r($entity_patterns, TRUE),
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a pattern for a bundle and a language, such as "node_article_es".
|
||||
$pattern = PathautoPattern::create([
|
||||
'id' => $entity_type . '_' . $extracted_bundle . '_' . str_replace('-', '_', $langcode),
|
||||
'label' => $entity_label . ' ' . $bundle_info[$extracted_bundle]['label'] . ' ' . $language->getName(),
|
||||
'type' => 'canonical_entities:' . $entity_type,
|
||||
'pattern' => $bundle_patterns['default'],
|
||||
'weight' => -10,
|
||||
]);
|
||||
|
||||
// Add the bundle condition.
|
||||
$pattern->addSelectionCondition([
|
||||
'id' => 'entity_bundle:' . $entity_type,
|
||||
'bundles' => array($extracted_bundle => $extracted_bundle),
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [ $entity_type => $entity_type ],
|
||||
]);
|
||||
|
||||
// Add the language condition.
|
||||
$language_mapping = $entity_type . ':' . $entity_type_manager->getDefinition($entity_type)->getKey('langcode') . ':language';
|
||||
$pattern->addSelectionCondition([
|
||||
'id' => 'language',
|
||||
'langcodes' => [ $langcode => $langcode ],
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
'language' => $language_mapping,
|
||||
]
|
||||
]);
|
||||
|
||||
// Add the context relationship for this language.
|
||||
$pattern->addRelationship($language_mapping, 'Language');
|
||||
|
||||
$pattern->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Delete the old configuration object that stores patterns.
|
||||
$config->delete();
|
||||
|
||||
// 4. Print out messages.
|
||||
if (!empty($messages)) {
|
||||
return implode('</br>', $messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update relationship storage.
|
||||
*/
|
||||
function pathauto_update_8101() {
|
||||
foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
|
||||
$pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
|
||||
|
||||
$relationships = [];
|
||||
foreach ((array) $pattern_config->get('context_definitions') as $context_definition) {
|
||||
$relationships[$context_definition['id']] = ['label' => $context_definition['label']];
|
||||
}
|
||||
|
||||
$pattern_config->clear('context_definitions');
|
||||
$pattern_config->set('relationships', $relationships);
|
||||
$pattern_config->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update node type conditions from entity_bundle to node_type.
|
||||
*/
|
||||
function pathauto_update_8102() {
|
||||
// Load all pattern configuration entities.
|
||||
foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
|
||||
$pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
|
||||
|
||||
// Loop patterns and swap the entity_bundle:node plugin by the node_type
|
||||
// plugin.
|
||||
if ($pattern_config->get('type') == 'canonical_entities:node') {
|
||||
$selection_criteria = $pattern_config->get('selection_criteria');
|
||||
foreach ($selection_criteria as $uuid => $condition) {
|
||||
if ($condition['id'] == 'entity_bundle:node') {
|
||||
$selection_criteria[$uuid]['id'] = 'node_type';
|
||||
$pattern_config->set('selection_criteria', $selection_criteria);
|
||||
$pattern_config->save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix invalid default value for ignore_words.
|
||||
*/
|
||||
function pathauto_update_8103() {
|
||||
$config_factory = \Drupal::configFactory();
|
||||
$config = $config_factory->getEditable('pathauto.settings');
|
||||
$ignore_words = $config->get('ignore_words');
|
||||
if ($ignore_words === ', in, is,that, the , this, with, ') {
|
||||
$config->set('ignore_words', 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with')->save(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resave patterns so that lookup keys are updated.
|
||||
*/
|
||||
function pathauto_update_8104() {
|
||||
\Drupal::entityTypeManager()->clearCachedDefinitions();
|
||||
// Load all pattern configuration entities and save them, so that the new
|
||||
// status lookup keys are saved.
|
||||
foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
|
||||
$pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
|
||||
$pattern_config->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the url_alias table exists.
|
||||
*/
|
||||
function pathauto_update_8105() {
|
||||
_pathauto_ensure_url_alias_table_exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update default configuration for enabled entity types.
|
||||
*/
|
||||
function pathauto_update_8106() {
|
||||
$config_factory = \Drupal::configFactory();
|
||||
$config = $config_factory->getEditable('pathauto.settings');
|
||||
$config->set('enabled_entity_types', ['user']);
|
||||
$config->save();
|
||||
}
|
21
web/modules/contrib/pathauto/pathauto.js
Normal file
21
web/modules/contrib/pathauto/pathauto.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
(function ($) {
|
||||
'use strict';
|
||||
Drupal.behaviors.pathFieldsetSummaries = {
|
||||
attach: function (context) {
|
||||
$('fieldset.path-form', context).drupalSetSummary(function (context) {
|
||||
var path = $('.form-item-path-alias input', context).val();
|
||||
var automatic = $('.form-item-path-pathauto input', context).attr('checked');
|
||||
|
||||
if (automatic) {
|
||||
return Drupal.t('Automatic alias');
|
||||
}
|
||||
else if (path) {
|
||||
return Drupal.t('Alias: @alias', {'@alias': path});
|
||||
}
|
||||
else {
|
||||
return Drupal.t('No alias');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
9
web/modules/contrib/pathauto/pathauto.libraries.yml
Normal file
9
web/modules/contrib/pathauto/pathauto.libraries.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
widget:
|
||||
version: 1.0
|
||||
js:
|
||||
pathauto.js: {}
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/drupal.form
|
6
web/modules/contrib/pathauto/pathauto.links.action.yml
Normal file
6
web/modules/contrib/pathauto/pathauto.links.action.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
entity.pathauto_pattern.add_form:
|
||||
route_name: 'entity.pathauto_pattern.add_form'
|
||||
title: 'Add Pathauto pattern'
|
||||
appears_on:
|
||||
- entity.pathauto_pattern.collection
|
||||
|
23
web/modules/contrib/pathauto/pathauto.links.task.yml
Normal file
23
web/modules/contrib/pathauto/pathauto.links.task.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
pathauto.patterns.form:
|
||||
route_name: entity.pathauto_pattern.collection
|
||||
base_route: path.admin_overview
|
||||
title: 'Patterns'
|
||||
weight: 10
|
||||
|
||||
pathauto.settings.form:
|
||||
route_name: pathauto.settings.form
|
||||
base_route: path.admin_overview
|
||||
title: 'Settings'
|
||||
weight: 20
|
||||
|
||||
pathauto.bulk.update.form:
|
||||
route_name: pathauto.bulk.update.form
|
||||
base_route: path.admin_overview
|
||||
title: 'Bulk generate'
|
||||
weight: 30
|
||||
|
||||
pathauto.admin.delete:
|
||||
route_name: pathauto.admin.delete
|
||||
base_route: path.admin_overview
|
||||
title: 'Delete aliases'
|
||||
weight: 40
|
188
web/modules/contrib/pathauto/pathauto.module
Normal file
188
web/modules/contrib/pathauto/pathauto.module
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @defgroup pathauto Pathauto: Automatically generates aliases for content
|
||||
*
|
||||
* The Pathauto module automatically generates path aliases for various kinds of
|
||||
* content (nodes, categories, users) without requiring the user to manually
|
||||
* specify the path alias. This allows you to get aliases like
|
||||
* /category/my-node-title.html instead of /node/123. The aliases are based upon
|
||||
* a "pattern" system which the administrator can control.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Main file for the Pathauto module, which automatically generates aliases for content.
|
||||
*
|
||||
* @ingroup pathauto
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
|
||||
/**
|
||||
* The default ignore word list.
|
||||
*/
|
||||
define('PATHAUTO_IGNORE_WORDS', 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with');
|
||||
|
||||
/**
|
||||
* Implements hook_hook_info().
|
||||
*/
|
||||
function pathauto_hook_info() {
|
||||
$hooks = array(
|
||||
'pathauto_pattern_alter',
|
||||
'pathauto_alias_alter',
|
||||
'pathauto_is_alias_reserved',
|
||||
);
|
||||
return array_fill_keys($hooks, array('group' => 'pathauto'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function pathauto_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.pathauto':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Pathauto module provides a mechanism to automate the creation of <a href="path">path</a> aliases. This makes URLs more readable and helps search engines index content more effectively. For more information, see the <a href=":online">online documentation for Pathauto</a>.', [':online' => 'https://www.drupal.org/documentation/modules/pathauto']) . '</p>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dd>' . t('Pathauto is accessed from the tabs it adds to the list of <a href=":aliases">URL aliases</a>.', [':aliases' => Url::fromRoute('path.admin_overview')->toString()]) . '</dd>';
|
||||
$output .= '<dt>' . t('Creating Pathauto Patterns') . '</dt>';
|
||||
$output .= '<dd>' . t('The <a href=":pathauto_pattern">"Patterns"</a> page is used to configure automatic path aliasing. New patterns are created here using the <a href=":add_form">Add Pathauto pattern</a> button which presents a form to simplify pattern creation thru the use of <a href="token">available tokens</a>. The patterns page provides a list of all patterns on the site and allows you to edit and reorder them. An alias is generated for the first pattern that applies.', [':pathauto_pattern' => Url::fromRoute('entity.pathauto_pattern.collection')->toString(), ':add_form' => Url::fromRoute('entity.pathauto_pattern.add_form')->toString()]) . '</dd>';
|
||||
$output .= '<dt>' . t('Pathauto Settings') . '</dt>';
|
||||
$output .= '<dd>' . t('The <a href=":settings">"Settings"</a> page is used to customize global Pathauto settings for automated pattern creation.', [':settings' => Url::fromRoute('pathauto.settings.form')->toString()]) . '</dd>';
|
||||
$output .= '<dd>' . t('The <strong>maximum alias length</strong> and <strong>maximum component length</strong> values default to 100 and have a limit of @max from Pathauto. You should enter a value that is the length of the "alias" column of the url_alias database table minus the length of any strings that might get added to the end of the URL. The recommended and default value is 100.', array('@max' => \Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength())) . '</dd>';
|
||||
$output .= '<dt>' . t('Bulk Generation') . '</dt>';
|
||||
$output .= '<dd>' . t('The <a href=":pathauto_bulk">"Bulk Generate"</a> page allows you to create URL aliases for items that currently have no aliases. This is typically used when installing Pathauto on a site that has existing un-aliased content that needs to be aliased in bulk.', [':pathauto_bulk' => Url::fromRoute('pathauto.bulk.update.form')->toString()]) . '</dd>';
|
||||
$output .= '<dt>' . t('Delete Aliases') . '</dt>';
|
||||
$output .= '<dd>' . t('The <a href=":pathauto_delete">"Delete Aliases"</a> page allows you to remove URL aliases from items that have previously been assigned aliases using pathauto.', [':pathauto_delete' => Url::fromRoute('pathauto.admin.delete')->toString()]) . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
|
||||
case 'entity.pathauto_pattern.collection':
|
||||
$output = '<p>' . t('This page provides a list of all patterns on the site and allows you to edit and reorder them.') . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'entity.pathauto_pattern.add_form':
|
||||
$output = '<p>' . t('You need to select a pattern type, then a pattern and filter, and a label. Additional types can be enabled on the <a href=":settings">Settings</a> page.', [':settings' => Url::fromRoute('pathauto.settings.form')->toString()]) . '</p>';
|
||||
return $output;
|
||||
|
||||
case 'pathauto.bulk.update.form':
|
||||
$output = '<p>' . t('Bulk generation can be used to generate URL aliases for items that currently have no aliases. This is typically used when installing Pathauto on a site that has existing un-aliased content that needs to be aliased in bulk.') . '<br>';
|
||||
$output .= t('It can also be used to regenerate URL aliases for items that have an old alias and for which the Pathauto pattern has been changed.') . '</p>';
|
||||
$output .= '<p>' . t('Note that this will only affect items which are configured to have their URL alias automatically set. Items whose URL alias is manually set are not affected.') . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_insert().
|
||||
*/
|
||||
function pathauto_entity_insert(EntityInterface $entity) {
|
||||
\Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'insert');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_update().
|
||||
*/
|
||||
function pathauto_entity_update(EntityInterface $entity) {
|
||||
\Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_update().
|
||||
*/
|
||||
function pathauto_entity_delete(EntityInterface $entity) {
|
||||
if ($entity->hasLinkTemplate('canonical') && $entity instanceof ContentEntityInterface && $entity->hasField('path')) {
|
||||
\Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($entity);
|
||||
$entity->path->first()->get('pathauto')->purge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_info_alter().
|
||||
*/
|
||||
function pathauto_field_info_alter(&$info) {
|
||||
$info['path']['class'] = '\Drupal\pathauto\PathautoItem';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_info_alter().
|
||||
*/
|
||||
function pathauto_field_widget_info_alter(&$widgets) {
|
||||
$widgets['path']['class'] = 'Drupal\pathauto\PathautoWidget';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
function pathauto_entity_base_field_info(EntityTypeInterface $entity_type) {
|
||||
$config = \Drupal::config('pathauto.settings');
|
||||
// Verify that the configuration data isn't null (as is the case before the
|
||||
// module's initialization, in tests), so that in_array() won't fail.
|
||||
if ($enabled_entity_types = $config->get('enabled_entity_types')) {
|
||||
if (in_array($entity_type->id(), $enabled_entity_types)) {
|
||||
$fields['path'] = BaseFieldDefinition::create('path')
|
||||
->setCustomStorage(TRUE)
|
||||
->setLabel(t('URL alias'))
|
||||
->setTranslatable(TRUE)
|
||||
->setComputed(TRUE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'path',
|
||||
'weight' => 30,
|
||||
))
|
||||
->setDisplayConfigurable('form', TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info_alter().
|
||||
*/
|
||||
function pathauto_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
|
||||
if (isset($fields['path'])) {
|
||||
// Path fields need to be computed so that the pathauto state can be
|
||||
// accessed even if there is no alias being set.
|
||||
$fields['path']->setComputed(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the pattern field, to ensure it doesn't contain any characters that
|
||||
* are invalid in URLs.
|
||||
*/
|
||||
function pathauto_pattern_validate($element, FormStateInterface $form_state) {
|
||||
|
||||
if (isset($element['#value'])) {
|
||||
$title = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];
|
||||
$invalid_characters = ['#', '?', '&'];
|
||||
$invalid_characters_used = [];
|
||||
|
||||
foreach ($invalid_characters as $invalid_character) {
|
||||
if (strpos($element['#value'], $invalid_character) !== FALSE) {
|
||||
$invalid_characters_used[] = $invalid_character;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($invalid_characters_used)) {
|
||||
$form_state->setError($element, t('The %element-title is using the following invalid characters: @invalid-characters.', array('%element-title' => $title, '@invalid-characters' => implode(', ', $invalid_characters_used))));
|
||||
}
|
||||
|
||||
if (preg_match('/(\s$)+/', $element['#value'])) {
|
||||
$form_state->setError($element, t('The %element-title doesn\'t allow the patterns ending with whitespace.', array('%element-title' => $title)));
|
||||
}
|
||||
}
|
||||
|
||||
return $element;
|
||||
|
||||
}
|
6
web/modules/contrib/pathauto/pathauto.permissions.yml
Normal file
6
web/modules/contrib/pathauto/pathauto.permissions.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
administer pathauto:
|
||||
title: 'Administer pathauto'
|
||||
description: 'Allows a user to configure patterns for automated aliases and bulk delete URL-aliases.'
|
||||
notify of path changes:
|
||||
title: 'Notify of Path Changes'
|
||||
description: 'Determines whether or not users are notified.'
|
54
web/modules/contrib/pathauto/pathauto.routing.yml
Normal file
54
web/modules/contrib/pathauto/pathauto.routing.yml
Normal file
|
@ -0,0 +1,54 @@
|
|||
entity.pathauto_pattern.collection:
|
||||
path: '/admin/config/search/path/patterns'
|
||||
defaults:
|
||||
_entity_list: 'pathauto_pattern'
|
||||
_title: 'Patterns'
|
||||
requirements:
|
||||
_permission: 'administer pathauto'
|
||||
|
||||
entity.pathauto_pattern.add_form:
|
||||
path: '/admin/config/search/path/patterns/add'
|
||||
defaults:
|
||||
_entity_form: 'pathauto_pattern.default'
|
||||
_title: 'Add Pathauto pattern'
|
||||
tempstore_id: 'pathauto.pattern'
|
||||
requirements:
|
||||
_permission: 'administer pathauto'
|
||||
|
||||
pathauto.settings.form:
|
||||
path: '/admin/config/search/path/settings'
|
||||
defaults:
|
||||
_form: '\Drupal\pathauto\Form\PathautoSettingsForm'
|
||||
_title: 'Settings'
|
||||
requirements:
|
||||
_permission: 'administer pathauto'
|
||||
|
||||
entity.pathauto_pattern.enable:
|
||||
path: '/admin/config/search/path/patterns/{pathauto_pattern}/enable'
|
||||
defaults:
|
||||
_entity_form: 'pathauto_pattern.enable'
|
||||
requirements:
|
||||
_entity_access: 'pathauto_pattern.update'
|
||||
|
||||
entity.pathauto_pattern.disable:
|
||||
path: '/admin/config/search/path/patterns/{pathauto_pattern}/disable'
|
||||
defaults:
|
||||
_entity_form: 'pathauto_pattern.disable'
|
||||
requirements:
|
||||
_entity_access: 'pathauto_pattern.update'
|
||||
|
||||
pathauto.bulk.update.form:
|
||||
path: '/admin/config/search/path/update_bulk'
|
||||
defaults:
|
||||
_form: '\Drupal\pathauto\Form\PathautoBulkUpdateForm'
|
||||
_title: 'Bulk generate'
|
||||
requirements:
|
||||
_permission: 'administer url aliases'
|
||||
|
||||
pathauto.admin.delete:
|
||||
path: '/admin/config/search/path/delete_bulk'
|
||||
defaults:
|
||||
_form: '\Drupal\pathauto\Form\PathautoAdminDelete'
|
||||
_title: 'Delete aliases'
|
||||
requirements:
|
||||
_permission: 'administer url aliases'
|
26
web/modules/contrib/pathauto/pathauto.services.yml
Normal file
26
web/modules/contrib/pathauto/pathauto.services.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
services:
|
||||
pathauto.generator:
|
||||
class: Drupal\pathauto\PathautoGenerator
|
||||
arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation', '@token.entity_mapper', '@entity_type.manager']
|
||||
pathauto.alias_cleaner:
|
||||
class: Drupal\pathauto\AliasCleaner
|
||||
arguments: ['@config.factory', '@pathauto.alias_storage_helper', '@language_manager', '@cache.discovery', '@transliteration', '@module_handler']
|
||||
pathauto.alias_storage_helper:
|
||||
class: Drupal\pathauto\AliasStorageHelper
|
||||
arguments: ['@config.factory', '@path.alias_storage', '@database','@pathauto.verbose_messenger', '@string_translation']
|
||||
tags:
|
||||
- { name: backend_overridable }
|
||||
pathauto.alias_uniquifier:
|
||||
class: Drupal\pathauto\AliasUniquifier
|
||||
arguments: ['@config.factory', '@pathauto.alias_storage_helper','@module_handler', '@router.route_provider', '@path.alias_manager']
|
||||
pathauto.verbose_messenger:
|
||||
class: Drupal\pathauto\VerboseMessenger
|
||||
arguments: ['@config.factory', '@current_user']
|
||||
plugin.manager.alias_type:
|
||||
class: Drupal\pathauto\AliasTypeManager
|
||||
parent: default_plugin_manager
|
||||
pathauto.settings_cache_tag:
|
||||
class: Drupal\pathauto\EventSubscriber\PathautoSettingsCacheTag
|
||||
arguments: ['@entity_field.manager', '@plugin.manager.alias_type']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
49
web/modules/contrib/pathauto/pathauto.tokens.inc
Normal file
49
web/modules/contrib/pathauto/pathauto.tokens.inc
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Token integration for the Pathauto module.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function pathauto_token_info() {
|
||||
$info = array();
|
||||
|
||||
$info['tokens']['array']['join-path'] = array(
|
||||
'name' => t('Joined path'),
|
||||
'description' => t('The array values each cleaned by Pathauto and then joined with the slash into a string that resembles an URL.'),
|
||||
);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function pathauto_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'array' && !empty($data['array'])) {
|
||||
$array = $data['array'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'join-path':
|
||||
$values = array();
|
||||
foreach (token_element_children($array) as $key) {
|
||||
$value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key];
|
||||
$value = \Drupal::service('pathauto.alias_cleaner')->cleanString($value, $options);
|
||||
$values[] = $value;
|
||||
}
|
||||
$replacements[$original] = implode('/', $values);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
351
web/modules/contrib/pathauto/src/AliasCleaner.php
Normal file
351
web/modules/contrib/pathauto/src/AliasCleaner.php
Normal file
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Component\Render\PlainTextOutput;
|
||||
use Drupal\Component\Transliteration\TransliterationInterface;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides an alias cleaner.
|
||||
*/
|
||||
class AliasCleaner implements AliasCleanerInterface {
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The alias storage helper.
|
||||
*
|
||||
* @var AliasStorageHelperInterface
|
||||
*/
|
||||
protected $aliasStorageHelper;
|
||||
|
||||
/**
|
||||
* Language manager.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* Calculated settings cache.
|
||||
*
|
||||
* @todo Split this up into separate properties.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cleanStringCache = array();
|
||||
|
||||
/**
|
||||
* Transliteration service.
|
||||
*
|
||||
* @var \Drupal\Component\Transliteration\TransliterationInterface
|
||||
*/
|
||||
protected $transliteration;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Creates a new AliasCleaner.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
|
||||
* The alias storage helper.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
|
||||
* The transliteration service.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, LanguageManagerInterface $language_manager, CacheBackendInterface $cache_backend, TransliterationInterface $transliteration, ModuleHandlerInterface $module_handler) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->aliasStorageHelper = $alias_storage_helper;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->cacheBackend = $cache_backend;
|
||||
$this->transliteration = $transliteration;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanAlias($alias) {
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
$alias_max_length = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength());
|
||||
|
||||
$output = $alias;
|
||||
|
||||
// Trim duplicate, leading, and trailing separators. Do this before cleaning
|
||||
// backslashes since a pattern like "[token1]/[token2]-[token3]/[token4]"
|
||||
// could end up like "value1/-/value2" and if backslashes were cleaned first
|
||||
// this would result in a duplicate blackslash.
|
||||
$output = $this->getCleanSeparators($output);
|
||||
|
||||
// Trim duplicate, leading, and trailing backslashes.
|
||||
$output = $this->getCleanSeparators($output, '/');
|
||||
|
||||
// Shorten to a logical place based on word boundaries.
|
||||
$output = Unicode::truncate($output, $alias_max_length, TRUE);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCleanSeparators($string, $separator = NULL) {
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
|
||||
if (!isset($separator)) {
|
||||
$separator = $config->get('separator');
|
||||
}
|
||||
|
||||
$output = $string;
|
||||
|
||||
if (strlen($separator)) {
|
||||
// Trim any leading or trailing separators.
|
||||
$output = trim($output, $separator);
|
||||
|
||||
// Escape the separator for use in regular expressions.
|
||||
$seppattern = preg_quote($separator, '/');
|
||||
|
||||
// Replace multiple separators with a single one.
|
||||
$output = preg_replace("/$seppattern+/", $separator, $output);
|
||||
|
||||
// Replace trailing separators around slashes.
|
||||
if ($separator !== '/') {
|
||||
$output = preg_replace("/\/+$seppattern\/+|$seppattern\/+|\/+$seppattern/", "/", $output);
|
||||
}
|
||||
else {
|
||||
// If the separator is a slash, we need to re-add the leading slash
|
||||
// dropped by the trim function.
|
||||
$output = '/' . $output;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanString($string, array $options = array()) {
|
||||
if (empty($this->cleanStringCache)) {
|
||||
// Generate and cache variables used in this method.
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
$this->cleanStringCache = array(
|
||||
'separator' => $config->get('separator'),
|
||||
'strings' => array(),
|
||||
'transliterate' => $config->get('transliterate'),
|
||||
'punctuation' => array(),
|
||||
'reduce_ascii' => (bool) $config->get('reduce_ascii'),
|
||||
'ignore_words_regex' => FALSE,
|
||||
'lowercase' => (bool) $config->get('case'),
|
||||
'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()),
|
||||
);
|
||||
|
||||
// Generate and cache the punctuation replacements for strtr().
|
||||
$punctuation = $this->getPunctuationCharacters();
|
||||
foreach ($punctuation as $name => $details) {
|
||||
$action = $config->get('punctuation.' . $name);
|
||||
switch ($action) {
|
||||
case PathautoGeneratorInterface::PUNCTUATION_REMOVE:
|
||||
$this->cleanStringCache['punctuation'][$details['value']] = '';
|
||||
break;
|
||||
|
||||
case PathautoGeneratorInterface::PUNCTUATION_REPLACE:
|
||||
$this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator'];
|
||||
break;
|
||||
|
||||
case PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING:
|
||||
// Literally do nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and cache the ignored words regular expression.
|
||||
$ignore_words = $config->get('ignore_words');
|
||||
$ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words);
|
||||
if ($ignore_words_regex) {
|
||||
$this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b';
|
||||
if (function_exists('mb_eregi_replace')) {
|
||||
mb_regex_encoding('UTF-8');
|
||||
$this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace';
|
||||
}
|
||||
else {
|
||||
$this->cleanStringCache['ignore_words_callback'] = 'preg_replace';
|
||||
$this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty strings do not need any processing.
|
||||
if ($string === '' || $string === NULL) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$langcode = NULL;
|
||||
if (!empty($options['language'])) {
|
||||
$langcode = $options['language']->getId();
|
||||
}
|
||||
elseif (!empty($options['langcode'])) {
|
||||
$langcode = $options['langcode'];
|
||||
}
|
||||
|
||||
// Check if the string has already been processed, and if so return the
|
||||
// cached result.
|
||||
if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) {
|
||||
return $this->cleanStringCache['strings'][$langcode][(string) $string];
|
||||
}
|
||||
|
||||
// Remove all HTML tags from the string.
|
||||
$output = Html::decodeEntities($string);
|
||||
$output = PlainTextOutput::renderFromHtml($output);
|
||||
|
||||
// Optionally transliterate.
|
||||
if ($this->cleanStringCache['transliterate']) {
|
||||
// If the reduce strings to letters and numbers is enabled, don't bother
|
||||
// replacing unknown characters with a question mark. Use an empty string
|
||||
// instead.
|
||||
$output = $this->transliteration->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?');
|
||||
}
|
||||
|
||||
// Replace or drop punctuation based on user settings.
|
||||
$output = strtr($output, $this->cleanStringCache['punctuation']);
|
||||
|
||||
// Reduce strings to letters and numbers.
|
||||
if ($this->cleanStringCache['reduce_ascii']) {
|
||||
$output = preg_replace('/[^a-zA-Z0-9\/]+/', $this->cleanStringCache['separator'], $output);
|
||||
}
|
||||
|
||||
// Get rid of words that are on the ignore list.
|
||||
if ($this->cleanStringCache['ignore_words_regex']) {
|
||||
$words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output);
|
||||
if (Unicode::strlen(trim($words_removed)) > 0) {
|
||||
$output = $words_removed;
|
||||
}
|
||||
}
|
||||
|
||||
// Always replace whitespace with the separator.
|
||||
$output = preg_replace('/\s+/', $this->cleanStringCache['separator'], $output);
|
||||
|
||||
// Trim duplicates and remove trailing and leading separators.
|
||||
$output = $this->getCleanSeparators($this->getCleanSeparators($output, $this->cleanStringCache['separator']));
|
||||
|
||||
// Optionally convert to lower case.
|
||||
if ($this->cleanStringCache['lowercase']) {
|
||||
$output = Unicode::strtolower($output);
|
||||
}
|
||||
|
||||
// Shorten to a logical place based on word boundaries.
|
||||
$output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE);
|
||||
|
||||
// Cache this result in the static array.
|
||||
$this->cleanStringCache['strings'][$langcode][(string) $string] = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPunctuationCharacters() {
|
||||
if (empty($this->punctuationCharacters)) {
|
||||
$langcode = $this->languageManager->getCurrentLanguage()->getId();
|
||||
|
||||
$cid = 'pathauto:punctuation:' . $langcode;
|
||||
if ($cache = $this->cacheBackend->get($cid)) {
|
||||
$this->punctuationCharacters = $cache->data;
|
||||
}
|
||||
else {
|
||||
$punctuation = array();
|
||||
$punctuation['double_quotes'] = array('value' => '"', 'name' => t('Double quotation marks'));
|
||||
$punctuation['quotes'] = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)"));
|
||||
$punctuation['backtick'] = array('value' => '`', 'name' => t('Back tick'));
|
||||
$punctuation['comma'] = array('value' => ',', 'name' => t('Comma'));
|
||||
$punctuation['period'] = array('value' => '.', 'name' => t('Period'));
|
||||
$punctuation['hyphen'] = array('value' => '-', 'name' => t('Hyphen'));
|
||||
$punctuation['underscore'] = array('value' => '_', 'name' => t('Underscore'));
|
||||
$punctuation['colon'] = array('value' => ':', 'name' => t('Colon'));
|
||||
$punctuation['semicolon'] = array('value' => ';', 'name' => t('Semicolon'));
|
||||
$punctuation['pipe'] = array('value' => '|', 'name' => t('Vertical bar (pipe)'));
|
||||
$punctuation['left_curly'] = array('value' => '{', 'name' => t('Left curly bracket'));
|
||||
$punctuation['left_square'] = array('value' => '[', 'name' => t('Left square bracket'));
|
||||
$punctuation['right_curly'] = array('value' => '}', 'name' => t('Right curly bracket'));
|
||||
$punctuation['right_square'] = array('value' => ']', 'name' => t('Right square bracket'));
|
||||
$punctuation['plus'] = array('value' => '+', 'name' => t('Plus sign'));
|
||||
$punctuation['equal'] = array('value' => '=', 'name' => t('Equal sign'));
|
||||
$punctuation['asterisk'] = array('value' => '*', 'name' => t('Asterisk'));
|
||||
$punctuation['ampersand'] = array('value' => '&', 'name' => t('Ampersand'));
|
||||
$punctuation['percent'] = array('value' => '%', 'name' => t('Percent sign'));
|
||||
$punctuation['caret'] = array('value' => '^', 'name' => t('Caret'));
|
||||
$punctuation['dollar'] = array('value' => '$', 'name' => t('Dollar sign'));
|
||||
$punctuation['hash'] = array('value' => '#', 'name' => t('Number sign (pound sign, hash)'));
|
||||
$punctuation['at'] = array('value' => '@', 'name' => t('At sign'));
|
||||
$punctuation['exclamation'] = array('value' => '!', 'name' => t('Exclamation mark'));
|
||||
$punctuation['tilde'] = array('value' => '~', 'name' => t('Tilde'));
|
||||
$punctuation['left_parenthesis'] = array('value' => '(', 'name' => t('Left parenthesis'));
|
||||
$punctuation['right_parenthesis'] = array('value' => ')', 'name' => t('Right parenthesis'));
|
||||
$punctuation['question_mark'] = array('value' => '?', 'name' => t('Question mark'));
|
||||
$punctuation['less_than'] = array('value' => '<', 'name' => t('Less-than sign'));
|
||||
$punctuation['greater_than'] = array('value' => '>', 'name' => t('Greater-than sign'));
|
||||
$punctuation['slash'] = array('value' => '/', 'name' => t('Slash'));
|
||||
$punctuation['back_slash'] = array('value' => '\\', 'name' => t('Backslash'));
|
||||
|
||||
// Allow modules to alter the punctuation list and cache the result.
|
||||
$this->moduleHandler->alter('pathauto_punctuation_chars', $punctuation);
|
||||
$this->cacheBackend->set($cid, $punctuation);
|
||||
$this->punctuationCharacters = $punctuation;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->punctuationCharacters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function cleanTokenValues(&$replacements, $data = array(), $options = array()) {
|
||||
foreach ($replacements as $token => $value) {
|
||||
// Only clean non-path tokens.
|
||||
if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) {
|
||||
$replacements[$token] = $this->cleanString($value, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCaches() {
|
||||
$this->cleanStringCache = array();
|
||||
}
|
||||
|
||||
}
|
103
web/modules/contrib/pathauto/src/AliasCleanerInterface.php
Normal file
103
web/modules/contrib/pathauto/src/AliasCleanerInterface.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
/**
|
||||
* @todo add class comment.
|
||||
*/
|
||||
interface AliasCleanerInterface {
|
||||
|
||||
/**
|
||||
* Clean up an URL alias.
|
||||
*
|
||||
* Performs the following alterations:
|
||||
* - Trim duplicate, leading, and trailing back-slashes.
|
||||
* - Trim duplicate, leading, and trailing separators.
|
||||
* - Shorten to a desired length and logical position based on word boundaries.
|
||||
*
|
||||
* @param string $alias
|
||||
* A string with the URL alias to clean up.
|
||||
*
|
||||
* @return string
|
||||
* The cleaned URL alias.
|
||||
*/
|
||||
public function cleanAlias($alias);
|
||||
|
||||
/**
|
||||
* Trims duplicate, leading, and trailing separators from a string.
|
||||
*
|
||||
* @param string $string
|
||||
* The string to clean path separators from.
|
||||
* @param string $separator
|
||||
* The path separator to use when cleaning.
|
||||
*
|
||||
* @return string
|
||||
* The cleaned version of the string.
|
||||
*
|
||||
* @see pathauto_cleanstring()
|
||||
* @see pathauto_clean_alias()
|
||||
*/
|
||||
public function getCleanSeparators($string, $separator = NULL);
|
||||
|
||||
/**
|
||||
* Clean up a string segment to be used in an URL alias.
|
||||
*
|
||||
* Performs the following possible alterations:
|
||||
* - Remove all HTML tags.
|
||||
* - Process the string through the transliteration module.
|
||||
* - Replace or remove punctuation with the separator character.
|
||||
* - Remove back-slashes.
|
||||
* - Replace non-ascii and non-numeric characters with the separator.
|
||||
* - Remove common words.
|
||||
* - Replace whitespace with the separator character.
|
||||
* - Trim duplicate, leading, and trailing separators.
|
||||
* - Convert to lower-case.
|
||||
* - Shorten to a desired length and logical position based on word boundaries.
|
||||
*
|
||||
* This function should *not* be called on URL alias or path strings
|
||||
* because it is assumed that they are already clean.
|
||||
*
|
||||
* @param string $string
|
||||
* A string to clean.
|
||||
* @param array $options
|
||||
* (optional) A keyed array of settings and flags to control the Pathauto
|
||||
* clean string replacement process. Supported options are:
|
||||
* - langcode: A language code to be used when translating strings.
|
||||
*
|
||||
* @return string
|
||||
* The cleaned string.
|
||||
*/
|
||||
public function cleanString($string, array $options = array());
|
||||
|
||||
/**
|
||||
* Return an array of arrays for punctuation values.
|
||||
*
|
||||
* Returns an array of arrays for punctuation values keyed by a name, including
|
||||
* the value and a textual description.
|
||||
* Can and should be expanded to include "all" non text punctuation values.
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays for punctuation values keyed by a name, including the
|
||||
* value and a textual description.
|
||||
*/
|
||||
public function getPunctuationCharacters();
|
||||
|
||||
/**
|
||||
* Clean tokens so they are URL friendly.
|
||||
*
|
||||
* @param array $replacements
|
||||
* An array of token replacements
|
||||
* that need to be "cleaned" for use in the URL.
|
||||
* @param array $data
|
||||
* An array of objects used to generate the replacements.
|
||||
* @param array $options
|
||||
* An array of options used to generate the replacements.
|
||||
*/
|
||||
public function cleanTokenValues(&$replacements, $data = array(), $options = array());
|
||||
|
||||
/**
|
||||
* Resets internal caches.
|
||||
*/
|
||||
public function resetCaches();
|
||||
|
||||
}
|
255
web/modules/contrib/pathauto/src/AliasStorageHelper.php
Normal file
255
web/modules/contrib/pathauto/src/AliasStorageHelper.php
Normal file
|
@ -0,0 +1,255 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Path\AliasStorageInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Provides helper methods for accessing alias storage.
|
||||
*/
|
||||
class AliasStorageHelper implements AliasStorageHelperInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Alias schema max length.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $aliasSchemaMaxLength = 255;
|
||||
|
||||
/**
|
||||
* Config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The alias storage.
|
||||
*
|
||||
* @var \Drupal\Core\Path\AliasStorageInterface
|
||||
*/
|
||||
protected $aliasStorage;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The messenger.
|
||||
*
|
||||
* @var \Drupal\pathauto\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
|
||||
* The alias storage.
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
* @param MessengerInterface $messenger
|
||||
* The messenger.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, AliasStorageInterface $alias_storage, Connection $database, MessengerInterface $messenger, TranslationInterface $string_translation) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->aliasStorage = $alias_storage;
|
||||
$this->database = $database;
|
||||
$this->messenger = $messenger;
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAliasSchemaMaxLength() {
|
||||
return $this->aliasSchemaMaxLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $path, $existing_alias = NULL, $op = NULL) {
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
|
||||
// Alert users if they are trying to create an alias that is the same as the
|
||||
// internal path.
|
||||
if ($path['source'] == $path['alias']) {
|
||||
$this->messenger->addMessage($this->t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias'])));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Skip replacing the current alias with an identical alias.
|
||||
if (empty($existing_alias) || $existing_alias['alias'] != $path['alias']) {
|
||||
$path += array(
|
||||
'pathauto' => TRUE,
|
||||
'original' => $existing_alias,
|
||||
'pid' => NULL,
|
||||
);
|
||||
|
||||
// If there is already an alias, respect some update actions.
|
||||
if (!empty($existing_alias)) {
|
||||
switch ($config->get('update_action')) {
|
||||
case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW:
|
||||
// Do not create the alias.
|
||||
return NULL;
|
||||
|
||||
case PathautoGeneratorInterface::UPDATE_ACTION_LEAVE:
|
||||
// Create a new alias instead of overwriting the existing by leaving
|
||||
// $path['pid'] empty.
|
||||
break;
|
||||
|
||||
case PathautoGeneratorInterface::UPDATE_ACTION_DELETE:
|
||||
// The delete actions should overwrite the existing alias.
|
||||
$path['pid'] = $existing_alias['pid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the path array.
|
||||
$this->aliasStorage->save($path['source'], $path['alias'], $path['language'], $path['pid']);
|
||||
|
||||
if (!empty($existing_alias['pid'])) {
|
||||
$this->messenger->addMessage($this->t(
|
||||
'Created new alias %alias for %source, replacing %old_alias.',
|
||||
array(
|
||||
'%alias' => $path['alias'],
|
||||
'%source' => $path['source'],
|
||||
'%old_alias' => $existing_alias['alias'],
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
$this->messenger->addMessage($this->t('Created new alias %alias for %source.', array(
|
||||
'%alias' => $path['alias'],
|
||||
'%source' => $path['source'],
|
||||
)));
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadBySource($source, $language = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
|
||||
$alias = $this->aliasStorage->load([
|
||||
'source' => $source,
|
||||
'langcode' => $language,
|
||||
]);
|
||||
// If no alias was fetched and if a language was specified, fallbacks to
|
||||
// undefined language.
|
||||
if (!$alias && ($language !== LanguageInterface::LANGCODE_NOT_SPECIFIED)) {
|
||||
$alias = $this->aliasStorage->load([
|
||||
'source' => $source,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
}
|
||||
return $alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteBySourcePrefix($source) {
|
||||
$pids = $this->loadBySourcePrefix($source);
|
||||
if ($pids) {
|
||||
$this->deleteMultiple($pids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
$this->database->truncate('url_alias')->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL) {
|
||||
$this->deleteBySourcePrefix('/' . $entity->toUrl('canonical')->getInternalPath());
|
||||
if (isset($default_uri) && $entity->toUrl('canonical')->toString() != $default_uri) {
|
||||
$this->deleteBySourcePrefix($default_uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadBySourcePrefix($source) {
|
||||
$select = $this->database->select('url_alias', 'u')
|
||||
->fields('u', array('pid'));
|
||||
|
||||
$or_group = $select->orConditionGroup()
|
||||
->condition('source', $source)
|
||||
->condition('source', rtrim($source, '/') . '/%', 'LIKE');
|
||||
|
||||
return $select
|
||||
->condition($or_group)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countBySourcePrefix($source) {
|
||||
$select = $this->database->select('url_alias', 'u')
|
||||
->fields('u', array('pid'));
|
||||
|
||||
$or_group = $select->orConditionGroup()
|
||||
->condition('source', $source)
|
||||
->condition('source', rtrim($source, '/') . '/%', 'LIKE');
|
||||
|
||||
return $select
|
||||
->condition($or_group)
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countAll() {
|
||||
return $this->database->select('url_alias')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple URL aliases.
|
||||
*
|
||||
* Intent of this is to abstract a potential path_delete_multiple() function
|
||||
* for Drupal 7 or 8.
|
||||
*
|
||||
* @param int[] $pids
|
||||
* An array of path IDs to delete.
|
||||
*/
|
||||
public function deleteMultiple($pids) {
|
||||
foreach ($pids as $pid) {
|
||||
$this->aliasStorage->delete(array('pid' => $pid));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
117
web/modules/contrib/pathauto/src/AliasStorageHelperInterface.php
Normal file
117
web/modules/contrib/pathauto/src/AliasStorageHelperInterface.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Provides helper methods for accessing alias storage.
|
||||
*/
|
||||
interface AliasStorageHelperInterface {
|
||||
|
||||
/**
|
||||
* Fetch the maximum length of the {url_alias}.alias field from the schema.
|
||||
*
|
||||
* @return int
|
||||
* An integer of the maximum URL alias length allowed by the database.
|
||||
*/
|
||||
public function getAliasSchemaMaxLength();
|
||||
|
||||
/**
|
||||
* Private function for Pathauto to create an alias.
|
||||
*
|
||||
* @param array $path
|
||||
* An associative array containing the following keys:
|
||||
* - source: The internal system path.
|
||||
* - alias: The URL alias.
|
||||
* - pid: (optional) Unique path alias identifier.
|
||||
* - language: (optional) The language of the alias.
|
||||
* @param array|bool|null $existing_alias
|
||||
* (optional) An associative array of the existing path alias.
|
||||
* @param string $op
|
||||
* An optional string with the operation being performed.
|
||||
*
|
||||
* @return array|bool
|
||||
* The saved path or NULL if the path was not saved.
|
||||
*/
|
||||
public function save(array $path, $existing_alias = NULL, $op = NULL);
|
||||
|
||||
/**
|
||||
* Fetches an existing URL alias given a path and optional language.
|
||||
*
|
||||
* @param string $source
|
||||
* An internal Drupal path.
|
||||
* @param string $language
|
||||
* An optional language code to look up the path in.
|
||||
*
|
||||
* @return bool|array
|
||||
* FALSE if no alias was found or an associative array containing the
|
||||
* following keys:
|
||||
* - source (string): The internal system path with a starting slash.
|
||||
* - alias (string): The URL alias with a starting slash.
|
||||
* - pid (int): Unique path alias identifier.
|
||||
* - langcode (string): The language code of the alias.
|
||||
*/
|
||||
public function loadBySource($source, $language = LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
|
||||
/**
|
||||
* Delete all aliases by source url.
|
||||
*
|
||||
* @param string $source
|
||||
* An internal Drupal path.
|
||||
*
|
||||
* @return bool
|
||||
* The URL alias source.
|
||||
*/
|
||||
public function deleteBySourcePrefix($source);
|
||||
|
||||
/**
|
||||
* Delete all aliases (truncate the url_alias table).
|
||||
*/
|
||||
public function deleteAll();
|
||||
|
||||
/**
|
||||
* Delete an entity URL alias and any of its sub-paths.
|
||||
*
|
||||
* This function also checks to see if the default entity URI is different
|
||||
* from the current entity URI and will delete any of the default aliases.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity object.
|
||||
* @param string $default_uri
|
||||
* The optional default uri path for the entity.
|
||||
*/
|
||||
public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL);
|
||||
|
||||
/**
|
||||
* Fetches an existing URL alias given a path prefix.
|
||||
*
|
||||
* @param string $source
|
||||
* An internal Drupal path prefix.
|
||||
*
|
||||
* @return integer[]
|
||||
* An array of PIDs.
|
||||
*/
|
||||
public function loadBySourcePrefix($source);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the count of url aliases for the source.
|
||||
*
|
||||
* @param $source
|
||||
* An internal Drupal path prefix.
|
||||
*
|
||||
* @return int
|
||||
* Number of url aliases for the source.
|
||||
*/
|
||||
public function countBySourcePrefix($source);
|
||||
|
||||
/**
|
||||
* Returns the total count of the url aliases.
|
||||
*
|
||||
* @return int
|
||||
* Total number of aliases.
|
||||
*/
|
||||
public function countAll();
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
/**
|
||||
* Alias types that support batch updates and deletions.
|
||||
*/
|
||||
interface AliasTypeBatchUpdateInterface extends AliasTypeInterface {
|
||||
|
||||
/**
|
||||
* Gets called to batch update all entries.
|
||||
*
|
||||
* @param string $action
|
||||
* One of:
|
||||
* - 'create' to generate a URL alias for paths having none.
|
||||
* - 'update' to recreate the URL alias for paths already having one, useful if the pattern changed.
|
||||
* - 'all' to do both actions above at the same time.
|
||||
* @param array $context
|
||||
* Batch context.
|
||||
*/
|
||||
public function batchUpdate($action, &$context);
|
||||
|
||||
/**
|
||||
* Gets called to batch delete all aliases created by pathauto.
|
||||
*
|
||||
* @param array $context
|
||||
* Batch context.
|
||||
*/
|
||||
public function batchDelete(&$context);
|
||||
|
||||
}
|
48
web/modules/contrib/pathauto/src/AliasTypeInterface.php
Normal file
48
web/modules/contrib/pathauto/src/AliasTypeInterface.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Component\Plugin\DerivativeInspectionInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for pathauto alias types.
|
||||
*/
|
||||
interface AliasTypeInterface extends ContextAwarePluginInterface, DerivativeInspectionInterface {
|
||||
|
||||
/**
|
||||
* Get the label.
|
||||
*
|
||||
* @return string
|
||||
* The label.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
/**
|
||||
* Get the token types.
|
||||
*
|
||||
* @return string[]
|
||||
* The token types.
|
||||
*/
|
||||
public function getTokenTypes();
|
||||
|
||||
/**
|
||||
* Returns the source prefix; used for bulk delete.
|
||||
*
|
||||
* @return string
|
||||
* The source path prefix.
|
||||
*/
|
||||
public function getSourcePrefix();
|
||||
|
||||
/**
|
||||
* Determines if this plugin type can apply a given object.
|
||||
*
|
||||
* @param object $object
|
||||
* The object used to determine if this plugin can apply.
|
||||
*
|
||||
* @return bool
|
||||
* Whether this plugin applies to the given object.
|
||||
*/
|
||||
public function applies($object);
|
||||
|
||||
}
|
71
web/modules/contrib/pathauto/src/AliasTypeManager.php
Normal file
71
web/modules/contrib/pathauto/src/AliasTypeManager.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Component\Plugin\FallbackPluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Manages pathauto alias type plugins.
|
||||
*/
|
||||
class AliasTypeManager extends DefaultPluginManager implements FallbackPluginManagerInterface {
|
||||
|
||||
/**
|
||||
* Constructs a new AliasType manager instance.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* Cache backend instance to use.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to invoke the alter hook with.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/pathauto/AliasType', $namespaces, $module_handler, 'Drupal\pathauto\AliasTypeInterface', 'Drupal\pathauto\Annotation\AliasType');
|
||||
$this->alterInfo('pathauto_alias_types');
|
||||
$this->setCacheBackend($cache_backend, 'pathauto_alias_types');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns plugin definitions that support a given token type.
|
||||
*
|
||||
* @param string $type
|
||||
* The type of token plugin must support to be useful.
|
||||
*
|
||||
* @return array
|
||||
* Plugin definitions.
|
||||
*/
|
||||
public function getPluginDefinitionByType($type) {
|
||||
$definitions = array_filter($this->getDefinitions(), function ($definition) use ($type) {
|
||||
if (!empty($definition['types']) && in_array($type, $definition['types'])) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
});
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFallbackPluginId($plugin_id, array $configuration = array()) {
|
||||
return 'broken';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition of all visible plugins for this type.
|
||||
*
|
||||
* @return array
|
||||
* An array of plugin definitions (empty array if no definitions were
|
||||
* found). Keys are plugin IDs.
|
||||
*/
|
||||
public function getVisibleDefinitions() {
|
||||
$definitions = $this->getDefinitions();
|
||||
unset($definitions['broken']);
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
}
|
167
web/modules/contrib/pathauto/src/AliasUniquifier.php
Normal file
167
web/modules/contrib/pathauto/src/AliasUniquifier.php
Normal file
|
@ -0,0 +1,167 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Path\AliasManagerInterface;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
|
||||
/**
|
||||
* Provides a utility for creating a unique path alias.
|
||||
*/
|
||||
class AliasUniquifier implements AliasUniquifierInterface {
|
||||
|
||||
/**
|
||||
* Config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The alias storage helper.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasStorageHelperInterface
|
||||
*/
|
||||
protected $aliasStorageHelper;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The route provider service.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteProviderInterface.
|
||||
*/
|
||||
protected $routeProvider;
|
||||
|
||||
/**
|
||||
* The alias manager.
|
||||
*
|
||||
* @var \Drupal\Core\Path\AliasManagerInterface
|
||||
*/
|
||||
protected $aliasManager;
|
||||
|
||||
/**
|
||||
* Creates a new AliasUniquifier.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
|
||||
* The alias storage helper.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
||||
* The route provider service.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, ModuleHandlerInterface $module_handler, RouteProviderInterface $route_provider, AliasManagerInterface $alias_manager) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->aliasStorageHelper = $alias_storage_helper;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->routeProvider = $route_provider;
|
||||
$this->aliasManager = $alias_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uniquify(&$alias, $source, $langcode) {
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
|
||||
if (!$this->isReserved($alias, $source, $langcode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the alias already exists, generate a new, hopefully unique, variant.
|
||||
$maxlength = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxlength());
|
||||
$separator = $config->get('separator');
|
||||
$original_alias = $alias;
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
// Append an incrementing numeric suffix until we find a unique alias.
|
||||
$unique_suffix = $separator . $i;
|
||||
$alias = Unicode::truncate($original_alias, $maxlength - Unicode::strlen($unique_suffix), TRUE) . $unique_suffix;
|
||||
$i++;
|
||||
} while ($this->isReserved($alias, $source, $langcode));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
|
||||
// Check if this alias already exists.
|
||||
if ($existing_source = $this->aliasManager->getPathByAlias($alias, $langcode)) {
|
||||
if ($existing_source != $alias) {
|
||||
// If it is an alias for the provided source, it is allowed to keep using
|
||||
// it. If not, then it is reserved.
|
||||
return $existing_source != $source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Then check if there is a route with the same path.
|
||||
if ($this->isRoute($alias)) {
|
||||
return TRUE;
|
||||
}
|
||||
// Finally check if any other modules have reserved the alias.
|
||||
$args = array(
|
||||
$alias,
|
||||
$source,
|
||||
$langcode,
|
||||
);
|
||||
$implementations = $this->moduleHandler->getImplementations('pathauto_is_alias_reserved');
|
||||
foreach ($implementations as $module) {
|
||||
|
||||
$result = $this->moduleHandler->invoke($module, 'pathauto_is_alias_reserved', $args);
|
||||
|
||||
if (!empty($result)) {
|
||||
// As soon as the first module says that an alias is in fact reserved,
|
||||
// then there is no point in checking the rest of the modules.
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the given path is a valid route.
|
||||
*
|
||||
* @param string $path
|
||||
* A string containing a relative path.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the path already exists.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function isRoute($path) {
|
||||
if (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
|
||||
// Do not allow existing files or directories to get assigned an automatic
|
||||
// alias. Note that we do not need to use is_link() to check for symbolic
|
||||
// links since this returns TRUE for either is_file() or is_dir() already.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
$routes = $this->routeProvider->getRoutesByPattern($path);
|
||||
|
||||
// Only return true for an exact match, ignore placeholders.
|
||||
foreach ($routes as $route) {
|
||||
if ($route->getPath() == $path) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for alias uniquifiers.
|
||||
*/
|
||||
interface AliasUniquifierInterface {
|
||||
|
||||
/**
|
||||
* Check to ensure a path alias is unique and add suffix variants if necessary.
|
||||
*
|
||||
* Given an alias 'content/test' if a path alias with the exact alias already
|
||||
* exists, the function will change the alias to 'content/test-0' and will
|
||||
* increase the number suffix until it finds a unique alias.
|
||||
*
|
||||
* @param string $alias
|
||||
* A string with the alias. Can be altered by reference.
|
||||
* @param string $source
|
||||
* A string with the path source.
|
||||
* @param string $langcode
|
||||
* A string with a language code.
|
||||
*/
|
||||
public function uniquify(&$alias, $source, $langcode);
|
||||
|
||||
/**
|
||||
* Checks if an alias is reserved.
|
||||
*
|
||||
* @param string $alias
|
||||
* The alias.
|
||||
* @param string $source
|
||||
* The source.
|
||||
* @param string $langcode
|
||||
* (optional) The language code.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the alias is reserved.
|
||||
*/
|
||||
public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
|
||||
}
|
37
web/modules/contrib/pathauto/src/Annotation/AliasType.php
Normal file
37
web/modules/contrib/pathauto/src/Annotation/AliasType.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines an AliasType annotation.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class AliasType extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The human-readable name of the action plugin.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $label;
|
||||
|
||||
/**
|
||||
* The token types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $types = array();
|
||||
|
||||
}
|
370
web/modules/contrib/pathauto/src/Entity/PathautoPattern.php
Normal file
370
web/modules/contrib/pathauto/src/Entity/PathautoPattern.php
Normal file
|
@ -0,0 +1,370 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\ContextException;
|
||||
use Drupal\Core\Condition\ConditionPluginCollection;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
|
||||
use Drupal\pathauto\PathautoPatternInterface;
|
||||
|
||||
/**
|
||||
* Defines the Pathauto pattern entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "pathauto_pattern",
|
||||
* label = @Translation("Pathauto pattern"),
|
||||
* handlers = {
|
||||
* "list_builder" = "Drupal\pathauto\PathautoPatternListBuilder",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\pathauto\Form\PatternEditForm",
|
||||
* "delete" = "Drupal\Core\Entity\EntityDeleteForm",
|
||||
* "enable" = "Drupal\pathauto\Form\PatternEnableForm",
|
||||
* "disable" = "Drupal\pathauto\Form\PatternDisableForm"
|
||||
* },
|
||||
* "route_provider" = {
|
||||
* "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
|
||||
* },
|
||||
* },
|
||||
* config_prefix = "pattern",
|
||||
* admin_permission = "administer pathauto",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label",
|
||||
* "uuid" = "uuid",
|
||||
* "weight" = "weight",
|
||||
* "status" = "status"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "label",
|
||||
* "type",
|
||||
* "pattern",
|
||||
* "selection_criteria",
|
||||
* "selection_logic",
|
||||
* "weight",
|
||||
* "relationships"
|
||||
* },
|
||||
* lookup_keys = {
|
||||
* "type",
|
||||
* "status",
|
||||
* },
|
||||
* links = {
|
||||
* "collection" = "/admin/config/search/path/patterns",
|
||||
* "edit-form" = "/admin/config/search/path/patterns/{pathauto_pattern}",
|
||||
* "delete-form" = "/admin/config/search/path/patterns/{pathauto_pattern}/delete",
|
||||
* "enable" = "/admin/config/search/path/patterns/{pathauto_pattern}/enable",
|
||||
* "disable" = "/admin/config/search/path/patterns/{pathauto_pattern}/disable"
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class PathautoPattern extends ConfigEntityBase implements PathautoPatternInterface {
|
||||
|
||||
/**
|
||||
* The Pathauto pattern ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The Pathauto pattern label.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The pattern type.
|
||||
*
|
||||
* A string denoting the type of pathauto pattern this is. For a node path
|
||||
* this would be 'node', for users it would be 'user', and so on. This allows
|
||||
* for arbitrary non-entity patterns to be possible if applicable.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
|
||||
*/
|
||||
protected $aliasTypeCollection;
|
||||
|
||||
/**
|
||||
* A tokenized string for alias generation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern;
|
||||
|
||||
/**
|
||||
* The plugin configuration for the selection criteria condition plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $selection_criteria = [];
|
||||
|
||||
/**
|
||||
* The selection logic for this pattern entity (either 'and' or 'or').
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $selection_logic = 'and';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* Keys are context tokens, and values are arrays with the following keys:
|
||||
* - label (string|null, optional): The human-readable label of this
|
||||
* relationship.
|
||||
*/
|
||||
protected $relationships = [];
|
||||
|
||||
/**
|
||||
* The plugin collection that holds the selection criteria condition plugins.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\LazyPluginCollection
|
||||
*/
|
||||
protected $selectionConditionCollection;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Not using core's default logic around ConditionPluginCollection since it
|
||||
* incorrectly assumes no condition will ever be applied twice.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
parent::preSave($storage);
|
||||
$criteria = [];
|
||||
foreach ($this->getSelectionConditions() as $id => $condition) {
|
||||
$criteria[$id] = $condition->getConfiguration();
|
||||
}
|
||||
$this->selection_criteria = $criteria;
|
||||
|
||||
// Invalidate the static caches.
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $entities) {
|
||||
parent::postDelete($storage, $entities);
|
||||
// Invalidate the static caches.
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
|
||||
$this->calculatePluginDependencies($this->getAliasType());
|
||||
|
||||
foreach ($this->getSelectionConditions() as $instance) {
|
||||
$this->calculatePluginDependencies($instance);
|
||||
}
|
||||
|
||||
return $this->getDependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPattern() {
|
||||
return $this->pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPattern($pattern) {
|
||||
$this->pattern = $pattern;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAliasType() {
|
||||
if (!$this->aliasTypeCollection) {
|
||||
$this->aliasTypeCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.alias_type'), $this->getType(), ['default' => $this->getPattern()]);
|
||||
}
|
||||
return $this->aliasTypeCollection->get($this->getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContexts() {
|
||||
$contexts = $this->getAliasType()->getContexts();
|
||||
foreach ($this->getRelationships() as $token => $definition) {
|
||||
/** @var \Drupal\ctools\TypedDataResolver $resolver */
|
||||
$resolver = \Drupal::service('ctools.typed_data.resolver');
|
||||
$context = $resolver->convertTokenToContext($token, $contexts);
|
||||
$context_definition = $context->getContextDefinition();
|
||||
if (!empty($definition['label'])) {
|
||||
$context_definition->setLabel($definition['label']);
|
||||
}
|
||||
$contexts[$token] = $context;
|
||||
}
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasRelationship($token) {
|
||||
return isset($this->relationships[$token]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRelationship($token, $label = NULL) {
|
||||
if (!$this->hasRelationship($token)) {
|
||||
$this->relationships[$token] = [
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function replaceRelationship($token, $label) {
|
||||
if ($this->hasRelationship($token)) {
|
||||
$this->relationships[$token] = [
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeRelationship($token) {
|
||||
unset($this->relationships[$token]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRelationships() {
|
||||
return $this->relationships;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSelectionConditions() {
|
||||
if (!$this->selectionConditionCollection) {
|
||||
$this->selectionConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this->get('selection_criteria'));
|
||||
}
|
||||
return $this->selectionConditionCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSelectionCondition(array $configuration) {
|
||||
$configuration['uuid'] = $this->uuidGenerator()->generate();
|
||||
$this->getSelectionConditions()->addInstanceId($configuration['uuid'], $configuration);
|
||||
return $configuration['uuid'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSelectionCondition($condition_id) {
|
||||
return $this->getSelectionConditions()->get($condition_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeSelectionCondition($condition_id) {
|
||||
$this->getSelectionConditions()->removeInstanceId($condition_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSelectionLogic() {
|
||||
return $this->selection_logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($object) {
|
||||
if ($this->getAliasType()->applies($object)) {
|
||||
$definitions = $this->getAliasType()->getContextDefinitions();
|
||||
if (count($definitions) > 1) {
|
||||
throw new \Exception("Alias types do not support more than one context.");
|
||||
}
|
||||
$keys = array_keys($definitions);
|
||||
// Set the context object on our Alias plugin before retrieving contexts.
|
||||
$this->getAliasType()->setContextValue($keys[0], $object);
|
||||
/** @var \Drupal\Core\Plugin\Context\ContextInterface[] $base_contexts */
|
||||
$contexts = $this->getContexts();
|
||||
/** @var \Drupal\Core\Plugin\Context\ContextHandler $context_handler */
|
||||
$context_handler = \Drupal::service('context.handler');
|
||||
$conditions = $this->getSelectionConditions();
|
||||
foreach ($conditions as $condition) {
|
||||
if ($condition instanceof ContextAwarePluginInterface) {
|
||||
try {
|
||||
$context_handler->applyContextMapping($condition, $contexts);
|
||||
}
|
||||
catch (ContextException $e) {
|
||||
watchdog_exception('pathauto', $e);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
$result = $condition->execute();
|
||||
if ($this->getSelectionLogic() == 'and' && !$result) {
|
||||
return FALSE;
|
||||
}
|
||||
elseif ($this->getSelectionLogic() == 'or' && $result) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\pathauto\AliasTypeManager;
|
||||
|
||||
/**
|
||||
* A subscriber to clear fielddefinition cache when saving pathauto settings.
|
||||
*/
|
||||
class PathautoSettingsCacheTag implements EventSubscriberInterface {
|
||||
|
||||
protected $entityFieldManager;
|
||||
protected $aliasTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a PathautoSettingsCacheTag object.
|
||||
*/
|
||||
public function __construct(EntityFieldManagerInterface $entity_field_manager, AliasTypeManager $alias_type_manager) {
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->aliasTypeManager = $alias_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the 'rendered' cache tag whenever the settings are modified.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The Event to process.
|
||||
*/
|
||||
public function onSave(ConfigCrudEvent $event) {
|
||||
if ($event->getConfig()->getName() === 'pathauto.settings') {
|
||||
$config = $event->getConfig();
|
||||
$original_entity_types = $config->getOriginal('enabled_entity_types');
|
||||
|
||||
// Clear cached field definitions if the values are changed.
|
||||
if ($original_entity_types != $config->get('enabled_entity_types')) {
|
||||
$this->entityFieldManager->clearCachedFieldDefinitions();
|
||||
$this->aliasTypeManager->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = ['onSave'];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
185
web/modules/contrib/pathauto/src/Form/PathautoAdminDelete.php
Normal file
185
web/modules/contrib/pathauto/src/Form/PathautoAdminDelete.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\pathauto\AliasTypeManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Alias mass delete form.
|
||||
*/
|
||||
class PathautoAdminDelete extends FormBase {
|
||||
|
||||
/**
|
||||
* The alias type manager.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasTypeManager
|
||||
*/
|
||||
protected $aliasTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a PathautoAdminDelete object.
|
||||
*
|
||||
* @param \Drupal\pathauto\AliasTypeManager $alias_type_manager
|
||||
* The alias type manager.
|
||||
*/
|
||||
public function __construct(AliasTypeManager $alias_type_manager) {
|
||||
$this->aliasTypeManager = $alias_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.alias_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'pathauto_admin_delete';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['delete'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Choose aliases to delete'),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
// First we do the "all" case.
|
||||
$storage_helper = \Drupal::service('pathauto.alias_storage_helper');
|
||||
$total_count = $storage_helper->countAll();
|
||||
$form['delete']['all_aliases'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('All aliases'),
|
||||
'#default_value' => FALSE,
|
||||
'#description' => $this->t('Delete all aliases. Number of aliases which will be deleted: %count.', ['%count' => $total_count]),
|
||||
];
|
||||
|
||||
// Next, iterate over all visible alias types.
|
||||
$definitions = $this->aliasTypeManager->getVisibleDefinitions();
|
||||
|
||||
foreach ($definitions as $id => $definition) {
|
||||
/** @var \Drupal\pathauto\AliasTypeInterface $alias_type */
|
||||
$alias_type = $this->aliasTypeManager->createInstance($id);
|
||||
$count = $storage_helper->countBySourcePrefix($alias_type->getSourcePrefix());
|
||||
$form['delete']['plugins'][$id] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => (string) $definition['label'],
|
||||
'#default_value' => FALSE,
|
||||
'#description' => $this->t('Delete aliases for all @label. Number of aliases which will be deleted: %count.', ['@label' => (string) $definition['label'], '%count' => $count]),
|
||||
];
|
||||
}
|
||||
|
||||
$form['options'] = [
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Delete options'),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
// Provide checkbox for not deleting custom aliases.
|
||||
$form['options']['keep_custom_aliases'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Only delete automatically generated aliases'),
|
||||
'#default_value' => TRUE,
|
||||
'#description' => $this->t('When checked, aliases which have been manually set are not affected by this mass-deletion.'),
|
||||
];
|
||||
|
||||
// Warn them and give a button that shows we mean business.
|
||||
$form['warning'] = ['#value' => '<p>' . $this->t('<strong>Note:</strong> there is no confirmation. Be sure of your action before clicking the "Delete aliases now!" button.<br />You may want to make a backup of the database and/or the url_alias table prior to using this feature.') . '</p>'];
|
||||
$form['buttons']['submit'] = [
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Delete aliases now!'),
|
||||
];
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$delete_all = $form_state->getValue(['delete', 'all_aliases']);
|
||||
// Keeping custom aliases forces us to go the slow way to correctly check
|
||||
// the automatic/manual flag.
|
||||
if ($form_state->getValue(['options', 'keep_custom_aliases'])) {
|
||||
$batch = [
|
||||
'title' => $this->t('Bulk deleting URL aliases'),
|
||||
'operations' => [['Drupal\pathauto\Form\PathautoAdminDelete::batchStart', [$delete_all]]],
|
||||
'finished' => 'Drupal\pathauto\Form\PathautoAdminDelete::batchFinished',
|
||||
];
|
||||
|
||||
if ($delete_all) {
|
||||
foreach (array_keys($form_state->getValue(['delete', 'plugins'])) as $id) {
|
||||
$batch['operations'][] = ['Drupal\pathauto\Form\PathautoAdminDelete::batchProcess', [$id]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (array_keys(array_filter($form_state->getValue(['delete', 'plugins']))) as $id) {
|
||||
$batch['operations'][] = ['Drupal\pathauto\Form\PathautoAdminDelete::batchProcess', [$id]];
|
||||
}
|
||||
}
|
||||
|
||||
batch_set($batch);
|
||||
}
|
||||
else if ($delete_all) {
|
||||
\Drupal::service('pathauto.alias_storage_helper')->deleteAll();
|
||||
drupal_set_message($this->t('All of your path aliases have been deleted.'));
|
||||
}
|
||||
else {
|
||||
$storage_helper = \Drupal::service('pathauto.alias_storage_helper');
|
||||
foreach (array_keys(array_filter($form_state->getValue(['delete', 'plugins']))) as $id) {
|
||||
$alias_type = $this->aliasTypeManager->createInstance($id);
|
||||
$storage_helper->deleteBySourcePrefix((string) $alias_type->getSourcePrefix());
|
||||
drupal_set_message($this->t('All of your %label path aliases have been deleted.', ['%label' => $alias_type->getLabel()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; record if aliases of all types must be deleted.
|
||||
*/
|
||||
public static function batchStart($delete_all, &$context) {
|
||||
$context['results']['delete_all'] = $delete_all;
|
||||
$context['results']['deletions'] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Common batch processing callback for all operations.
|
||||
*/
|
||||
public static function batchProcess($id, &$context) {
|
||||
/** @var \Drupal\pathauto\AliasTypeBatchUpdateInterface $alias_type */
|
||||
$alias_type = \Drupal::service('plugin.manager.alias_type')->createInstance($id);
|
||||
$alias_type->batchDelete($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch finished callback.
|
||||
*/
|
||||
public static function batchFinished($success, $results, $operations) {
|
||||
if ($success) {
|
||||
if ($results['delete_all']) {
|
||||
drupal_set_message(t('All of your automatically generated path aliases have been deleted.'));
|
||||
}
|
||||
else if (isset($results['deletions'])) {
|
||||
foreach (array_values($results['deletions']) as $label) {
|
||||
drupal_set_message(t('All of your automatically generated %label path aliases have been deleted.', ['%label' => $label]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$error_operation = reset($operations);
|
||||
drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
164
web/modules/contrib/pathauto/src/Form/PathautoBulkUpdateForm.php
Normal file
164
web/modules/contrib/pathauto/src/Form/PathautoBulkUpdateForm.php
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\pathauto\AliasTypeBatchUpdateInterface;
|
||||
use Drupal\pathauto\AliasTypeManager;
|
||||
use Drupal\pathauto\PathautoGeneratorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure file system settings for this site.
|
||||
*/
|
||||
class PathautoBulkUpdateForm extends FormBase {
|
||||
|
||||
/**
|
||||
* The alias type manager.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasTypeManager
|
||||
*/
|
||||
protected $aliasTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a PathautoBulkUpdateForm object.
|
||||
*
|
||||
* @param \Drupal\pathauto\AliasTypeManager $alias_type_manager
|
||||
* The alias type manager.
|
||||
*/
|
||||
public function __construct(AliasTypeManager $alias_type_manager) {
|
||||
$this->aliasTypeManager = $alias_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.alias_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'pathauto_bulk_update_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
|
||||
$form = array();
|
||||
|
||||
$form['#update_callbacks'] = array();
|
||||
|
||||
$form['update'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Select the types of paths for which to generate URL aliases'),
|
||||
'#options' => array(),
|
||||
'#default_value' => array(),
|
||||
);
|
||||
|
||||
$definitions = $this->aliasTypeManager->getVisibleDefinitions();
|
||||
|
||||
foreach ($definitions as $id => $definition) {
|
||||
$alias_type = $this->aliasTypeManager->createInstance($id);
|
||||
if ($alias_type instanceof AliasTypeBatchUpdateInterface) {
|
||||
$form['update']['#options'][$id] = $alias_type->getLabel();
|
||||
}
|
||||
}
|
||||
|
||||
$form['action'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Select which URL aliases to generate'),
|
||||
'#options' => ['create' => $this->t('Generate a URL alias for un-aliased paths only')],
|
||||
'#default_value' => 'create',
|
||||
);
|
||||
|
||||
$config = $this->config('pathauto.settings');
|
||||
|
||||
if ($config->get('update_action') == PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW) {
|
||||
// Existing aliases should not be updated.
|
||||
$form['warning'] = array(
|
||||
'#markup' => $this->t('<a href=":url">Pathauto settings</a> are set to ignore paths which already have a URL alias. You can only create URL aliases for paths having none.', [':url' => Url::fromRoute('pathauto.settings.form')->toString()]),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$form['action']['#options']['update'] = $this->t('Update the URL alias for paths having an old URL alias');
|
||||
$form['action']['#options']['all'] = $this->t('Regenerate URL aliases for all paths');
|
||||
}
|
||||
|
||||
$form['actions']['#type'] = 'actions';
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => $this->t('Update'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$batch = array(
|
||||
'title' => $this->t('Bulk updating URL aliases'),
|
||||
'operations' => array(
|
||||
array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchStart', array()),
|
||||
),
|
||||
'finished' => 'Drupal\pathauto\Form\PathautoBulkUpdateForm::batchFinished',
|
||||
);
|
||||
|
||||
$action = $form_state->getValue('action');
|
||||
|
||||
foreach ($form_state->getValue('update') as $id) {
|
||||
if (!empty($id)) {
|
||||
$batch['operations'][] = array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchProcess', [$id, $action]);
|
||||
}
|
||||
}
|
||||
|
||||
batch_set($batch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch callback; initialize the number of updated aliases.
|
||||
*/
|
||||
public static function batchStart(&$context) {
|
||||
$context['results']['updates'] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common batch processing callback for all operations.
|
||||
*
|
||||
* Required to load our include the proper batch file.
|
||||
*/
|
||||
public static function batchProcess($id, $action, &$context) {
|
||||
/** @var \Drupal\pathauto\AliasTypeBatchUpdateInterface $alias_type */
|
||||
$alias_type = \Drupal::service('plugin.manager.alias_type')->createInstance($id);
|
||||
$alias_type->batchUpdate($action, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch finished callback.
|
||||
*/
|
||||
public static function batchFinished($success, $results, $operations) {
|
||||
if ($success) {
|
||||
if ($results['updates']) {
|
||||
drupal_set_message(\Drupal::translation()->formatPlural($results['updates'], 'Generated 1 URL alias.', 'Generated @count URL aliases.'));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(t('No new URL aliases to generate.'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$error_operation = reset($operations);
|
||||
drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
266
web/modules/contrib/pathauto/src/Form/PathautoSettingsForm.php
Normal file
266
web/modules/contrib/pathauto/src/Form/PathautoSettingsForm.php
Normal file
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\pathauto\AliasTypeManager;
|
||||
use Drupal\pathauto\PathautoGeneratorInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Configure file system settings for this site.
|
||||
*/
|
||||
class PathautoSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\pathauto\AliasTypeManager
|
||||
*/
|
||||
protected $aliasTypeManager;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, AliasTypeManager $alias_type_manager) {
|
||||
parent::__construct($config_factory);
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->aliasTypeManager = $alias_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('plugin.manager.alias_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'pathauto_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['pathauto.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('pathauto.settings');
|
||||
|
||||
$form['enabled_entity_types'] = [
|
||||
'#type' => 'details',
|
||||
'#open' => TRUE,
|
||||
'#title' => $this->t('Enabled entity types'),
|
||||
'#description' => $this->t('Enable to add a path field and allow to define alias patterns for the given type. Disabled types already define a path field themselves or currently have a pattern.'),
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
// Get all applicable entity types.
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
// Disable a checkbox if it already exists and if the entity type has
|
||||
// patterns currently defined or if it isn't defined by us.
|
||||
$patterns_count = \Drupal::entityQuery('pathauto_pattern')
|
||||
->condition('type', 'canonical_entities:' . $entity_type_id)
|
||||
->count()
|
||||
->execute();
|
||||
|
||||
if (is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class) && $entity_type->hasLinkTemplate('canonical')) {
|
||||
$field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
|
||||
$form['enabled_entity_types'][$entity_type_id] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $entity_type->getLabel(),
|
||||
'#default_value' => isset($field_definitions['path']) || in_array($entity_type_id, $config->get('enabled_entity_types')),
|
||||
'#disabled' => isset($field_definitions['path']) && ($field_definitions['path']->getProvider() != 'pathauto' || $patterns_count),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$form['verbose'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Verbose'),
|
||||
'#default_value' => $config->get('verbose'),
|
||||
'#description' => $this->t('Display alias changes (except during bulk updates).'),
|
||||
);
|
||||
|
||||
$form['separator'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Separator'),
|
||||
'#size' => 1,
|
||||
'#maxlength' => 1,
|
||||
'#default_value' => $config->get('separator'),
|
||||
'#description' => $this->t('Character used to separate words in titles. This will replace any spaces and punctuation characters. Using a space or + character can cause unexpected results.'),
|
||||
);
|
||||
|
||||
$form['case'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Character case'),
|
||||
'#default_value' => $config->get('case'),
|
||||
'#description' => $this->t('Convert token values to lowercase.'),
|
||||
);
|
||||
|
||||
$max_length = \Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength();
|
||||
|
||||
$help_link = '';
|
||||
if (\Drupal::moduleHandler()->moduleExists('help')) {
|
||||
$help_link = ' ' . $this->t('See <a href=":pathauto-help">Pathauto help</a> for details.', [':pathauto-help' => Url::fromRoute('help.page', ['name' => 'pathauto'])->toString()]);
|
||||
}
|
||||
|
||||
$form['max_length'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Maximum alias length'),
|
||||
'#size' => 3,
|
||||
'#maxlength' => 3,
|
||||
'#default_value' => $config->get('max_length'),
|
||||
'#min' => 1,
|
||||
'#max' => $max_length,
|
||||
'#description' => $this->t('Maximum length of aliases to generate. 100 is the recommended length. @max is the maximum possible length.', array('@max' => $max_length)) . $help_link,
|
||||
);
|
||||
|
||||
$form['max_component_length'] = array(
|
||||
'#type' => 'number',
|
||||
'#title' => $this->t('Maximum component length'),
|
||||
'#size' => 3,
|
||||
'#maxlength' => 3,
|
||||
'#default_value' => $config->get('max_component_length'),
|
||||
'#min' => 1,
|
||||
'#max' => $max_length,
|
||||
'#description' => $this->t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length.', ['@max' => $max_length]) . $help_link,
|
||||
);
|
||||
|
||||
$description = $this->t('What should Pathauto do when updating an existing content item which already has an alias?');
|
||||
if (\Drupal::moduleHandler()->moduleExists('redirect')) {
|
||||
$description .= ' ' . $this->t('The <a href=":url">Redirect module settings</a> affect whether a redirect is created when an alias is deleted.', array(':url' => Url::fromRoute('redirect.settings')->toString()));
|
||||
}
|
||||
else {
|
||||
$description .= ' ' . $this->t('Considering installing the <a href=":url">Redirect module</a> to get redirects when your aliases change.', array(':url' => 'http://drupal.org/project/redirect'));
|
||||
}
|
||||
|
||||
$form['update_action'] = array(
|
||||
'#type' => 'radios',
|
||||
'#title' => $this->t('Update action'),
|
||||
'#default_value' => $config->get('update_action'),
|
||||
'#options' => array(
|
||||
PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW => $this->t('Do nothing. Leave the old alias intact.'),
|
||||
PathautoGeneratorInterface::UPDATE_ACTION_LEAVE => $this->t('Create a new alias. Leave the existing alias functioning.'),
|
||||
PathautoGeneratorInterface::UPDATE_ACTION_DELETE => $this->t('Create a new alias. Delete the old alias.'),
|
||||
),
|
||||
'#description' => $description,
|
||||
);
|
||||
|
||||
$form['transliterate'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Transliterate prior to creating alias'),
|
||||
'#default_value' => $config->get('transliterate'),
|
||||
'#description' => $this->t('When a pattern includes certain characters (such as those with accents) should Pathauto attempt to transliterate them into the US-ASCII alphabet?'),
|
||||
);
|
||||
|
||||
$form['reduce_ascii'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Reduce strings to letters and numbers'),
|
||||
'#default_value' => $config->get('reduce_ascii'),
|
||||
'#description' => $this->t('Filters the new alias to only letters and numbers found in the ASCII-96 set.'),
|
||||
);
|
||||
|
||||
$form['ignore_words'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Strings to Remove'),
|
||||
'#default_value' => $config->get('ignore_words'),
|
||||
'#description' => $this->t('Words to strip out of the URL alias, separated by commas. Do not use this to remove punctuation.'),
|
||||
'#wysiwyg' => FALSE,
|
||||
);
|
||||
|
||||
$form['punctuation'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('Punctuation'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
);
|
||||
|
||||
$punctuation = \Drupal::service('pathauto.alias_cleaner')->getPunctuationCharacters();
|
||||
|
||||
foreach ($punctuation as $name => $details) {
|
||||
// Use the value from config if it exists.
|
||||
if ($config->get('punctuation.' . $name) !== NULL) {
|
||||
$details['default'] = $config->get('punctuation.' . $name);
|
||||
}
|
||||
else {
|
||||
// Otherwise use the correct default.
|
||||
$details['default'] = $details['value'] == $config->get('separator') ? PathautoGeneratorInterface::PUNCTUATION_REPLACE : PathautoGeneratorInterface::PUNCTUATION_REMOVE;
|
||||
}
|
||||
$form['punctuation'][$name] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $details['name'] . ' (<code>' . Html::escape($details['value']) . '</code>)',
|
||||
'#default_value' => $details['default'],
|
||||
'#options' => array(
|
||||
PathautoGeneratorInterface::PUNCTUATION_REMOVE => $this->t('Remove'),
|
||||
PathautoGeneratorInterface::PUNCTUATION_REPLACE => $this->t('Replace by separator'),
|
||||
PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING => $this->t('No action (do not replace)'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
|
||||
$config = $this->config('pathauto.settings');
|
||||
|
||||
$form_state->cleanValues();
|
||||
|
||||
$original_entity_types = $config->get('enabled_entity_types');
|
||||
foreach ($form_state->getValues() as $key => $value) {
|
||||
if ($key == 'enabled_entity_types') {
|
||||
$enabled_entity_types = [];
|
||||
foreach ($value as $entity_type_id => $enabled) {
|
||||
$field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
|
||||
// Verify that the entity type is enabled and that it is not defined
|
||||
// or defined by us before adding it to the configuration, so that
|
||||
// we do not store an entity type that cannot be enabled or disabled.
|
||||
if ($enabled && (!isset($field_definitions['path']) || ($field_definitions['path']->getProvider() === 'pathauto'))) {
|
||||
$enabled_entity_types[] = $entity_type_id;
|
||||
}
|
||||
}
|
||||
$value = $enabled_entity_types;
|
||||
}
|
||||
$config->set($key, $value);
|
||||
}
|
||||
$config->save();
|
||||
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
}
|
52
web/modules/contrib/pathauto/src/Form/PatternDisableForm.php
Normal file
52
web/modules/contrib/pathauto/src/Form/PatternDisableForm.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides the pathauto pattern disable disable form.
|
||||
*/
|
||||
class PatternDisableForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to disable the pattern %label?', array('%label' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.pathauto_pattern.collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->t('Disabled patterns are ignored when generating aliases.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->disable()->save();
|
||||
drupal_set_message($this->t('Disabled pattern %label.', array('%label' => $this->entity->label())));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
285
web/modules/contrib/pathauto/src/Form/PatternEditForm.php
Normal file
285
web/modules/contrib/pathauto/src/Form/PatternEditForm.php
Normal file
|
@ -0,0 +1,285 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\pathauto\AliasTypeManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Edit form for pathauto patterns.
|
||||
*/
|
||||
class PatternEditForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* @var \Drupal\pathauto\AliasTypeManager
|
||||
*/
|
||||
protected $manager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\pathauto\PathautoPatternInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The entity type bundle info service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.alias_type'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('language_manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* PatternEditForm constructor.
|
||||
*
|
||||
* @param \Drupal\pathauto\AliasTypeManager $manager
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
*/
|
||||
function __construct(AliasTypeManager $manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager) {
|
||||
$this->manager = $manager;
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
|
||||
$options = [];
|
||||
foreach ($this->manager->getVisibleDefinitions() as $plugin_id => $plugin_definition) {
|
||||
$options[$plugin_id] = $plugin_definition['label'];
|
||||
}
|
||||
$form['type'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Pattern type'),
|
||||
'#default_value' => $this->entity->getType(),
|
||||
'#options' => $options,
|
||||
'#required' => TRUE,
|
||||
'#limit_validation_errors' => array(array('type')),
|
||||
'#submit' => array('::submitSelectType'),
|
||||
'#executes_submit_callback' => TRUE,
|
||||
'#ajax' => array(
|
||||
'callback' => '::ajaxReplacePatternForm',
|
||||
'wrapper' => 'pathauto-pattern',
|
||||
'method' => 'replace',
|
||||
),
|
||||
];
|
||||
|
||||
$form['pattern_container'] = [
|
||||
'#type' => 'container',
|
||||
'#prefix' => '<div id="pathauto-pattern">',
|
||||
'#suffix' => '</div>',
|
||||
];
|
||||
|
||||
// if there is no type yet, stop here.
|
||||
if ($this->entity->getType()) {
|
||||
|
||||
$alias_type = $this->entity->getAliasType();
|
||||
|
||||
$form['pattern_container']['pattern'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => 'Path pattern',
|
||||
'#default_value' => $this->entity->getPattern(),
|
||||
'#size' => 65,
|
||||
'#maxlength' => 1280,
|
||||
'#element_validate' => array('token_element_validate', 'pathauto_pattern_validate'),
|
||||
'#after_build' => array('token_element_validate'),
|
||||
'#token_types' => $alias_type->getTokenTypes(),
|
||||
'#min_tokens' => 1,
|
||||
'#required' => TRUE,
|
||||
);
|
||||
|
||||
// Show the token help relevant to this pattern type.
|
||||
$form['pattern_container']['token_help'] = array(
|
||||
'#theme' => 'token_tree_link',
|
||||
'#token_types' => $alias_type->getTokenTypes(),
|
||||
);
|
||||
|
||||
// Expose bundle and language conditions.
|
||||
if ($alias_type->getDerivativeId() && $entity_type = $this->entityTypeManager->getDefinition($alias_type->getDerivativeId())) {
|
||||
|
||||
$default_bundles = [];
|
||||
$default_languages = [];
|
||||
foreach ($this->entity->getSelectionConditions() as $condition_id => $condition) {
|
||||
if (in_array($condition->getPluginId(), ['entity_bundle:' . $entity_type->id(), 'node_type'])) {
|
||||
$default_bundles = $condition->getConfiguration()['bundles'];
|
||||
}
|
||||
elseif ($condition->getPluginId() == 'language') {
|
||||
$default_languages = $condition->getConfiguration()['langcodes'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity_type->hasKey('bundle') && $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id())) {
|
||||
$bundle_options = [];
|
||||
foreach ($bundles as $id => $info) {
|
||||
$bundle_options[$id] = $info['label'];
|
||||
}
|
||||
$form['pattern_container']['bundles'] = array(
|
||||
'#title' => $entity_type->getBundleLabel(),
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $bundle_options,
|
||||
'#default_value' => $default_bundles,
|
||||
'#description' => $this->t('Check to which types this pattern should be applied. Leave empty to allow any.'),
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->languageManager->isMultilingual() && $entity_type->isTranslatable()) {
|
||||
$language_options = [];
|
||||
foreach ($this->languageManager->getLanguages() as $id => $language) {
|
||||
$language_options[$id] = $language->getName();
|
||||
}
|
||||
$form['pattern_container']['languages'] = array(
|
||||
'#title' => $this->t('Languages'),
|
||||
'#type' => 'checkboxes',
|
||||
'#options' => $language_options,
|
||||
'#default_value' => $default_languages,
|
||||
'#description' => $this->t('Check to which languages this pattern should be applied. Leave empty to allow any.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form['label'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => $this->t('Label'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $this->entity->label(),
|
||||
'#required' => TRUE,
|
||||
'#description' => $this->t('A short name to help you identify this pattern in the patterns list.'),
|
||||
);
|
||||
|
||||
$form['id'] = array(
|
||||
'#type' => 'machine_name',
|
||||
'#title' => $this->t('ID'),
|
||||
'#maxlength' => 255,
|
||||
'#default_value' => $this->entity->id(),
|
||||
'#required' => TRUE,
|
||||
'#disabled' => !$this->entity->isNew(),
|
||||
'#machine_name' => array(
|
||||
'exists' => 'Drupal\pathauto\Entity\PathautoPattern::load',
|
||||
),
|
||||
);
|
||||
|
||||
$form['status'] = [
|
||||
'#title' => $this->t('Enabled'),
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => $this->entity->status(),
|
||||
];
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\pathauto\PathautoPatternInterface $entity */
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
|
||||
// Will only be used for new patterns.
|
||||
$default_weight = 0;
|
||||
|
||||
$alias_type = $entity->getAliasType();
|
||||
if ($alias_type->getDerivativeId() && $this->entityTypeManager->hasDefinition($alias_type->getDerivativeId())) {
|
||||
$entity_type = $alias_type->getDerivativeId();
|
||||
// First, remove bundle and language conditions.
|
||||
foreach ($entity->getSelectionConditions() as $condition_id => $condition) {
|
||||
if (in_array($condition->getPluginId(), ['entity_bundle:' . $entity_type, 'node_type', 'language'])) {
|
||||
$entity->removeSelectionCondition($condition_id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bundles = array_filter((array) $form_state->getValue('bundles'))) {
|
||||
$default_weight -= 5;
|
||||
$plugin_id = $entity_type == 'node' ? 'node_type' : 'entity_bundle:' . $entity_type;
|
||||
$entity->addSelectionCondition(
|
||||
[
|
||||
'id' => $plugin_id,
|
||||
'bundles' => $bundles,
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
$entity_type => $entity_type,
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ($languages = array_filter((array) $form_state->getValue('languages'))) {
|
||||
$default_weight -= 5;
|
||||
$language_mapping = $entity_type . ':' . $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode') . ':language';
|
||||
$entity->addSelectionCondition(
|
||||
[
|
||||
'id' => 'language',
|
||||
'langcodes' => array_combine($languages, $languages),
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
'language' => $language_mapping,
|
||||
]
|
||||
]
|
||||
);
|
||||
$entity->addRelationship($language_mapping, t('Language'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($entity->isNew()) {
|
||||
$entity->setWeight($default_weight);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
parent::save($form, $form_state);
|
||||
drupal_set_message($this->t('Pattern @label saved.', ['@label' => $this->entity->label()]));
|
||||
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles switching the type selector.
|
||||
*/
|
||||
public function ajaxReplacePatternForm($form, FormStateInterface $form_state) {
|
||||
return $form['pattern_container'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles submit call when alias type is selected.
|
||||
*/
|
||||
public function submitSelectType(array $form, FormStateInterface $form_state) {
|
||||
$this->entity = $this->buildEntity($form, $form_state);
|
||||
$form_state->setRebuild();
|
||||
}
|
||||
|
||||
}
|
52
web/modules/contrib/pathauto/src/Form/PatternEnableForm.php
Normal file
52
web/modules/contrib/pathauto/src/Form/PatternEnableForm.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides the pathauto pattern disable disable form.
|
||||
*/
|
||||
class PatternEnableForm extends EntityConfirmFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getQuestion() {
|
||||
return $this->t('Are you sure you want to enable the pattern %label?', array('%label' => $this->entity->label()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCancelUrl() {
|
||||
return new Url('entity.pathauto_pattern.collection');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfirmText() {
|
||||
return $this->t('Enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$this->entity->enable()->save();
|
||||
drupal_set_message($this->t('Enabled pattern %label.', array('%label' => $this->entity->label())));
|
||||
|
||||
$form_state->setRedirectUrl($this->getCancelUrl());
|
||||
}
|
||||
|
||||
}
|
20
web/modules/contrib/pathauto/src/MessengerInterface.php
Normal file
20
web/modules/contrib/pathauto/src/MessengerInterface.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
/**
|
||||
* Provides an interface for Messengers.
|
||||
*/
|
||||
interface MessengerInterface {
|
||||
|
||||
/**
|
||||
* Adds a message.
|
||||
*
|
||||
* @param string $message
|
||||
* The message to add.
|
||||
* @param string $op
|
||||
* (optional) The operation being performed.
|
||||
*/
|
||||
public function addMessage($message, $op = NULL);
|
||||
|
||||
}
|
365
web/modules/contrib/pathauto/src/PathautoGenerator.php
Normal file
365
web/modules/contrib/pathauto/src/PathautoGenerator.php
Normal file
|
@ -0,0 +1,365 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\RevisionableInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\Core\Utility\Token;
|
||||
use Drupal\token\TokenEntityMapperInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides methods for generating path aliases.
|
||||
*/
|
||||
class PathautoGenerator implements PathautoGeneratorInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Token service.
|
||||
*
|
||||
* @var \Drupal\Core\Utility\Token
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Calculated pattern for a specific entity.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $patterns = array();
|
||||
|
||||
/**
|
||||
* Available patterns per entity type ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $patternsByEntityType = array();
|
||||
|
||||
/**
|
||||
* The alias cleaner.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasCleanerInterface
|
||||
*/
|
||||
protected $aliasCleaner;
|
||||
|
||||
/**
|
||||
* The alias storage helper.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasStorageHelperInterface
|
||||
*/
|
||||
protected $aliasStorageHelper;
|
||||
|
||||
/**
|
||||
* The alias uniquifier.
|
||||
*
|
||||
* @var \Drupal\pathauto\AliasUniquifierInterface
|
||||
*/
|
||||
protected $aliasUniquifier;
|
||||
|
||||
/**
|
||||
* The messenger service.
|
||||
*
|
||||
* @var \Drupal\pathauto\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* @var \Drupal\token\TokenEntityMapperInterface
|
||||
*/
|
||||
protected $tokenEntityMapper;
|
||||
|
||||
/**
|
||||
* @var Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Creates a new Pathauto manager.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Utility\Token $token
|
||||
* The token utility.
|
||||
* @param \Drupal\pathauto\AliasCleanerInterface $alias_cleaner
|
||||
* The alias cleaner.
|
||||
* @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
|
||||
* The alias storage helper.
|
||||
* @param AliasUniquifierInterface $alias_uniquifier
|
||||
* The alias uniquifier.
|
||||
* @param MessengerInterface $messenger
|
||||
* The messenger service.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
* @param Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mappper, EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->token = $token;
|
||||
$this->aliasCleaner = $alias_cleaner;
|
||||
$this->aliasStorageHelper = $alias_storage_helper;
|
||||
$this->aliasUniquifier = $alias_uniquifier;
|
||||
$this->messenger = $messenger;
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->tokenEntityMapper = $token_entity_mappper;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createEntityAlias(EntityInterface $entity, $op) {
|
||||
// Retrieve and apply the pattern for this content type.
|
||||
$pattern = $this->getPatternByEntity($entity);
|
||||
if (empty($pattern)) {
|
||||
// No pattern? Do nothing (otherwise we may blow away existing aliases...)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$source = '/' . $entity->toUrl()->getInternalPath();
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
$langcode = $entity->language()->getId();
|
||||
|
||||
// Core does not handle aliases with language Not Applicable.
|
||||
if ($langcode == LanguageInterface::LANGCODE_NOT_APPLICABLE) {
|
||||
$langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
|
||||
// Build token data.
|
||||
$data = [
|
||||
$this->tokenEntityMapper->getTokenTypeForEntityType($entity->getEntityTypeId()) => $entity,
|
||||
];
|
||||
|
||||
// Allow other modules to alter the pattern.
|
||||
$context = array(
|
||||
'module' => $entity->getEntityType()->getProvider(),
|
||||
'op' => $op,
|
||||
'source' => $source,
|
||||
'data' => $data,
|
||||
'bundle' => $entity->bundle(),
|
||||
'language' => &$langcode,
|
||||
);
|
||||
// @todo Is still hook still useful?
|
||||
$this->moduleHandler->alter('pathauto_pattern', $pattern, $context);
|
||||
|
||||
// Special handling when updating an item which is already aliased.
|
||||
$existing_alias = NULL;
|
||||
if ($op == 'update' || $op == 'bulkupdate') {
|
||||
if ($existing_alias = $this->aliasStorageHelper->loadBySource($source, $langcode)) {
|
||||
switch ($config->get('update_action')) {
|
||||
case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW:
|
||||
// If an alias already exists,
|
||||
// and the update action is set to do nothing,
|
||||
// then gosh-darn it, do nothing.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace any tokens in the pattern.
|
||||
// Uses callback option to clean replacements. No sanitization.
|
||||
// Pass empty BubbleableMetadata object to explicitly ignore cacheablity,
|
||||
// as the result is never rendered.
|
||||
$alias = $this->token->replace($pattern->getPattern(), $data, array(
|
||||
'clear' => TRUE,
|
||||
'callback' => array($this->aliasCleaner, 'cleanTokenValues'),
|
||||
'langcode' => $langcode,
|
||||
'pathauto' => TRUE,
|
||||
), new BubbleableMetadata());
|
||||
|
||||
// Check if the token replacement has not actually replaced any values. If
|
||||
// that is the case, then stop because we should not generate an alias.
|
||||
// @see token_scan()
|
||||
$pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern->getPattern());
|
||||
if ($alias === $pattern_tokens_removed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$alias = $this->aliasCleaner->cleanAlias($alias);
|
||||
|
||||
// Allow other modules to alter the alias.
|
||||
$context['source'] = &$source;
|
||||
$context['pattern'] = $pattern;
|
||||
$this->moduleHandler->alter('pathauto_alias', $alias, $context);
|
||||
|
||||
// If we have arrived at an empty string, discontinue.
|
||||
if (!Unicode::strlen($alias)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// If the alias already exists, generate a new, hopefully unique, variant.
|
||||
$original_alias = $alias;
|
||||
$this->aliasUniquifier->uniquify($alias, $source, $langcode);
|
||||
if ($original_alias != $alias) {
|
||||
// Alert the user why this happened.
|
||||
$this->messenger->addMessage($this->t('The automatically generated alias %original_alias conflicted with an existing alias. Alias changed to %alias.', array(
|
||||
'%original_alias' => $original_alias,
|
||||
'%alias' => $alias,
|
||||
)), $op);
|
||||
}
|
||||
|
||||
// Return the generated alias if requested.
|
||||
if ($op == 'return') {
|
||||
return $alias;
|
||||
}
|
||||
|
||||
// Build the new path alias array and send it off to be created.
|
||||
$path = array(
|
||||
'source' => $source,
|
||||
'alias' => $alias,
|
||||
'language' => $langcode,
|
||||
);
|
||||
|
||||
return $this->aliasStorageHelper->save($path, $existing_alias, $op);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads pathauto patterns for a given entity type ID
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* An entity type ID.
|
||||
*
|
||||
* @return \Drupal\pathauto\PathautoPatternInterface[]
|
||||
* A list of patterns, sorted by weight.
|
||||
*/
|
||||
protected function getPatternByEntityType($entity_type_id) {
|
||||
if (!isset($this->patternsByEntityType[$entity_type_id])) {
|
||||
$ids = \Drupal::entityQuery('pathauto_pattern')
|
||||
->condition('type', array_keys(\Drupal::service('plugin.manager.alias_type')
|
||||
->getPluginDefinitionByType($this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id))))
|
||||
->condition('status', 1)
|
||||
->sort('weight')
|
||||
->execute();
|
||||
|
||||
$this->patternsByEntityType[$entity_type_id] = \Drupal::entityTypeManager()
|
||||
->getStorage('pathauto_pattern')
|
||||
->loadMultiple($ids);
|
||||
}
|
||||
|
||||
return $this->patternsByEntityType[$entity_type_id];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPatternByEntity(EntityInterface $entity) {
|
||||
$langcode = $entity->language()->getId();
|
||||
if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) {
|
||||
foreach ($this->getPatternByEntityType($entity->getEntityTypeId()) as $pattern) {
|
||||
if ($pattern->applies($entity)) {
|
||||
$this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = $pattern;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If still not set.
|
||||
if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) {
|
||||
$this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = NULL;
|
||||
}
|
||||
}
|
||||
return $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetCaches() {
|
||||
$this->patterns = [];
|
||||
$this->patternsByEntityType = [];
|
||||
$this->aliasCleaner->resetCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateEntityAlias(EntityInterface $entity, $op, array $options = array()) {
|
||||
// Skip if the entity does not have the path field.
|
||||
if (!($entity instanceof ContentEntityInterface) || !$entity->hasField('path')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Skip if pathauto processing is disabled.
|
||||
if ($entity->path->pathauto != PathautoState::CREATE && empty($options['force'])) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Only act if this is the default revision.
|
||||
if ($entity instanceof RevisionableInterface && !$entity->isDefaultRevision()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$options += array('language' => $entity->language()->getId());
|
||||
$type = $entity->getEntityTypeId();
|
||||
|
||||
// Skip processing if the entity has no pattern.
|
||||
if (!$this->getPatternByEntity($entity)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Deal with taxonomy specific logic.
|
||||
// @todo Update and test forum related code.
|
||||
if ($type == 'taxonomy_term') {
|
||||
|
||||
$config_forum = $this->configFactory->get('forum.settings');
|
||||
if ($entity->getVocabularyId() == $config_forum->get('vocabulary')) {
|
||||
$type = 'forum';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->createEntityAlias($entity, $op);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
drupal_set_message($e->getMessage(), 'error');
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// @todo Move this to a method on the pattern plugin.
|
||||
if ($type == 'taxonomy_term') {
|
||||
foreach ($this->loadTermChildren($entity->id()) as $subterm) {
|
||||
$this->updateEntityAlias($subterm, $op, $options);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all children of a term ID.
|
||||
*
|
||||
* @param int $tid
|
||||
* Term ID to retrieve parents for.
|
||||
*
|
||||
* @return \Drupal\taxonomy\TermInterface[]
|
||||
* An array of term objects that are the children of the term $tid.
|
||||
*/
|
||||
protected function loadTermChildren($tid) {
|
||||
return $this->entityTypeManager->getStorage('taxonomy_term')->loadChildren($tid);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Provides and interface for PathautoGenerator.
|
||||
*/
|
||||
interface PathautoGeneratorInterface {
|
||||
|
||||
/**
|
||||
* "Do nothing. Leave the old alias intact."
|
||||
*/
|
||||
const UPDATE_ACTION_NO_NEW = 0;
|
||||
|
||||
/**
|
||||
* "Create a new alias. Leave the existing alias functioning."
|
||||
*/
|
||||
const UPDATE_ACTION_LEAVE = 1;
|
||||
|
||||
/**
|
||||
* "Create a new alias. Delete the old alias."
|
||||
*/
|
||||
const UPDATE_ACTION_DELETE = 2;
|
||||
|
||||
/**
|
||||
* Remove the punctuation from the alias.
|
||||
*/
|
||||
const PUNCTUATION_REMOVE = 0;
|
||||
|
||||
/**
|
||||
* Replace the punctuation with the separator in the alias.
|
||||
*/
|
||||
const PUNCTUATION_REPLACE = 1;
|
||||
|
||||
/**
|
||||
* Leave the punctuation as it is in the alias.
|
||||
*/
|
||||
const PUNCTUATION_DO_NOTHING = 2;
|
||||
|
||||
/**
|
||||
* Resets internal caches.
|
||||
*/
|
||||
public function resetCaches();
|
||||
|
||||
/**
|
||||
* Load an alias pattern entity by entity, bundle, and language.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* An entity.
|
||||
* @return \Drupal\pathauto\PathautoPatternInterface|null
|
||||
*/
|
||||
public function getPatternByEntity(EntityInterface $entity);
|
||||
|
||||
/**
|
||||
* Apply patterns to create an alias.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
* @param string $op
|
||||
* Operation being performed on the content being aliased
|
||||
* ('insert', 'update', 'return', or 'bulkupdate').
|
||||
*
|
||||
* @return array|string
|
||||
* The alias that was created.
|
||||
*
|
||||
* @see _pathauto_set_alias()
|
||||
*/
|
||||
public function createEntityAlias(EntityInterface $entity, $op);
|
||||
|
||||
/**
|
||||
* Creates or updates an alias for the given entity.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* Entity for which to update the alias.
|
||||
* @param string $op
|
||||
* The operation performed (insert, update)
|
||||
* @param array $options
|
||||
* - force: will force updating the path
|
||||
* - language: the language for which to create the alias
|
||||
*
|
||||
* @return array|null
|
||||
* - An array with alias data in case the alias has been created or updated.
|
||||
* - NULL if no operation performed.
|
||||
*/
|
||||
public function updateEntityAlias(EntityInterface $entity, $op, array $options = array());
|
||||
|
||||
}
|
58
web/modules/contrib/pathauto/src/PathautoItem.php
Normal file
58
web/modules/contrib/pathauto/src/PathautoItem.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\path\Plugin\Field\FieldType\PathItem;
|
||||
|
||||
/**
|
||||
* Extends the default PathItem implementation to generate aliases.
|
||||
*/
|
||||
class PathautoItem extends PathItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties = parent::propertyDefinitions($field_definition);
|
||||
$properties['pathauto'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Pathauto state'))
|
||||
->setDescription(t('Whether an automated alias should be created or not.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\pathauto\PathautoState');
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave($update) {
|
||||
// Only allow the parent implementation to act if pathauto will not create
|
||||
// an alias.
|
||||
if ($this->pathauto == PathautoState::SKIP) {
|
||||
parent::postSave($update);
|
||||
}
|
||||
$this->get('pathauto')->persist();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
// Make sure that the pathauto state flag does not get lost if just that is
|
||||
// changed.
|
||||
return !$this->alias && !$this->get('pathauto')->hasValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyDefaultValue($notify = TRUE) {
|
||||
parent::applyDefaultValue($notify);
|
||||
// Created fields default creating a new alias.
|
||||
$this->setValue(array('pathauto' => PathautoState::CREATE), $notify);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
181
web/modules/contrib/pathauto/src/PathautoPatternInterface.php
Normal file
181
web/modules/contrib/pathauto/src/PathautoPatternInterface.php
Normal file
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining Pathauto pattern entities.
|
||||
*/
|
||||
interface PathautoPatternInterface extends ConfigEntityInterface {
|
||||
|
||||
/**
|
||||
* Get the tokenized pattern used during alias generation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPattern();
|
||||
|
||||
/**
|
||||
* Set the tokenized pattern to use during alias generation.
|
||||
*
|
||||
* @param string $pattern
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setPattern($pattern);
|
||||
|
||||
/**
|
||||
* Gets the type of this pattern.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* @return \Drupal\pathauto\AliasTypeInterface
|
||||
*/
|
||||
public function getAliasType();
|
||||
|
||||
/**
|
||||
* Gets the weight of this pattern (compared to other patterns of this type).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWeight();
|
||||
|
||||
/**
|
||||
* Sets the weight of this pattern (compared to other patterns of this type).
|
||||
*
|
||||
* @param int $weight
|
||||
* The weight of the variant.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight);
|
||||
|
||||
/**
|
||||
* Returns the contexts of this pattern.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextInterface[]
|
||||
*/
|
||||
public function getContexts();
|
||||
|
||||
/**
|
||||
* Returns whether a relationship exists.
|
||||
*
|
||||
* @param string $token
|
||||
* Relationship identifier.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the relationship exists, FALSE otherwise.
|
||||
*/
|
||||
public function hasRelationship($token);
|
||||
|
||||
/**
|
||||
* Adds a relationship.
|
||||
*
|
||||
* The relationship will not be changed if it already exists.
|
||||
*
|
||||
* @param string $token
|
||||
* Relationship identifier.
|
||||
* @param string|null $label
|
||||
* (optional) A label, will use the label of the referenced context if not
|
||||
* provided.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addRelationship($token, $label = NULL);
|
||||
|
||||
/**
|
||||
* Replaces a relationship.
|
||||
*
|
||||
* Only already existing relationships are updated.
|
||||
*
|
||||
* @param string $token
|
||||
* Relationship identifier.
|
||||
* @param string|null $label
|
||||
* (optional) A label, will use the label of the referenced context if not
|
||||
* provided.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function replaceRelationship($token, $label);
|
||||
|
||||
/**
|
||||
* Removes a relationship.
|
||||
*
|
||||
* @param string $token
|
||||
* Relationship identifier.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeRelationship($token);
|
||||
|
||||
/**
|
||||
* Returns a list of relationships.
|
||||
*
|
||||
* @return array[]
|
||||
* Keys are context tokens, and values are arrays with the following keys:
|
||||
* - label (string|null, optional): The human-readable label of this
|
||||
* relationship.
|
||||
*/
|
||||
public function getRelationships();
|
||||
|
||||
/**
|
||||
* Gets the selection condition collection.
|
||||
*
|
||||
* @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
|
||||
*/
|
||||
public function getSelectionConditions();
|
||||
|
||||
/**
|
||||
* Adds selection criteria.
|
||||
*
|
||||
* @param array $configuration
|
||||
* Configuration of the selection criteria.
|
||||
*
|
||||
* @return string
|
||||
* The condition id of the new criteria.
|
||||
*/
|
||||
public function addSelectionCondition(array $configuration);
|
||||
|
||||
/**
|
||||
* Gets selection criteria by condition id.
|
||||
*
|
||||
* @param string $condition_id
|
||||
* The id of the condition.
|
||||
*
|
||||
* @return \Drupal\Core\Condition\ConditionInterface
|
||||
*/
|
||||
public function getSelectionCondition($condition_id);
|
||||
|
||||
/**
|
||||
* Removes selection criteria by condition id.
|
||||
*
|
||||
* @param string $condition_id
|
||||
* The id of the condition.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeSelectionCondition($condition_id);
|
||||
|
||||
/**
|
||||
* Gets the selection logic used by the criteria (ie. "and" or "or").
|
||||
*
|
||||
* @return string
|
||||
* Either "and" or "or"; represents how the selection criteria are combined.
|
||||
*/
|
||||
public function getSelectionLogic();
|
||||
|
||||
/**
|
||||
* Determines if this pattern can apply a given object.
|
||||
*
|
||||
* @param $object
|
||||
* The object used to determine if this plugin can apply.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function applies($object);
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Config\Entity\DraggableListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Provides a listing of Pathauto pattern entities.
|
||||
*/
|
||||
class PathautoPatternListBuilder extends DraggableListBuilder {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $limit = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'pathauto_pattern_list';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildHeader() {
|
||||
$header['label'] = $this->t('Label');
|
||||
$header['pattern'] = $this->t('Pattern');
|
||||
$header['type'] = $this->t('Pattern type');
|
||||
$header['conditions'] = $this->t('Conditions');
|
||||
return $header + parent::buildHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildRow(EntityInterface $entity) {
|
||||
/* @var \Drupal\pathauto\PathautoPatternInterface $entity */
|
||||
$row['label'] = $entity->label();
|
||||
$row['patern']['#markup'] = $entity->getPattern();
|
||||
$row['type']['#markup'] = $entity->getAliasType()->getLabel();
|
||||
$row['conditions']['#theme'] = 'item_list';
|
||||
foreach ($entity->getSelectionConditions() as $condition) {
|
||||
$row['conditions']['#items'][] = $condition->summary();
|
||||
}
|
||||
return $row + parent::buildRow($entity);
|
||||
}
|
||||
|
||||
}
|
97
web/modules/contrib/pathauto/src/PathautoState.php
Normal file
97
web/modules/contrib/pathauto/src/PathautoState.php
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
|
||||
/**
|
||||
* A property that stores in keyvalue whether an entity should receive an alias.
|
||||
*/
|
||||
class PathautoState extends TypedData {
|
||||
|
||||
/**
|
||||
* An automatic alias should not be created.
|
||||
*/
|
||||
const SKIP = 0;
|
||||
|
||||
/**
|
||||
* An automatic alias should be created.
|
||||
*/
|
||||
const CREATE = 1;
|
||||
|
||||
/**
|
||||
* Pathauto state.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Field\FieldItemInterface
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
if ($this->value === NULL) {
|
||||
// If no value has been set or loaded yet, try to load a value if this
|
||||
// entity has already been saved.
|
||||
$this->value = \Drupal::keyValue($this->getCollection())
|
||||
->get($this->parent->getEntity()->id());
|
||||
// If it was not yet saved or no value was found, then set the flag to
|
||||
// create the alias if there is a matching pattern.
|
||||
if ($this->value === NULL) {
|
||||
$entity = $this->parent->getEntity();
|
||||
$pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
|
||||
$this->value = !empty($pattern) ? static::CREATE : static::SKIP;
|
||||
}
|
||||
}
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($value, $notify = TRUE) {
|
||||
$this->value = $value;
|
||||
// Notify the parent of any changes.
|
||||
if ($notify && isset($this->parent)) {
|
||||
$this->parent->onChange($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if a value was set.
|
||||
*/
|
||||
public function hasValue() {
|
||||
return $this->value !== NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the state.
|
||||
*/
|
||||
public function persist() {
|
||||
\Drupal::keyValue($this->getCollection())->set(
|
||||
$this->parent->getEntity()
|
||||
->id(), $this->value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the stored state.
|
||||
*/
|
||||
public function purge() {
|
||||
\Drupal::keyValue($this->getCollection())
|
||||
->delete($this->parent->getEntity()->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value collection that should be used for the given entity.
|
||||
* @return string
|
||||
*/
|
||||
protected function getCollection() {
|
||||
return 'pathauto_state.' . $this->parent->getEntity()->getEntityTypeId();
|
||||
}
|
||||
|
||||
}
|
71
web/modules/contrib/pathauto/src/PathautoWidget.php
Normal file
71
web/modules/contrib/pathauto/src/PathautoWidget.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\path\Plugin\Field\FieldWidget\PathWidget;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Extends the core path widget.
|
||||
*/
|
||||
class PathautoWidget extends PathWidget {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element = parent::formElement($items, $delta, $element, $form, $form_state);
|
||||
$entity = $items->getEntity();
|
||||
|
||||
// Taxonomy terms do not have an actual fieldset for path settings.
|
||||
// Merge in the defaults.
|
||||
// @todo Impossible to do this in widget, use another solution
|
||||
/*
|
||||
$form['path'] += array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => $this->t('URL path settings'),
|
||||
'#collapsible' => TRUE,
|
||||
'#collapsed' => empty($form['path']['alias']),
|
||||
'#group' => 'additional_settings',
|
||||
'#attributes' => array(
|
||||
'class' => array('path-form'),
|
||||
),
|
||||
'#access' => \Drupal::currentUser()->hasPermission('create url aliases') || \Drupal::currentUser()->hasPermission('administer url aliases'),
|
||||
'#weight' => 30,
|
||||
'#tree' => TRUE,
|
||||
'#element_validate' => array('path_form_element_validate'),
|
||||
);*/
|
||||
|
||||
$pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
|
||||
if (empty($pattern)) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
if (\Drupal::currentUser()->hasPermission('administer pathauto')) {
|
||||
$description = $this->t('Uncheck this to create a custom alias below. <a href="@admin_link">Configure URL alias patterns.</a>', ['@admin_link' => Url::fromRoute('entity.pathauto_pattern.collection')->toString()]);
|
||||
}
|
||||
else {
|
||||
$description = $this->t('Uncheck this to create a custom alias below.');
|
||||
}
|
||||
|
||||
$element['pathauto'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Generate automatic URL alias'),
|
||||
'#default_value' => $entity->path->pathauto,
|
||||
'#description' => $description,
|
||||
'#weight' => -1,
|
||||
);
|
||||
|
||||
// Add JavaScript that will disable the path textfield when the automatic
|
||||
// alias checkbox is checked.
|
||||
$element['alias']['#states']['disabled']['input[name="path[' . $delta . '][pathauto]"]'] = array('checked' => TRUE);
|
||||
|
||||
// Override path.module's vertical tabs summary.
|
||||
$element['alias']['#attached']['library'] = ['pathauto/widget'];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Plugin\Action;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Action\ActionBase;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
|
||||
/**
|
||||
* Pathauto entity update action.
|
||||
*
|
||||
* @Action(
|
||||
* id = "pathauto_update_alias",
|
||||
* label = @Translation("Update URL alias of an entity"),
|
||||
* )
|
||||
*/
|
||||
class UpdateAction extends ActionBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($entity = NULL) {
|
||||
$entity->path->pathauto = PathautoState::CREATE;
|
||||
\Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', array('message' => TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
|
||||
$result = AccessResult::allowedIfHasPermission($account, 'create url aliases');
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Plugin\Deriver;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
use Drupal\token\TokenEntityMapperInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Deriver that exposes content entities as alias type plugins.
|
||||
*/
|
||||
class EntityAliasTypeDeriver extends DeriverBase implements ContainerDeriverInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* @var \Drupal\token\TokenEntityMapperInterface
|
||||
*/
|
||||
protected $tokenEntityMapper;
|
||||
|
||||
/**
|
||||
* Constructs new EntityAliasTypeDeriver.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
|
||||
* The entity field manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
* @apram \Drupal\Token\TokenEntityMapperInterface $token_entity_mapper
|
||||
* The token entity mapper.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mapper) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityFieldManager = $entity_field_manager;
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->tokenEntityMapper = $token_entity_mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('entity_field.manager'),
|
||||
$container->get('string_translation'),
|
||||
$container->get('token.entity_mapper')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
// An entity type must have a canonical link template and support fields.
|
||||
if ($entity_type->hasLinkTemplate('canonical') && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class)) {
|
||||
$base_fields = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
|
||||
if (!isset($base_fields['path'])) {
|
||||
// The entity type does not have a path field and is therefore not
|
||||
// supported.
|
||||
continue;
|
||||
}
|
||||
$this->derivatives[$entity_type_id] = $base_plugin_definition;
|
||||
$this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel();
|
||||
$this->derivatives[$entity_type_id]['types'] = [$this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id)];
|
||||
$this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider();
|
||||
$this->derivatives[$entity_type_id]['context'] = [
|
||||
$entity_type_id => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()]))
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Plugin\pathauto\AliasType;
|
||||
|
||||
/**
|
||||
* Defines a fallback plugin for missing block plugins.
|
||||
*
|
||||
* @AliasType(
|
||||
* id = "broken",
|
||||
* label = @Translation("Broken"),
|
||||
* admin_label = @Translation("Broken/Missing"),
|
||||
* category = @Translation("AliasType"),
|
||||
* )
|
||||
*/
|
||||
class Broken extends EntityAliasTypeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->t('Broken type');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,342 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Plugin\pathauto\AliasType;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginBase;
|
||||
use Drupal\pathauto\AliasTypeBatchUpdateInterface;
|
||||
use Drupal\pathauto\AliasTypeInterface;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A pathauto alias type plugin for entities with canonical links.
|
||||
*
|
||||
* @AliasType(
|
||||
* id = "canonical_entities",
|
||||
* deriver = "\Drupal\pathauto\Plugin\Deriver\EntityAliasTypeDeriver"
|
||||
* )
|
||||
*/
|
||||
class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInterface, AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The entity manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The key/value manager service.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
|
||||
*/
|
||||
protected $keyValue;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The path prefix for this entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Constructs a EntityAliasTypeBase instance.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity manager service.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
|
||||
* The key/value manager service.
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager, KeyValueFactoryInterface $key_value, Connection $database) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->languageManager = $language_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->keyValue = $key_value;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('module_handler'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('keyvalue'),
|
||||
$container->get('database')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
$definition = $this->getPluginDefinition();
|
||||
// Cast the admin label to a string since it is an object.
|
||||
// @see \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
return (string) $definition['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTokenTypes() {
|
||||
$definition = $this->getPluginDefinition();
|
||||
return $definition['types'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function batchUpdate($action, &$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
|
||||
$id_key = $entity_type->getKey('id');
|
||||
|
||||
$query = $this->database->select($entity_type->get('base_table'), 'base_table');
|
||||
$query->leftJoin('url_alias', 'ua', "CONCAT('" . $this->getSourcePrefix() . "' , base_table.$id_key) = ua.source");
|
||||
$query->addField('base_table', $id_key, 'id');
|
||||
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
$query->isNull('ua.source');
|
||||
break;
|
||||
case 'update':
|
||||
$query->isNotNull('ua.source');
|
||||
break;
|
||||
case 'all':
|
||||
// Nothing to do. We want all paths.
|
||||
break;
|
||||
default:
|
||||
// Unknown action. Abort!
|
||||
return;
|
||||
}
|
||||
$query->condition('base_table.' . $id_key, $context['sandbox']['current'], '>');
|
||||
$query->orderBy('base_table.' . $id_key);
|
||||
$query->addTag('pathauto_bulk_update');
|
||||
$query->addMetaData('entity', $this->getEntityTypeId());
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no entities to update, then stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 25);
|
||||
$ids = $query->execute()->fetchCol();
|
||||
|
||||
$updates = $this->bulkUpdate($ids);
|
||||
$context['sandbox']['count'] += count($ids);
|
||||
$context['sandbox']['current'] = max($ids);
|
||||
$context['results']['updates'] += $updates;
|
||||
$context['message'] = $this->t('Updated alias for %label @id.', array('%label' => $entity_type->getLabel(), '@id' => end($ids)));
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function batchDelete(&$context) {
|
||||
if (!isset($context['sandbox']['current'])) {
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
}
|
||||
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
|
||||
$id_key = $entity_type->getKey('id');
|
||||
|
||||
$query = $this->database->select($entity_type->get('base_table'), 'base_table');
|
||||
$query->innerJoin('url_alias', 'ua', "CONCAT('" . $this->getSourcePrefix() . "' , base_table.$id_key) = ua.source");
|
||||
$query->addField('base_table', $id_key, 'id');
|
||||
$query->addField('ua', 'pid');
|
||||
$query->condition('ua.pid', $context['sandbox']['current'], '>');
|
||||
$query->orderBy('ua.pid');
|
||||
$query->addTag('pathauto_bulk_delete');
|
||||
$query->addMetaData('entity', $this->getEntityTypeId());
|
||||
|
||||
// Get the total amount of items to process.
|
||||
if (!isset($context['sandbox']['total'])) {
|
||||
$context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// If there are no entities to delete, then stop immediately.
|
||||
if (!$context['sandbox']['total']) {
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$query->range(0, 100);
|
||||
$pids_by_id = $query->execute()->fetchAllKeyed();
|
||||
|
||||
$this->bulkDelete($pids_by_id);
|
||||
$context['sandbox']['count'] += count($pids_by_id);
|
||||
$context['sandbox']['current'] = max($pids_by_id);
|
||||
$context['results']['deletions'][] = $this->getLabel();
|
||||
|
||||
if ($context['sandbox']['count'] != $context['sandbox']['total']) {
|
||||
$context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity type ID.
|
||||
*
|
||||
* @return string
|
||||
* The entity type ID.
|
||||
*/
|
||||
protected function getEntityTypeId() {
|
||||
return $this->getDerivativeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the URL aliases for multiple entities.
|
||||
*
|
||||
* @param array $ids
|
||||
* An array of entity IDs.
|
||||
* @param array $options
|
||||
* An optional array of additional options.
|
||||
*
|
||||
* @return int
|
||||
* The number of updated URL aliases.
|
||||
*/
|
||||
protected function bulkUpdate(array $ids, array $options = array()) {
|
||||
$options += array('message' => FALSE);
|
||||
$updates = 0;
|
||||
|
||||
$entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids);
|
||||
foreach ($entities as $entity) {
|
||||
// Update aliases for the entity's default language and its translations.
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$translated_entity = $entity->getTranslation($langcode);
|
||||
$result = \Drupal::service('pathauto.generator')->updateEntityAlias($translated_entity, 'bulkupdate', $options);
|
||||
if ($result) {
|
||||
$updates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($options['message'])) {
|
||||
drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated 1 %label URL alias.', 'Updated @count %label URL aliases.'), array('%label' => $this->getLabel()));
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the URL aliases for multiple entities.
|
||||
*
|
||||
* @param int[] $pids_by_id
|
||||
* A list of path IDs keyed by entity ID.
|
||||
*/
|
||||
protected function bulkDelete(array $pids_by_id) {
|
||||
$collection = 'pathauto_state.' . $this->getEntityTypeId();
|
||||
$states = $this->keyValue->get($collection)->getMultiple(array_keys($pids_by_id));
|
||||
|
||||
$pids = [];
|
||||
foreach ($pids_by_id as $id => $pid) {
|
||||
// Only delete aliases that were created by this module.
|
||||
if (isset($states[$id]) && $states[$id] == PathautoState::CREATE) {
|
||||
$pids[] = $pid;
|
||||
}
|
||||
}
|
||||
\Drupal::service('pathauto.alias_storage_helper')->deleteMultiple($pids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = [];
|
||||
$dependencies['module'][] = $this->entityTypeManager->getDefinition($this->getEntityTypeId())->getProvider();
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($object) {
|
||||
return $object instanceof FieldableEntityInterface && $object->getEntityTypeId() == $this->getEntityTypeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourcePrefix() {
|
||||
if (empty($this->prefix)) {
|
||||
$entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
|
||||
$path = $entity_type->getLinkTemplate('canonical');
|
||||
$this->prefix = substr($path, 0, strpos($path, '{'));
|
||||
}
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setContextValue($name, $value) {
|
||||
// Overridden to avoid merging existing cacheability metadata, which is not
|
||||
// relevant for alias type plugins.
|
||||
$this->context[$name] = new Context($this->getContextDefinition($name), $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Plugin\pathauto\AliasType;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* A pathauto alias type plugin for forum terms.
|
||||
*
|
||||
* @AliasType(
|
||||
* id = "forum",
|
||||
* label = @Translation("Forum"),
|
||||
* types = {"term"},
|
||||
* provider = "forum",
|
||||
* context = {
|
||||
* "taxonomy_term" = @ContextDefinition("entity:taxonomy_term")
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class ForumAliasType extends EntityAliasTypeBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs a ForumAliasType instance.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity manager service.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
|
||||
* The key/value manager service.
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database service.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager, KeyValueFactoryInterface $key_value, Connection $database, ConfigFactoryInterface $config_factory) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $module_handler, $language_manager, $entity_type_manager, $key_value, $database);
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('module_handler'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('keyvalue'),
|
||||
$container->get('database'),
|
||||
$container->get('config.factory')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntityTypeId() {
|
||||
return 'taxonomy_term';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSourcePrefix() {
|
||||
return '/forum/';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies($object) {
|
||||
if (parent::applies($object)) {
|
||||
/** @var \Drupal\taxonomy\TermInterface $object */
|
||||
$vid = $this->configFactory->get('forum.settings')->get('vocabulary');
|
||||
return $object->getVocabularyId() == $vid;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\pathauto\PathautoGeneratorInterface;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Bulk update functionality tests.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoBulkUpdateTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'pathauto', 'forum');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* The created nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* The created patterns.
|
||||
*
|
||||
* @var \Drupal\pathauto\PathautoPatternInterface
|
||||
*/
|
||||
protected $patterns;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer forums',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->patterns = array();
|
||||
$this->patterns['node'] = $this->createPattern('node', '/content/[node:title]');
|
||||
$this->patterns['user'] = $this->createPattern('user', '/users/[user:name]');
|
||||
$this->patterns['forum'] = $this->createPattern('forum', '/forums/[term:name]');
|
||||
}
|
||||
|
||||
function testBulkUpdate() {
|
||||
// Create some nodes.
|
||||
$this->nodes = array();
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->nodes[$node->id()] = $node;
|
||||
}
|
||||
|
||||
// Clear out all aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
// Bulk create aliases.
|
||||
$edit = array(
|
||||
'update[canonical_entities:node]' => TRUE,
|
||||
'update[canonical_entities:user]' => TRUE,
|
||||
'update[forum]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
|
||||
// This has generated 8 aliases: 5 nodes, 2 users and 1 forum.
|
||||
$this->assertText('Generated 8 URL aliases.');
|
||||
|
||||
// Check that aliases have actually been created.
|
||||
foreach ($this->nodes as $node) {
|
||||
$this->assertEntityAliasExists($node);
|
||||
}
|
||||
$this->assertEntityAliasExists($this->adminUser);
|
||||
// This is the default "General discussion" forum.
|
||||
$this->assertAliasExists(['source' => '/taxonomy/term/1']);
|
||||
|
||||
// Add a new node.
|
||||
$new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => PathautoState::SKIP)));
|
||||
|
||||
// Run the update again which should not run against any nodes.
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText('No new URL aliases to generate.');
|
||||
$this->assertNoEntityAliasExists($new_node);
|
||||
|
||||
// Make sure existing aliases can be overriden.
|
||||
$this->drupalPostForm('admin/config/search/path/settings', ['update_action' => PathautoGeneratorInterface::UPDATE_ACTION_DELETE], t('Save configuration'));
|
||||
|
||||
// Patterns did not change, so no aliases should be regenerated.
|
||||
$edit['action'] = 'all';
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText('No new URL aliases to generate.');
|
||||
|
||||
// Update the node pattern, and leave other patterns alone. Existing nodes should get a new alias,
|
||||
// except the node above whose alias is manually set. Other aliases must be left alone.
|
||||
$this->patterns['node']->delete();
|
||||
$this->patterns['node'] = $this->createPattern('node', '/archive/node-[node:nid]');
|
||||
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText('Generated 5 URL aliases.');
|
||||
|
||||
// Prevent existing aliases to be overriden. The bulk generate page should only offer
|
||||
// to create an alias for paths which have none.
|
||||
$this->drupalPostForm('admin/config/search/path/settings', ['update_action' => PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW], t('Save configuration'));
|
||||
|
||||
$this->drupalGet('admin/config/search/path/update_bulk');
|
||||
$this->assertFieldByName('action', 'create');
|
||||
$this->assertText('Pathauto settings are set to ignore paths which already have a URL alias.');
|
||||
$this->assertNoFieldByName('action', 'update');
|
||||
$this->assertNoFieldByName('action', 'all');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests alias generation for nodes that existed before installing Pathauto.
|
||||
*/
|
||||
function testBulkUpdateExistingContent() {
|
||||
// Create a node.
|
||||
$node = $this->drupalCreateNode();
|
||||
|
||||
// Delete its alias and Pathauto metadata.
|
||||
\Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($node);
|
||||
$node->path->first()->get('pathauto')->purge();
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache(array($node->id()));
|
||||
|
||||
// Execute bulk generation.
|
||||
// Bulk create aliases.
|
||||
$edit = array(
|
||||
'update[canonical_entities:node]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
|
||||
// Verify that the alias was created for the node.
|
||||
$this->assertText('Generated 1 URL alias.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Tests pathauto settings form.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoEnablingEntityTypesTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'pathauto', 'comment');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
$this->addDefaultCommentField('node', 'article');
|
||||
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer nodes',
|
||||
'post comments',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* A suite of tests to verify if the feature to enable and disable the
|
||||
* ability to define alias patterns for a given entity type works. Test with
|
||||
* the comment module, as it is not enabled by default.
|
||||
*/
|
||||
function testEnablingEntityTypes() {
|
||||
// Verify that the comment entity type is not available when trying to add
|
||||
// a new pattern, nor "broken".
|
||||
$this->drupalGet('/admin/config/search/path/patterns/add');
|
||||
$this->assertEqual(count($this->cssSelect('option[value = "canonical_entities:comment"]:contains(Comment)')), 0);
|
||||
$this->assertEqual(count($this->cssSelect('option:contains(Broken)')), 0);
|
||||
|
||||
// Enable the entity type and create a pattern for it.
|
||||
$this->drupalGet('/admin/config/search/path/settings');
|
||||
$edit = [
|
||||
'enabled_entity_types[comment]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save configuration');
|
||||
$this->createPattern('comment', '/comment/[comment:body]');
|
||||
|
||||
// Create a node, a comment type and a comment entity.
|
||||
$node = $this->drupalCreateNode(['type' => 'article']);
|
||||
$this->drupalGet('/node/' . $node->id());
|
||||
$edit = [
|
||||
'comment_body[0][value]' => 'test-body',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
|
||||
// Verify that an alias has been generated and that the type can no longer
|
||||
// be disabled.
|
||||
$this->assertAliasExists(['alias' => '/comment/test-body']);
|
||||
$this->drupalGet('/admin/config/search/path/settings');
|
||||
$this->assertEqual(count($this->cssSelect('input[name = "enabled_entity_types[comment]"][disabled = "disabled"]')), 1);
|
||||
}
|
||||
|
||||
}
|
201
web/modules/contrib/pathauto/src/Tests/PathautoLocaleTest.php
Normal file
201
web/modules/contrib/pathauto/src/Tests/PathautoLocaleTest.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Test pathauto functionality with localization and translation.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoLocaleTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'pathauto', 'locale', 'content_translation');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create Article node type.
|
||||
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when an English node is updated, its old English alias is
|
||||
* updated and its newer French alias is left intact.
|
||||
*/
|
||||
function testLanguageAliases() {
|
||||
|
||||
$this->createPattern('node', '/content/[node:title]');
|
||||
|
||||
// Add predefined French language.
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$node = array(
|
||||
'title' => 'English node',
|
||||
'langcode' => 'en',
|
||||
'path' => array(array(
|
||||
'alias' => '/english-node',
|
||||
'pathauto' => FALSE,
|
||||
)),
|
||||
);
|
||||
$node = $this->drupalCreateNode($node);
|
||||
$english_alias = \Drupal::service('path.alias_storage')->load(array('alias' => '/english-node', 'langcode' => 'en'));
|
||||
$this->assertTrue($english_alias, 'Alias created with proper language.');
|
||||
|
||||
// Also save a French alias that should not be left alone, even though
|
||||
// it is the newer alias.
|
||||
$this->saveEntityAlias($node, '/french-node', 'fr');
|
||||
|
||||
// Add an alias with the soon-to-be generated alias, causing the upcoming
|
||||
// alias update to generate a unique alias with the '-0' suffix.
|
||||
$this->saveAlias('/node/invalid', '/content/english-node', Language::LANGCODE_NOT_SPECIFIED);
|
||||
|
||||
// Update the node, triggering a change in the English alias.
|
||||
$node->path->pathauto = PathautoState::CREATE;
|
||||
$node->save();
|
||||
|
||||
// Check that the new English alias replaced the old one.
|
||||
$this->assertEntityAlias($node, '/content/english-node-0', 'en');
|
||||
$this->assertEntityAlias($node, '/french-node', 'fr');
|
||||
$this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => '/content/english-node-0'));
|
||||
|
||||
// Create a new node with the same title as before but without
|
||||
// specifying a language.
|
||||
$node = $this->drupalCreateNode(array('title' => 'English node', 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED));
|
||||
|
||||
// Check that the new node had a unique alias generated with the '-0'
|
||||
// suffix.
|
||||
$this->assertEntityAlias($node, '/content/english-node-0', LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that patterns work on multilingual content.
|
||||
*/
|
||||
function testLanguagePatterns() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
// Add French language.
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'fr',
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
$this->enableArticleTranslation();
|
||||
|
||||
// Create a pattern for English articles.
|
||||
$this->drupalGet('admin/config/search/path/patterns/add');
|
||||
$edit = array(
|
||||
'type' => 'canonical_entities:node',
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'type');
|
||||
$edit += array(
|
||||
'pattern' => '/the-articles/[node:title]',
|
||||
'label' => 'English articles',
|
||||
'id' => 'english_articles',
|
||||
'bundles[article]' => TRUE,
|
||||
'languages[en]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Pattern English articles saved.');
|
||||
|
||||
// Create a pattern for French articles.
|
||||
$this->drupalGet('admin/config/search/path/patterns/add');
|
||||
$edit = array(
|
||||
'type' => 'canonical_entities:node',
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'type');
|
||||
$edit += array(
|
||||
'pattern' => '/les-articles/[node:title]',
|
||||
'label' => 'French articles',
|
||||
'id' => 'french_articles',
|
||||
'bundles[article]' => TRUE,
|
||||
'languages[fr]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Pattern French articles saved.');
|
||||
|
||||
// Create a node and its translation. Assert aliases.
|
||||
$edit = array(
|
||||
'title[0][value]' => 'English node',
|
||||
'langcode[0][value]' => 'en',
|
||||
);
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$english_node = $this->drupalGetNodeByTitle('English node');
|
||||
$this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en');
|
||||
|
||||
$this->drupalGet('node/' . $english_node->id() . '/translations');
|
||||
$this->clickLink(t('Add'));
|
||||
$edit = array(
|
||||
'title[0][value]' => 'French node',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save and keep published (this translation)'));
|
||||
$this->rebuildContainer();
|
||||
$english_node = $this->drupalGetNodeByTitle('English node');
|
||||
$french_node = $english_node->getTranslation('fr');
|
||||
$this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr');
|
||||
|
||||
// Bulk delete and Bulk generate patterns. Assert aliases.
|
||||
$this->deleteAllAliases();
|
||||
// Bulk create aliases.
|
||||
$edit = array(
|
||||
'update[canonical_entities:node]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
|
||||
$this->assertText(t('Generated 2 URL aliases.'));
|
||||
$this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en');
|
||||
$this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the alias created for a node with language Not Applicable.
|
||||
*/
|
||||
public function testLanguageNotApplicable() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->enableArticleTranslation();
|
||||
|
||||
// Create a pattern for nodes.
|
||||
$pattern = $this->createPattern('node', '/content/[node:title]', -1);
|
||||
$pattern->save();
|
||||
|
||||
// Create a node with language Not Applicable.
|
||||
$node = $this->createNode(['type' => 'article', 'title' => 'Test node', 'langcode' => LanguageInterface::LANGCODE_NOT_APPLICABLE]);
|
||||
|
||||
// Check that the generated alias has language Not Specified.
|
||||
$alias = \Drupal::service('pathauto.alias_storage_helper')->loadBySource('/node/' . $node->id());
|
||||
$this->assertEqual($alias['langcode'], LanguageInterface::LANGCODE_NOT_SPECIFIED, 'PathautoGenerator::createEntityAlias() adjusts the alias langcode from Not Applicable to Not Specified.');
|
||||
|
||||
// Check that the alias works.
|
||||
$this->drupalGet('content/test-node');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables content translation on articles.
|
||||
*/
|
||||
protected function enableArticleTranslation() {
|
||||
// Enable content translation on articles.
|
||||
$this->drupalGet('admin/config/regional/content-language');
|
||||
$edit = array(
|
||||
'entity_types[node]' => TRUE,
|
||||
'settings[node][article][translatable]' => TRUE,
|
||||
'settings[node][article][settings][language][language_alterable]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Mass delete functionality tests.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoMassDeleteTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'taxonomy', 'pathauto');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* The test nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* The test accounts.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $accounts;
|
||||
|
||||
/**
|
||||
* The test terms.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface
|
||||
*/
|
||||
protected $terms;
|
||||
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->createPattern('node', '/content/[node:title]');
|
||||
$this->createPattern('user', '/users/[user:name]');
|
||||
$this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the deletion of all the aliases.
|
||||
*/
|
||||
function testDeleteAll() {
|
||||
// 1. Test that deleting all the aliases, of any type, works.
|
||||
$this->generateAliases();
|
||||
$edit = array(
|
||||
'delete[all_aliases]' => TRUE,
|
||||
'options[keep_custom_aliases]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
|
||||
$this->assertText(t('All of your path aliases have been deleted.'));
|
||||
$this->assertUrl('admin/config/search/path/delete_bulk');
|
||||
|
||||
// Make sure that all of them are actually deleted.
|
||||
$aliases = \Drupal::database()->select('url_alias', 'ua')->fields('ua', array())->execute()->fetchAll();
|
||||
$this->assertEqual($aliases, array(), "All the aliases have been deleted.");
|
||||
|
||||
// 2. Test deleting only specific (entity type) aliases.
|
||||
$manager = $this->container->get('plugin.manager.alias_type');
|
||||
$pathauto_plugins = array('canonical_entities:node' => 'nodes', 'canonical_entities:taxonomy_term' => 'terms', 'canonical_entities:user' => 'accounts');
|
||||
foreach ($pathauto_plugins as $pathauto_plugin => $attribute) {
|
||||
$this->generateAliases();
|
||||
$edit = array(
|
||||
'delete[plugins][' . $pathauto_plugin . ']' => TRUE,
|
||||
'options[keep_custom_aliases]' => FALSE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
|
||||
$alias_type = $manager->createInstance($pathauto_plugin);
|
||||
$this->assertRaw(t('All of your %label path aliases have been deleted.', array('%label' => $alias_type->getLabel())));
|
||||
// Check that the aliases were actually deleted.
|
||||
foreach ($this->{$attribute} as $entity) {
|
||||
$this->assertNoEntityAlias($entity);
|
||||
}
|
||||
|
||||
// Check that the other aliases are not deleted.
|
||||
foreach ($pathauto_plugins as $_pathauto_plugin => $_attribute) {
|
||||
// Skip the aliases that should be deleted.
|
||||
if ($_pathauto_plugin == $pathauto_plugin) {
|
||||
continue;
|
||||
}
|
||||
foreach ($this->{$_attribute} as $entity) {
|
||||
$this->assertEntityAliasExists($entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Test deleting automatically generated aliases only.
|
||||
$this->generateAliases();
|
||||
$edit = array(
|
||||
'delete[all_aliases]' => TRUE,
|
||||
'options[keep_custom_aliases]' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
|
||||
$this->assertText(t('All of your automatically generated path aliases have been deleted.'));
|
||||
$this->assertUrl('admin/config/search/path/delete_bulk');
|
||||
|
||||
// Make sure that only custom aliases and aliases with no information about
|
||||
// their state still exist.
|
||||
$aliases = \Drupal::database()->select('url_alias', 'ua')->fields('ua', ['source'])->execute()->fetchCol();
|
||||
$this->assertEqual($aliases, ['/node/101', '/node/104', '/node/105'], 'Custom aliases still exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to generate aliases.
|
||||
*/
|
||||
function generateAliases() {
|
||||
// Delete all aliases to avoid duplicated aliases. They will be recreated below.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
// We generate a bunch of aliases for nodes, users and taxonomy terms. If
|
||||
// the entities are already created we just update them, otherwise we create
|
||||
// them.
|
||||
if (empty($this->nodes)) {
|
||||
// Create a large number of nodes (100+) to make sure that the batch code works.
|
||||
for ($i = 1; $i <= 105; $i++) {
|
||||
// Set the alias of two nodes manually.
|
||||
$settings = ($i > 103) ? ['path' => ['alias' => "/custom_alias_$i", 'pathauto' => PathautoState::SKIP]] : [];
|
||||
$node = $this->drupalCreateNode($settings);
|
||||
$this->nodes[$node->id()] = $node;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->nodes as $node) {
|
||||
if ($node->id() > 103) {
|
||||
// The alias is set manually.
|
||||
$node->set('path', ['alias' => '/custom_alias_' . $node->id()]);
|
||||
}
|
||||
$node->save();
|
||||
}
|
||||
}
|
||||
// Delete information about the state of an alias to make sure that aliases
|
||||
// with no such data are left alone by default.
|
||||
\Drupal::keyValue('pathauto_state.node')->delete(101);
|
||||
|
||||
if (empty($this->accounts)) {
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$account = $this->drupalCreateUser();
|
||||
$this->accounts[$account->id()] = $account;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->accounts as $id => $account) {
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->terms)) {
|
||||
$vocabulary = $this->addVocabulary(array('name' => 'test vocabulary', 'vid' => 'test_vocabulary'));
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$term = $this->addTerm($vocabulary);
|
||||
$this->terms[$term->id()] = $term;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->terms as $term) {
|
||||
$term->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we have aliases for the entities.
|
||||
foreach (array('nodes', 'accounts', 'terms') as $attribute) {
|
||||
foreach ($this->{$attribute} as $entity) {
|
||||
$this->assertEntityAliasExists($entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
292
web/modules/contrib/pathauto/src/Tests/PathautoNodeWebTest.php
Normal file
292
web/modules/contrib/pathauto/src/Tests/PathautoNodeWebTest.php
Normal file
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
use Drupal\pathauto\Entity\PathautoPattern;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests pathauto node UI integration.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoNodeWebTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'pathauto', 'views', 'taxonomy', 'pathauto_views_test');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
'access content overview',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->createPattern('node', '/content/[node:title]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing nodes with different settings.
|
||||
*/
|
||||
function testNodeEditing() {
|
||||
// Ensure that the Pathauto checkbox is checked by default on the node add form.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertFieldChecked('edit-path-0-pathauto');
|
||||
|
||||
// Create a node by saving the node form.
|
||||
$title = ' Testing: node title [';
|
||||
$automatic_alias = '/content/testing-node-title';
|
||||
$this->drupalPostForm(NULL, array('title[0][value]' => $title), t('Save and publish'));
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
|
||||
// Look for alias generated in the form.
|
||||
$this->drupalGet("node/{$node->id()}/edit");
|
||||
$this->assertFieldChecked('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
|
||||
|
||||
// Check whether the alias actually works.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertText($title, 'Node accessible through automatic alias.');
|
||||
|
||||
// Manually set the node's alias.
|
||||
$manual_alias = '/content/' . $node->id();
|
||||
$edit = array(
|
||||
'path[0][pathauto]' => FALSE,
|
||||
'path[0][alias]' => $manual_alias,
|
||||
);
|
||||
$this->drupalPostForm($node->toUrl('edit-form'), $edit, t('Save and keep published'));
|
||||
$this->assertText(t('@type @title has been updated.', array('@type' => 'page', '@title' => $title)));
|
||||
|
||||
// Check that the automatic alias checkbox is now unchecked by default.
|
||||
$this->drupalGet("node/{$node->id()}/edit");
|
||||
$this->assertNoFieldChecked('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', $manual_alias);
|
||||
|
||||
// Submit the node form with the default values.
|
||||
$this->drupalPostForm(NULL, array('path[0][pathauto]' => FALSE), t('Save and keep published'));
|
||||
$this->assertText(t('@type @title has been updated.', array('@type' => 'page', '@title' => $title)));
|
||||
|
||||
// Test that the old (automatic) alias has been deleted and only accessible
|
||||
// through the new (manual) alias.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertResponse(404, 'Node not accessible through automatic alias.');
|
||||
$this->drupalGet($manual_alias);
|
||||
$this->assertText($title, 'Node accessible through manual alias.');
|
||||
|
||||
// Test that the manual alias is not kept for new nodes when the pathauto
|
||||
// checkbox is ticked.
|
||||
$title = 'Automatic Title';
|
||||
$edit = array(
|
||||
'title[0][value]' => $title,
|
||||
'path[0][pathauto]' => TRUE,
|
||||
'path[0][alias]' => '/should-not-get-created',
|
||||
);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Save and publish'));
|
||||
$this->assertNoAliasExists(array('alias' => 'should-not-get-created'));
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->assertEntityAlias($node, '/content/automatic-title');
|
||||
|
||||
// Remove the pattern for nodes, the pathauto checkbox should not be
|
||||
// displayed.
|
||||
$ids = \Drupal::entityQuery('pathauto_pattern')
|
||||
->condition('type', 'canonical_entities:node')
|
||||
->execute();
|
||||
foreach (PathautoPattern::loadMultiple($ids) as $pattern) {
|
||||
$pattern->delete();
|
||||
}
|
||||
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertNoFieldById('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', '');
|
||||
|
||||
$edit = array();
|
||||
$edit['title'] = 'My test article';
|
||||
$this->drupalCreateNode($edit);
|
||||
//$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title']);
|
||||
|
||||
// Pathauto checkbox should still not exist.
|
||||
$this->drupalGet($node->toUrl('edit-form'));
|
||||
$this->assertNoFieldById('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', '');
|
||||
$this->assertNoEntityAlias($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test node operations.
|
||||
*/
|
||||
function testNodeOperations() {
|
||||
$node1 = $this->drupalCreateNode(array('title' => 'node1'));
|
||||
$node2 = $this->drupalCreateNode(array('title' => 'node2'));
|
||||
|
||||
// Delete all current URL aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
$this->drupalGet('admin/content');
|
||||
|
||||
// Check which of the two nodes is first.
|
||||
if (strpos($this->getTextContent(), 'node1') < strpos($this->getTextContent(), 'node2')) {
|
||||
$index = 0;
|
||||
}
|
||||
else {
|
||||
$index = 1;
|
||||
}
|
||||
|
||||
$edit = array(
|
||||
'action' => 'pathauto_update_alias_node',
|
||||
'node_bulk_form[' . $index . ']' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$this->assertText('Update URL alias was applied to 1 item.');
|
||||
|
||||
$this->assertEntityAlias($node1, '/content/' . $node1->getTitle());
|
||||
$this->assertEntityAlias($node2, '/node/' . $node2->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Merge this with existing node test methods?
|
||||
*/
|
||||
public function testNodeState() {
|
||||
$nodeNoAliasUser = $this->drupalCreateUser(array('bypass node access'));
|
||||
$nodeAliasUser = $this->drupalCreateUser(array('bypass node access', 'create url aliases'));
|
||||
|
||||
$node = $this->drupalCreateNode(array(
|
||||
'title' => 'Node version one',
|
||||
'type' => 'page',
|
||||
'path' => array(
|
||||
'pathauto' => PathautoState::SKIP,
|
||||
),
|
||||
));
|
||||
|
||||
$this->assertNoEntityAlias($node);
|
||||
|
||||
// Set a manual path alias for the node.
|
||||
$node->path->alias = '/test-alias';
|
||||
$node->save();
|
||||
|
||||
// Ensure that the pathauto field was saved to the database.
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache();
|
||||
$node = Node::load($node->id());
|
||||
$this->assertIdentical($node->path->pathauto, PathautoState::SKIP);
|
||||
|
||||
// Ensure that the manual path alias was saved and an automatic alias was not generated.
|
||||
$this->assertEntityAlias($node, '/test-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-one');
|
||||
|
||||
// Save the node as a user who does not have access to path fieldset.
|
||||
$this->drupalLogin($nodeNoAliasUser);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoFieldByName('path[0][pathauto]');
|
||||
|
||||
$edit = array('title[0][value]' => 'Node version two');
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Basic page Node version two has been updated.');
|
||||
|
||||
$this->assertEntityAlias($node, '/test-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-one');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-two');
|
||||
|
||||
// Load the edit node page and check that the Pathauto checkbox is unchecked.
|
||||
$this->drupalLogin($nodeAliasUser);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertNoFieldChecked('edit-path-0-pathauto');
|
||||
|
||||
// Edit the manual alias and save the node.
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Node version three',
|
||||
'path[0][alias]' => '/manually-edited-alias',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Basic page Node version three has been updated.');
|
||||
|
||||
$this->assertEntityAlias($node, '/manually-edited-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/test-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-one');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-two');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-three');
|
||||
|
||||
// Programatically save the node with an automatic alias.
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache();
|
||||
$node = Node::load($node->id());
|
||||
$node->path->pathauto = PathautoState::CREATE;
|
||||
$node->save();
|
||||
|
||||
// Ensure that the pathauto field was saved to the database.
|
||||
\Drupal::entityTypeManager()->getStorage('node')->resetCache();
|
||||
$node = Node::load($node->id());
|
||||
$this->assertIdentical($node->path->pathauto, PathautoState::CREATE);
|
||||
|
||||
$this->assertEntityAlias($node, '/content/node-version-three');
|
||||
$this->assertNoEntityAliasExists($node, '/manually-edited-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/test-alias');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-one');
|
||||
$this->assertNoEntityAliasExists($node, '/content/node-version-two');
|
||||
|
||||
$node->delete();
|
||||
$this->assertNull(\Drupal::keyValue('pathauto_state.node')->get($node->id()), 'Pathauto state was deleted');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that nodes without a Pathauto pattern can set custom aliases.
|
||||
*/
|
||||
public function testCustomAliasWithoutPattern() {
|
||||
// First, delete all patterns to be sure that there will be no match.
|
||||
$entity_ids = \Drupal::entityQuery('pathauto_pattern')->execute();
|
||||
$entities = PathautoPattern::loadMultiple($entity_ids);
|
||||
foreach ($entities as $entity) {
|
||||
$entity->delete();
|
||||
}
|
||||
|
||||
// Next, create a node with a custom alias.
|
||||
$edit = [
|
||||
'title[0][value]' => 'Sample article',
|
||||
'path[0][alias]' => '/sample-article',
|
||||
];
|
||||
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
|
||||
$this->assertText(t('article Sample article has been created.'));
|
||||
|
||||
// Test the alias.
|
||||
$this->assertAliasExists(array('alias' => '/sample-article'));
|
||||
$this->drupalGet('sample-article');
|
||||
$this->assertResponse(200, 'A node without a pattern can have a custom alias.');
|
||||
|
||||
// Now create a node through the API.
|
||||
$node = Node::create(['type' => 'article', 'title' => 'Sample article API', 'path' => ['alias' => '/sample-article-api']]);
|
||||
$node->save();
|
||||
|
||||
// Test the alias.
|
||||
$this->assertAliasExists(['alias' => '/sample-article-api']);
|
||||
$this->drupalGet('sample-article-api');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\pathauto\PathautoGeneratorInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests pathauto settings form.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoSettingsFormWebTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'pathauto');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Form values that are set by default.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultFormValues = array(
|
||||
'verbose' => FALSE,
|
||||
'separator' => '-',
|
||||
'case' => '1',
|
||||
'max_length' => '100',
|
||||
'max_component_length' => '100',
|
||||
'update_action' => '2',
|
||||
'transliterate' => '1',
|
||||
'reduce_ascii' => FALSE,
|
||||
'ignore_words' => 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with',
|
||||
);
|
||||
|
||||
/**
|
||||
* Punctuation form items with default values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultPunctuations = array(
|
||||
'punctuation[double_quotes]' => '0',
|
||||
'punctuation[quotes]' => '0',
|
||||
'punctuation[backtick]' => '0',
|
||||
'punctuation[comma]' => '0',
|
||||
'punctuation[period]' => '0',
|
||||
'punctuation[hyphen]' => '1',
|
||||
'punctuation[underscore]' => '0',
|
||||
'punctuation[colon]' => '0',
|
||||
'punctuation[semicolon]' => '0',
|
||||
'punctuation[pipe]' => '0',
|
||||
'punctuation[left_curly]' => '0',
|
||||
'punctuation[left_square]' => '0',
|
||||
'punctuation[right_curly]' => '0',
|
||||
'punctuation[right_square]' => '0',
|
||||
'punctuation[plus]' => '0',
|
||||
'punctuation[equal]' => '0',
|
||||
'punctuation[asterisk]' => '0',
|
||||
'punctuation[ampersand]' => '0',
|
||||
'punctuation[percent]' => '0',
|
||||
'punctuation[caret]' => '0',
|
||||
'punctuation[dollar]' => '0',
|
||||
'punctuation[hash]' => '0',
|
||||
'punctuation[exclamation]' => '0',
|
||||
'punctuation[tilde]' => '0',
|
||||
'punctuation[left_parenthesis]' => '0',
|
||||
'punctuation[right_parenthesis]' => '0',
|
||||
'punctuation[question_mark]' => '0',
|
||||
'punctuation[less_than]' => '0',
|
||||
'punctuation[greater_than]' => '0',
|
||||
'punctuation[slash]' => '0',
|
||||
'punctuation[back_slash]' => '0',
|
||||
);
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'notify of path changes',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->createPattern('node', '/content/[node:title]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the default values are shown correctly in the form.
|
||||
*/
|
||||
function testDefaultFormValues() {
|
||||
$this->drupalGet('/admin/config/search/path/settings');
|
||||
$this->assertNoFieldChecked('edit-verbose');
|
||||
$this->assertField('edit-separator', $this->defaultFormValues['separator']);
|
||||
$this->assertFieldChecked('edit-case');
|
||||
$this->assertField('edit-max-length', $this->defaultFormValues['max_length']);
|
||||
$this->assertField('edit-max-component-length', $this->defaultFormValues['max_component_length']);
|
||||
$this->assertFieldChecked('edit-update-action-2');
|
||||
$this->assertFieldChecked('edit-transliterate');
|
||||
$this->assertNoFieldChecked('edit-reduce-ascii');
|
||||
$this->assertField('edit-ignore-words', $this->defaultFormValues['ignore_words']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the verbose option.
|
||||
*/
|
||||
function testVerboseOption() {
|
||||
$edit = array('verbose' => '1');
|
||||
$this->drupalPostForm('/admin/config/search/path/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$this->assertFieldChecked('edit-verbose');
|
||||
|
||||
$title = 'Verbose settings test';
|
||||
$this->drupalGet('/node/add/article');
|
||||
$this->assertFieldChecked('edit-path-0-pathauto');
|
||||
$this->drupalPostForm(NULL, array('title[0][value]' => $title), t('Save and publish'));
|
||||
$this->assertText('Created new alias /content/verbose-settings-test for');
|
||||
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->drupalPostForm('/node/' . $node->id() . '/edit', array('title[0][value]' => 'Updated title'), t('Save and keep published'));
|
||||
$this->assertText('Created new alias /content/updated-title for');
|
||||
$this->assertText('replacing /content/verbose-settings-test.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests generating aliases with different settings.
|
||||
*/
|
||||
function testSettingsForm() {
|
||||
// Ensure the separator settings apply correctly.
|
||||
$this->checkAlias('My awesome content', '/content/my.awesome.content', array('separator' => '.'));
|
||||
|
||||
// Ensure the character case setting works correctly.
|
||||
// Leave case the same as source token values.
|
||||
$this->checkAlias('My awesome Content', '/content/My-awesome-Content', array('case' => FALSE));
|
||||
$this->checkAlias('Change Lower', '/content/change-lower', array('case' => '1'));
|
||||
|
||||
// Ensure the maximum alias length is working.
|
||||
$this->checkAlias('My awesome Content', '/content/my-awesome', array('max_length' => '23'));
|
||||
|
||||
// Ensure the maximum component length is working.
|
||||
$this->checkAlias('My awesome Content', '/content/my', array('max_component_length' => '2'));
|
||||
|
||||
// Ensure transliteration option is working.
|
||||
$this->checkAlias('è é àl ö äl ü', '/content/e-e-al-o-al-u', array('transliterate' => '1'));
|
||||
$this->checkAlias('è é àl äl ö ü', '/content/è-é-àl-äl-ö-ü', array('transliterate' => FALSE));
|
||||
|
||||
$ignore_words = 'a, new, very, should';
|
||||
$this->checkAlias('a very new alias to test', '/content/alias-to-test', array('ignore_words' => $ignore_words));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the punctuation setting form items.
|
||||
*/
|
||||
function testPunctuationSettings() {
|
||||
// Test the replacement of punctuations.
|
||||
$settings = [];
|
||||
foreach ($this->defaultPunctuations as $key => $punctuation) {
|
||||
$settings[$key] = PathautoGeneratorInterface::PUNCTUATION_REPLACE;
|
||||
}
|
||||
|
||||
$title = 'aa"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
|
||||
$alias = '/content/aa-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-1-2-3';
|
||||
$this->checkAlias($title, $alias, $settings);
|
||||
|
||||
// Test the removal of punctuations.
|
||||
$settings = [];
|
||||
foreach ($this->defaultPunctuations as $key => $punctuation) {
|
||||
$settings[$key] = PathautoGeneratorInterface::PUNCTUATION_REMOVE;
|
||||
}
|
||||
|
||||
$title = 'a"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
|
||||
$alias = '/content/abcdefghijklmnopqrstuvwxyz123';
|
||||
$this->checkAlias($title, $alias, $settings);
|
||||
|
||||
// Keep all punctuations in alias.
|
||||
$settings = [];
|
||||
foreach ($this->defaultPunctuations as $key => $punctuation) {
|
||||
$settings[$key] = PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING;
|
||||
}
|
||||
|
||||
$title = 'al"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
|
||||
$alias = '/content/al"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
|
||||
$this->checkAlias($title, $alias, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check the an aliases.
|
||||
*
|
||||
* @param string $title
|
||||
* The node title to build the aliases from.
|
||||
* @param string $alias
|
||||
* The expected alias.
|
||||
* @param array $settings
|
||||
* The form values the alias should be generated with.
|
||||
*/
|
||||
protected function checkAlias($title, $alias, $settings = array()) {
|
||||
// Submit the settings form.
|
||||
$edit = array_merge($this->defaultFormValues + $this->defaultPunctuations, $settings);
|
||||
$this->drupalPostForm('/admin/config/search/path/settings', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
|
||||
// If we do not clear the caches here, AliasCleaner will use its
|
||||
// cleanStringCache instance variable. Due to that the creation of aliases
|
||||
// with $this->createNode() will only work correctly on the first call.
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
|
||||
// Create a node and check if the settings applied.
|
||||
$node = $this->createNode(
|
||||
array(
|
||||
'title' => $title,
|
||||
'type' => 'article',
|
||||
)
|
||||
);
|
||||
|
||||
$this->drupalGet($alias);
|
||||
$this->assertResponse(200);
|
||||
$this->assertEntityAlias($node, $alias);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests pathauto taxonomy UI integration.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoTaxonomyWebTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy', 'pathauto', 'views');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer taxonomy',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic functional testing of Pathauto with taxonomy terms.
|
||||
*/
|
||||
function testTermEditing() {
|
||||
$this->drupalGet('admin/structure');
|
||||
$this->drupalGet('admin/structure/taxonomy');
|
||||
|
||||
// Add vocabulary "tags".
|
||||
$vocabulary = $this->addVocabulary(array('name' => 'tags', 'vid' => 'tags'));
|
||||
|
||||
// Create term for testing.
|
||||
$name = 'Testing: term name [';
|
||||
$automatic_alias = '/tags/testing-term-name';
|
||||
$this->drupalPostForm('admin/structure/taxonomy/manage/tags/add', array('name[0][value]' => $name), 'Save');
|
||||
$name = trim($name);
|
||||
$this->assertText("Created new term $name.");
|
||||
$term = $this->drupalGetTermByName($name);
|
||||
|
||||
// Look for alias generated in the form.
|
||||
$this->drupalGet("taxonomy/term/{$term->id()}/edit");
|
||||
$this->assertFieldChecked('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
|
||||
|
||||
// Check whether the alias actually works.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertText($name, 'Term accessible through automatic alias.');
|
||||
|
||||
// Manually set the term's alias.
|
||||
$manual_alias = '/tags/' . $term->id();
|
||||
$edit = array(
|
||||
'path[0][pathauto]' => FALSE,
|
||||
'path[0][alias]' => $manual_alias,
|
||||
);
|
||||
$this->drupalPostForm("taxonomy/term/{$term->id()}/edit", $edit, t('Save'));
|
||||
$this->assertText("Updated term $name.");
|
||||
|
||||
// Check that the automatic alias checkbox is now unchecked by default.
|
||||
$this->drupalGet("taxonomy/term/{$term->id()}/edit");
|
||||
$this->assertNoFieldChecked('edit-path-0-pathauto');
|
||||
$this->assertFieldByName('path[0][alias]', $manual_alias);
|
||||
|
||||
// Submit the term form with the default values.
|
||||
$this->drupalPostForm(NULL, array('path[0][pathauto]' => FALSE), t('Save'));
|
||||
$this->assertText("Updated term $name.");
|
||||
|
||||
// Test that the old (automatic) alias has been deleted and only accessible
|
||||
// through the new (manual) alias.
|
||||
$this->drupalGet($automatic_alias);
|
||||
$this->assertResponse(404, 'Term not accessible through automatic alias.');
|
||||
$this->drupalGet($manual_alias);
|
||||
$this->assertText($name, 'Term accessible through manual alias.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\pathauto\Entity\PathautoPattern;
|
||||
use Drupal\pathauto\PathautoPatternInterface;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
|
||||
/**
|
||||
* Helper test class with some added functions for testing.
|
||||
*/
|
||||
trait PathautoTestHelperTrait {
|
||||
|
||||
/**
|
||||
* Creates a pathauto pattern.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type.
|
||||
* @param string $pattern
|
||||
* The path pattern.
|
||||
* @param int $weight
|
||||
* (optional) The pattern weight.
|
||||
*
|
||||
* @return \Drupal\pathauto\PathautoPatternInterface
|
||||
* The created pattern.
|
||||
*/
|
||||
protected function createPattern($entity_type_id, $pattern, $weight = 10) {
|
||||
$type = ($entity_type_id == 'forum') ? 'forum' : 'canonical_entities:' . $entity_type_id;
|
||||
|
||||
$pattern = PathautoPattern::create([
|
||||
'id' => Unicode::strtolower($this->randomMachineName()),
|
||||
'type' => $type,
|
||||
'pattern' => $pattern,
|
||||
'weight' => $weight,
|
||||
]);
|
||||
$pattern->save();
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a bundle condition to a pathauto pattern.
|
||||
*
|
||||
* @param \Drupal\pathauto\PathautoPatternInterface $pattern
|
||||
* The pattern.
|
||||
* @param string $entity_type
|
||||
* The entity type ID.
|
||||
* @param string $bundle
|
||||
* The bundle
|
||||
*/
|
||||
protected function addBundleCondition(PathautoPatternInterface $pattern, $entity_type, $bundle) {
|
||||
$plugin_id = $entity_type == 'node' ? 'node_type' : 'entity_bundle:' . $entity_type;
|
||||
|
||||
$pattern->addSelectionCondition(
|
||||
[
|
||||
'id' => $plugin_id,
|
||||
'bundles' => [
|
||||
$bundle => $bundle,
|
||||
],
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
$entity_type => $entity_type,
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function assertToken($type, $object, $token, $expected) {
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object), [], $bubbleable_metadata);
|
||||
$tokens += array($token => '');
|
||||
$this->assertIdentical($tokens[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $tokens[$token], '@expected' => $expected)));
|
||||
}
|
||||
|
||||
public function saveAlias($source, $alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) {
|
||||
\Drupal::service('path.alias_storage')->delete(array('source' => $source, 'language', 'langcode' => $langcode));
|
||||
return \Drupal::service('path.alias_storage')->save($source, $alias, $langcode);
|
||||
}
|
||||
|
||||
public function saveEntityAlias(EntityInterface $entity, $alias, $langcode = NULL) {
|
||||
// By default, use the entity language.
|
||||
if (!$langcode) {
|
||||
$langcode = $entity->language()->getId();
|
||||
}
|
||||
return $this->saveAlias('/' . $entity->toUrl()->getInternalPath(), $alias, $langcode);
|
||||
}
|
||||
|
||||
public function assertEntityAlias(EntityInterface $entity, $expected_alias, $langcode = NULL) {
|
||||
// By default, use the entity language.
|
||||
if (!$langcode) {
|
||||
$langcode = $entity->language()->getId();
|
||||
}
|
||||
$this->assertAlias('/' . $entity->toUrl()->getInternalPath(), $expected_alias, $langcode);
|
||||
}
|
||||
|
||||
public function assertEntityAliasExists(EntityInterface $entity) {
|
||||
return $this->assertAliasExists(array('source' => '/' . $entity->toUrl()->getInternalPath()));
|
||||
}
|
||||
|
||||
public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) {
|
||||
// By default, use the entity language.
|
||||
if (!$langcode) {
|
||||
$langcode = $entity->language()->getId();
|
||||
}
|
||||
$this->assertEntityAlias($entity, '/' . $entity->toUrl()->getInternalPath(), $langcode);
|
||||
}
|
||||
|
||||
public function assertNoEntityAliasExists(EntityInterface $entity, $alias = NULL) {
|
||||
$path = array('source' => '/' . $entity->toUrl()->getInternalPath());
|
||||
if (!empty($alias)) {
|
||||
$path['alias'] = $alias;
|
||||
}
|
||||
$this->assertNoAliasExists($path);
|
||||
}
|
||||
|
||||
public function assertAlias($source, $expected_alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) {
|
||||
\Drupal::service('path.alias_manager')->cacheClear($source);
|
||||
$this->assertEqual($expected_alias, \Drupal::service('path.alias_manager')->getAliasByPath($source, $langcode), t("Alias for %source with language '@language' is correct.",
|
||||
array('%source' => $source, '@language' => $langcode)));
|
||||
}
|
||||
|
||||
public function assertAliasExists($conditions) {
|
||||
$path = \Drupal::service('path.alias_storage')->load($conditions);
|
||||
$this->assertTrue($path, t('Alias with conditions @conditions found.', array('@conditions' => var_export($conditions, TRUE))));
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function assertNoAliasExists($conditions) {
|
||||
$alias = \Drupal::service('path.alias_storage')->load($conditions);
|
||||
$this->assertFalse($alias, t('Alias with conditions @conditions not found.', array('@conditions' => var_export($conditions, TRUE))));
|
||||
}
|
||||
|
||||
public function deleteAllAliases() {
|
||||
\Drupal::database()->delete('url_alias')->execute();
|
||||
\Drupal::service('path.alias_manager')->cacheClear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @return \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
public function addVocabulary(array $values = array()) {
|
||||
$name = Unicode::strtolower($this->randomMachineName(5));
|
||||
$values += array(
|
||||
'name' => $name,
|
||||
'vid' => $name,
|
||||
);
|
||||
$vocabulary = Vocabulary::create($values);
|
||||
$vocabulary->save();
|
||||
|
||||
return $vocabulary;
|
||||
}
|
||||
|
||||
public function addTerm(VocabularyInterface $vocabulary, array $values = array()) {
|
||||
$values += array(
|
||||
'name' => Unicode::strtolower($this->randomMachineName(5)),
|
||||
'vid' => $vocabulary->id(),
|
||||
);
|
||||
|
||||
$term = Term::create($values);
|
||||
$term->save();
|
||||
return $term;
|
||||
}
|
||||
|
||||
public function assertEntityPattern($entity_type, $bundle, $langcode = Language::LANGCODE_NOT_SPECIFIED, $expected) {
|
||||
|
||||
$values = [
|
||||
'langcode' => $langcode,
|
||||
\Drupal::entityTypeManager()->getDefinition($entity_type)->getKey('bundle') => $bundle,
|
||||
];
|
||||
$entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
|
||||
|
||||
$pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
|
||||
$this->assertIdentical($expected, $pattern->getPattern());
|
||||
}
|
||||
|
||||
public function drupalGetTermByName($name, $reset = FALSE) {
|
||||
if ($reset) {
|
||||
// @todo - implement cache reset.
|
||||
}
|
||||
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(array('name' => $name));
|
||||
return !empty($terms) ? reset($terms) : FALSE;
|
||||
}
|
||||
|
||||
}
|
189
web/modules/contrib/pathauto/src/Tests/PathautoUiTest.php
Normal file
189
web/modules/contrib/pathauto/src/Tests/PathautoUiTest.php
Normal file
|
@ -0,0 +1,189 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\pathauto\Entity\PathautoPattern;
|
||||
|
||||
/**
|
||||
* Test basic pathauto functionality.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoUiTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('pathauto', 'node');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer nodes',
|
||||
'bypass node access',
|
||||
'access content overview',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
function testSettingsValidation() {
|
||||
$edit = array();
|
||||
$edit['max_length'] = 'abc';
|
||||
$edit['max_component_length'] = 'abc';
|
||||
$this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
/*$this->assertText('The field Maximum alias length is not a valid number.');
|
||||
$this->assertText('The field Maximum component length is not a valid number.');*/
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['max_length'] = '0';
|
||||
$edit['max_component_length'] = '0';
|
||||
$this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
/*$this->assertText('The field Maximum alias length cannot be less than 1.');
|
||||
$this->assertText('The field Maximum component length cannot be less than 1.');*/
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['max_length'] = '999';
|
||||
$edit['max_component_length'] = '999';
|
||||
$this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
/*$this->assertText('The field Maximum alias length cannot be greater than 255.');
|
||||
$this->assertText('The field Maximum component length cannot be greater than 255.');*/
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['max_length'] = '50';
|
||||
$edit['max_component_length'] = '50';
|
||||
$this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
|
||||
$this->assertText('The configuration options have been saved.');
|
||||
}
|
||||
|
||||
function testPatternsWorkflow() {
|
||||
// Try to save an empty pattern, should not be allowed.
|
||||
$this->drupalGet('admin/config/search/path/patterns/add');
|
||||
$edit = array(
|
||||
'type' => 'canonical_entities:node',
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'type');
|
||||
$edit += array(
|
||||
'bundles[page]' => TRUE,
|
||||
'label' => 'Page pattern',
|
||||
'id' => 'page_pattern',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Path pattern field is required.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
// Try to save an invalid pattern.
|
||||
$edit += array(
|
||||
'pattern' => '[node:title]/[user:name]/[term:name]',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Path pattern is using the following invalid tokens: [user:name], [term:name].');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
$edit['pattern'] = '#[node:title]';
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('The Path pattern is using the following invalid characters: #.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
// Checking whitespace ending of the string.
|
||||
$edit['pattern'] = '[node:title] ';
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('The Path pattern doesn\'t allow the patterns ending with whitespace.');
|
||||
$this->assertNoText('The configuration options have been saved.');
|
||||
|
||||
// Fix the pattern, then check that it gets saved successfully.
|
||||
$edit['pattern'] = '[node:title]';
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertText('Pattern Page pattern saved.');
|
||||
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
|
||||
// Create a node with pattern enabled and check if the pattern applies.
|
||||
$title = 'Page Pattern enabled';
|
||||
$alias = '/page-pattern-enabled';
|
||||
$node = $this->createNode(['title' => $title, 'type' => 'page']);
|
||||
$this->drupalGet($alias);
|
||||
$this->assertResponse(200);
|
||||
$this->assertEntityAlias($node, $alias);
|
||||
|
||||
// Edit workflow, set a new label and weight for the pattern.
|
||||
$this->drupalPostForm('/admin/config/search/path/patterns', ['entities[page_pattern][weight]' => '4'], t('Save'));
|
||||
$this->clickLink(t('Edit'));
|
||||
$this->assertUrl('/admin/config/search/path/patterns/page_pattern');
|
||||
$this->assertFieldByName('pattern', '[node:title]');
|
||||
$this->assertFieldByName('label', 'Page pattern');
|
||||
$this->assertFieldChecked('edit-status');
|
||||
$this->assertLink(t('Delete'));
|
||||
|
||||
$edit = array('label' => 'Test');
|
||||
$this->drupalPostForm('/admin/config/search/path/patterns/page_pattern', $edit, t('Save'));
|
||||
$this->assertText('Pattern Test saved.');
|
||||
// Check that the pattern weight did not change.
|
||||
$this->assertOptionSelected('edit-entities-page-pattern-weight', '4');
|
||||
|
||||
// Disable workflow.
|
||||
$this->drupalGet('/admin/config/search/path/patterns');
|
||||
$this->assertNoLink(t('Enable'));
|
||||
$this->clickLink(t('Disable'));
|
||||
$this->assertUrl('/admin/config/search/path/patterns/page_pattern/disable');
|
||||
$this->drupalPostForm(NULL, [], t('Disable'));
|
||||
$this->assertText('Disabled pattern Test.');
|
||||
|
||||
// Load the pattern from storage and check if its disabled.
|
||||
$pattern = PathautoPattern::load('page_pattern');
|
||||
$this->assertFalse($pattern->status());
|
||||
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
|
||||
// Create a node with pattern disabled and check that we have no new alias.
|
||||
$title = 'Page Pattern disabled';
|
||||
$node = $this->createNode(['title' => $title, 'type' => 'page']);
|
||||
$this->assertNoEntityAlias($node);
|
||||
|
||||
// Enable workflow.
|
||||
$this->drupalGet('/admin/config/search/path/patterns');
|
||||
$this->assertNoLink(t('Disable'));
|
||||
$this->clickLink(t('Enable'));
|
||||
$this->assertUrl('/admin/config/search/path/patterns/page_pattern/enable');
|
||||
$this->drupalPostForm(NULL, [], t('Enable'));
|
||||
$this->assertText('Enabled pattern Test.');
|
||||
|
||||
// Reload pattern from storage and check if its enabled.
|
||||
$pattern = PathautoPattern::load('page_pattern');
|
||||
$this->assertTrue($pattern->status());
|
||||
|
||||
// Delete workflow.
|
||||
$this->drupalGet('/admin/config/search/path/patterns');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertUrl('/admin/config/search/path/patterns/page_pattern/delete');
|
||||
$this->assertText(t('This action cannot be undone.'));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertText('The pathauto pattern Test has been deleted.');
|
||||
|
||||
$this->assertFalse(PathautoPattern::load('page_pattern'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto\Tests;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests pathauto user UI integration.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoUserWebTest extends WebTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('pathauto', 'views');
|
||||
|
||||
/**
|
||||
* Admin user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {inheritdoc}
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Allow other modules to add additional permissions for the admin user.
|
||||
$permissions = array(
|
||||
'administer pathauto',
|
||||
'administer url aliases',
|
||||
'create url aliases',
|
||||
'administer users',
|
||||
);
|
||||
$this->adminUser = $this->drupalCreateUser($permissions);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
$this->createPattern('user', '/users/[user:name]');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic functional testing of Pathauto with users.
|
||||
*/
|
||||
function testUserEditing() {
|
||||
// There should be no Pathauto checkbox on user forms.
|
||||
$this->drupalGet('user/' . $this->adminUser->id() . '/edit');
|
||||
$this->assertNoFieldById('path[0][pathauto]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user operations.
|
||||
*/
|
||||
function testUserOperations() {
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Delete all current URL aliases.
|
||||
$this->deleteAllAliases();
|
||||
|
||||
// Find the position of just created account in the user_admin_people view.
|
||||
$view = Views::getView('user_admin_people');
|
||||
$view->initDisplay();
|
||||
$view->preview('page_1');
|
||||
|
||||
|
||||
foreach ($view->result as $key => $row) {
|
||||
if ($view->field['name']->getValue($row) == $account->getUsername()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$edit = array(
|
||||
'action' => 'pathauto_update_alias_user',
|
||||
"user_bulk_form[$key]" => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
|
||||
$this->assertText('Update URL alias was applied to 1 item.');
|
||||
|
||||
$this->assertEntityAlias($account, '/users/' . Unicode::strtolower($account->getUsername()));
|
||||
$this->assertEntityAlias($this->adminUser, '/user/' . $this->adminUser->id());
|
||||
}
|
||||
|
||||
}
|
63
web/modules/contrib/pathauto/src/VerboseMessenger.php
Normal file
63
web/modules/contrib/pathauto/src/VerboseMessenger.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\pathauto;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Provides a verbose messenger.
|
||||
*/
|
||||
class VerboseMessenger implements MessengerInterface {
|
||||
|
||||
/**
|
||||
* The verbose flag.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isVerbose;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The current user account.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Creates a verbose messenger.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, AccountInterface $account) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMessage($message, $op = NULL) {
|
||||
|
||||
if (!isset($this->isVerbose)) {
|
||||
$config = $this->configFactory->get('pathauto.settings');
|
||||
$this->isVerbose = $config->get('verbose') && $this->account->hasPermission('notify of path changes');
|
||||
}
|
||||
|
||||
if (!$this->isVerbose || (isset($op) && in_array($op, array('bulkupdate', 'return')))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ($message) {
|
||||
drupal_set_message($message);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- taxonomy
|
||||
- user
|
||||
id: articles
|
||||
label: articles
|
||||
module: pathauto_views_test
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: node_field_data
|
||||
base_field: nid
|
||||
core: 8.x
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access content'
|
||||
cache:
|
||||
type: tag
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 10
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
tags:
|
||||
previous: '‹ Previous'
|
||||
next: 'Next ›'
|
||||
first: '« First'
|
||||
last: 'Last »'
|
||||
quantity: 9
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: 'entity:node'
|
||||
options:
|
||||
view_mode: teaser
|
||||
fields:
|
||||
title:
|
||||
id: title
|
||||
table: node_field_data
|
||||
field: title
|
||||
entity_type: node
|
||||
entity_field: title
|
||||
label: ''
|
||||
alter:
|
||||
alter_text: false
|
||||
make_link: false
|
||||
absolute: false
|
||||
trim: false
|
||||
word_boundary: false
|
||||
ellipsis: false
|
||||
strip_tags: false
|
||||
html: false
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
settings:
|
||||
link_to_entity: true
|
||||
plugin_id: field
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exclude: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
filters:
|
||||
status:
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
plugin_id: boolean
|
||||
entity_type: node
|
||||
entity_field: status
|
||||
id: status
|
||||
expose:
|
||||
operator: ''
|
||||
group: 1
|
||||
type:
|
||||
id: type
|
||||
table: node_field_data
|
||||
field: type
|
||||
value:
|
||||
article: article
|
||||
entity_type: node
|
||||
entity_field: type
|
||||
plugin_id: bundle
|
||||
sorts:
|
||||
created:
|
||||
id: created
|
||||
table: node_field_data
|
||||
field: created
|
||||
order: DESC
|
||||
entity_type: node
|
||||
entity_field: created
|
||||
plugin_id: date
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
title: articles
|
||||
header: { }
|
||||
footer: { }
|
||||
empty: { }
|
||||
relationships: { }
|
||||
arguments:
|
||||
tid:
|
||||
id: tid
|
||||
table: taxonomy_index
|
||||
field: tid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
default_action: ignore
|
||||
exception:
|
||||
value: all
|
||||
title_enable: false
|
||||
title: All
|
||||
title_enable: false
|
||||
title: ''
|
||||
default_argument_type: fixed
|
||||
default_argument_options:
|
||||
argument: ''
|
||||
default_argument_skip_url: false
|
||||
summary_options:
|
||||
base_path: ''
|
||||
count: true
|
||||
items_per_page: 25
|
||||
override: false
|
||||
summary:
|
||||
sort_order: asc
|
||||
number_of_records: 0
|
||||
format: default_summary
|
||||
specify_validation: false
|
||||
validate:
|
||||
type: none
|
||||
fail: 'not found'
|
||||
validate_options: { }
|
||||
break_phrase: false
|
||||
add_table: false
|
||||
require_value: false
|
||||
reduce_duplicates: false
|
||||
plugin_id: taxonomy_index_tid
|
||||
display_extenders: { }
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: articles
|
||||
cache_metadata:
|
||||
max-age: -1
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- 'user.node_grants:view'
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -0,0 +1,14 @@
|
|||
name: 'Views Test Config'
|
||||
type: module
|
||||
description: 'Provides default views for tests.'
|
||||
package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-04-29
|
||||
version: '8.x-1.0'
|
||||
core: '8.x'
|
||||
project: 'pathauto'
|
||||
datestamp: 1493468049
|
|
@ -0,0 +1,572 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\pathauto\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\pathauto\PathautoGeneratorInterface;
|
||||
use Drupal\pathauto\PathautoState;
|
||||
use Drupal\pathauto\Tests\PathautoTestHelperTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Unit tests for Pathauto functions.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoKernelTest extends KernelTestBase {
|
||||
|
||||
use PathautoTestHelperTrait;
|
||||
|
||||
public static $modules = array('system', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'filter', 'ctools', 'language');
|
||||
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* @var \Drupal\pathauto\PathautoPatternInterface
|
||||
*/
|
||||
protected $nodePattern;
|
||||
|
||||
/**
|
||||
* @var \Drupal\pathauto\PathautoPatternInterface
|
||||
*/
|
||||
protected $userPattern;
|
||||
|
||||
public function setUp() {
|
||||
parent::setup();
|
||||
|
||||
$this->installConfig(array('pathauto', 'taxonomy', 'system', 'node'));
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$this->installSchema('node', array('node_access'));
|
||||
$this->installSchema('system', array('url_alias', 'sequences', 'router'));
|
||||
|
||||
$type = NodeType::create(['type' => 'page']);
|
||||
$type->save();
|
||||
node_add_body_field($type);
|
||||
|
||||
$this->nodePattern = $this->createPattern('node', '/content/[node:title]');
|
||||
$this->userPattern = $this->createPattern('user', '/users/[user:name]');
|
||||
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
$this->currentUser = User::create(array('name' => $this->randomMachineName()));
|
||||
$this->currentUser->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test _pathauto_get_schema_alias_maxlength().
|
||||
*/
|
||||
public function testGetSchemaAliasMaxLength() {
|
||||
$this->assertIdentical(\Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength(), 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_pattern_load_by_entity().
|
||||
*/
|
||||
public function testPatternLoadByEntity() {
|
||||
$pattern = $this->createPattern('node', '/article/[node:title]', -1);
|
||||
$this->addBundleCondition($pattern, 'node', 'article');
|
||||
$pattern->save();
|
||||
|
||||
$pattern = $this->createPattern('node', '/article/en/[node:title]', -2);
|
||||
$this->addBundleCondition($pattern, 'node', 'article');
|
||||
$pattern->addSelectionCondition(
|
||||
[
|
||||
'id' => 'language',
|
||||
'langcodes' => [
|
||||
'en' => 'en',
|
||||
],
|
||||
'negate' => FALSE,
|
||||
'context_mapping' => [
|
||||
'language' => 'node:langcode:language',
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$pattern->addRelationship('node:langcode:language');
|
||||
$pattern->save();
|
||||
|
||||
$pattern = $this->createPattern('node', '/[node:title]', -1);
|
||||
$this->addBundleCondition($pattern, 'node', 'page');
|
||||
$pattern->save();
|
||||
|
||||
$tests = array(
|
||||
array(
|
||||
'entity' => 'node',
|
||||
'values' => [
|
||||
'title' => 'Article fr',
|
||||
'type' => 'article',
|
||||
'langcode' => 'fr',
|
||||
],
|
||||
'expected' => '/article/[node:title]',
|
||||
),
|
||||
array(
|
||||
'entity' => 'node',
|
||||
'values' => [
|
||||
'title' => 'Article en',
|
||||
'type' => 'article',
|
||||
'langcode' => 'en',
|
||||
],
|
||||
'expected' => '/article/en/[node:title]',
|
||||
),
|
||||
array(
|
||||
'entity' => 'node',
|
||||
'values' => [
|
||||
'title' => 'Article und',
|
||||
'type' => 'article',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
],
|
||||
'expected' => '/article/[node:title]',
|
||||
),
|
||||
array(
|
||||
'entity' => 'node',
|
||||
'values' => [
|
||||
'title' => 'Page',
|
||||
'type' => 'page',
|
||||
],
|
||||
'expected' => '/[node:title]',
|
||||
),
|
||||
array(
|
||||
'entity' => 'user',
|
||||
'values' => [
|
||||
'name' => 'User',
|
||||
],
|
||||
'expected' => '/users/[user:name]',
|
||||
),
|
||||
);
|
||||
foreach ($tests as $test) {
|
||||
$entity = \Drupal::entityTypeManager()->getStorage($test['entity'])->create($test['values']);
|
||||
$entity->save();
|
||||
$actual = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
|
||||
$this->assertIdentical($actual->getPattern(), $test['expected'], t("Correct pattern returned for @entity_type with @values", array(
|
||||
'@entity' => $test['entity'],
|
||||
'@values' => print_r($test['values'], TRUE),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test potential conflicts with the same alias in different languages.
|
||||
*/
|
||||
public function testSameTitleDifferentLanguages() {
|
||||
// Create two English articles with the same title.
|
||||
$edit = [
|
||||
'title' => 'Sample page',
|
||||
'type' => 'page',
|
||||
'langcode' => 'en',
|
||||
];
|
||||
$node1 = $this->drupalCreateNode($edit);
|
||||
$this->assertEntityAlias($node1, '/content/sample-page', 'en');
|
||||
|
||||
$node2 = $this->drupalCreateNode($edit);
|
||||
$this->assertEntityAlias($node2, '/content/sample-page-0', 'en');
|
||||
|
||||
// Now, create a French article with the same title, and verify that it gets
|
||||
// the basic alias with the correct langcode.
|
||||
$edit['langcode'] = 'fr';
|
||||
$node3 = $this->drupalCreateNode($edit);
|
||||
$this->assertEntityAlias($node3, '/content/sample-page', 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_cleanstring().
|
||||
*/
|
||||
public function testCleanString() {
|
||||
|
||||
// Test with default settings defined in pathauto.settings.yml.
|
||||
$this->installConfig(array('pathauto'));
|
||||
\Drupal::service('pathauto.generator')->resetCaches();
|
||||
|
||||
$tests = array();
|
||||
|
||||
// Test the 'ignored words' removal.
|
||||
$tests['this'] = 'this';
|
||||
$tests['this with that'] = 'this-with-that';
|
||||
$tests['this thing with that thing'] = 'thing-thing';
|
||||
|
||||
// Test 'ignored words' removal and duplicate separator removal.
|
||||
$tests[' - Pathauto is the greatest - module ever - '] = 'pathauto-greatest-module-ever';
|
||||
|
||||
// Test length truncation and lowering of strings.
|
||||
$long_string = $this->randomMachineName(120);
|
||||
$tests[$long_string] = strtolower(substr($long_string, 0, 100));
|
||||
|
||||
// Test that HTML tags are removed.
|
||||
$tests['This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.'] = 'text-has-html-tags';
|
||||
$tests[Html::escape('This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.')] = 'text-has-html-tags';
|
||||
|
||||
// Transliteration.
|
||||
$tests['ľščťžýáíéňô'] = 'lsctzyaieno';
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = \Drupal::service('pathauto.alias_cleaner')->cleanString($input);
|
||||
$this->assertEqual($output, $expected, t("Drupal::service('pathauto.alias_cleaner')->cleanString('@input') expected '@expected', actual '@output'", array(
|
||||
'@input' => $input,
|
||||
'@expected' => $expected,
|
||||
'@output' => $output,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_clean_alias().
|
||||
*/
|
||||
public function testCleanAlias() {
|
||||
$tests = array();
|
||||
$tests['one/two/three'] = '/one/two/three';
|
||||
$tests['/one/two/three/'] = '/one/two/three';
|
||||
$tests['one//two///three'] = '/one/two/three';
|
||||
$tests['one/two--three/-/--/-/--/four---five'] = '/one/two-three/four-five';
|
||||
$tests['one/-//three--/four'] = '/one/three/four';
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = \Drupal::service('pathauto.alias_cleaner')->cleanAlias($input);
|
||||
$this->assertEqual($output, $expected, t("Drupal::service('pathauto.generator')->cleanAlias('@input') expected '@expected', actual '@output'", array(
|
||||
'@input' => $input,
|
||||
'@expected' => $expected,
|
||||
'@output' => $output,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pathauto_path_delete_multiple().
|
||||
*/
|
||||
public function testPathDeleteMultiple() {
|
||||
$this->saveAlias('/node/1', '/node-1-alias');
|
||||
$this->saveAlias('/node/1/view', '/node-1-alias/view');
|
||||
$this->saveAlias('/node/1', '/node-1-alias-en', 'en');
|
||||
$this->saveAlias('/node/1', '/node-1-alias-fr', 'fr');
|
||||
$this->saveAlias('/node/2', '/node-2-alias');
|
||||
$this->saveAlias('/node/10', '/node-10-alias');
|
||||
|
||||
\Drupal::service('pathauto.alias_storage_helper')->deleteBySourcePrefix('/node/1');
|
||||
$this->assertNoAliasExists(array('source' => "/node/1"));
|
||||
$this->assertNoAliasExists(array('source' => "/node/1/view"));
|
||||
$this->assertAliasExists(array('source' => "/node/2"));
|
||||
$this->assertAliasExists(array('source' => "/node/10"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the different update actions in \Drupal::service('pathauto.generator')->createEntityAlias().
|
||||
*/
|
||||
public function testUpdateActions() {
|
||||
$config = $this->config('pathauto.settings');
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'insert'.
|
||||
$config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW);
|
||||
$config->save();
|
||||
$node = $this->drupalCreateNode(array('title' => 'First title'));
|
||||
$this->assertEntityAlias($node, '/content/first-title');
|
||||
|
||||
$node->path->pathauto = PathautoState::CREATE;
|
||||
|
||||
// Default action is PATHAUTO_UPDATE_ACTION_DELETE.
|
||||
$config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE);
|
||||
$config->save();
|
||||
$node->setTitle('Second title');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/second-title');
|
||||
$this->assertNoAliasExists(array('alias' => '/content/first-title'));
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_LEAVE
|
||||
$config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_LEAVE);
|
||||
$config->save();
|
||||
$node->setTitle('Third title');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/third-title');
|
||||
$this->assertAliasExists(array('source' => '/' . $node->toUrl()->getInternalPath(), 'alias' => '/content/second-title'));
|
||||
|
||||
$config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE);
|
||||
$config->save();
|
||||
$node->setTitle('Fourth title');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/fourth-title');
|
||||
$this->assertNoAliasExists(array('alias' => '/content/third-title'));
|
||||
// The older second alias is not deleted yet.
|
||||
$older_path = $this->assertAliasExists(array('source' => '/' . $node->toUrl()->getInternalPath(), 'alias' => '/content/second-title'));
|
||||
\Drupal::service('path.alias_storage')->delete($older_path);
|
||||
|
||||
$config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW);
|
||||
$config->save();
|
||||
$node->setTitle('Fifth title');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/fourth-title');
|
||||
$this->assertNoAliasExists(array('alias' => '/content/fifth-title'));
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'update'.
|
||||
$this->deleteAllAliases();
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/fifth-title');
|
||||
|
||||
// Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'.
|
||||
$this->deleteAllAliases();
|
||||
$node->setTitle('Sixth title');
|
||||
\Drupal::service('pathauto.generator')->updateEntityAlias($node, 'bulkupdate');
|
||||
$this->assertEntityAlias($node, '/content/sixth-title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that \Drupal::service('pathauto.generator')->createEntityAlias() will not create an alias for a pattern
|
||||
* that does not get any tokens replaced.
|
||||
*/
|
||||
public function testNoTokensNoAlias() {
|
||||
$this->installConfig(['filter']);
|
||||
$this->nodePattern
|
||||
->setPattern('/content/[node:body]')
|
||||
->save();
|
||||
|
||||
$node = $this->drupalCreateNode();
|
||||
$this->assertNoEntityAliasExists($node);
|
||||
|
||||
$node->body->value = 'hello';
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/content/hello');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the handling of path vs non-path tokens in pathauto_clean_token_values().
|
||||
*/
|
||||
public function testPathTokens() {
|
||||
$this->createPattern('taxonomy_term', '/[term:parent:url:path]/[term:name]');
|
||||
|
||||
$vocab = $this->addVocabulary();
|
||||
|
||||
$term1 = $this->addTerm($vocab, array('name' => 'Parent term'));
|
||||
$this->assertEntityAlias($term1, '/parent-term');
|
||||
|
||||
$term2 = $this->addTerm($vocab, array('name' => 'Child term', 'parent' => $term1->id()));
|
||||
$this->assertEntityAlias($term2, '/parent-term/child-term');
|
||||
|
||||
$this->saveEntityAlias($term1, '/My Crazy/Alias/');
|
||||
$term2->save();
|
||||
$this->assertEntityAlias($term2, '/My Crazy/Alias/child-term');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test using fields for path structures.
|
||||
*/
|
||||
function testParentChildPathTokens() {
|
||||
// First create a field which will be used to create the path. It must
|
||||
// begin with a letter.
|
||||
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
Vocabulary::create(['vid' => 'tags'])->save();
|
||||
|
||||
$fieldname = 'a' . Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(['entity_type' => 'taxonomy_term', 'field_name' => $fieldname, 'type' => 'string']);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create(['field_storage' => $field_storage, 'bundle' => 'tags']);
|
||||
$field->save();
|
||||
|
||||
$display = entity_get_display('taxonomy_term', 'tags', 'default');
|
||||
$display->setComponent($fieldname, ['type' => 'string']);
|
||||
$display->save();
|
||||
|
||||
// Make the path pattern of a field use the value of this field appended
|
||||
// to the parent taxonomy term's pattern if there is one.
|
||||
$this->createPattern('taxonomy_term', '/[term:parents:join-path]/[term:' . $fieldname . ']');
|
||||
|
||||
// Start by creating a parent term.
|
||||
$parent = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'name' => $this->randomMachineName()]);
|
||||
$parent->save();
|
||||
|
||||
// Create the child term.
|
||||
$child = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'parent' => $parent, 'name' => $this->randomMachineName()]);
|
||||
$child->save();
|
||||
$this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value));
|
||||
|
||||
// Re-saving the parent term should not modify the child term's alias.
|
||||
$parent->save();
|
||||
$this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests aliases on taxonomy terms.
|
||||
*/
|
||||
public function testTaxonomyPattern() {
|
||||
// Create a vocabulary and test that it's pattern variable works.
|
||||
$vocab = $this->addVocabulary(array('vid' => 'name'));
|
||||
$this->createPattern('taxonomy_term', 'base');
|
||||
$pattern = $this->createPattern('taxonomy_term', 'bundle', -1);
|
||||
$this->addBundleCondition($pattern, 'taxonomy_term', 'name');
|
||||
$pattern->save();
|
||||
$this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'bundle');
|
||||
}
|
||||
|
||||
function testNoExistingPathAliases() {
|
||||
$this->config('pathauto.settings')
|
||||
->set('punctuation.period', PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING)
|
||||
->save();
|
||||
|
||||
$this->nodePattern
|
||||
->setPattern('[node:title]')
|
||||
->save();
|
||||
|
||||
// Check that Pathauto does not create an alias of '/admin'.
|
||||
$node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page'));
|
||||
$this->assertEntityAlias($node, '/admin-0');
|
||||
|
||||
// Check that Pathauto does not create an alias of '/modules'.
|
||||
$node->setTitle('Modules');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/modules-0');
|
||||
|
||||
// Check that Pathauto does not create an alias of '/index.php'.
|
||||
$node->setTitle('index.php');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/index.php-0');
|
||||
|
||||
// Check that a safe value gets an automatic alias. This is also a control
|
||||
// to ensure the above tests work properly.
|
||||
$node->setTitle('Safe value');
|
||||
$node->save();
|
||||
$this->assertEntityAlias($node, '/safe-value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test programmatic entity creation for aliases.
|
||||
*/
|
||||
function testProgrammaticEntityCreation() {
|
||||
$this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
|
||||
$node = $this->drupalCreateNode(array('title' => 'Test node', 'path' => array('pathauto' => TRUE)));
|
||||
$this->assertEntityAlias($node, '/content/test-node');
|
||||
|
||||
$vocabulary = $this->addVocabulary(array('name' => 'Tags'));
|
||||
$term = $this->addTerm($vocabulary, array('name' => 'Test term', 'path' => array('pathauto' => TRUE)));
|
||||
$this->assertEntityAlias($term, '/tags/test-term');
|
||||
|
||||
$edit['name'] = 'Test user';
|
||||
$edit['mail'] = 'test-user@example.com';
|
||||
$edit['pass'] = user_password();
|
||||
$edit['path'] = array('pathauto' => TRUE);
|
||||
$edit['status'] = 1;
|
||||
$account = User::create($edit);
|
||||
$account->save();
|
||||
$this->assertEntityAlias($account, '/users/test-user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests word safe alias truncating.
|
||||
*/
|
||||
function testPathAliasUniquifyWordsafe() {
|
||||
$this->config('pathauto.settings')
|
||||
->set('max_length', 26)
|
||||
->save();
|
||||
|
||||
$node_1 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page'));
|
||||
$node_2 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page'));
|
||||
|
||||
// Check that alias uniquifying is truncating with $wordsafe param set to
|
||||
// TRUE.
|
||||
// If it doesn't path alias result would be content/thequick-brownf-0
|
||||
$this->assertEntityAlias($node_1, '/content/thequick-brownfox');
|
||||
$this->assertEntityAlias($node_2, '/content/thequick-0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if aliases are (not) generated with enabled/disabled patterns.
|
||||
*/
|
||||
function testPatternStatus() {
|
||||
// Create a node to get an alias for.
|
||||
$title = 'Pattern enabled';
|
||||
$alias = '/content/pattern-enabled';
|
||||
$node1 = $this->drupalCreateNode(['title' => $title, 'type' => 'page']);
|
||||
$this->assertEntityAlias($node1, $alias);
|
||||
|
||||
// Disable the pattern, save the node again and make sure the alias is still
|
||||
// working.
|
||||
$this->nodePattern->setStatus(FALSE)->save();
|
||||
|
||||
$node1->save();
|
||||
$this->assertEntityAlias($node1, $alias);
|
||||
|
||||
// Create a new node with disabled pattern and make sure there is no new
|
||||
// alias created.
|
||||
$title = 'Pattern disabled';
|
||||
$node2 = $this->drupalCreateNode(['title' => $title, 'type' => 'page']);
|
||||
$this->assertNoEntityAlias($node2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that enabled entity types genrates the necessary fields and plugins.
|
||||
*/
|
||||
public function testSettingChangeInvalidatesCache() {
|
||||
|
||||
$this->installConfig(['pathauto']);
|
||||
|
||||
$this->enableModules(['entity_test']);
|
||||
|
||||
$definitions = \Drupal::service('plugin.manager.alias_type')->getDefinitions();
|
||||
$this->assertFalse(isset($definitions['canonical_entities:entity_test']));
|
||||
|
||||
$fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('entity_test');
|
||||
$this->assertFalse(isset($fields['path']));
|
||||
|
||||
$this->config('pathauto.settings')
|
||||
->set('enabled_entity_types', ['user', 'entity_test'])
|
||||
->save();
|
||||
|
||||
$definitions = \Drupal::service('plugin.manager.alias_type')->getDefinitions();
|
||||
$this->assertTrue(isset($definitions['canonical_entities:entity_test']));
|
||||
|
||||
$fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('entity_test');
|
||||
$this->assertTrue(isset($fields['path']));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that aliases are only generated for default revisions.
|
||||
*/
|
||||
public function testDefaultRevision() {
|
||||
$node1 = $this->drupalCreateNode(['title' => 'Default revision', 'type' => 'page']);
|
||||
$this->assertEntityAlias($node1, '/content/default-revision');
|
||||
|
||||
$node1->setNewRevision(TRUE);
|
||||
$node1->isDefaultRevision(FALSE);
|
||||
$node1->setTitle('New non-default-revision');
|
||||
$node1->save();
|
||||
|
||||
$this->assertEntityAlias($node1, '/content/default-revision');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a node programmatically.
|
||||
*
|
||||
* @param array $settings
|
||||
* The array of values for the node.
|
||||
*
|
||||
* @return \Drupal\node\Entity\Node
|
||||
* The created node.
|
||||
*/
|
||||
protected function drupalCreateNode(array $settings = array()) {
|
||||
// Populate defaults array.
|
||||
$settings += array(
|
||||
'title' => $this->randomMachineName(8),
|
||||
'type' => 'page',
|
||||
);
|
||||
|
||||
$node = Node::create($settings);
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\pathauto\Kernel;
|
||||
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests tokens provided by Pathauto.
|
||||
*
|
||||
* @group pathauto
|
||||
*/
|
||||
class PathautoTokenTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'token', 'pathauto');
|
||||
|
||||
public function testPathautoTokens() {
|
||||
|
||||
$this->installConfig(array('pathauto'));
|
||||
|
||||
$array = array(
|
||||
'test first arg',
|
||||
'The Array / value',
|
||||
);
|
||||
|
||||
$tokens = array(
|
||||
'join-path' => 'test-first-arg/array-value',
|
||||
);
|
||||
$data['array'] = $array;
|
||||
$replacements = $this->assertTokens('array', $data, $tokens);
|
||||
|
||||
// Ensure that the cleanTokenValues() method does not alter this token value.
|
||||
/* @var \Drupal\pathauto\AliasCleanerInterface $alias_cleaner */
|
||||
$alias_cleaner = \Drupal::service('pathauto.alias_cleaner');
|
||||
$alias_cleaner->cleanTokenValues($replacements, $data, array());
|
||||
$this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Function copied from TokenTestHelper::assertTokens().
|
||||
*/
|
||||
public function assertTokens($type, array $data, array $tokens, array $options = array()) {
|
||||
$input = $this->mapTokenNames($type, array_keys($tokens));
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$replacements = \Drupal::token()->generate($type, $input, $data, $options, $bubbleable_metadata);
|
||||
foreach ($tokens as $name => $expected) {
|
||||
$token = $input[$name];
|
||||
if (!isset($expected)) {
|
||||
$this->assertTrue(!isset($values[$token]), t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
|
||||
}
|
||||
elseif (!isset($replacements[$token])) {
|
||||
$this->fail(t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
|
||||
}
|
||||
elseif (!empty($options['regex'])) {
|
||||
$this->assertTrue(preg_match('/^' . $expected . '$/', $replacements[$token]), t("Token value for @token was '@actual', matching regular expression pattern '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
|
||||
}
|
||||
else {
|
||||
$this->assertIdentical($replacements[$token], $expected, t("Token value for @token was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
||||
|
||||
public function mapTokenNames($type, array $tokens = array()) {
|
||||
$return = array();
|
||||
foreach ($tokens as $token) {
|
||||
$return[$token] = "[$type:$token]";
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\pathauto\Unit {
|
||||
|
||||
use Drupal\pathauto\VerboseMessenger;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\pathauto\VerboseMessenger
|
||||
* @group pathauto
|
||||
*/
|
||||
class VerboseMessengerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The messenger under test.
|
||||
*
|
||||
* @var \Drupal\pathauto\VerboseMessenger
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$config_factory = $this->getConfigFactoryStub(array('pathauto.settings' => array('verbose' => TRUE)));
|
||||
$account = $this->getMock('\Drupal\Core\Session\AccountInterface');
|
||||
$account->expects($this->once())
|
||||
->method('hasPermission')
|
||||
->withAnyParameters()
|
||||
->willReturn(TRUE);
|
||||
|
||||
$this->messenger = new VerboseMessenger($config_factory, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests add messages.
|
||||
* @covers ::addMessage
|
||||
*/
|
||||
public function testAddMessage() {
|
||||
$this->assertTrue($this->messenger->addMessage("Test message"), "The message was added");
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addMessage
|
||||
*/
|
||||
public function testDoNotAddMessageWhileBulkupdate() {
|
||||
$this->assertFalse($this->messenger->addMessage("Test message", "bulkupdate"), "The message was NOT added");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
namespace {
|
||||
// @todo Delete after https://drupal.org/node/1858196 is in.
|
||||
if (!function_exists('drupal_set_message')) {
|
||||
function drupal_set_message() {
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue