diff --git a/composer.json b/composer.json
index 440480482e0e8c7acdffeddf6e503dd53b647fb8..baf02ead48f18acab60a5de7e848f2a96826d459 100644
--- a/composer.json
+++ b/composer.json
@@ -95,6 +95,7 @@
         "drupal/embed": "1.0",
         "drupal/entity": "1.0-beta1",
         "drupal/entity_browser": "1.4",
+        "drupal/entity_clone": "^1.0@beta",
         "drupal/entity_embed": "1.0-beta2",
         "drupal/entity_reference_revisions": "1.3",
         "drupal/externalauth": "1.1",
diff --git a/composer.lock b/composer.lock
index 81b26543f40b5b589a04f99083a4d8be8a8d7e0c..4ee33676772693ef5723f6fb42e8c9b0cd9eeccf 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "69a66388caf4a15e8bfb8680c7774d79",
+    "content-hash": "354da2ce0fea7aab24e06b2dd23b17c9",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -3402,6 +3402,53 @@
                 "irc": "irc://irc.freenode.org/drupal-contribute"
             }
         },
+        {
+            "name": "drupal/entity_clone",
+            "version": "1.0.0-beta1",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupal.org/project/entity_clone",
+                "reference": "8.x-1.0-beta1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta1.zip",
+                "reference": "8.x-1.0-beta1",
+                "shasum": "d0ace20bbe1672fbe6d0cdd6da8f5b80cd156643"
+            },
+            "require": {
+                "drupal/core": "~8.0"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                },
+                "drupal": {
+                    "version": "8.x-1.0-beta1",
+                    "datestamp": "1546583284",
+                    "security-coverage": {
+                        "status": "not-covered",
+                        "message": "Beta releases are not covered by Drupal security advisories."
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "vpeltot",
+                    "homepage": "https://www.drupal.org/user/1361586"
+                }
+            ],
+            "description": "Add a clone action for all entities",
+            "homepage": "https://www.drupal.org/project/entity_clone",
+            "support": {
+                "source": "http://cgit.drupalcode.org/entity_clone"
+            }
+        },
         {
             "name": "drupal/entity_embed",
             "version": "1.0.0-beta2",
@@ -10614,6 +10661,7 @@
     "aliases": [],
     "minimum-stability": "dev",
     "stability-flags": {
+        "drupal/entity_clone": 10,
         "drupal/migrate_devel": 20,
         "drupal/roleassign": 15,
         "drupal/view_unpublished": 15
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 60c5c44d629d92a74dc9e204d320455cdadc3a71..95ecc75af79b6b0035e0f23f6e296965cdfe0cd7 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -3503,6 +3503,55 @@
             "irc": "irc://irc.freenode.org/drupal-contribute"
         }
     },
+    {
+        "name": "drupal/entity_clone",
+        "version": "1.0.0-beta1",
+        "version_normalized": "1.0.0.0-beta1",
+        "source": {
+            "type": "git",
+            "url": "https://git.drupal.org/project/entity_clone",
+            "reference": "8.x-1.0-beta1"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://ftp.drupal.org/files/projects/entity_clone-8.x-1.0-beta1.zip",
+            "reference": "8.x-1.0-beta1",
+            "shasum": "d0ace20bbe1672fbe6d0cdd6da8f5b80cd156643"
+        },
+        "require": {
+            "drupal/core": "~8.0"
+        },
+        "type": "drupal-module",
+        "extra": {
+            "branch-alias": {
+                "dev-1.x": "1.x-dev"
+            },
+            "drupal": {
+                "version": "8.x-1.0-beta1",
+                "datestamp": "1546583284",
+                "security-coverage": {
+                    "status": "not-covered",
+                    "message": "Beta releases are not covered by Drupal security advisories."
+                }
+            }
+        },
+        "installation-source": "dist",
+        "notification-url": "https://packages.drupal.org/8/downloads",
+        "license": [
+            "GPL-2.0-or-later"
+        ],
+        "authors": [
+            {
+                "name": "vpeltot",
+                "homepage": "https://www.drupal.org/user/1361586"
+            }
+        ],
+        "description": "Add a clone action for all entities",
+        "homepage": "https://www.drupal.org/project/entity_clone",
+        "support": {
+            "source": "http://cgit.drupalcode.org/entity_clone"
+        }
+    },
     {
         "name": "drupal/entity_embed",
         "version": "1.0.0-beta2",
diff --git a/web/modules/entity_clone/LICENSE.txt b/web/modules/entity_clone/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/web/modules/entity_clone/LICENSE.txt
@@ -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.
diff --git a/web/modules/entity_clone/config/schema/entity_clone.settings.schema.yml b/web/modules/entity_clone/config/schema/entity_clone.settings.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..602453c34951a139657c0b58169b375a6d6ab93c
--- /dev/null
+++ b/web/modules/entity_clone/config/schema/entity_clone.settings.schema.yml
@@ -0,0 +1,88 @@
+entity_clone.settings:
+  type: config_object
+  label: 'Module entity_clone settings'
+  mapping:
+    form_settings:
+      type: mapping
+      mapping:
+        taxonomy_term:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        block_content:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        comment:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        contact_message:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        file:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        node:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        shortcut:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        user:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
+        menu_link_content:
+          type: mapping
+          mapping:
+            default_value:
+              type: boolean
+            disable:
+              type: boolean
+            hidden:
+              type: boolean
diff --git a/web/modules/entity_clone/entity_clone.info.yml b/web/modules/entity_clone/entity_clone.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4f70fcb5ee80bce7d4600dd58d029cd100ef0c70
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.info.yml
@@ -0,0 +1,11 @@
+name: Entity Clone
+description: Add a clone action for all entities
+# core:  "8.x"
+type: module
+configure: entity_clone.settings
+
+# Information added by Drupal.org packaging script on 2019-01-04
+version: '8.x-1.0-beta1'
+core: '8.x'
+project: 'entity_clone'
+datestamp: 1546583287
diff --git a/web/modules/entity_clone/entity_clone.links.menu.yml b/web/modules/entity_clone/entity_clone.links.menu.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f013a12f9ba486b55461ea6f72f73cfea553749c
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.links.menu.yml
@@ -0,0 +1,5 @@
+entity_clone.settings:
+  title: 'Entity clone settings'
+  description: 'Entity clone settings.'
+  route_name: entity_clone.settings
+  parent: 'system.admin_config_content'
diff --git a/web/modules/entity_clone/entity_clone.links.task.yml b/web/modules/entity_clone/entity_clone.links.task.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427f5759dce38294a6a0ed1fc695759a08de174f
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.links.task.yml
@@ -0,0 +1,8 @@
+entity_clone.clone:
+  deriver: 'Drupal\entity_clone\Plugin\Derivative\DynamicLocalTasks'
+  weight: 100
+entity_clone.settings:
+  title: 'Entity clone settings'
+  route_name: entity_clone.settings
+  base_route: entity_clone.settings
+  weight: 0
diff --git a/web/modules/entity_clone/entity_clone.module b/web/modules/entity_clone/entity_clone.module
new file mode 100644
index 0000000000000000000000000000000000000000..7b6a3a919abde6c18d4dad747ae684c5aa4066ef
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.module
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains entity_clone.module.
+ */
+
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
+use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneBase;
+use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase;
+use Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone;
+use Drupal\entity_clone\EntityClone\Config\FieldConfigEntityClone;
+use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneBase;
+use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneFormBase;
+use Drupal\entity_clone\EntityClone\Content\FileEntityClone;
+use Drupal\entity_clone\EntityClone\Content\TaxonomyTermEntityClone;
+use Drupal\entity_clone\EntityClone\Content\UserEntityClone;
+
+/**
+ * Implements hook_help().
+ */
+function entity_clone_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    // Main module help for the entity_clone module.
+    case 'help.page.entity_clone':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('Provides a new operation to clone all Entities.') . '</p>';
+      return $output;
+
+    default:
+      return;
+
+  }
+}
+
+/**
+ * Implements hook_entity_type_build().
+ */
+function entity_clone_entity_type_build(array &$entity_types) {
+  $specific_handler = [
+    'file' => [
+      'entity_clone' => FileEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
+    ],
+    'user' => [
+      'entity_clone' => UserEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
+    ],
+    'field_config' => [
+      'entity_clone' => FieldConfigEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'node_type' => [
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'comment_type' => [
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'block_content_type' => [
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'contact_form' => [
+      'entity_clone' => ConfigWithFieldEntityClone::class,
+      'entity_clone_form' => ConfigEntityCloneFormBase::class,
+    ],
+    'taxonomy_term' => [
+      'entity_clone' => TaxonomyTermEntityClone::class,
+      'entity_clone_form' => ContentEntityCloneFormBase::class,
+    ],
+  ];
+
+  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
+  foreach ($entity_types as &$entity_type) {
+    if (isset($specific_handler[$entity_type->id()])) {
+      $entity_type->setHandlerClass('entity_clone', $specific_handler[$entity_type->id()]['entity_clone']);
+      if (isset($specific_handler[$entity_type->id()]['entity_clone_form'])) {
+        $entity_type->setHandlerClass('entity_clone_form', $specific_handler[$entity_type->id()]['entity_clone_form']);
+      }
+    }
+    elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ContentEntityTypeInterface) {
+      $entity_type->setHandlerClass('entity_clone', ContentEntityCloneBase::class);
+      $entity_type->setHandlerClass('entity_clone_form', ContentEntityCloneFormBase::class);
+    }
+    elseif (!$entity_type->getHandlerClass('entity_clone') && $entity_type instanceof ConfigEntityTypeInterface) {
+      $entity_type->setHandlerClass('entity_clone', ConfigEntityCloneBase::class);
+      $entity_type->setHandlerClass('entity_clone_form', ConfigEntityCloneFormBase::class);
+    }
+  }
+}
+
+/**
+ * Declares entity operations.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity on which the linked operations will be performed.
+ *
+ * @return array
+ *   An operations array as returned by
+ *   EntityListBuilderInterface::getOperations().
+ *
+ * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
+ */
+function entity_clone_entity_operation(EntityInterface $entity) {
+  if ($entity->hasLinkTemplate('clone-form')) {
+    return [
+      'clone' => [
+        'title' => t('Clone'),
+        'weight' => 50,
+        'url' => $entity->toUrl('clone-form'),
+      ],
+    ];
+  }
+
+  return [];
+}
+
+/**
+ * Implements hook_entity_type_alter().
+ */
+function entity_clone_entity_type_alter(array &$entity_types) {
+  /** @var \Drupal\Core\Entity\EntityType[] $entity_types */
+  foreach ($entity_types as $entity_type_id => $entity_type) {
+    $entity_type->setLinkTemplate('clone-form', "/entity_clone/$entity_type_id/{{$entity_type_id}}");
+  }
+}
diff --git a/web/modules/entity_clone/entity_clone.permissions.yml b/web/modules/entity_clone/entity_clone.permissions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6e259f5f594a6b1543e836dbc1cb38fc6c964773
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.permissions.yml
@@ -0,0 +1,2 @@
+permission_callbacks:
+  - Drupal\entity_clone\EntityClonePermissions::permissions
diff --git a/web/modules/entity_clone/entity_clone.post_update.php b/web/modules/entity_clone/entity_clone.post_update.php
new file mode 100644
index 0000000000000000000000000000000000000000..67349bb564ebca59939235e9c9f0f17ca95df699
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.post_update.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains entity_clone.post_update.php.
+ */
+
+/**
+ * Populates new entity_clone form settings.
+ */
+function entity_clone_post_update_populate_form_settings() {
+  /** @var \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager */
+  $entity_clone_settings_manager = \Drupal::service('entity_clone.settings.manager');
+  $form_settings = [];
+  foreach (array_keys($entity_clone_settings_manager->getContentEntityTypes()) as $entity_type_id) {
+    $form_settings[$entity_type_id] = [
+      'default_value' => FALSE,
+      'disable' => FALSE,
+      'hidden' => FALSE,
+    ];
+  }
+
+  \Drupal::configFactory()->getEditable('entity_clone.settings')->set('form_settings', $form_settings)->save();
+}
diff --git a/web/modules/entity_clone/entity_clone.routing.yml b/web/modules/entity_clone/entity_clone.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b89a94b73d350ae0aafbeafad79dc9a6cf7e8834
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.routing.yml
@@ -0,0 +1,9 @@
+entity_clone.settings:
+  path: '/admin/config/system/entity-clone'
+  defaults:
+    _form: 'Drupal\entity_clone\Form\EntityCloneSettingsForm'
+    _title: 'Entity clone settings'
+  options:
+    _admin_route: TRUE
+  requirements:
+    _permission: 'administer entity clone'
diff --git a/web/modules/entity_clone/entity_clone.services.yml b/web/modules/entity_clone/entity_clone.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..20cf5db105eb6c965ad63eb73bb49622e14b64f5
--- /dev/null
+++ b/web/modules/entity_clone/entity_clone.services.yml
@@ -0,0 +1,9 @@
+services:
+  entity_clone.settings.manager:
+    class: Drupal\entity_clone\EntityCloneSettingsManager
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@config.factory']
+  entity_clone.route_subscriber:
+    class: Drupal\entity_clone\Routing\RouteSubscriber
+    arguments: ['@entity_type.manager']
+    tags:
+      - { name: event_subscriber }
diff --git a/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneBase.php b/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..c0c33490a956af671df83c129fc1c2605a006e69
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneBase.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Config;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\entity_clone\EntityClone\EntityCloneInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ConfigEntityCloneBase.
+ */
+class ConfigEntityCloneBase implements EntityHandlerInterface, EntityCloneInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity type ID.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * Constructs a new ConfigEntityCloneBase.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param string $entity_type_id
+   *   The entity type ID.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, $entity_type_id) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeId = $entity_type_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $entity_type->id()
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    /** @var \Drupal\core\Config\Entity\ConfigEntityInterface $cloned_entity */
+    $id_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('id');
+    $label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label');
+
+    // Set new entity properties.
+    if (isset($properties['id'])) {
+      if ($id_key) {
+        $cloned_entity->set($id_key, $properties['id']);
+      }
+      unset($properties['id']);
+    }
+
+    if (isset($properties['label'])) {
+      if ($label_key) {
+        $cloned_entity->set($label_key, $properties['label']);
+      }
+      unset($properties['label']);
+    }
+
+    foreach ($properties as $key => $property) {
+      $cloned_entity->set($key, $property);
+    }
+
+    $cloned_entity->save();
+    return $cloned_entity;
+
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneFormBase.php b/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneFormBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f1c4360921397d0dc3825af337350b9afbc4d73
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Config/ConfigEntityCloneFormBase.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Config;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Drupal\entity_clone\EntityClone\EntityCloneFormInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ConfigEntityCloneFormBase.
+ */
+class ConfigEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFormInterface {
+
+  /**
+   * The string translation.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translationManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new ConfigEntityCloneFormBase.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $translation_manager
+   *   The string translation manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationManager $translation_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->translationManager = $translation_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(EntityInterface $entity, $parent = TRUE) {
+    $form = [];
+
+    if ($this->entityTypeManager->getDefinition($entity->getEntityTypeId())->getKey('label')) {
+      $form['label'] = [
+        '#type' => 'textfield',
+        '#title' => $this->translationManager->translate('New Label'),
+        '#maxlength' => 255,
+        '#required' => TRUE,
+      ];
+    }
+
+    $form['id'] = [
+      '#type' => 'machine_name',
+      '#title' => $this->translationManager->translate('New Id'),
+      '#maxlength' => 255,
+      '#required' => TRUE,
+    ];
+
+    // If entity must have a prefix
+    // (e.g. entity_form_mode, entity_view_mode, ...).
+    if (method_exists($entity, 'getTargetType')) {
+      $form['id']['#field_prefix'] = $entity->getTargetType() . '.';
+    }
+
+    if (method_exists($entity, 'load')) {
+      $form['id']['#machine_name'] = [
+        'exists' => get_class($entity) . '::load',
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValues(FormStateInterface $form_state) {
+    // If entity must have a prefix
+    // (e.g. entity_form_mode, entity_view_mode, ...).
+    $field_prefix = '';
+    if (isset($form_state->getCompleteForm()['id']['#field_prefix'])) {
+      $field_prefix = $form_state->getCompleteForm()['id']['#field_prefix'];
+    }
+
+    return [
+      'id' => $field_prefix . $form_state->getValue('id'),
+      'label' => $form_state->getValue('label'),
+    ];
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Config/ConfigWithFieldEntityClone.php b/web/modules/entity_clone/src/EntityClone/Config/ConfigWithFieldEntityClone.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cdb093ed48d08149ab8433d6184706670646bb5
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Config/ConfigWithFieldEntityClone.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Config;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Field\FieldConfigInterface;
+
+/**
+ * Class ContentEntityCloneBase.
+ */
+class ConfigWithFieldEntityClone extends ConfigEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    $cloned_entity = parent::cloneEntity($entity, $cloned_entity, $properties);
+    $bundle_of = $cloned_entity->getEntityType()->getBundleOf();
+    if ($bundle_of) {
+      $this->cloneFields($entity->id(), $cloned_entity->id(), $bundle_of);
+    }
+
+    $view_displays = \Drupal::service('entity_display.repository')->getViewModes($bundle_of);
+    $view_displays = array_merge($view_displays, ['default' => 'default']);
+    if (!empty($view_displays)) {
+      $this->cloneDisplays('view', $entity->id(), $cloned_entity->id(), $view_displays, $bundle_of);
+    }
+
+    $view_displays = \Drupal::service('entity_display.repository')->getFormModes($bundle_of);
+    $view_displays = array_merge($view_displays, ['default' => 'default']);
+    if (!empty($view_displays)) {
+      $this->cloneDisplays('form', $entity->id(), $cloned_entity->id(), $view_displays, $bundle_of);
+    }
+
+    return $cloned_entity;
+  }
+
+  /**
+   * Clone all fields. Each field re-use existing field storage.
+   *
+   * @param string $entity_id
+   *   The base entity ID.
+   * @param string $cloned_entity_id
+   *   The cloned entity ID.
+   * @param string $bundle_of
+   *   The bundle of the cloned entity.
+   */
+  protected function cloneFields($entity_id, $cloned_entity_id, $bundle_of) {
+    /** @var \Drupal\Core\Entity\EntityFieldManager $field_manager */
+    $field_manager = \Drupal::service('entity_field.manager');
+    $fields = $field_manager->getFieldDefinitions($bundle_of, $entity_id);
+    foreach ($fields as $field_definition) {
+      if ($field_definition instanceof FieldConfigInterface) {
+        if ($this->entityTypeManager->hasHandler($this->entityTypeManager->getDefinition($field_definition->getEntityTypeId())
+          ->id(), 'entity_clone')
+        ) {
+          /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $field_config_clone_handler */
+          $field_config_clone_handler = $this->entityTypeManager->getHandler($this->entityTypeManager->getDefinition($field_definition->getEntityTypeId())
+            ->id(), 'entity_clone');
+          $field_config_properties = [
+            'id' => $field_definition->getName(),
+            'label' => $field_definition->label(),
+            'skip_storage' => TRUE,
+          ];
+          $cloned_field_definition = $field_definition->createDuplicate();
+          $cloned_field_definition->set('bundle', $cloned_entity_id);
+          $field_config_clone_handler->cloneEntity($field_definition, $cloned_field_definition, $field_config_properties);
+        }
+      }
+    }
+  }
+
+  /**
+   * Clone all fields. Each field re-use existing field storage.
+   *
+   * @param string $type
+   *   The type of display (view or form).
+   * @param string $entity_id
+   *   The base entity ID.
+   * @param string $cloned_entity_id
+   *   The cloned entity ID.
+   * @param array $view_displays
+   *   All view available display for this type.
+   * @param string $bundle_of
+   *   The bundle of the cloned entity.
+   */
+  protected function cloneDisplays($type, $entity_id, $cloned_entity_id, array $view_displays, $bundle_of) {
+    foreach ($view_displays as $view_display_id => $view_display) {
+      /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */
+      $display = $this->entityTypeManager->getStorage('entity_' . $type . '_display')->load($bundle_of . '.' . $entity_id . '.' . $view_display_id);
+      if ($display) {
+        /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $view_display_clone_handler */
+        $view_display_clone_handler = $this->entityTypeManager->getHandler($this->entityTypeManager->getDefinition($display->getEntityTypeId())
+          ->id(), 'entity_clone');
+        $view_display_properties = [
+          'id' => $bundle_of . '.' . $cloned_entity_id . '.' . $view_display_id,
+        ];
+        $cloned_view_display = $display->createDuplicate();
+        $cloned_view_display->set('bundle', $cloned_entity_id);
+        $view_display_clone_handler->cloneEntity($display, $cloned_view_display, $view_display_properties);
+      }
+    }
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Config/FieldConfigEntityClone.php b/web/modules/entity_clone/src/EntityClone/Config/FieldConfigEntityClone.php
new file mode 100644
index 0000000000000000000000000000000000000000..b16d1edaf9613632637bd6dbd89f1f4788f4100f
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Config/FieldConfigEntityClone.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Config;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Class FieldConfigEntityClone.
+ */
+class FieldConfigEntityClone extends ConfigEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $field_config, EntityInterface $cloned_field_config, array $properties = []) {
+    /** @var \Drupal\field\Entity\FieldConfig $field_config */
+    /** @var \Drupal\field\Entity\FieldConfig $cloned_field_config */
+    /** @var \Drupal\field\Entity\FieldStorageConfig $cloned_field_storage */
+
+    if ((!isset($properties['skip_storage']) || !$properties['skip_storage'])) {
+      $cloned_field_storage = $field_config->getFieldStorageDefinition()->createDuplicate();
+      $cloned_field_storage->set('field_name', $properties['id']);
+      $cloned_field_storage->set('id', $properties['id'] . '.' . $cloned_field_storage->getTargetEntityTypeId());
+      $cloned_field_storage->save();
+    }
+    unset($properties['skip_storage']);
+
+    $properties['field_name'] = $properties['id'];
+    $properties['id'] = $cloned_field_config->getTargetEntityTypeId() . '.' . $cloned_field_config->getTargetBundle() . '.' . $properties['id'];
+    return parent::cloneEntity($field_config, $cloned_field_config, $properties);
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..b3b7de1faef6ec638597f47fa7e6cb42600da4f7
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneBase.php
@@ -0,0 +1,177 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldConfigInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\entity_clone\EntityClone\EntityCloneInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ContentEntityCloneBase.
+ */
+class ContentEntityCloneBase implements EntityHandlerInterface, EntityCloneInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity type ID.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * Constructs a new ContentEntityCloneBase.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param string $entity_type_id
+   *   The entity type ID.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, $entity_type_id) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeId = $entity_type_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $entity_type->id()
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    // Clone referenced entities.
+    if ($cloned_entity instanceof FieldableEntityInterface && $entity instanceof FieldableEntityInterface) {
+      foreach ($cloned_entity->getFieldDefinitions() as $field_id => $field_definition) {
+        if ($this->fieldIsClonable($field_definition)) {
+          $field = $entity->get($field_id);
+          /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
+          if ($field->count() > 0) {
+            $cloned_entity->set($field_id, $this->cloneReferencedEntities($field, $field_definition, $properties));
+          }
+        }
+      }
+    }
+
+    $this->setClonedEntityLabel($entity, $cloned_entity);
+    $cloned_entity->save();
+    return $cloned_entity;
+  }
+
+  /**
+   * Determines if a field is clonable.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition.
+   *
+   * @return bool
+   *   TRUE if th field is clonable; FALSE otherwise.
+   */
+  protected function fieldIsClonable(FieldDefinitionInterface $field_definition) {
+    $clonable_field_types = [
+      'entity_reference',
+      'entity_reference_revisions',
+    ];
+
+    $type_is_clonable = in_array($field_definition->getType(), $clonable_field_types, TRUE);
+    if (($field_definition instanceof FieldConfigInterface) && $type_is_clonable) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Sets the cloned entity's label.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $original_entity
+   *   The original entity.
+   * @param \Drupal\Core\Entity\EntityInterface $cloned_entity
+   *   The entity cloned from the original.
+   */
+  protected function setClonedEntityLabel(EntityInterface $original_entity, EntityInterface $cloned_entity) {
+    $label_key = $this->entityTypeManager->getDefinition($this->entityTypeId)->getKey('label');
+    if ($label_key) {
+      $cloned_entity->set($label_key, $original_entity->label() . ' - Cloned');
+    }
+  }
+
+  /**
+   * Clone referenced entities.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $field
+   *   The field item.
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param array $properties
+   *   All new properties to replace old.
+   *
+   * @return array
+   *   Referenced entities.
+   */
+  protected function cloneReferencedEntities(FieldItemListInterface $field, FieldConfigInterface $field_definition, array $properties) {
+    $referenced_entities = [];
+    foreach ($field as $value) {
+      // Check if we're not dealing with an entity that has been deleted in the meantime
+      if (!$referenced_entity = $value->get('entity')->getTarget()) {
+        continue;
+      }
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */
+      $referenced_entity = $value->get('entity')->getTarget()->getValue();
+      if (!empty($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['clone'])) {
+
+        $cloned_reference = $referenced_entity->createDuplicate();
+        /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $entity_clone_handler */
+        $entity_clone_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone');
+        $child_properties = $this->getChildProperties($properties, $field_definition, $referenced_entity);
+        $entity_clone_handler->cloneEntity($referenced_entity, $cloned_reference, $child_properties);
+
+        $referenced_entities[] = $cloned_reference;
+      }
+      else {
+        $referenced_entities[] = $referenced_entity;
+      }
+    }
+    return $referenced_entities;
+  }
+
+  /**
+   * Fetches the properties of a child entity.
+   *
+   * @param array $properties
+   *   Properties of the clone operation.
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param \Drupal\Core\Entity\EntityInterface $referenced_entity
+   *   The field's target entity.
+   *
+   * @return array
+   *   Child properties.
+   */
+  protected function getChildProperties(array $properties, FieldConfigInterface $field_definition, EntityInterface $referenced_entity) {
+    $child_properties = [];
+    if (isset($properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['children'])) {
+      $child_properties = $properties['recursive'][$field_definition->id()]['references'][$referenced_entity->id()]['children'];
+    }
+    return $child_properties;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..f4e5be8353dc4931c3a7dc174b33728faf9f9b55
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Content/ContentEntityCloneFormBase.php
@@ -0,0 +1,269 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldConfigInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Drupal\entity_clone\EntityClone\EntityCloneFormInterface;
+use Drupal\entity_clone\EntityCloneSettingsManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ContentEntityCloneFormBase.
+ */
+class ContentEntityCloneFormBase implements EntityHandlerInterface, EntityCloneFormInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translationManager;
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManager
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity clone settings manager service.
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager
+   */
+  protected $entityCloneSettingsManager;
+
+  /**
+   * Entities we've found while cloning.
+   *
+   * @var \Drupal\Core\Entity\ContentEntityInterface[]
+   */
+  protected $discovered_entities = [];
+
+  /**
+   * Constructs a new ContentEntityCloneFormBase.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $translation_manager
+   *   The string translation manager.
+   * @param \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager
+   *   The entity clone settings manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationManager $translation_manager, EntityCloneSettingsManager $entity_clone_settings_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->translationManager = $translation_manager;
+    $this->entityCloneSettingsManager = $entity_clone_settings_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation'),
+      $container->get('entity_clone.settings.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(EntityInterface $entity, $parent = TRUE) {
+    $form = [
+      'recursive' => []
+    ];
+
+    if ($entity instanceof FieldableEntityInterface) {
+      foreach ($entity->getFieldDefinitions() as $field_id => $field_definition) {
+        if ($field_definition instanceof FieldConfigInterface && in_array($field_definition->getType(), ['entity_reference', 'entity_reference_revisions'], TRUE)) {
+          $field = $entity->get($field_id);
+          /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
+          if ($field->count() > 0) {
+            $form['recursive'] = array_merge($form['recursive'], $this->getRecursiveFormElement($field_definition, $field_id, $field));
+          }
+        }
+      }
+
+      if ($parent) {
+        $form = array_merge([
+          'description' => [
+            '#markup' => $this->t("
+            <p>Specify the child entities (the entities referenced by this entity) that should also be cloned as part of
+            the cloning process.  If they're not included, these fields' referenced entities will be the same as in the
+            original.  In other words, fields in both the original entity and the cloned entity will refer to the same
+            referenced entity.  Examples:</p>
+
+            <p>If you have a Paragraph field in your entity, and you choose not to clone it here, deleting the original
+            or cloned entity will also delete the Paragraph field from the other one.  So you probably want to clone
+            Paragraph fields.</p>
+
+            <p>However, if you have a User reference field, you probably don't want to clone it here because a new User
+            will be created for referencing by the clone.</p>
+
+            <p>Some options may be disabled here, preventing you from changing them, as set by your administrator.  Some
+            options may also be missing, hidden by your administrator, forcing you to clone with the default settings.
+            It's possible that there are no options here for you at all, or none need to be set, in which case you may
+            simply hit the <em>Clone</em> button.</p>
+          "),
+            '#access' => $this->descriptionShouldBeShown($form),
+          ],
+        ], $form);
+      }
+    }
+
+    return $form;
+  }
+
+  /**
+   * Get the recursive form element.
+   *
+   * @param \Drupal\Core\Field\FieldConfigInterface $field_definition
+   *   The field definition.
+   * @param string $field_id
+   *   The field ID.
+   * @param \Drupal\Core\Field\FieldItemListInterface $field
+   *   The field item.
+   *
+   * @return array
+   *   The form element for a recursive clone.
+   */
+  protected function getRecursiveFormElement(FieldConfigInterface $field_definition, $field_id, FieldItemListInterface $field) {
+    $form_element = [
+      '#tree' => TRUE,
+    ];
+
+    $fieldset_access = !$this->entityCloneSettingsManager->getHiddenValue($field_definition->getFieldStorageDefinition()->getSetting('target_type'));
+    $form_element[$field_definition->id()] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Entities referenced by field <em>@label (@field_id)</em>.', [
+        '@label' => $field_definition->label(),
+        '@field_id' => $field_id,
+      ]),
+      '#access' => $fieldset_access,
+      '#description_should_be_shown' => $fieldset_access,
+    ];
+
+    foreach ($field as $value) {
+      // Check if we're not dealing with an entity that has been deleted in the meantime
+      if (!$referenced_entity = $value->get('entity')->getTarget()) {
+        continue;
+      }
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $referenced_entity */
+      $referenced_entity = $value->get('entity')->getTarget()->getValue();
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Clone entity <strong>ID:</strong> <em>@entity_id</em>, <strong>Type:</strong> <em>@entity_type - @bundle</em>, <strong>Label:</strong> <em>@entity_label</em>', [
+          '@entity_id' => $referenced_entity->id(),
+          '@entity_type' => $referenced_entity->getEntityTypeId(),
+          '@bundle' => $referenced_entity->bundle(),
+          '@entity_label' => $referenced_entity->label(),
+        ]),
+        '#default_value' => $this->entityCloneSettingsManager->getDefaultValue($referenced_entity->getEntityTypeId()),
+        '#access' => $referenced_entity->access('view label'),
+      ];
+
+      if ($this->entityCloneSettingsManager->getDisableValue($referenced_entity->getEntityTypeId())) {
+        $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#attributes'] = [
+          'disabled' => TRUE,
+        ];
+        $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#value'] = $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['clone']['#default_value'];
+      }
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['target_entity_type_id'] = [
+        '#type' => 'hidden',
+        '#value' => $referenced_entity->getEntityTypeId(),
+      ];
+
+      $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['target_bundle'] = [
+        '#type' => 'hidden',
+        '#value' => $referenced_entity->bundle(),
+      ];
+      if ($referenced_entity instanceOf ContentEntityInterface) {
+        $form_element[$field_definition->id()]['references'][$referenced_entity->id()]['children'] = $this->getChildren($referenced_entity);
+      }
+    }
+
+    return $form_element;
+  }
+
+  /**
+   * Fetches clonable children from a field.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $referenced_entity
+   *   The field item list.
+   *
+   * @return array
+   *   The list of children.
+   */
+  protected function getChildren(ContentEntityInterface $referenced_entity) {
+    // Use memoization to prevent circular references.
+    if (array_key_exists($referenced_entity->id(), $this->discovered_entities)) {
+      return;
+    }
+
+    /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */
+    if ($this->entityTypeManager->hasHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form')) {
+      // Record that we've found this entity.
+      $this->discovered_entities[$referenced_entity->id()] = $referenced_entity;
+
+      $entity_clone_form_handler = $this->entityTypeManager->getHandler($referenced_entity->getEntityTypeId(), 'entity_clone_form');
+      return $entity_clone_form_handler->formElement($referenced_entity, FALSE);
+    }
+
+    return;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValues(FormStateInterface $form_state) {
+    return $form_state->getValues();
+  }
+
+  /**
+   * Checks if description should be shown.
+   *
+   * If there are no recursive elements visible, the description should be
+   * hidden.
+   *
+   * @param array $form
+   *   Form.
+   *
+   * @return bool
+   *   TRUE if description should be shown.
+   */
+  protected function descriptionShouldBeShown(array $form) {
+    $show_description = TRUE;
+    if (!isset($form['recursive'])) {
+      $show_description = FALSE;
+    }
+
+    $visible = FALSE;
+    array_walk_recursive($form['recursive'], function ($item, $key) use (&$visible) {
+      if ($key === '#description_should_be_shown') {
+        $visible = ($visible || $item);
+      }
+    });
+
+    if (!$visible) {
+      $show_description = FALSE;
+    }
+    return $show_description;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php
new file mode 100644
index 0000000000000000000000000000000000000000..fb552c46312f8a8facc47fe6f857bd068d7710fc
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Content/FileEntityClone.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Class ContentEntityCloneBase.
+ */
+class FileEntityClone extends ContentEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    /** @var \Drupal\file\FileInterface $cloned_entity */
+    $cloned_file = file_copy($cloned_entity, $cloned_entity->getFileUri(), FILE_EXISTS_RENAME);
+    return parent::cloneEntity($entity, $cloned_file, $properties);
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php
new file mode 100644
index 0000000000000000000000000000000000000000..15388c666fe74bc02bdea30e7e11c81014abeab8
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Content/TaxonomyTermEntityClone.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Class TaxonomyTermEntityClone.
+ */
+class TaxonomyTermEntityClone extends ContentEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    /** @var \Drupal\core\Entity\ContentEntityInterface $cloned_entity */
+
+    // Enforce a parent if the cloned term doesn't have a parent.
+    // (First level of a taxonomy tree).
+    if (!isset($cloned_entity->parent->target_id)) {
+      $cloned_entity->set('parent', 0);
+    }
+    return parent::cloneEntity($entity, $cloned_entity, $properties);
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php b/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php
new file mode 100644
index 0000000000000000000000000000000000000000..68e64b49b3598f75450d89753e5204bc33b94676
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/Content/UserEntityClone.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone\Content;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Class ContentEntityCloneBase.
+ */
+class UserEntityClone extends ContentEntityCloneBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    /** @var \Drupal\user\UserInterface $cloned_entity */
+    $cloned_entity->set('name', $cloned_entity->getAccountName() . '_cloned');
+    return parent::cloneEntity($entity, $cloned_entity, $properties);
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php b/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..55346c1f89b08f56fd077076502f97a9fa30f379
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/EntityCloneFormInterface.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Defines a common interface for all entity clone form objects.
+ */
+interface EntityCloneFormInterface {
+
+  /**
+   * Get all specific form element.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   *
+   * @param bool $parent
+   *   Is the parent form element.
+   *
+   * @return array
+   *   The form elements.
+   */
+  public function formElement(EntityInterface $entity, $parent = TRUE);
+
+  /**
+   * Get all new values provided by the specific form element.
+   *
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   *
+   * @return array
+   *   An array containing all new values.
+   */
+  public function getValues(FormStateInterface $form_state);
+
+}
diff --git a/web/modules/entity_clone/src/EntityClone/EntityCloneInterface.php b/web/modules/entity_clone/src/EntityClone/EntityCloneInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ed020233a981e9d67e7c768cad60c9562d9aa003
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClone/EntityCloneInterface.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\entity_clone\EntityClone;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Defines a common interface for all entity clone objects.
+ */
+interface EntityCloneInterface {
+
+  /**
+   * Clone an entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param \Drupal\Core\Entity\EntityInterface $cloned_entity
+   *   The cloned entity.
+   * @param array $properties
+   *   All new properties to replace old.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The new saved entity.
+   */
+  public function cloneEntity(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []);
+
+}
diff --git a/web/modules/entity_clone/src/EntityClonePermissions.php b/web/modules/entity_clone/src/EntityClonePermissions.php
new file mode 100644
index 0000000000000000000000000000000000000000..3057d57d9a7f942bff4cfebe88ec20f9a8fd86a1
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityClonePermissions.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\entity_clone;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides dynamic permissions of the entity_clone module.
+ */
+class EntityClonePermissions implements ContainerInjectionInterface {
+
+  /**
+   * The entoty type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The string translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translationManager;
+
+
+  /**
+   * Constructs a new EntityClonePermissions instance.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $string_translation
+   *   The string translation manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_manager, TranslationManager $string_translation) {
+    $this->entityTypeManager = $entity_manager;
+    $this->translationManager = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * Returns an array of entity_clone permissions.
+   *
+   * @return array
+   *   The permission list.
+   */
+  public function permissions() {
+    $permissions = [];
+
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      $permissions['clone ' . $entity_type_id . ' entity'] = $this->translationManager->translate('Clone all <em>@label</em> entities', [
+        '@label' => $entity_type->getLabel(),
+      ]);
+    }
+
+    return $permissions;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/EntityCloneSettingsManager.php b/web/modules/entity_clone/src/EntityCloneSettingsManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..59899a3fb0199e8d9603a8d20bb1c11b3e5a5673
--- /dev/null
+++ b/web/modules/entity_clone/src/EntityCloneSettingsManager.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\entity_clone;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+
+/**
+ * Manage entity clone configuration.
+ */
+class EntityCloneSettingsManager {
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity type bundle info service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * The immutable entity clone settings configuration entity.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig
+   */
+  protected $config;
+
+  /**
+   * The editable entity clone settings configuration entity.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $editableConfig;
+
+  /**
+   * EntityCloneSettingsManager constructor.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager service.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   *   The entity type bundle info service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, ConfigFactoryInterface $config_factory) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    $this->config = $config_factory->get('entity_clone.settings');
+    $this->editableConfig = $config_factory->getEditable('entity_clone.settings');
+  }
+
+  /**
+   * Get all content entity types.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityTypeInterface[]
+   *   An array containing all content entity types.
+   */
+  public function getContentEntityTypes() {
+    $definitions = $this->entityTypeManager->getDefinitions();
+    $ret = [];
+    foreach ($definitions as $machine => $type) {
+      if ($type instanceof ContentEntityTypeInterface) {
+        $ret[$machine] = $type;
+      }
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Set the entity clone settings.
+   *
+   * @param array $settings
+   *   The settings from the form.
+   */
+  public function setFormSettings(array $settings) {
+    if (isset($settings['table'])) {
+      array_walk_recursive($settings['table'], function (&$item) {
+        if ($item == '1') {
+          $item = TRUE;
+        }
+        else {
+          $item = FALSE;
+        }
+      });
+      $this->editableConfig->set('form_settings', $settings['table'])->save();
+    }
+  }
+
+  /**
+   * Get the checkbox default value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The default value.
+   */
+  public function getDefaultValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['default_value'])) {
+      return $form_settings[$entity_type_id]['default_value'];
+    }
+    return FALSE;
+  }
+
+  /**
+   * Get the checkbox disable value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The disable value.
+   */
+  public function getDisableValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['disable'])) {
+      return $form_settings[$entity_type_id]['disable'];
+    }
+    return FALSE;
+  }
+
+  /**
+   * Get the checkbox hidden value for a given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return bool
+   *   The hidden value.
+   */
+  public function getHiddenValue($entity_type_id) {
+    $form_settings = $this->config->get('form_settings');
+    if (isset($form_settings[$entity_type_id]['hidden'])) {
+      return $form_settings[$entity_type_id]['hidden'];
+    }
+    return FALSE;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Event/EntityCloneEvent.php b/web/modules/entity_clone/src/Event/EntityCloneEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..8889928858ad02c73e3192f379109cc1970bd79a
--- /dev/null
+++ b/web/modules/entity_clone/src/Event/EntityCloneEvent.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\entity_clone\Event;
+
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Represents entity selection as event.
+ */
+class EntityCloneEvent extends Event {
+
+  /**
+   * Entity being cloned.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $entity;
+
+  /**
+   * New cloned entity.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $clonedEntity;
+
+  /**
+   * Properties.
+   *
+   * @var array
+   */
+  protected $properties;
+
+  /**
+   * Constructs an EntityCloneEvent object.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The original entity that was cloned.
+   * @param \Drupal\Core\Entity\EntityInterface $cloned_entity
+   *   The clone of the original entity.
+   * @param array $properties
+   *   The entity's properties.
+   */
+  public function __construct(EntityInterface $entity, EntityInterface $cloned_entity, array $properties = []) {
+    $this->entity = $entity;
+    $this->clonedEntity = $cloned_entity;
+    $this->properties = $properties;
+  }
+
+  /**
+   * Gets entity being cloned.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The original entity.
+   */
+  public function getEntity() {
+    return $this->entity;
+  }
+
+  /**
+   * Gets new cloned entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The cloned entity.
+   */
+  public function getClonedEntity() {
+    return $this->clonedEntity;
+  }
+
+  /**
+   * Gets entity properties.
+   *
+   * @return array
+   *   The list of properties.
+   */
+  public function getProperties() {
+    return $this->properties;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Event/EntityCloneEvents.php b/web/modules/entity_clone/src/Event/EntityCloneEvents.php
new file mode 100644
index 0000000000000000000000000000000000000000..7faf8e2293c1a78e2f98c5e131aa8b330b5cb949
--- /dev/null
+++ b/web/modules/entity_clone/src/Event/EntityCloneEvents.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\entity_clone\Event;
+
+/**
+ * Contains all events thrown by entity clone.
+ */
+final class EntityCloneEvents {
+
+  /**
+   * The PRE_CLONE event occurs before the entity was cloned.
+   *
+   * @var string
+   */
+  const PRE_CLONE = 'entity_clone.pre_clone';
+
+  /**
+   * The POST_CLONE event occurs when an entity has been cloned and saved.
+   *
+   * @var string
+   */
+  const POST_CLONE = 'entity_clone.post_clone';
+
+}
diff --git a/web/modules/entity_clone/src/Form/EntityCloneForm.php b/web/modules/entity_clone/src/Form/EntityCloneForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e7e658ca654d0ff242b7dc8c64d9649d53bebe3
--- /dev/null
+++ b/web/modules/entity_clone/src/Form/EntityCloneForm.php
@@ -0,0 +1,202 @@
+<?php
+
+namespace Drupal\entity_clone\Form;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Drupal\entity_clone\Event\EntityCloneEvent;
+use Drupal\entity_clone\Event\EntityCloneEvents;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * Implements an entity Clone form.
+ */
+class EntityCloneForm extends FormBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity ready to clone.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $entity;
+
+  /**
+   * The entity type définition.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityTypeDefinition;
+
+  /**
+   * The string translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $stringTranslationManager;
+
+  /**
+   * Event dispatcher service.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * Constructs a new Entity Clone form.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match service.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $string_translation
+   *   The string translation manager.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
+   *   The event dispatcher service.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match, TranslationManager $string_translation, EventDispatcherInterface $eventDispatcher) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->stringTranslationManager = $string_translation;
+    $this->eventDispatcher = $eventDispatcher;
+
+    $parameter_name = $route_match->getRouteObject()->getOption('_entity_clone_entity_type_id');
+    $this->entity = $route_match->getParameter($parameter_name);
+
+    $this->entityTypeDefinition = $entity_type_manager->getDefinition($this->entity->getEntityTypeId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('current_route_match'),
+      $container->get('string_translation'),
+      $container->get('event_dispatcher')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'entity_clone_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    if ($this->entity && $this->entityTypeDefinition->hasHandlerClass('entity_clone')) {
+
+      /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */
+      if ($this->entityTypeManager->hasHandler($this->entityTypeDefinition->id(), 'entity_clone_form')) {
+        $entity_clone_form_handler = $this->entityTypeManager->getHandler($this->entityTypeDefinition->id(), 'entity_clone_form');
+        $form = array_merge($form, $entity_clone_form_handler->formElement($this->entity));
+      }
+
+      $form['clone'] = [
+        '#type' => 'submit',
+        '#value' => 'Clone',
+      ];
+
+      $form['abort'] = [
+        '#type' => 'submit',
+        '#value' => 'Abort',
+        '#submit' => ['::cancelForm'],
+      ];
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    /** @var \Drupal\entity_clone\EntityClone\EntityCloneInterface $entity_clone_handler */
+    $entity_clone_handler = $this->entityTypeManager->getHandler($this->entityTypeDefinition->id(), 'entity_clone');
+    if ($this->entityTypeManager->hasHandler($this->entityTypeDefinition->id(), 'entity_clone_form')) {
+      $entity_clone_form_handler = $this->entityTypeManager->getHandler($this->entityTypeDefinition->id(), 'entity_clone_form');
+    }
+
+    $properties = [];
+    if (isset($entity_clone_form_handler) && $entity_clone_form_handler) {
+      $properties = $entity_clone_form_handler->getValues($form_state);
+    }
+
+    $duplicate = $this->entity->createDuplicate();
+
+    $this->eventDispatcher->dispatch(EntityCloneEvents::PRE_CLONE, new EntityCloneEvent($this->entity, $duplicate, $properties));
+    $cloned_entity = $entity_clone_handler->cloneEntity($this->entity, $duplicate, $properties);
+    $this->eventDispatcher->dispatch(EntityCloneEvents::POST_CLONE, new EntityCloneEvent($this->entity, $duplicate, $properties));
+
+    drupal_set_message($this->stringTranslationManager->translate('The entity <em>@entity (@entity_id)</em> of type <em>@type</em> was cloned', [
+      '@entity' => $this->entity->label(),
+      '@entity_id' => $this->entity->id(),
+      '@type' => $this->entity->getEntityTypeId(),
+    ]));
+
+    $this->formSetRedirect($form_state, $cloned_entity);
+  }
+
+  /**
+   * Cancel form handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  public function cancelForm(array &$form, FormStateInterface $form_state) {
+    $this->formSetRedirect($form_state, $this->entity);
+  }
+
+  /**
+   * Set a redirect on form state.
+   *
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The cloned entity.
+   */
+  protected function formSetRedirect(FormStateInterface $form_state, EntityInterface $entity) {
+    if ($entity && $entity->hasLinkTemplate('canonical')) {
+      $form_state->setRedirect($entity->toUrl()->getRouteName(), $entity->toUrl()->getRouteParameters());
+    }
+    else {
+      $form_state->setRedirect('<front>');
+    }
+  }
+
+  /**
+   * Gets the entity of this form.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The entity.
+   */
+  public function getEntity() {
+    return $this->entity;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Form/EntityCloneSettingsForm.php b/web/modules/entity_clone/src/Form/EntityCloneSettingsForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..9dde82bf5b60c2eec92c0e1cdf6cbc6f3396ef7a
--- /dev/null
+++ b/web/modules/entity_clone/src/Form/EntityCloneSettingsForm.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Drupal\entity_clone\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\entity_clone\EntityCloneSettingsManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provide the settings form for entity clone.
+ */
+class EntityCloneSettingsForm extends ConfigFormBase implements ContainerInjectionInterface {
+
+  /**
+   * The entity clone settings manager.
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager
+   */
+  protected $entityCloneSettingsManager;
+
+  /**
+   * {@inheritdoc}
+   *
+   * @var \Drupal\entity_clone\EntityCloneSettingsManager $entity_clone_settings_manager
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, EntityCloneSettingsManager $entity_clone_settings_manager) {
+    parent::__construct($config_factory);
+    $this->entityCloneSettingsManager = $entity_clone_settings_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('entity_clone.settings.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEditableConfigNames() {
+    return ['entity_clone.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'entity_clone_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['#tree'] = TRUE;
+
+    $form['form_settings'] = [
+      '#tree' => TRUE,
+      '#type' => 'fieldset',
+      '#title' => $this->t('Clone form settings'),
+      '#description' => $this->t("
+        For each type of child entity (the entity that's referenced by the entity being
+        cloned), please set your cloning preferences. This will affect the clone form presented to users when they
+        clone entities. Default behaviour for whether or not the child entities should be cloned is specified in
+        the left-most column.  To prevent users from altering behaviour for each type when they're actually cloning
+        (but still allowing them to see what will happen), use the middle column. The right-most column can be used
+        to hide the form options from users completely. This will run the clone operation with the defaults set here
+        (in the left-most column). See the clone form (by cloning an entity) for more information.
+      "),
+      '#open' => TRUE,
+      '#collapsible' => FALSE,
+    ];
+
+    $form['form_settings']['table'] = [
+      '#type' => 'table',
+      '#header' => [
+        'label' => $this->t('Label'),
+        'default_value' => $this->t('Checkboxes default value'),
+        'disable'  => $this->t('Disable checkboxes'),
+        'hidden' => $this->t('Hide checkboxes'),
+      ],
+    ];
+
+    foreach ($this->entityCloneSettingsManager->getContentEntityTypes() as $type_id => $type) {
+      $form['form_settings']['table'][$type_id] = [
+        'label' => [
+          '#type' => 'label',
+          '#title' => $this->t('@type', [
+            '@type' => $type->getLabel(),
+          ]),
+        ],
+        'default_value' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getDefaultValue($type_id),
+        ],
+        'disable' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getDisableValue($type_id),
+        ],
+        'hidden' => [
+          '#type' => 'checkbox',
+          '#default_value' => $this->entityCloneSettingsManager->getHiddenValue($type_id),
+        ],
+      ];
+    }
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entityCloneSettingsManager->setFormSettings($form_state->getValue('form_settings'));
+    parent::submitForm($form, $form_state);
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php b/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php
new file mode 100644
index 0000000000000000000000000000000000000000..44e1689a263ef7f31307695207856b3398fc96d6
--- /dev/null
+++ b/web/modules/entity_clone/src/Plugin/Derivative/DynamicLocalTasks.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Drupal\entity_clone\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines dynamic local tasks.
+ */
+class DynamicLocalTasks extends DeriverBase implements ContainerDeriverInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translationManager;
+
+  /**
+   * Constructs a new DynamicLocalTasks.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationManager $string_translation
+   *   The translation manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationManager $string_translation) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->translationManager = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $this->derivatives = [];
+
+    /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      $has_clone_path = $entity_type->hasLinkTemplate('clone-form');
+      $has_canonical_path = $entity_type->hasLinkTemplate('canonical');
+
+      if ($has_clone_path) {
+        $this->derivatives["$entity_type_id.clone_tab"] = array(
+          'route_name' => "entity.$entity_type_id.clone_form",
+          'title' => $this->translationManager->translate('Clone'),
+          'base_route' => "entity.$entity_type_id." . ($has_canonical_path ? "canonical" : "edit_form"),
+          'weight' => 100,
+        );
+      }
+    }
+
+    return $this->derivatives;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Routing/RouteSubscriber.php b/web/modules/entity_clone/src/Routing/RouteSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3bf092f73d2837e765e04055d26f031368b256f
--- /dev/null
+++ b/web/modules/entity_clone/src/Routing/RouteSubscriber.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\entity_clone\Routing;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Routing\RouteSubscriberBase;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Subscriber for Entity Clone routes.
+ */
+class RouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The entity type manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new RouteSubscriber object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_manager) {
+    $this->entityTypeManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      if ($route = $this->getEntityCloneRoute($entity_type)) {
+        $collection->add("entity.$entity_type_id.clone_form", $route);
+      }
+    }
+  }
+
+  /**
+   * Gets the entity_clone route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function getEntityCloneRoute(EntityTypeInterface $entity_type) {
+    if ($clone_form = $entity_type->getLinkTemplate('clone-form')) {
+      $entity_type_id = $entity_type->id();
+      $route = new Route($clone_form);
+      $route
+        ->addDefaults([
+          '_form' => '\Drupal\entity_clone\Form\EntityCloneForm',
+          '_title' => 'Clone ' . $entity_type->getLabel(),
+        ])
+        ->addRequirements([
+          '_permission' => 'clone ' . $entity_type->id() . ' entity',
+        ])
+        ->setOption('_entity_clone_entity_type_id', $entity_type_id)
+        ->setOption('_admin_route', TRUE)
+        ->setOption('parameters', [
+          $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+        ]);
+
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events = parent::getSubscribedEvents();
+    $events[RoutingEvents::ALTER] = 'onAlterRoutes';
+    return $events;
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php b/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f60728753444b52e2c06f9ab28a8e8a0141e4297
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneActionTest.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneActionTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create an action and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneActionTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'action'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer actions',
+    'clone action entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure actions settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testActionEntityClone() {
+    foreach (\Drupal::service('plugin.manager.action')->getDefinitions() as $id => $definition) {
+      if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface') && $definition['label'] == 'Send email') {
+        $action_key = $id;
+        break;
+      }
+    }
+
+    $edit = [
+      'label' => 'Test send email action for clone',
+      'id' => 'test_send_email_for_clone',
+      'recipient' => 'test@recipient.com',
+      'subject' => 'test subject',
+      'message' => 'test message',
+    ];
+    $this->drupalPostForm("admin/config/system/actions/add/$action_key", $edit, t('Save'));
+
+    $actions = \Drupal::entityTypeManager()
+      ->getStorage('action')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $action = reset($actions);
+
+    $edit = [
+      'label' => 'Test send email action cloned',
+      'id' => 'test_send_email_cloned',
+    ];
+    $this->drupalPostForm('entity_clone/action/' . $action->id(), $edit, t('Clone'));
+
+    $actions = \Drupal::entityTypeManager()
+      ->getStorage('action')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $action = reset($actions);
+    $this->assertTrue($action, 'Test action cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php b/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..07295d543147fba4f51f21dcc94e7daf0fba7013
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneBlockTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneBlockTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\block\Entity\Block;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create an block and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneBlockTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer blocks',
+    'clone block entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure blocks settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testBlockEntityClone() {
+    $config = \Drupal::configFactory();
+    $block = Block::create([
+      'plugin' => 'test_block',
+      'region' => 'sidebar_first',
+      'id' => 'test_block',
+      'theme' => $config->get('system.theme')->get('default'),
+      'label' => $this->randomMachineName(8),
+      'visibility' => [],
+      'weight' => 0,
+    ]);
+    $block->save();
+
+    $edit = [
+      'id' => 'test_block_cloned',
+    ];
+    $this->drupalPostForm('entity_clone/block/' . $block->id(), $edit, t('Clone'));
+
+    $blocks = \Drupal::entityTypeManager()
+      ->getStorage('block')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $block = reset($blocks);
+    $this->assertTrue($block, 'Test block cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php b/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..33ce635a046fce4cce8017ace10876f658cd3c66
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneCommentTest.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneCommentTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\comment\Tests\CommentTestBase;
+use Drupal\comment\Tests\CommentTestTrait;
+
+/**
+ * Create a comment and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneCommentTest extends CommentTestBase {
+
+  use CommentTestTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block', 'comment', 'node', 'history', 'field_ui', 'datetime'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer content types',
+    'administer comments',
+    'administer comment types',
+    'administer comment fields',
+    'administer comment display',
+    'skip comment approval',
+    'post comments',
+    'access comments',
+    'access user profiles',
+    'access content',
+    'clone comment entity'
+  ];
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testCommentEntityClone() {
+    $subject = 'Test comment for clone';
+    $body = $this->randomMachineName();
+    $comment = $this->postComment($this->node, $body, $subject, TRUE);
+
+    $this->drupalPostForm('entity_clone/comment/' . $comment->id(), [], t('Clone'));
+
+    $comments = \Drupal::entityTypeManager()
+      ->getStorage('comment')
+      ->loadByProperties([
+        'subject' => $subject . ' - Cloned',
+        'comment_body' => $body,
+      ]);
+    $comments = reset($comments);
+    $this->assertTrue($comments, 'Test comment cloned found in database.');
+  }
+
+}
\ No newline at end of file
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..589bfcfb8a472a72bcf9cab30a89e94b47d3943e
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneContentRecursiveTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\node\Entity\Node;
+use Drupal\node\Tests\NodeTestBase;
+use Drupal\taxonomy\Entity\Term;
+
+/**
+ * Create a content and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneContentRecursiveTest extends NodeTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block', 'node', 'datetime'];
+
+  /**
+   * Profile to install.
+   *
+   * @var string
+   */
+  protected $profile = 'standard';
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'bypass node access',
+    'administer nodes',
+    'clone node entity',
+  ];
+
+  /**
+   * A user with permission to bypass content access checks.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Test clone a content entity with another entities attached.
+   */
+  public function testContentEntityClone() {
+
+    $term_title = $this->randomMachineName(8);
+    $term = Term::create([
+      'vid' => 'tags',
+      'name' => $term_title,
+    ]);
+    $term->save();
+
+    $node_title = $this->randomMachineName(8);
+    $node = Node::create([
+      'type' => 'article',
+      'title' => $node_title,
+      'field_tags' => [
+        'target_id' => $term->id(),
+      ],
+    ]);
+    $node->save();
+
+    $settings = [
+      'taxonomy_term' => [
+        'default_value' => 1,
+        'disable' => 0,
+        'hidden' => 0,
+      ]
+    ];
+    \Drupal::service('config.factory')->getEditable('entity_clone.settings')->set('form_settings', $settings)->save();
+
+    $this->drupalPostForm('entity_clone/node/' . $node->id(), [
+      'recursive[node.article.field_tags][references][' . $term->id() . '][clone]' => 1,
+    ], t('Clone'));
+
+    $nodes = \Drupal::entityTypeManager()
+      ->getStorage('node')
+      ->loadByProperties([
+        'title' => $node_title . ' - Cloned',
+      ]);
+    /** @var \Drupal\node\Entity\Node $node */
+    $node = reset($nodes);
+    $this->assertTrue($node, 'Test node cloned found in database.');
+
+    $terms = \Drupal::entityTypeManager()
+      ->getStorage('taxonomy_term')
+      ->loadByProperties([
+        'name' => $term_title . ' - Cloned',
+      ]);
+    /** @var \Drupal\taxonomy\Entity\Term $term */
+    $term = reset($terms);
+    $this->assertTrue($term, 'Test term referenced by node cloned too found in database.');
+
+    $node->delete();
+    $term->delete();
+
+    $nodes = \Drupal::entityTypeManager()
+      ->getStorage('node')
+      ->loadByProperties([
+        'title' => $node_title,
+      ]);
+    $node = reset($nodes);
+    $this->assertTrue($node, 'Test original node found in database.');
+
+    $terms = \Drupal::entityTypeManager()
+      ->getStorage('taxonomy_term')
+      ->loadByProperties([
+        'name' => $term_title,
+      ]);
+    $term = reset($terms);
+    $this->assertTrue($term, 'Test original term found in database.');
+  }
+
+}
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php b/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0e751fe2b52fd96f0936df51f810ecc5f7acbe55
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneContentTest.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneContentTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\node\Entity\Node;
+use Drupal\node\Tests\NodeTestBase;
+
+/**
+ * Create a content and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneContentTest extends NodeTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block', 'node', 'datetime'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'bypass node access',
+    'administer nodes',
+    'clone node entity'
+  ];
+
+  /**
+   * A user with permission to bypass content access checks.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testContentEntityClone() {
+    $node_title = $this->randomMachineName(8);
+    $node = Node::create([
+      'type' => 'page',
+      'title' => $node_title,
+    ]);
+    $node->save();
+
+    $this->drupalPostForm('entity_clone/node/' . $node->id(), [], t('Clone'));
+
+    $nodes = \Drupal::entityTypeManager()
+      ->getStorage('node')
+      ->loadByProperties([
+        'title' => $node_title . ' - Cloned',
+      ]);
+    $node = reset($nodes);
+    $this->assertTrue($node, 'Test node cloned found in database.');
+  }
+
+}
\ No newline at end of file
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php b/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6da92651e28bd0327fa3145940b6c15711d77c01
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneCustomBlockTest.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneCustomBlockTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\block_content\Tests\BlockContentTestBase;
+
+/**
+ * Creat ea block and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneCustomBlockTest extends BlockContentTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * Enable dummy module that implements hook_block_insert() for exceptions and
+   * field_ui to edit display settings.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'block', 'block_content'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = ['administer blocks', 'clone block_content entity'];
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testCustomBlockEntityClone() {
+
+    $edit = array();
+    $edit['info[0][value]'] = 'Test block ready to clone';
+    $edit['body[0][value]'] = $this->randomMachineName(16);
+    $this->drupalPostForm('block/add/basic', $edit, t('Save'));
+
+    $blocks = \Drupal::entityTypeManager()
+      ->getStorage('block_content')
+      ->loadByProperties([
+        'info' => $edit['info[0][value]'],
+      ]);
+    $block = reset($blocks);
+    $this->assertTrue($block, 'Test Block for clone found in database.');
+
+    $this->drupalPostForm('entity_clone/block_content/' . $block->id(), [], t('Clone'));
+
+    $blocks = \Drupal::entityTypeManager()
+      ->getStorage('block_content')
+      ->loadByProperties([
+        'info' => $edit['info[0][value]'] . ' - Cloned',
+        'body' => $edit['body[0][value]'],
+      ]);
+    $block = reset($blocks);
+    $this->assertTrue($block, 'Test Block cloned found in database.');
+  }
+
+}
\ No newline at end of file
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php b/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..40a7442128d9be337c44ea97bf7a76c0daca278a
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneDateFormatTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneDateFormatTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a date format and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneDateFormatTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone date_format entity',
+    'administer site configuration'
+  ];
+
+  /**
+   * An administrative user with permission to configure date formats settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testDateFormatEntityClone() {
+    $edit = [
+      'label' => 'Test date format for clone',
+      'id' => 'test_date_format_for_clone',
+      'date_format_pattern' => 'Y m d',
+    ];
+    $this->drupalPostForm("admin/config/regional/date-time/formats/add", $edit, t('Add format'));
+
+    $date_formats = \Drupal::entityTypeManager()
+      ->getStorage('date_format')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $date_format = reset($date_formats);
+
+    $edit = [
+      'id' => 'test_date_format_cloned',
+      'label' => 'Test date format cloned',
+    ];
+    $this->drupalPostForm('entity_clone/date_format/' . $date_format->id(), $edit, t('Clone'));
+
+    $date_formats = \Drupal::entityTypeManager()
+      ->getStorage('date_format')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $date_format = reset($date_formats);
+    $this->assertTrue($date_format, 'Test date format cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php b/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..68235fe2acfebb4eef0d251b54c0feb42bc802e9
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneEntityFormModeTest.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneEntityFormModeTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Test an entity form mode clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneEntityFormModeTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone entity_form_mode entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure entity form modes settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testEntityFormModeEntityClone() {
+    $entity_form_modes = \Drupal::entityTypeManager()
+      ->getStorage('entity_form_mode')
+      ->loadByProperties([
+        'id' => 'user.register',
+      ]);
+    $entity_form_mode = reset($entity_form_modes);
+
+    $edit = [
+      'label' => 'User register cloned form mode',
+      'id' => 'register_clone',
+    ];
+    $this->drupalPostForm('entity_clone/entity_form_mode/' . $entity_form_mode->id(), $edit, t('Clone'));
+
+    $entity_form_modes = \Drupal::entityTypeManager()
+      ->getStorage('entity_form_mode')
+      ->loadByProperties([
+        'id' => 'user.' . $edit['id'],
+      ]);
+    $entity_form_mode = reset($entity_form_modes);
+    $this->assertTrue($entity_form_mode, 'Test entity form mode cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php b/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..78768071a410c20e6d9dc43df4eb886defa139d9
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneEntityViewModeTest.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneEntityViewModeTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Test an entity view mode clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneEntityViewModeTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone entity_view_mode entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure entity view modes settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testEntityViewModeEntityClone() {
+    $entity_view_modes = \Drupal::entityTypeManager()
+      ->getStorage('entity_view_mode')
+      ->loadByProperties([
+        'id' => 'user.full',
+      ]);
+    $entity_view_mode = reset($entity_view_modes);
+
+    $edit = [
+      'label' => 'User full cloned view mode',
+      'id' => 'register_clone',
+    ];
+    $this->drupalPostForm('entity_clone/entity_view_mode/' . $entity_view_mode->id(), $edit, t('Clone'));
+
+    $entity_view_modes = \Drupal::entityTypeManager()
+      ->getStorage('entity_view_mode')
+      ->loadByProperties([
+        'id' => 'user.' . $edit['id'],
+      ]);
+    $entity_view_mode = reset($entity_view_modes);
+    $this->assertTrue($entity_view_mode, 'Test entity view mode cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php b/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d477aa0f9fa7b16ee4f259a4ec158bc7683f5814
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneFileTest.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneFileTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\file\Entity\File;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a filer and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneFileTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'file'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone file entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure files settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testFileEntityClone() {
+    /** @var \Drupal\file\FileInterface $file */
+    $file = File::create(array(
+      'uid' => 1,
+      'filename' => 'druplicon.txt',
+      'uri' => 'public://druplicon.txt',
+      'filemime' => 'text/plain',
+      'status' => FILE_STATUS_PERMANENT,
+    ));
+    file_put_contents($file->getFileUri(), 'hello world');
+    $file->save();
+
+    $this->drupalPostForm('entity_clone/file/' . $file->id(), [], t('Clone'));
+
+    $files = \Drupal::entityTypeManager()
+      ->getStorage('file')
+      ->loadByProperties([
+        'filename' => 'druplicon.txt - Cloned',
+      ]);
+    $file = reset($files);
+    $this->assertTrue($file, 'Test file cloned found in database.');
+
+    $this->assertEqual($file->getFileUri(), 'public://druplicon_0.txt', 'The stored file is also cloned.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php b/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c458e2334a2160f84957ed096e8acfca716c9a48
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneFilterFormatTest.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneFilterFormatTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a filter format and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneFilterFormatTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'filter'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone filter_format entity',
+    'administer filters'
+  ];
+
+  /**
+   * An administrative user with permission to configure filter formats settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testFilterFormatEntityClone() {
+    $edit = [
+      'name' => 'Test filter format for clone',
+      'format' => 'test_filter_format_for_clone',
+    ];
+    $this->drupalPostForm("admin/config/content/formats/add", $edit, t('Save configuration'));
+
+    $filter_formats = \Drupal::entityTypeManager()
+      ->getStorage('filter_format')
+      ->loadByProperties([
+        'format' => $edit['format'],
+      ]);
+    $filter_format = reset($filter_formats);
+
+    $edit = [
+      'id' => 'test_filter_format_cloned',
+      'label' => 'Test filter format cloned',
+    ];
+    $this->drupalPostForm('entity_clone/filter_format/' . $filter_format->id(), $edit, t('Clone'));
+
+    $filter_formats = \Drupal::entityTypeManager()
+      ->getStorage('filter_format')
+      ->loadByProperties([
+        'format' => $edit['id'],
+      ]);
+    $filter_format = reset($filter_formats);
+    $this->assertTrue($filter_format, 'Test filter format cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..648e22778470a62641f87c85cbff9a2c73a994db
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneImageStyleTest.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneImageStyleTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create an image style and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneImageStyleTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'image'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone image_style entity',
+    'administer image styles'
+  ];
+
+  /**
+   * An administrative user with permission to configure image styles settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testImageStyleEntityClone() {
+    $edit = [
+      'label' => 'Test image style for clone',
+      'name' => 'test_image_style_for_clone',
+    ];
+    $this->drupalPostForm("admin/config/media/image-styles/add", $edit, t('Create new style'));
+
+    $image_styles = \Drupal::entityTypeManager()
+      ->getStorage('image_style')
+      ->loadByProperties([
+        'name' => $edit['name'],
+      ]);
+    $image_style = reset($image_styles);
+
+    $edit = [
+      'id' => 'test_iamge_style_cloned',
+      'label' => 'Test image_style cloned',
+    ];
+    $this->drupalPostForm('entity_clone/image_style/' . $image_style->id(), $edit, t('Clone'));
+
+    $image_styles = \Drupal::entityTypeManager()
+      ->getStorage('image_style')
+      ->loadByProperties([
+        'name' => $edit['id'],
+      ]);
+    $image_style = reset($image_styles);
+    $this->assertTrue($image_style, 'Test image style cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php b/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..bffba1912764765428f4702b4b6df22ff2cd6935
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneLanguageTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneLanguageTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create an language and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneLanguageTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'language'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer languages',
+    'clone configurable_language entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure languages settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testLanguageEntityClone() {
+    $edit = [
+      'predefined_langcode' => 'fr'
+    ];
+    $this->drupalPostForm("/admin/config/regional/language/add", $edit, t('Add language'));
+
+    $languages = \Drupal::entityTypeManager()
+      ->getStorage('configurable_language')
+      ->loadByProperties([
+        'id' => 'fr',
+      ]);
+    $language = reset($languages);
+
+    $edit = [
+      'id' => 'test_language_cloned',
+      'label' => 'French language cloned',
+    ];
+    $this->drupalPostForm('entity_clone/configurable_language/' . $language->id(), $edit, t('Clone'));
+
+    $languages = \Drupal::entityTypeManager()
+      ->getStorage('configurable_language')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $language = reset($languages);
+    $this->assertTrue($language, 'Test language cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php b/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..81c393df06b58bbf0255ab99e9281674b0f8607c
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneMenuTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneMenuTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a menu and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneMenuTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'menu_ui'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone menu entity',
+    'administer menu'
+  ];
+
+  /**
+   * An administrative user with permission to configure menus settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testMenuEntityClone() {
+
+    $menus = \Drupal::entityTypeManager()
+      ->getStorage('menu')
+      ->loadByProperties([
+        'id' => 'account',
+      ]);
+    $menu = reset($menus);
+
+    $edit = [
+      'label' => 'Test menu cloned',
+      'id' => 'test_menu_cloned',
+    ];
+    $this->drupalPostForm('entity_clone/menu/' . $menu->id(), $edit, t('Clone'));
+
+    $menus = \Drupal::entityTypeManager()
+      ->getStorage('menu')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $menu = reset($menus);
+    $this->assertTrue($menu, 'Test menu cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d6385fa89d8347375de3a473ce7729ea574401db
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneResponsiveImageStyleTest.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneResponsiveImageStyleTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a responsive image style and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneResponsiveImageStyleTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'responsive_image'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone responsive_image_style entity',
+    'administer responsive images'
+  ];
+
+  /**
+   * An administrative user with permission to configure image styles settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testResponsiveImageStyleEntityClone() {
+    $edit = [
+      'label' => 'Test responsive image style for clone',
+      'id' => 'test_responsive_image_style_for_clone',
+      'breakpoint_group' => 'responsive_image',
+      'fallback_image_style' => 'large',
+    ];
+    $this->drupalPostForm("admin/config/media/responsive-image-style/add", $edit, t('Save'));
+
+    $responsive_image_styles = \Drupal::entityTypeManager()
+      ->getStorage('responsive_image_style')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $responsive_image_style = reset($responsive_image_styles);
+
+    $edit = [
+      'id' => 'test_responsive_image_style_cloned',
+      'label' => 'Test responsive image style cloned',
+    ];
+    $this->drupalPostForm('entity_clone/responsive_image_style/' . $responsive_image_style->id(), $edit, t('Clone'));
+
+    $responsive_image_styles = \Drupal::entityTypeManager()
+      ->getStorage('responsive_image_style')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $responsive_image_style = reset($responsive_image_styles);
+    $this->assertTrue($responsive_image_style, 'Test responsive image style cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php b/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b551305754e669e18424d2f2ab2dacc0e5f92f0c
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneRoleTest.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneRoleTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a role and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneRoleTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'user'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer permissions',
+    'clone user_role entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure roles settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testRoleEntityClone() {
+    $edit = [
+      'label' => 'Test role for clone',
+      'id' => 'test_role_for_clone',
+    ];
+    $this->drupalPostForm("/admin/people/roles/add", $edit, t('Save'));
+
+    $roles = \Drupal::entityTypeManager()
+      ->getStorage('user_role')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $role = reset($roles);
+
+    $edit = [
+      'id' => 'test_role_cloned',
+      'label' => 'Test role cloned',
+    ];
+    $this->drupalPostForm('entity_clone/user_role/' . $role->id(), $edit, t('Clone'));
+
+    $roles = \Drupal::entityTypeManager()
+      ->getStorage('user_role')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $role = reset($roles);
+    $this->assertTrue($role, 'Test role cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php b/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..24cee534f17e5e7a2a26fb150e3b8ce5e41ae610
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneSearchPageTest.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneSearchPageTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a search page and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneSearchPageTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'search', 'node'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'administer search',
+    'clone search_page entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure search pages settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testSearchPageEntityClone() {
+    $edit = [
+      'label' => 'Test search page for clone',
+      'id' => 'test_search_page_for_clone',
+      'path' => 'test_search_page_for_clone_url',
+    ];
+    $this->drupalPostForm("/admin/config/search/pages/add/node_search", $edit, t('Save'));
+
+    $search_pages = \Drupal::entityTypeManager()
+      ->getStorage('search_page')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $search_page = reset($search_pages);
+
+    $edit = [
+      'id' => 'test_search_page_cloned',
+      'label' => 'Test search page cloned',
+    ];
+    $this->drupalPostForm('entity_clone/search_page/' . $search_page->id(), $edit, t('Clone'));
+
+    $search_pages = \Drupal::entityTypeManager()
+      ->getStorage('search_page')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $search_page = reset($search_pages);
+    $this->assertTrue($search_page, 'Test search page cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php b/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..46c8c0e4a2ed23a55dcb5073b217a1b13cc79ccd
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneShortcutSetTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneShortcutSetTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a shortcut set and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneShortcutSetTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'shortcut'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone shortcut_set entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure shortcuts settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testShortcutSetEntityClone() {
+    $edit = [
+      'id' => 'test_shortcut_set_cloned',
+      'label' => 'Test shortcut set cloned',
+    ];
+    $this->drupalPostForm('entity_clone/shortcut_set/default', $edit, t('Clone'));
+
+    $shortcut_sets = \Drupal::entityTypeManager()
+      ->getStorage('shortcut_set')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $shortcut_set = reset($shortcut_sets);
+    $this->assertTrue($shortcut_set, 'Test default shortcut set cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php b/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1de2d157d32571d5aef9987ebddea8a5361eeef5
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneUserTest.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneUserTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a user and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneUserTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'user'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone user entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure users settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions, 'test_user');
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testUserEntityClone() {
+    $this->drupalPostForm('entity_clone/user/' . $this->adminUser->id(), [], t('Clone'));
+
+    $users = \Drupal::entityTypeManager()
+      ->getStorage('user')
+      ->loadByProperties([
+        'name' => 'test_user_cloned',
+      ]);
+    $user = reset($users);
+    $this->assertTrue($user, 'Test user cloned found in database.');
+  }
+
+}
+
diff --git a/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php b/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..900ad05c8882dbc44d338479315b2c830d6ee6a3
--- /dev/null
+++ b/web/modules/entity_clone/src/Tests/EntityCloneViewTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_clone\Tests\EntityCloneViewTest.
+ */
+
+namespace Drupal\entity_clone\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Create a view and test a clone.
+ *
+ * @group entity_clone
+ */
+class EntityCloneViewTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['entity_clone', 'views'];
+
+  /**
+   * Permissions to grant admin user.
+   *
+   * @var array
+   */
+  protected $permissions = [
+    'clone view entity'
+  ];
+
+  /**
+   * An administrative user with permission to configure views settings.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Sets the test up.
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->adminUser = $this->drupalCreateUser($this->permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  public function testViewEntityClone() {
+    $edit = [
+      'id' => 'test_view_cloned',
+      'label' => 'Test view cloned',
+    ];
+    $this->drupalPostForm('entity_clone/view/who_s_new', $edit, t('Clone'));
+
+    $views = \Drupal::entityTypeManager()
+      ->getStorage('view')
+      ->loadByProperties([
+        'id' => $edit['id'],
+      ]);
+    $view = reset($views);
+    $this->assertTrue($view, 'Test default view cloned found in database.');
+  }
+
+}
+