Bachir Soussi Chiadmi 7 лет назад
Родитель
Сommit
5a43b028b7
68 измененных файлов с 6987 добавлено и 0 удалено
  1. 17 0
      sites/all/modules/contrib/fields/field_group/CHANGELOG.txt
  2. 339 0
      sites/all/modules/contrib/fields/field_group/LICENSE.txt
  3. 50 0
      sites/all/modules/contrib/fields/field_group/README.txt
  4. 8 0
      sites/all/modules/contrib/fields/field_group/composer.json
  5. 61 0
      sites/all/modules/contrib/fields/field_group/config/schema/field_group.entity_display.schema.yml
  6. 105 0
      sites/all/modules/contrib/fields/field_group/config/schema/field_group.field_group_formatter_plugin.schema.yml
  7. 14 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/field_group_migrate.info.yml
  8. 41 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d6_field_group_entity_form_display.yml
  9. 39 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d6_field_group_entity_view_display.yml
  10. 28 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d7_field_group.yml
  11. 39 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/FieldGroupEntityFormDisplay.php
  12. 43 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/FieldGroupEntityViewDisplay.php
  13. 97 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php
  14. 188 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/source/d6/FieldGroup.php
  15. 104 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/source/d7/FieldGroup.php
  16. 147 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/fixtures/drupal7.php
  17. 120 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/src/Kernel/Migrate/d7/MigrateFieldGroupTest.php
  18. 102 0
      sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/src/Unit/Migrate/d7/FieldGroupTest.php
  19. 14 0
      sites/all/modules/contrib/fields/field_group/css/field_group.field_ui.css
  20. 13 0
      sites/all/modules/contrib/fields/field_group/field_group.info.yml
  21. 63 0
      sites/all/modules/contrib/fields/field_group/field_group.libraries.yml
  22. 3 0
      sites/all/modules/contrib/fields/field_group/field_group.links.action.yml
  23. 810 0
      sites/all/modules/contrib/fields/field_group/field_group.module
  24. 13 0
      sites/all/modules/contrib/fields/field_group/field_group.services.yml
  25. 68 0
      sites/all/modules/contrib/fields/field_group/formatters/accordion/accordion.js
  26. 23 0
      sites/all/modules/contrib/fields/field_group/formatters/details/details.js
  27. 24 0
      sites/all/modules/contrib/fields/field_group/formatters/fieldset/fieldset.js
  28. 69 0
      sites/all/modules/contrib/fields/field_group/formatters/html_element/html-element.js
  29. 129 0
      sites/all/modules/contrib/fields/field_group/formatters/tabs/horizontal-tabs.css
  30. 248 0
      sites/all/modules/contrib/fields/field_group/formatters/tabs/horizontal-tabs.js
  31. 39 0
      sites/all/modules/contrib/fields/field_group/formatters/tabs/tabs.js
  32. 368 0
      sites/all/modules/contrib/fields/field_group/includes/field_ui.inc
  33. 64 0
      sites/all/modules/contrib/fields/field_group/includes/helpers.inc
  34. 123 0
      sites/all/modules/contrib/fields/field_group/js/field_group.field_ui.js
  35. 58 0
      sites/all/modules/contrib/fields/field_group/js/field_group.js
  36. 80 0
      sites/all/modules/contrib/fields/field_group/src/Annotation/FieldGroupFormatter.php
  37. 57 0
      sites/all/modules/contrib/fields/field_group/src/Element/Accordion.php
  38. 24 0
      sites/all/modules/contrib/fields/field_group/src/Element/AccordionItem.php
  39. 88 0
      sites/all/modules/contrib/fields/field_group/src/Element/HorizontalTabs.php
  40. 67 0
      sites/all/modules/contrib/fields/field_group/src/Element/HtmlElement.php
  41. 179 0
      sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterBase.php
  42. 58 0
      sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterInterface.php
  43. 143 0
      sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterPluginManager.php
  44. 97 0
      sites/all/modules/contrib/fields/field_group/src/FieldgroupUi.php
  45. 276 0
      sites/all/modules/contrib/fields/field_group/src/Form/FieldGroupAddForm.php
  46. 82 0
      sites/all/modules/contrib/fields/field_group/src/Form/FieldGroupDeleteForm.php
  47. 106 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/Derivative/FieldGroupLocalAction.php
  48. 89 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Accordion.php
  49. 119 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php
  50. 118 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Details.php
  51. 123 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php
  52. 220 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php
  53. 123 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Tab.php
  54. 139 0
      sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Tabs.php
  55. 32 0
      sites/all/modules/contrib/fields/field_group/src/Routing/FieldGroupConverter.php
  56. 161 0
      sites/all/modules/contrib/fields/field_group/src/Routing/RouteSubscriber.php
  57. 39 0
      sites/all/modules/contrib/fields/field_group/templates/field-group-accordion-item.html.twig
  58. 21 0
      sites/all/modules/contrib/fields/field_group/templates/field-group-accordion.html.twig
  59. 30 0
      sites/all/modules/contrib/fields/field_group/templates/field-group-html-element.html.twig
  60. 15 0
      sites/all/modules/contrib/fields/field_group/templates/horizontal-tabs.html.twig
  61. 94 0
      sites/all/modules/contrib/fields/field_group/templates/theme.inc
  62. 11 0
      sites/all/modules/contrib/fields/field_group/tests/modules/field_group_test/field_group_test.info.yml
  63. 27 0
      sites/all/modules/contrib/fields/field_group/tests/modules/field_group_test/field_group_test.module
  64. 346 0
      sites/all/modules/contrib/fields/field_group/tests/src/Functional/EntityDisplayTest.php
  65. 59 0
      sites/all/modules/contrib/fields/field_group/tests/src/Functional/FieldGroupTestTrait.php
  66. 30 0
      sites/all/modules/contrib/fields/field_group/tests/src/Functional/FieldGroupWithoutFieldUiTest.php
  67. 158 0
      sites/all/modules/contrib/fields/field_group/tests/src/Functional/ManageDisplayTest.php
  68. 105 0
      sites/all/modules/contrib/fields/field_group/tests/src/FunctionalJavascript/FieldGroupUiTest.php

+ 17 - 0
sites/all/modules/contrib/fields/field_group/CHANGELOG.txt

@@ -0,0 +1,17 @@
+8.3.0-beta1, 2017-11-10
+-------------------
+- JS error: Modernizr is not defined.
+- Add the new region property to the schema.
+- Adding Multiple Fields wrapped by a Tabs Group cause maximum execution error.
+- Branch tests are failing.
+- .
+- Creating Duplicate Fieldgroup Name Overwrites Existing Fieldgroup.
+- Field groups are not compatible with field layout. Part 1: Make sure regions are changed when changing layout.
+- Typo in Field Group Formatter Plugin HtmlElement::prerender.
+- Revert "Issue #2846589 by huzooka: Typo in Field Group Formatter Plugin HtmlElement::prerender".
+- Undefined index: form_display.
+- Typo in Field Group Formatter Plugin HtmlElement::prerender.
+- Replace all deprecated uses.
+- MessageWarning: Invalid argument supplied for foreach() in field_group_info_groups() (line 663 of modules/contrib/field_group/field_group.module).
+- Replace removed formBuilder->setError with formstate->setError.
+- Undefined index: id in template_preprocess_fieldset() notice.

+ 339 - 0
sites/all/modules/contrib/fields/field_group/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.

+ 50 - 0
sites/all/modules/contrib/fields/field_group/README.txt

@@ -0,0 +1,50 @@
+History:
+  Field_group was originally written when drupal 7 was released. For drupal 6, the module is
+  located in the CCK module (http://drupal.org/project/cck).
+  As drupal core has a fields API drupal > 6, the field_group module
+  is considered a contribution.
+
+Description:
+  field_group is a module that will group a set of fields. In Drupal8,
+  with fields, one means all fields that come from fieldable entities.
+  You can add fieldgroups in several types with their own format settings.
+  field_group uses plugins to add your own formatter and rendering for
+  it.
+  One of the biggest improvements to previous versions, is that fieldgroups
+  have unlimited nesting, better display control.
+  Note that field_group will only group fields, it can not be used to hide
+  certain fields since this a permission matter.
+
+Module project page:
+  http://drupal.org/project/field_group
+
+Documentation page (D7 version):
+  http://drupal.org/node/1017838
+  http://drupal.org/node/1017962
+
+Available group types:
+  - Html element
+  - Fieldsets
+  - Tabs (horizontal and vertical)
+  - Accordions
+  - Details (Use this if you want collapsible fieldsets)
+
+To submit bug reports and feature suggestions, or to track changes:
+  http://drupal.org/project/issues/field_group
+
+-- Configuration --
+1. You can configure the field groups for different displays like, in managed_form_display and managed_display of the entity type.
+2. You can create different field groups under managed_form_display by adding a new group under "Add new group" label and the format the grouping using the desired formatter for displaying the same.
+3. Same thing can be done in managed_display.
+4. The field grouping done in managed display will be reflected on the view detail page of the entity, while that done in the managed_form_display will be
+reflected in the add/edit form of the entity.
+
+-- MAINTAINERS --
+
+stalski - http://drupal.org/user/322618
+zuuperman - http://drupal.org/user/361625
+swentel - http://drupal.org/user/107403
+
+-- INSPIRATORS --
+
+yched - http://drupal.org/user/39567

+ 8 - 0
sites/all/modules/contrib/fields/field_group/composer.json

@@ -0,0 +1,8 @@
+{
+    "name": "drupal/field_group",
+    "description": "Provides the field_group module.",
+    "type": "drupal-module",
+    "license": "GPL-2.0+",
+    "minimum-stability": "dev",
+    "require": {}
+}

+ 61 - 0
sites/all/modules/contrib/fields/field_group/config/schema/field_group.entity_display.schema.yml

@@ -0,0 +1,61 @@
+core.entity_view_display.*.*.*.third_party.field_group:
+  type: sequence
+  label: 'Field group settings on entity view'
+  sequence:
+    - type: mapping
+      label: A field group
+      mapping:
+        children:
+          type: sequence
+          label: 'The fields belonging to the group'
+          sequence:
+            - type: string
+              label: 'The field name'
+        label:
+          type: string
+          label: Readable name of the group
+        parent_name:
+          type: string
+          label: 'The parent group of this group'
+        region:
+          type: string
+          label: 'The region of this group'
+        weight:
+          type: integer
+          label: 'The weight of the group'
+        format_type:
+          type: string
+          label: 'The formatter of the group'
+        format_settings:
+          type: field_group.field_group_formatter_plugin.[%parent.format_type]
+
+core.entity_form_display.*.*.*.third_party.field_group:
+  type: sequence
+  label: 'Field group settings on entity form'
+  sequence:
+    - type: mapping
+      label: A field group
+      mapping:
+        children:
+          type: sequence
+          label: 'The fields belonging to the group'
+          sequence:
+            - type: string
+              label: 'The field name'
+        label:
+          type: string
+          label: Readable name of the group
+        region:
+          type: string
+          label: 'The region of this group'
+        parent_name:
+          type: string
+          label: 'The parent group of this group'
+        weight:
+          type: integer
+          label: 'The weight of the group'
+        format_type:
+          type: string
+          label: 'The formatter of the group'
+        format_settings:
+          type: field_group.field_group_formatter_plugin.[%parent.format_type]

+ 105 - 0
sites/all/modules/contrib/fields/field_group/config/schema/field_group.field_group_formatter_plugin.schema.yml

@@ -0,0 +1,105 @@
+field_group.field_group_formatter_plugin.accordion:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the accordion formatter settings'
+  mapping:
+    effect:
+      type: string
+      label: 'Effect on the accordion'
+
+field_group.field_group_formatter_plugin.accordion_item:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the accordion item formatter settings'
+  mapping:
+    formatter:
+      type: string
+      label: 'Formatting of the item'
+    description:
+      type: string
+      label: 'description of the item'
+    required_fields:
+      type: boolean
+      label: 'Mark for required fields'
+
+field_group.field_group_formatter_plugin.details:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the details formatter settings'
+  mapping:
+    open:
+      type: boolean
+      label: 'Display element open by default.'
+    required_fields:
+      type: boolean
+      label: 'Mark for required fields'
+
+field_group.field_group_formatter_plugin.fieldset:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the fieldset formatter settings'
+  mapping:
+    description:
+      type: string
+      label: 'description of the item'
+    required_fields:
+      type: boolean
+      label: 'Mark for required fields'
+
+field_group.field_group_formatter_plugin.html_element:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the html element formatter settings'
+  mapping:
+    element:
+      type: string
+      label: 'html element tag to be used'
+    show_label:
+      type: boolean
+      label: 'show the label'
+    label_element:
+      type: string
+      label: 'html element tag to be used for the label'
+    attributes:
+      type: string
+      label: 'html attributes for the element'
+    effect:
+      type: string
+      label: 'effect on the element'
+    speed:
+      type: string
+      label: 'speed of the effect'
+    required_fields:
+      type: boolean
+      label: 'Mark for required fields'
+
+field_group.field_group_formatter_plugin.tab:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the tab formatter settings'
+  mapping:
+    formatter:
+      type: string
+      label: 'default state for the tab'
+    description:
+      type: string
+      label: 'description of the tab'
+    required_fields:
+      type: boolean
+      label: 'Mark for required fields'
+
+field_group.field_group_formatter_plugin.tabs:
+  type: field_group.field_group_formatter_plugin.base
+  label: 'Mapping for the tab formatter settings'
+  mapping:
+    direction:
+      type: string
+      label: 'Direction of the tabs'
+
+field_group.field_group_formatter_plugin.base:
+  type: mapping
+  label: 'Mapping for the base formatter settings'
+  mapping:
+    label:
+      type: string
+      label: 'Label of the fieldgroup'
+    classes:
+      type: string
+      label:  'Classes of the fieldgroup'
+    id:
+      type: string
+      label: 'Html id of the fieldgroup'

+ 14 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/field_group_migrate.info.yml

@@ -0,0 +1,14 @@
+name: 'Field Group Migrate'
+type: module
+description: 'Provides the ability to migrate field groups from D6/D7 to D8.'
+package: Migrate
+# version: VERSION
+# core: 8.x
+dependencies:
+  - field_group:field_group
+
+# Information added by Drupal.org packaging script on 2017-11-10
+version: '8.x-3.0-beta1'
+core: '8.x'
+project: 'field_group'
+datestamp: 1510352294

+ 41 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d6_field_group_entity_form_display.yml

@@ -0,0 +1,41 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - migrate.migration.d6_field_instance
+  module:
+    - field_group_migrate
+    - node
+id: d6_field_group_entity_form_display
+migration_tags:
+  - 'Drupal 6'
+label: 'Field groups'
+source:
+  plugin: d6_field_group
+  constants:
+    mode: entity_form_display
+    entity_type: node
+    form_mode: default
+    third_party_settings: {  }
+process:
+  mode: constants/mode
+  entity_type: constants/entity_type
+  bundle: type_name
+  form_mode: constants/form_mode
+  id:
+    plugin: concat
+    source:
+      - group_name
+    delimiter: .
+  field_group/label: label
+  field_group/weight: weight
+  field_group/children: children
+  field_group/format_type: converted_settings/format_type
+  field_group/format_settings: converted_settings/format_settings
+destination:
+  plugin: field_group_entity_form_display
+template: d6_field_instance_widget_settings
+migration_dependencies:
+  required:
+    - d6_field_instance
+migration_group: null

+ 39 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d6_field_group_entity_view_display.yml

@@ -0,0 +1,39 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - migrate.migration.d6_field_instance
+  module:
+    - field_group_migrate
+    - node
+id: d6_field_group_entity_view_display
+migration_tags:
+  - 'Drupal 6'
+label: 'Field groups'
+source:
+  plugin: d6_field_group
+  constants:
+    mode: entity_view_display
+    entity_type: node
+    third_party_settings: {  }
+process:
+  mode: constants/mode
+  entity_type: constants/entity_type
+  bundle: type_name
+  id:
+    plugin: concat
+    source:
+      - group_name
+    delimiter: .
+  field_group/label: label
+  field_group/weight: weight
+  field_group/children: children
+  field_group/format_type: converted_settings/format_type
+  field_group/format_settings: converted_settings/format_settings
+destination:
+  plugin: field_group_entity_view_display
+template: d6_field_instance_widget_settings
+migration_dependencies:
+  required:
+    - d6_field_instance
+migration_group: null

+ 28 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/migration_templates/d7_field_group.yml

@@ -0,0 +1,28 @@
+id: d7_field_group
+label: Field groups
+migration_tags:
+  - Drupal 7
+source:
+  plugin: d7_field_group
+process:
+  entity_type: entity_type
+  bundle: bundle
+  mode:
+    plugin: static_map
+    source: mode
+    bypass: true
+    map:
+      form: default
+  type:
+    plugin: static_map
+    source: mode
+    default_value: entity_view_display
+    map:
+      form: entity_form_display
+  group_name: group_name
+  settings: settings
+destination:
+  plugin: d7_field_group
+migration_dependencies:
+  required:
+    - d7_field_formatter_settings

+ 39 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/FieldGroupEntityFormDisplay.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\field_group_migrate\Plugin\migrate\destination;
+
+use Drupal\migrate\Plugin\migrate\destination\PerComponentEntityFormDisplay;
+use Drupal\migrate\Row;
+
+/**
+ * This class imports one field_group of an entity form display.
+ *
+ * @MigrateDestination(
+ *   id = "field_group_entity_form_display"
+ * )
+ */
+class FieldGroupEntityFormDisplay extends PerComponentEntityFormDisplay {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function import(Row $row, array $old_destination_id_values = array()) {
+    $values = array();
+    // array_intersect_key() won't work because the order is important because
+    // this is also the return value.
+    foreach (array_keys($this->getIds()) as $id) {
+      $values[$id] = $row->getDestinationProperty($id);
+    }
+    $entity = $this->getEntity($values['entity_type'], $values['bundle'], $values[static::MODE_NAME]);
+    if (!$entity->isNew()) {
+      $settings = $row->getDestinationProperty('field_group');
+      $entity->setThirdPartySetting('field_group', $row->getDestinationProperty('id'), $settings);
+      if (isset($settings['format_type']) && ($settings['format_type'] == 'no_style' || $settings['format_type'] == 'hidden')) {
+        $entity->unsetThirdPartySetting('field_group', $row->getDestinationProperty('id'));
+      }
+      $entity->save();
+    }
+    return array_values($values);
+  }
+
+}

+ 43 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/FieldGroupEntityViewDisplay.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\field_group_migrate\Plugin\migrate\destination;
+
+use Drupal\migrate\Plugin\migrate\destination\PerComponentEntityDisplay;
+use Drupal\migrate\Row;
+
+/**
+ * This class imports one field_group of an entity form display.
+ *
+ * @MigrateDestination(
+ *   id = "field_group_entity_view_display"
+ * )
+ */
+class FieldGroupEntityViewDisplay extends PerComponentEntityDisplay {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function import(Row $row, array $old_destination_id_values = array()) {
+    $values = array();
+    // array_intersect_key() won't work because the order is important because
+    // this is also the return value.
+    foreach (array_keys($this->getIds()) as $id) {
+      $values[$id] = $row->getDestinationProperty($id);
+    }
+
+    foreach ($row->getSourceProperty('view_modes') as $view_mode => $settings) {
+      $entity = $this->getEntity($values['entity_type'], $values['bundle'], $view_mode);
+      if (!$entity->isNew()) {
+        $settings = array_merge($row->getDestinationProperty('field_group'), $settings);
+        $entity->setThirdPartySetting('field_group', $row->getDestinationProperty('id'), $settings);
+        if (isset($settings['format_type']) && ($settings['format_type'] == 'no_style' || $settings['format_type'] == 'hidden')) {
+          $entity->unsetThirdPartySetting('field_group', $row->getDestinationProperty('id'));
+        }
+        $entity->save();
+      }
+    }
+
+    return array_values($values);
+  }
+
+}

+ 97 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php

@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_group_migrate\Plugin\migrate\destination\d7\FieldGroup.
+ */
+
+namespace Drupal\field_group_migrate\Plugin\migrate\destination\d7;
+
+use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+
+/**
+ * This class imports one field_group of an entity form display.
+ *
+ * @MigrateDestination(
+ *   id = "d7_field_group"
+ * )
+ */
+class FieldGroup extends DestinationBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function import(Row $row, array $old_destination_id_values = array()) {
+    $values = array();
+    // array_intersect_key() won't work because the order is important because
+    // this is also the return value.
+    foreach (array_keys($this->getIds()) as $id) {
+      $values[$id] = $row->getDestinationProperty($id);
+    }
+
+    $entity = $this->getEntity($values['entity_type'], $values['bundle'], $values['mode'], $values['type']);
+    if (!$entity->isNew()) {
+      $settings = $row->getDestinationProperty('settings');
+      $entity->setThirdPartySetting('field_group', $row->getDestinationProperty('group_name'), $settings);
+      if (isset($settings['format_type']) && ($settings['format_type'] == 'hidden')) {
+        $entity->unsetThirdPartySetting('field_group', $row->getDestinationProperty('group_name'));
+      }
+      $entity->save();
+    }
+
+    return array_values($values);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    $ids['entity_type']['type'] = 'string';
+    $ids['bundle']['type'] = 'string';
+    $ids['mode']['type'] = 'string';
+    $ids['type']['type'] = 'string';
+    $ids['group_name']['type'] = 'string';
+    return $ids;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rollback(array $destination_identifier) {
+    $entity = $this->getEntity($destination_identifier['entity_type'], $destination_identifier['bundle'], $destination_identifier['mode'], $destination_identifier['type']);
+    if (!$entity->isNew()) {
+      $entity->unsetThirdPartySetting('field_group', $destination_identifier['group_name']);
+      $entity->save();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields(MigrationInterface $migration = NULL) {
+    // This is intentionally left empty.
+  }
+
+  /**
+   * Gets the entity.
+   *
+   * @param string $entity_type
+   *   The entity type to retrieve.
+   * @param string $bundle
+   *   The entity bundle.
+   * @param string $mode
+   *   The display mode.
+   * @param string $type
+   *   The destination type.
+   *
+   * @return \Drupal\Core\Entity\Display\EntityDisplayInterface
+   *   The entity display object.
+   */
+  protected function getEntity($entity_type, $bundle, $mode, $type) {
+    $function = $type == 'entity_form_display' ? 'entity_get_form_display' : 'entity_get_display';
+    return $function($entity_type, $bundle, $mode);
+  }
+
+}

+ 188 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/source/d6/FieldGroup.php

@@ -0,0 +1,188 @@
+<?php
+
+namespace Drupal\field_group_migrate\Plugin\migrate\source\d6;
+
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * Drupal 6 field_group source.
+ *
+ * @MigrateSource(
+ *   id = "d6_field_group"
+ * )
+ */
+class FieldGroup extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    $query = $this->select('content_group', 'g')
+    ->fields('g', [
+      'group_type',
+      'type_name',
+      'group_name',
+      'label',
+      'settings',
+      'weight',
+    ]);
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow(Row $row) {
+    $query = $this->select('content_group_fields', 'f');
+    $query->fields('f', ['field_name'])
+      ->condition('type_name', $row->getSourceProperty('type_name'))
+      ->condition('group_name', $row->getSourceProperty('group_name'));
+    $fields = $query->execute()->fetchCol();
+    $row->setSourceProperty('children', $fields);
+    $row->setSourceProperty('settings', unserialize($row->getSourceProperty('settings')));
+
+    switch ($row->getSourceProperty('constants/mode')) {
+      case 'entity_form_display':
+        $this->transformEntityFormDisplaySettings($row);
+        break;
+
+      case 'entity_view_display':
+        $this->transformEntityViewDisplaySettings($row);
+        break;
+    }
+
+    return parent::prepareRow($row);
+  }
+
+  protected function transformEntityFormDisplaySettings(Row $row) {
+    $row->setSourceProperty('extracted_settings', $row->getSourceProperty('settings/form'));
+    $source_settings = $row->getSourceProperty('extracted_settings');
+    $settings = [
+      'format_type' => 'details',
+      'format_settings' => [],
+    ];
+
+    switch ($source_settings['style']) {
+      case 'no_style':
+        $settings['format_type'] = 'no_style';
+        break;
+
+      case 'simple':
+        $settings['format_type'] = 'html_element';
+        $settings['format_settings']['element'] = 'div';
+        $settings['format_settings']['label_element'] = 'h2';
+        break;
+
+      case 'fieldset':
+        $settings['format_type'] = 'fieldset';
+        break;
+
+      case 'fieldset_collapsible':
+        $settings['format_type'] = 'details';
+        $settings['format_settings']['open'] = TRUE;
+        break;
+
+      case 'fieldset_collapsed':
+        $settings['format_type'] = 'details';
+        $settings['format_settings']['open'] = FALSE;
+        break;
+
+      case 'hidden':
+        $settings['format_type'] = 'hidden';
+        break;
+    }
+
+    $row->setSourceProperty('converted_settings', $settings);
+  }
+
+  protected function transformEntityViewDisplaySettings(Row $row) {
+    $row->setSourceProperty('extracted_settings', $row->getSourceProperty('settings/display'));
+    $view_modes = array_diff(array_keys($row->getSourceProperty('extracted_settings')), ['label', 'description', 'weight']);
+    $view_modes = array_filter($view_modes, function ($value) {
+      return !is_numeric($value);
+    });
+    $row->setSourceProperty('view_mode_keys', $view_modes);
+    $view_modes = [];
+
+    foreach ($row->getSourceProperty('view_mode_keys') as $view_mode) {
+      $source_settings = $row->getSourceProperty('extracted_settings/' . $view_mode);
+      $row->setSourceProperty('view_modes', []);
+      $settings = [
+        'format_type' => 'details',
+        'format_settings' => [],
+      ];
+
+      switch ($source_settings['format']) {
+        case 'no_style':
+          $settings['format_type'] = 'no_style';
+          break;
+
+        case 'simple':
+          $settings['format_type'] = 'html_element';
+          $settings['format_settings']['element'] = 'div';
+          $settings['format_settings']['label_element'] = 'h2';
+          break;
+
+        case 'fieldset':
+          $settings['format_type'] = 'fieldset';
+          break;
+
+        case 'fieldset_collapsible':
+          $settings['format_type'] = 'details';
+          $settings['format_settings']['open'] = TRUE;
+          break;
+
+        case 'fieldset_collapsed':
+          $settings['format_type'] = 'details';
+          $settings['format_settings']['open'] = FALSE;
+          break;
+
+        case 'hidden':
+          $settings['format_type'] = 'hidden';
+          break;
+      }
+
+      /**
+       * @todo: ?
+       */
+      if ($view_mode == 'full') {
+        $view_mode = 'default';
+      }
+
+      // $row->setSourceProperty('view_modes/' . $view_mode, $settings);
+      $view_modes[$view_mode] = $settings;
+    }
+
+    $row->setSourceProperty('view_modes', $view_modes);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    $ids['type_name']['type'] = 'string';
+    $ids['type_name']['alias'] = 'g';
+
+    $ids['group_name']['type'] = 'string';
+    $ids['group_name']['alias'] = 'g';
+
+    return $ids;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    $fields = [
+      'group_type',
+      'type_name',
+      'group_name',
+      'label',
+      'settings',
+      'weight'
+    ];
+    return array_combine($fields, $fields);
+  }
+
+}

+ 104 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/src/Plugin/migrate/source/d7/FieldGroup.php

@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_group_migrate\Plugin\migrate\source\d7\FieldGroup.
+ */
+
+namespace Drupal\field_group_migrate\Plugin\migrate\source\d7;
+
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * Drupal 7 field_group source.
+ *
+ * @MigrateSource(
+ *   id = "d7_field_group"
+ * )
+ */
+class FieldGroup extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    return $this->select('field_group', 'f')->fields('f');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow(Row $row) {
+    $data = unserialize($row->getSourceProperty('data'));
+    $format_settings = $data['format_settings'] + $data['format_settings']['instance_settings'];
+    unset($format_settings['instance_settings']);
+    $settings = array(
+      'children' => $data['children'],
+      'parent_name' => $row->getSourceProperty('parent_name'),
+      'weight' => $data['weight'],
+      'label' => $data['label'],
+      'format_settings' => $format_settings,
+      'format_type' => $data['format_type'],
+    );
+    switch ($data['format_type']) {
+      case 'div':
+        $settings['format_type'] = 'html_element';
+        $settings['format_settings']['element'] = 'div';
+        break;
+
+      case 'tabs':
+        $settings['format_type'] = 'tabs';
+        $settings['format_settings']['direction'] = 'vertical';
+        break;
+
+      case 'htabs':
+        $settings['format_type'] = 'tabs';
+        $settings['format_settings']['direction'] = 'horizontal';
+        break;
+
+      case 'htab':
+        $settings['format_type'] = 'tab';
+        break;
+
+      case 'multipage-group':
+        // @todo Check if there is a better way to deal with this format type.
+        $settings['format_type'] = 'tabs';
+        break;
+
+      case 'multipage':
+      // @todo Check if there is a better way to deal with this format type.
+        $settings['format_type'] = 'tab';
+        break;
+
+    }
+    $row->setSourceProperty('settings', $settings);
+    return parent::prepareRow($row);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    $ids['id']['type'] = 'integer';
+    return $ids;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    $fields = array(
+      'id' => $this->t('ID'),
+      'identifier' => $this->t('Identifier'),
+      'group_name' => $this->t('Group name'),
+      'entity_type' => $this->t('Entity type'),
+      'bundle' => $this->t('Bundle'),
+      'mode' => $this->t('View mode'),
+      'parent_name' => $this->t('Parent name'),
+      'data' => $this->t('Data'),
+    );
+    return $fields;
+  }
+
+}

+ 147 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/fixtures/drupal7.php

@@ -0,0 +1,147 @@
+<?php
+/**
+ * @file
+ * A database agnostic dump for testing purposes.
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+$connection->schema()->createTable('field_group', array(
+  'fields' => array(
+    'id' => array(
+      'type' => 'serial',
+      'not null' => TRUE,
+      'size' => 'normal',
+    ),
+    'identifier' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '255',
+      'default' => '',
+    ),
+    'group_name' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '32',
+      'default' => '',
+    ),
+    'entity_type' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '32',
+      'default' => '',
+    ),
+    'bundle' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '128',
+      'default' => '',
+    ),
+    'mode' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '128',
+      'default' => '',
+    ),
+    'parent_name' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '32',
+      'default' => '',
+    ),
+    'data' => array(
+      'type' => 'blob',
+      'not null' => TRUE,
+      'size' => 'big',
+    ),
+  ),
+  'primary key' => array(
+    'id',
+  ),
+  'unique keys' => array(
+    'identifier' => array(
+      'identifier',
+    ),
+  ),
+  'indexes' => array(
+    'group_name' => array(
+      'group_name',
+    ),
+  ),
+  'mysql_character_set' => 'utf8',
+));
+
+$connection->insert('field_group')
+->fields(array(
+  'id',
+  'identifier',
+  'group_name',
+  'entity_type',
+  'bundle',
+  'mode',
+  'parent_name',
+  'data',
+))
+->values(array(
+  'id' => '1',
+  'identifier' => 'group_page|node|page|default',
+  'group_name' => 'group_page',
+  'entity_type' => 'node',
+  'bundle' => 'page',
+  'mode' => 'default',
+  'parent_name' => '',
+  'data' => 'a:5:{s:5:"label";s:10:"Node group";s:6:"weight";i:0;s:8:"children";a:0:{}s:11:"format_type";s:5:"htabs";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+))
+->values(array(
+  'id' => '2',
+  'identifier' => 'group_user|user|user|default',
+  'group_name' => 'group_user',
+  'entity_type' => 'user',
+  'bundle' => 'user',
+  'mode' => 'default',
+  'parent_name' => '',
+  'data' => 'a:5:{s:5:"label";s:17:"User group parent";s:6:"weight";i:1;s:8:"children";a:0:{}s:11:"format_type";s:3:"div";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+))
+->values(array(
+  'id' => '3',
+  'identifier' => 'group_user_child|user|user|default',
+  'group_name' => 'group_user_child',
+  'entity_type' => 'user',
+  'bundle' => 'user',
+  'mode' => 'default',
+  'parent_name' => 'group_user',
+  'data' => 'a:5:{s:5:"label";s:16:"User group child";s:6:"weight";i:99;s:8:"children";a:1:{i:0;s:12:"user_picture";}s:11:"format_type";s:4:"tabs";s:15:"format_settings";a:2:{s:5:"label";s:16:"User group child";s:17:"instance_settings";a:2:{s:7:"classes";s:16:"user-group-child";s:2:"id";s:33:"group_article_node_article_teaser";}}}',
+))
+->values(array(
+  'id' => '4',
+  'identifier' => 'group_article|node|article|teaser',
+  'group_name' => 'group_article',
+  'entity_type' => 'node',
+  'bundle' => 'article',
+  'mode' => 'teaser',
+  'parent_name' => '',
+  'data' => 'a:5:{s:5:"label";s:10:"htab group";s:6:"weight";i:2;s:8:"children";a:1:{i:0;s:11:"field_image";}s:11:"format_type";s:4:"htab";s:15:"format_settings";a:1:{s:17:"instance_settings";a:1:{s:7:"classes";s:10:"htab-group";}}}',
+))
+->values(array(
+  'id' => '5',
+  'identifier' => 'group_page|node|page|form',
+  'group_name' => 'group_page',
+  'entity_type' => 'node',
+  'bundle' => 'page',
+  'mode' => 'form',
+  'parent_name' => '',
+  'data' => 'a:5:{s:5:"label";s:15:"Node form group";s:6:"weight";i:0;s:8:"children";a:0:{}s:11:"format_type";s:5:"htabs";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+))
+->values(array(
+  'id' => '6',
+  'identifier' => 'group_article|node|article|form',
+  'group_name' => 'group_article',
+  'entity_type' => 'node',
+  'bundle' => 'article',
+  'mode' => 'form',
+  'parent_name' => '',
+  'data' => 'a:5:{s:5:"label";s:15:"htab form group";s:6:"weight";i:2;s:8:"children";a:1:{i:0;s:11:"field_image";}s:11:"format_type";s:4:"htab";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+))
+->execute();

+ 120 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/src/Kernel/Migrate/d7/MigrateFieldGroupTest.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace Drupal\Tests\field_group_migrate\Kernel\Migrate\d7;
+
+use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
+
+/**
+ * Tests field group migration.
+ *
+ * @group field_group
+ */
+class MigrateFieldGroupTest extends MigrateDrupal7TestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'field_group',
+    'field_group_migrate',
+    'comment',
+    'datetime',
+    'image',
+    'link',
+    'node',
+    'taxonomy',
+    'telephone',
+    'text',
+    'taxonomy',
+    'menu_ui',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->loadFixture(__DIR__ . '/../../../../fixtures/drupal7.php');
+
+    $this->installConfig(static::$modules);
+
+    $this->executeMigrations([
+      'd7_node_type',
+      'd7_comment_type',
+      'd7_taxonomy_vocabulary',
+      'd7_view_modes',
+      'd7_field',
+      'd7_field_instance',
+      'd7_field_formatter_settings',
+      'd7_field_group',
+    ]);
+  }
+
+  /**
+   * Asserts various aspects of a migrated field group.
+   *
+   * @param $id
+   *   The id of the entity display to which the field group applies.
+   * @param $type
+   *   The destination type.
+   * @param $group_name
+   *   The name of the field group.
+   * @param $expected_label
+   *   The expected label.
+   * @param int $expected_weight
+   *   The expected label.
+   * @param array $expected_format_settings
+   *   The expected format settings.
+   * @param string $expected_format_type
+   *   The expected format type.
+   * @param array $expected_children
+   *   The expected children.
+   * @param string $expected_parent_name
+   *   The expected parent name.
+   */
+  protected function assertEntity($id, $type, $group_name, $expected_label, $expected_weight = 0, $expected_format_settings = [], $expected_format_type = 'tabs', $expected_children = [], $expected_parent_name = '') {
+    /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $entity */
+    $entity = \Drupal::entityTypeManager()
+      ->getStorage($type)
+      ->load($id);
+    $field_group_settings = $entity->getThirdPartySettings('field_group');
+    $this->assertNotEmpty($field_group_settings);
+    $this->assertArrayHasKey($group_name, $field_group_settings);
+
+    $field_group = $field_group_settings[$group_name];
+    $this->assertEquals($expected_label, $field_group['label']);
+    $this->assertEquals($expected_format_settings, $field_group['format_settings']);
+    $this->assertEquals($expected_children, $field_group['children']);
+    $this->assertEquals($expected_parent_name, $field_group['parent_name']);
+    $this->assertEquals($expected_weight, $field_group['weight']);
+    $this->assertEquals($expected_format_type, $field_group['format_type']);
+  }
+
+  /**
+   * Test field group migration from Drupal 7 to 8.
+   */
+  public function testFieldGroup() {
+    $this->assertEntity('node.page.default', 'entity_view_display', 'group_page', 'Node group', 0, ['direction' => 'horizontal']);
+    $this->assertEntity('user.user.default', 'entity_view_display', 'group_user', 'User group parent', 1, ['element' => 'div'], 'html_element');
+    $this->assertEntity('user.user.default', 'entity_view_display', 'group_user_child', 'User group child', 99, ['direction' => 'vertical', 'label' => 'User group child', 'classes' => 'user-group-child', 'id' => 'group_article_node_article_teaser'], 'tabs', ['user_picture'], 'group_user');
+    $this->assertEntity('node.article.teaser', 'entity_view_display', 'group_article', 'htab group', 2, ['classes' => 'htab-group'], 'tab', ['field_image']);
+
+    // Check an entity_view_display without a field group.
+    /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $entity */
+    $entity = \Drupal::entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->load('node.page.teaser');
+    $field_group_settings = $entity->getThirdPartySettings('field_group');
+    $this->assertEmpty($field_group_settings);
+
+    $this->assertEntity('node.page.default', 'entity_form_display', 'group_page', 'Node form group', 0, ['direction' => 'horizontal']);
+    $this->assertEntity('node.article.default', 'entity_form_display', 'group_article', 'htab form group', 2, [], 'tab', ['field_image']);
+
+    // Check an entity_form_display without a field group.
+    $entity = \Drupal::entityTypeManager()
+      ->getStorage('entity_form_display')
+      ->load('node.blog.default');
+    $field_group_settings = $entity->getThirdPartySettings('field_group');
+    $this->assertEmpty($field_group_settings);
+  }
+}

+ 102 - 0
sites/all/modules/contrib/fields/field_group/contrib/field_group_migrate/tests/src/Unit/Migrate/d7/FieldGroupTest.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Tests\field_group_migrate\Unit\Migrate\d7;
+
+use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
+
+/**
+ * Tests D7 field group source plugin.
+ *
+ * @group field_group
+ */
+class FieldGroupTest extends MigrateSqlSourceTestBase {
+
+  const PLUGIN_CLASS = 'Drupal\field_group_migrate\Plugin\migrate\source\d7\FieldGroup';
+
+  protected $migrationConfiguration = [
+    'id' => 'test',
+    'source' => [
+      'plugin' => 'd7_field_group',
+    ],
+  ];
+
+  protected $expectedResults = [
+    [
+      'id' => '1',
+      'identifier' => 'group_page|node|page|default',
+      'group_name' => 'group_page',
+      'entity_type' => 'node',
+      'bundle' => 'page',
+      'mode' => 'default',
+      'parent_name' => '',
+      'data' => 'a:5:{s:5:"label";s:10:"Node group";s:6:"weight";i:0;s:8:"children";a:0:{}s:11:"format_type";s:5:"htabs";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+    ],
+    [
+      'id' => '2',
+      'identifier' => 'group_user|user|user|default',
+      'group_name' => 'group_user',
+      'entity_type' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+      'parent_name' => '',
+      'data' => 'a:5:{s:5:"label";s:17:"User group parent";s:6:"weight";i:1;s:8:"children";a:0:{}s:11:"format_type";s:3:"div";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+    ],
+    [
+      'id' => '3',
+      'identifier' => 'group_user_child|user|user|default',
+      'group_name' => 'group_user_child',
+      'entity_type' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+      'parent_name' => 'group_user',
+      'data' => 'a:5:{s:5:"label";s:16:"User group child";s:6:"weight";i:99;s:8:"children";a:1:{i:0;s:12:"user_picture";}s:11:"format_type";s:4:"tabs";s:15:"format_settings";a:2:{s:5:"label";s:16:"User group child";s:17:"instance_settings";a:2:{s:7:"classes";s:16:"user-group-child";s:2:"id";s:33:"group_article_node_article_teaser";}}}',
+    ],
+    [
+      'id' => '4',
+      'identifier' => 'group_article|node|article|teaser',
+      'group_name' => 'group_article',
+      'entity_type' => 'node',
+      'bundle' => 'article',
+      'mode' => 'teaser',
+      'parent_name' => '',
+      'data' => 'a:5:{s:5:"label";s:10:"htab group";s:6:"weight";i:2;s:8:"children";a:1:{i:0;s:11:"field_image";}s:11:"format_type";s:4:"htab";s:15:"format_settings";a:1:{s:17:"instance_settings";a:1:{s:7:"classes";s:10:"htab-group";}}}',
+    ],
+    [
+      'id' => '5',
+      'identifier' => 'group_page|node|page|form',
+      'group_name' => 'group_page',
+      'entity_type' => 'node',
+      'bundle' => 'page',
+      'mode' => 'form',
+      'parent_name' => '',
+      'data' => 'a:5:{s:5:"label";s:15:"Node form group";s:6:"weight";i:0;s:8:"children";a:0:{}s:11:"format_type";s:5:"htabs";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+    ],
+    [
+      'id' => '6',
+      'identifier' => 'group_article|node|article|form',
+      'group_name' => 'group_article',
+      'entity_type' => 'node',
+      'bundle' => 'article',
+      'mode' => 'form',
+      'parent_name' => '',
+      'data' => 'a:5:{s:5:"label";s:15:"htab form group";s:6:"weight";i:2;s:8:"children";a:1:{i:0;s:11:"field_image";}s:11:"format_type";s:4:"htab";s:15:"format_settings";a:1:{s:17:"instance_settings";a:0:{}}}',
+    ],
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->databaseContents['field_group'] = $this->expectedResults;
+    parent::setUp();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providerSource() {
+    // @TODO FIX.
+    return [];
+  }
+
+}

+ 14 - 0
sites/all/modules/contrib/fields/field_group/css/field_group.field_ui.css

@@ -0,0 +1,14 @@
+
+#field-overview tr.field-group .group-label,
+#field-display-overview tr.field-group .group-label {
+  font-weight: bold;
+}
+
+#field-overview tr.static-region,
+#field-display-overview tr.static-region {
+	background-color: #ddd;
+}
+
+#edit-refresh {
+  display:none;
+}

+ 13 - 0
sites/all/modules/contrib/fields/field_group/field_group.info.yml

@@ -0,0 +1,13 @@
+name: 'Field Group'
+type: module
+description: 'Provides the ability to group your fields on both form and display.'
+package : Fields
+# core: 8.x
+dependencies:
+  - drupal:field
+
+# Information added by Drupal.org packaging script on 2017-11-10
+version: '8.x-3.0-beta1'
+core: '8.x'
+project: 'field_group'
+datestamp: 1510352294

+ 63 - 0
sites/all/modules/contrib/fields/field_group/field_group.libraries.yml

@@ -0,0 +1,63 @@
+field_ui:
+  version: VERSION
+  js:
+    js/field_group.field_ui.js: {}
+  css:
+    component:
+      css/field_group.field_ui.css: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
+    - core/drupalSettings
+
+core:
+  version: VERSION
+  js:
+    js/field_group.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
+    - core/drupalSettings
+
+formatter.accordion:
+  version: VERSION
+  js:
+    formatters/accordion/accordion.js: {}
+  dependencies:
+    - core/jquery.ui.accordion
+
+formatter.html_element:
+  version: VERSION
+  js:
+    formatters/html_element/html-element.js: {}
+
+formatter.fieldset:
+  version: VERSION
+  js:
+    formatters/fieldset/fieldset.js: {}
+
+formatter.details:
+  version: VERSION
+  js:
+    formatters/details/details.js: {}
+
+formatter.tabs:
+  version: VERSION
+  js:
+    formatters/tabs/tabs.js: {}
+  dependencies:
+    - core/modernizr
+
+element.horizontal_tabs:
+  version: VERSION
+  js:
+    # Load before field_group/core.
+    formatters/tabs/horizontal-tabs.js: {weight: -1}
+  css:
+    component:
+      formatters/tabs/horizontal-tabs.css: {}
+  dependencies:
+    - core/drupal.collapse
+    - core/modernizr

+ 3 - 0
sites/all/modules/contrib/fields/field_group/field_group.links.action.yml

@@ -0,0 +1,3 @@
+field_group.field_group_add:
+  class: \Drupal\Core\Menu\LocalActionDefault
+  deriver: \Drupal\field_group\Plugin\Derivative\FieldGroupLocalAction

+ 810 - 0
sites/all/modules/contrib/fields/field_group/field_group.module

@@ -0,0 +1,810 @@
+<?php
+
+/**
+ * @file
+ * Allows administrators to attach custom fields to fieldable types.
+ */
+
+use Drupal\Core\Entity\ContentEntityFormInterface;
+use Drupal\Core\Entity\Display\EntityDisplayInterface;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Form\ConfirmFormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+
+require_once __DIR__ . '/includes/helpers.inc';
+
+/**
+ * Implements hook_theme_registry_alter().
+ */
+function field_group_theme_registry_alter(&$theme_registry) {
+
+  // Inject field_group_build_entity_groups in all entity theming functions.
+  $entity_info = Drupal::entityTypeManager()->getDefinitions();
+  $entity_types = array();
+  foreach ($entity_info as $entity_type_id => $entity_type) {
+    if ($route_name = $entity_type->get('field_ui_base_route')) {
+      $entity_types[] = $entity_type_id;
+    }
+  }
+
+  foreach ($theme_registry as $theme_hook => $info) {
+    if (in_array($theme_hook, $entity_types) || (!empty($info['base hook']) && in_array($info['base hook'], $entity_types))) {
+      $theme_registry[$theme_hook]['preprocess functions'][] = 'field_group_build_entity_groups';
+    }
+  }
+
+  // ECK does not use the eck as theme function.
+  if (isset($theme_registry['eck_entity'])) {
+    $theme_registry['eck_entity']['preprocess functions'][] = 'field_group_build_entity_groups';
+  }
+
+}
+
+/**
+ * Implements hook_theme().
+ */
+function field_group_theme() {
+  return array(
+    'horizontal_tabs' => array(
+      'render element' => 'element',
+      'template' => 'horizontal-tabs',
+      'file' => 'templates/theme.inc',
+    ),
+    'field_group_accordion_item' => array(
+      'render element' => 'element',
+      'template' => 'field-group-accordion-item',
+      'file' => 'templates/theme.inc',
+    ),
+    'field_group_accordion' => array(
+      'render element' => 'element',
+      'template' => 'field-group-accordion',
+      'file' => 'templates/theme.inc',
+    ),
+    'field_group_html_element' => array(
+      'render element' => 'element',
+      'template' => 'field-group-html-element',
+      'file' => 'templates/theme.inc',
+    ),
+  );
+}
+
+/**
+ * Implements hook_theme_suggestions_alter().
+ *
+ * @param array $suggestions
+ * @param array $variables
+ * @param $hook
+ */
+function field_group_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  switch ($hook) {
+    case 'horizontal_tabs':
+    case 'field_group_accordion_item':
+    case 'field_group_accordion':
+    case 'field_group_html_element':
+      $element = $variables['element'];
+
+      $name = $element['#group_name'];
+      $entity_type = $element['#entity_type'];
+      $bundle = $element['#bundle'];
+
+      $wrapper = '';
+      if (isset($element['#wrapper_element'])) {
+        $wrapper = $element['#wrapper_element'];
+        $suggestions[] = $hook . '__' . $wrapper;
+      }
+
+      $suggestions[] = $hook . '__' . $entity_type;
+      $suggestions[] = $hook . '__' . $bundle;
+      $suggestions[] = $hook . '__' . $name;
+
+      if ($wrapper) {
+        $suggestions[] = $hook . '__' . $entity_type . '__' . $wrapper;
+      }
+
+      $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle;
+      $suggestions[] = $hook . '__' . $entity_type . '__' . $name;
+
+      if ($wrapper) {
+        $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $wrapper;
+      }
+      $suggestions[] = $hook . '__' . $entity_type . '__' . $bundle . '__' . $name;
+      break;
+  }
+
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ * Using hook_form_field_ui_form_display_overview_form_alter.
+ */
+function field_group_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
+  $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
+  field_group_field_ui_display_form_alter($form, $form_state);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ * Using hook_form_field_ui_display_overview_form_alter.
+ */
+function field_group_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
+  $form_state->loadInclude('field_group', 'inc', 'includes/field_ui');
+  field_group_field_ui_display_form_alter($form, $form_state);
+}
+
+/**
+ * Implements hook_field_info_max_weight().
+ */
+function field_group_field_info_max_weight($entity_type, $bundle, $context, $context_mode) {
+
+  $groups = field_group_info_groups($entity_type, $bundle, $context, $context_mode);
+
+  $weights = array();
+  foreach ($groups as $group) {
+    $weights[] = $group->weight;
+  }
+  return $weights ? max($weights) : NULL;
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function field_group_form_alter(array &$form, FormStateInterface $form_state) {
+
+  $form_object = $form_state->getFormObject();
+  if ($form_object instanceof ContentEntityFormInterface && !$form_object instanceof ConfirmFormInterface) {
+
+    /**
+     * @var EntityFormDisplayInterface $form_display
+     */
+    $storage = $form_state->getStorage();
+    if (!empty($storage['form_display'])) {
+      $form_display = $storage['form_display'];
+      $entity = $form_object->getEntity();
+
+      $context = array(
+        'entity_type' => $entity->getEntityTypeId(),
+        'bundle' => $entity->bundle(),
+        'entity' => $entity,
+        'context' => 'form',
+        'display_context' => 'form',
+        'mode' => $form_display->getMode(),
+      );
+
+      field_group_attach_groups($form, $context);
+      $form['#pre_render'][] = 'field_group_form_pre_render';
+    }
+  }
+
+}
+
+/**
+ * Implements hook_inline_entity_form_entity_form_alter().
+ */
+function field_group_inline_entity_form_entity_form_alter(&$entity_form, FormStateInterface $form_state) {
+
+  // Attach the fieldgroups to current entity form.
+  $context = [
+    'entity_type' => $entity_form['#entity']->getEntityTypeId(),
+    'bundle' => $entity_form['#entity']->bundle(),
+    'entity' => $entity_form['#entity'],
+    'display_context' => 'form',
+    'mode' => isset($entity_form['#form_mode']) ? $entity_form['#form_mode'] : 'default',
+  ];
+
+  field_group_attach_groups($entity_form, $context);
+  $entity_form['#pre_render'][] = 'field_group_form_pre_render';
+}
+
+/**
+ * Implements hook_entity_view_alter().
+ */
+function field_group_entity_view_alter(&$build, EntityInterface $entity, EntityDisplayInterface $display) {
+
+  $context = array(
+    'entity_type' => $display->getTargetEntityTypeId(),
+    'bundle' => $entity->bundle(),
+    'entity' => $entity,
+    'display_context' => 'view',
+    'mode' => $display->getMode(),
+  );
+
+  field_group_attach_groups($build, $context);
+
+  // If no theme hook, we have no theme hook to preprocess.
+  // Add a prerender.
+  if (empty($build['#theme'])) {
+
+    $ds_enabled = false;
+    if (Drupal::moduleHandler()->moduleExists('ds')) {
+      // Check if DS is enabled for this display.
+      if ($display->getThirdPartySetting('ds', 'layout') && !Drupal\ds\Ds::isDisabled()) {
+        $ds_enabled = true;
+      }
+    }
+
+    // If DS is enabled, no pre render is needed (DS adds fieldgroup preprocessing).
+    if (!$ds_enabled) {
+      $build['#pre_render'][] = 'field_group_entity_view_pre_render';
+    }
+  }
+}
+
+/**
+ * Pre render callback for rendering groups.
+ * @see field_group_field_attach_form
+ * @param $element Form that is being rendered.
+ */
+function field_group_form_pre_render($element) {
+  if (empty($element['#field_group_form_pre_render'])) {
+    $element['#field_group_form_pre_render'] = TRUE;
+    field_group_build_entity_groups($element, 'form');
+  }
+  return $element;
+}
+
+/**
+ * Pre render callback for rendering groups on entities without theme hook.
+ * @param $element
+ *   Entity being rendered.
+ */
+function field_group_entity_view_pre_render($element) {
+  field_group_build_entity_groups($element, 'view');
+  return $element;
+}
+
+/**
+ * Implements hook_field_group_pre_render().
+ *
+ * @param Array $element
+ *   Group beïng rendered.
+ * @param Object $group
+ *   The Field group info.
+ * @param $rendering_object
+ *   The entity / form beïng rendered
+ */
+function field_group_field_group_pre_render(&$element, &$group, &$rendering_object) {
+
+  // Add all field_group format types to the js settings.
+  $element['#attached']['drupalSettings']['field_group'] = array(
+    $group->format_type => [
+      'mode' => $group->mode,
+      'context' => $group->context,
+      'settings' => $group->format_settings,
+    ],
+  );
+
+  $element['#weight'] = $group->weight;
+
+  // Call the pre render function for the format type.
+  $manager = Drupal::service('plugin.manager.field_group.formatters');
+  $plugin = $manager->getInstance(array(
+    'format_type' => $group->format_type,
+    'configuration' => array('label' => $group->label, 'settings' => $group->format_settings),
+    'group' => $group,
+  ));
+  $plugin->preRender($element, $rendering_object);
+
+}
+
+/**
+ * Implements hook_field_group_build_pre_render_alter().
+ * @param Array $elements by address.
+ */
+function field_group_field_group_build_pre_render_alter(& $element) {
+
+  // Someone is doing a node view, in a node view. Reset content.
+  if (isset($element['#node']->content) && count($element['#node']->content) > 0) {
+    $element['#node']->content = array();
+  }
+
+  $display = isset($element['#view_mode']);
+  $groups = array_keys($element['#fieldgroups']);
+
+  // Dish the fieldgroups with no fields for non-forms.
+  if ($display) {
+    field_group_remove_empty_display_groups($element, $groups);
+  }
+  else {
+    // Fix the problem on forms with additional settings.
+    field_group_remove_empty_form_groups('form', $element, $groups, $element['#fieldgroups'], $element['#entity_type']);
+  }
+
+}
+
+/**
+ * Attach groups to the (form) build.
+ *
+ * @param Array $element
+ *   The part of the form.
+ *  @param Array $context
+ *   The contextual information.
+ */
+function field_group_attach_groups(&$element, $context) {
+
+  $entity_type = $context['entity_type'];
+  $bundle = $context['bundle'];
+  $mode = $context['mode'];
+  $display_context = $context['display_context'];
+
+  $element['#fieldgroups'] = field_group_info_groups($entity_type, $bundle, $display_context, $mode);
+
+  // Create a lookup array.
+  $group_children = array();
+  foreach ($element['#fieldgroups'] as $group_name => $group) {
+    foreach ($group->children as $child) {
+      $group_children[$child] = $group_name;
+    }
+  }
+  $element['#group_children'] = $group_children;
+  $element['#entity_type'] = $entity_type;
+
+}
+
+/**
+ * Preprocess/ Pre-render callback.
+ *
+ * @see field_group_form_pre_render()
+ * @see field_group_theme_registry_alter
+ * @see field_group_fields_nest()
+ * @param $vars preprocess vars or form element
+ * @param $context The display context (entity type, form or view)
+ * @return $element Array with re-arranged fields in groups.
+ */
+function field_group_build_entity_groups(&$vars, $context = 'view') {
+
+  if ($context == 'form') {
+    $element = &$vars;
+    $nest_vars = NULL;
+  }
+  else {
+    if (isset($vars['elements'])) {
+      $element = &$vars['elements'];
+    }
+    elseif (isset($vars['content'])) {
+      $element = &$vars['content'];
+    }
+    else {
+      if ($context === 'eck_entity') {
+        $element = &$vars['entity'];
+      }
+      else {
+        $element = &$vars;
+      }
+    }
+
+    $nest_vars = &$vars;
+  }
+
+  // No groups on the entity.
+  if (empty($element['#fieldgroups'])) {
+    return $element;
+  }
+
+  // Nest the fields in the corresponding field groups.
+  field_group_fields_nest($element, $nest_vars, $context);
+
+  // Allow others to alter the pre_rendered build.
+  Drupal::moduleHandler()->alter('field_group_build_pre_render', $element);
+
+  // Return the element on forms.
+  if ($context == 'form') {
+    return $element;
+  }
+
+  // No groups on the entity. Prerender removed empty field groups.
+  if (empty($element['#fieldgroups'])) {
+    return $element;
+  }
+
+  // Put groups inside content if we are rendering an entity_view.
+  foreach ($element['#fieldgroups'] as $group) {
+    if (!empty($element[$group->group_name])) {
+      $key = field_group_get_content_element_key($context);
+      if (isset($vars[$key])) {
+        $vars[$key][$group->group_name] = $element[$group->group_name];
+      }
+    }
+  }
+
+}
+
+/**
+ * Recursive function to nest fields in the field groups.
+ *
+ * This function will take out all the elements in the form and
+ * place them in the correct container element, a fieldgroup.
+ * The current group element in the loop is passed recursively so we can
+ * stash fields and groups in it while we go deeper in the array.
+ * @param Array $element
+ *   The current element to analyse for grouping.
+ * @param Array $vars
+ *   Rendering vars from the entity being viewed.
+ * @param Array $context
+ *   The display context (entity type, form or view).
+ */
+function field_group_fields_nest(&$element, &$vars = NULL, $context = NULL) {
+
+  // Create all groups and keep a flat list of references to these groups.
+  $group_references = array();
+  foreach ($element['#fieldgroups'] as $group_name => $group) {
+    // Construct own weight, as some fields (for example preprocess fields) don't have weight set.
+    $element[$group_name] = array();
+    $group_references[$group_name] = &$element[$group_name];
+  }
+
+  // Loop through all form children looking for those that are supposed to be
+  // in groups, and insert placeholder element for the new group field in the
+  // correct location within the form structure.
+  $element_clone = array();
+  foreach (Element::children($element) as $child_name) {
+    $element_clone[$child_name] = $element[$child_name];
+    // If this element is in a group, create the placeholder element.
+    if (isset($element['#group_children'][$child_name])) {
+      $element_clone[$element['#group_children'][$child_name]] = array();
+    }
+  }
+  $element = array_merge($element_clone, $element);
+
+  // Move all children to their parents. Use the flat list of references for
+  // direct access as we don't know where in the root_element hierarchy the
+  // parent currently is situated.
+  foreach ($element['#group_children'] as $child_name => $parent_name) {
+
+    // Entity being viewed
+    if ($vars) {
+      // If not a group, check the content variable for empty field.
+      $key = field_group_get_content_element_key($context);
+      if (!isset($element['#fieldgroups'][$child_name]) && isset($vars[$key][$child_name])) {
+        $group_references[$parent_name][$child_name] = $vars[$key][$child_name];
+        unset($vars[$key][$child_name]);
+      }
+      // If this is a group, we have to use a reference to keep the reference
+      // list intact (but if it is a field we don't mind).
+      else {
+        $group_references[$parent_name][$child_name] = &$element[$child_name];
+        unset($element[$child_name]);
+      }
+    }
+    // Form being viewed
+    else {
+      // Block denied fields (#access) before they are put in groups.
+      // Fields (not groups) that don't have children (like field_permissions) are removed
+      // in field_group_field_group_build_pre_render_alter.
+      if (isset($element[$child_name]) && (!isset($element[$child_name]['#access']) || $element[$child_name]['#access'])) {
+        // If this is a group, we have to use a reference to keep the reference
+        // list intact (but if it is a field we don't mind).
+        $group_references[$parent_name][$child_name] = &$element[$child_name];
+        $group_references[$parent_name]['#weight'] = $element['#fieldgroups'][$parent_name]->weight;
+      }
+
+      // The child has been copied to its parent: remove it from the root element.
+      unset($element[$child_name]);
+    }
+
+  }
+
+  // Bring extra element wrappers to achieve a grouping of fields.
+  // This will mainly be prefix and suffix altering.
+  foreach ($element['#fieldgroups'] as $group_name => $group) {
+    field_group_pre_render($group_references[$group_name], $group, $element);
+  }
+
+}
+
+/**
+ * Function to pre render the field group element.
+ *
+ * @see field_group_fields_nest()
+ *
+ * @param $element
+ *   Render array of group element that needs to be created.
+ * @param $group
+ *   Object with the group information.
+ * @param $rendering_object
+ *   The entity / form beïng rendered.
+ */
+function field_group_pre_render(& $element, $group, & $rendering_object) {
+
+  // Only run the pre_render function if the group has elements.
+  // $group->group_name
+  if ($element == array()) {
+    return;
+  }
+
+  // Let modules define their wrapping element.
+  // Note that the group element has no properties, only elements.
+  foreach (Drupal::moduleHandler()->getImplementations('field_group_pre_render') as $module) {
+    // The intention here is to have the opportunity to alter the
+    // elements, as defined in hook_field_group_formatter_info.
+    // Note, implement $element by reference!
+    $function = $module . '_field_group_pre_render';
+    $function($element, $group, $rendering_object);
+  }
+
+  // Allow others to alter the pre_render.
+  Drupal::moduleHandler()->alter('field_group_pre_render', $element, $group, $rendering_object);
+
+}
+
+/**
+ * Provides the content element key for a display context.
+ *
+ * This allows entity modules to specify their content element for field group
+ * support, or other modules to add entity module support.
+ *
+ * @param $context
+ *   The display context (entity type, form or view).
+ */
+function field_group_get_content_element_key($context = 'default') {
+  $keys = &drupal_static('field_group_content_elements');
+  if (!isset($keys)) {
+    $keys['default'] = 'content';
+    // Allow other modules to alter the array.
+    Drupal::moduleHandler()->alter('field_group_content_element_keys', $keys);
+  }
+
+  // Check if we have a specific content element key for this entity type.
+  $key = $keys['default'];
+  if (isset($keys[$context])) {
+    $key = $keys[$context];
+  }
+  return $key;
+}
+
+
+/**
+ * Saves a group definition.
+ *
+ * This function is called by ctools export when calls are made through
+ * ctools_export_crud_save(). It's also used as an api method to add groups to a
+ * display.
+ *
+ * @param \stdClass $group
+ *   A group definition.
+ * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $display
+ *   The display to update if known.
+ *
+ * @return \Drupal\Core\Entity\Display\EntityDisplayInterface|NULL
+ *   The updated entity display.
+ */
+function field_group_group_save($group, $display = NULL) {
+  if ($display === NULL) {
+    if ($group->context == 'form') {
+      $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
+    }
+    elseif ($group->context == 'view') {
+      $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
+    }
+  }
+
+  // If no display was found. It doesn't exist yet, create it.
+  if (!isset($display)) {
+    if ($group->context == 'form') {
+      $display = EntityFormDisplay::create(array(
+        'targetEntityType' => $group->entity_type,
+        'bundle' => $group->bundle,
+        'mode' => $group->mode,
+      ))->setStatus(TRUE);
+    }
+    elseif ($group->context == 'view') {
+      $display = EntityViewDisplay::create(array(
+        'targetEntityType' => $group->entity_type,
+        'bundle' => $group->bundle,
+        'mode' => $group->mode,
+      ))->setStatus(TRUE);
+    }
+
+  }
+
+  if (isset($display)) {
+    $data = (array) $group;
+    unset($data['group_name'], $data['entity_type'], $data['bundle'], $data['mode'], $data['form'], $data['context']);
+    $display->setThirdPartySetting('field_group', $group->group_name, $data);
+    $display->save();
+  }
+
+  return $display;
+}
+
+/**
+ * Delete a field group.
+ *
+ * @param $group
+ *   A group definition.
+ */
+function field_group_group_delete($group) {
+  if ($group->context == 'form') {
+    $display = EntityFormDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
+  }
+  elseif ($group->context == 'view') {
+    $display = EntityViewDisplay::load($group->entity_type . '.' . $group->bundle . '.' . $group->mode);
+  }
+
+  /**
+   * @var $display \Drupal\Core\Entity\Display\EntityDisplayInterface
+   */
+  if (isset($display)) {
+    $display->unsetThirdPartySetting('field_group', $group->group_name);
+    $display->save();
+  }
+
+  Drupal::moduleHandler()->invokeAll('field_group_delete_field_group', array($group));
+}
+
+/**
+ * Get all groups.
+ *
+ * @param $entity_type
+ *   The name of the entity.
+ * @param $bundle
+ *   The name of the bundle.
+ * @param $context
+ *   The context of the view mode (form or view)
+ * @param $mode
+ *   The view mode.
+ */
+function field_group_info_groups($entity_type, $bundle, $context, $mode) {
+  if ($context == 'form') {
+    $display = EntityFormDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
+    if (!$display) {
+      return array();
+    }
+    $data = $display->getThirdPartySettings('field_group');
+  }
+  if ($context == 'view') {
+    $display = EntityViewDisplay::load($entity_type . '.' . $bundle . '.' . $mode);
+    if (!$display) {
+      return array();
+    }
+    $data = $display->getThirdPartySettings('field_group');
+  }
+  $groups = array();
+  if (isset($data) && is_array($data)) {
+    foreach ($data as $group_name => $definition) {
+      $definition += array(
+        'group_name' => $group_name,
+        'entity_type' => $entity_type,
+        'bundle' => $bundle,
+        'context' => $context,
+        'mode' => $mode,
+      );
+      $groups[$group_name] = (object) $definition;
+    }
+  }
+  return $groups;
+}
+
+/**
+ * Loads a group definition.
+ *
+ * @param $group_name
+ *   The name of the group.
+ * @param $entity_type
+ *   The name of the entity.
+ * @param $bundle
+ *   The name of the bundle.
+ * @param $context
+ *   The context of the view mode (form or view)
+ * @param $mode
+ *   The view mode to load.
+ */
+function field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode) {
+  $groups = field_group_info_groups($entity_type, $bundle, $context, $mode);
+  if (isset($groups[$group_name])) {
+    return $groups[$group_name];
+  }
+}
+
+/**
+ * Checks if a field_group exists in required context.
+ *
+ * @param String $group_name
+ *   The name of the group.
+ * @param String $entity_type
+ *   The name of the entity.
+ * @param String $bundle
+ *   The bundle for the entity.
+ * @param $context
+ *   The context of the view mode (form or view)
+ * @param String $mode
+ *   The view mode context the group will be rendered.
+ */
+function field_group_exists($group_name, $entity_type, $bundle, $context, $mode) {
+  return (bool) field_group_load_field_group($group_name, $entity_type, $bundle, $context, $mode);
+}
+
+/**
+ * Remove empty groups on forms.
+ *
+ * @param String $parent_name
+ *   The name of the element.
+ * @param array $element
+ *   The element to check the empty state.
+ * @param array $groups
+ *   Array of group objects.
+ */
+function field_group_remove_empty_form_groups($name, & $element, $groups, &$form_groups, $entity) {
+
+  $exceptions = array('user__account', 'comment__author');
+
+  $children = Element::children($element);
+
+  $hasChildren = FALSE;
+  if (count($children)) {
+    foreach ($children as $childname) {
+
+      if (in_array($childname, $groups)) {
+        field_group_remove_empty_form_groups($childname, $element[$childname], $groups, $form_groups, $entity);
+      }
+      $exception = $entity . '__' . $childname;
+      $hasChildren = $hasChildren ? TRUE : (isset($element[$childname]['#type']) || isset($element[$childname]['#markup']) || in_array($exception, $exceptions));
+
+    }
+  }
+
+  if (!$hasChildren) {
+
+    // Remove empty elements from the #fieldgroups.
+    if (empty($element) && isset($form_groups[$name]) && !is_array($form_groups[$name])) {
+      foreach ($form_groups as $group_name => $group) {
+        if (isset($group->children)) {
+          $group_children = array_flip($group->children);
+          if (isset($group_children[$name])) {
+            unset($form_groups[$group_name]->children[$group_children[$name]]);
+          }
+        }
+      }
+    }
+
+    $element['#access'] = FALSE;
+
+  }
+
+}
+
+/**
+ * Remove empty groups on entity display.
+ *
+ * @param array $element
+ *   The element to check the empty state.
+ * @param array $groups
+ *   Array of group objects.
+ */
+function field_group_remove_empty_display_groups(& $element, $groups) {
+
+  $empty_child = TRUE;
+  $empty_group = TRUE;
+
+  // Loop through the visible children for current element.
+  foreach (Element::getVisibleChildren($element) as $name) {
+
+    // Descend if the child is a group.
+    if (in_array($name, $groups)) {
+      $empty_child = field_group_remove_empty_display_groups($element[$name], $groups);
+      if (!$empty_child) {
+        $empty_group = FALSE;
+      }
+    }
+    // Child is a field or a renderable array and the element is not empty.
+    elseif (!empty($element[$name])) {
+      $clone_element = $element[$name];
+      // Weight parameter can make empty element seen as not empty.
+      unset($clone_element['#weight']);
+      if (!Element::isEmpty($clone_element)) {
+        $empty_group = FALSE;
+      }
+    }
+
+  }
+
+  // Reset an empty group.
+  if ($empty_group) {
+    $element = [];
+  }
+
+  return $empty_group;
+
+}

+ 13 - 0
sites/all/modules/contrib/fields/field_group/field_group.services.yml

@@ -0,0 +1,13 @@
+services:
+  plugin.manager.field_group.formatters:
+    class: Drupal\field_group\FieldGroupFormatterPluginManager
+    parent: default_plugin_manager
+  field_group.subscriber:
+    class: Drupal\field_group\Routing\RouteSubscriber
+    arguments: ['@entity.manager']
+    tags:
+     - { name: event_subscriber }
+  field_group.param_converter:
+    class: Drupal\field_group\Routing\FieldGroupConverter
+    tags:
+      - { name: paramconverter }

+ 68 - 0
sites/all/modules/contrib/fields/field_group/formatters/accordion/accordion.js

@@ -0,0 +1,68 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * Implements Drupal.FieldGroup.processHook().
+   */
+  Drupal.FieldGroup.Effects.processAccordion = {
+    execute: function (context, settings, group_info) {
+      $('div.field-group-accordion-wrapper', context).once('fieldgroup-effects').each(function () {
+        var wrapper = $(this);
+
+        // Get the index to set active.
+        var active_index = false;
+        wrapper.find('.accordion-item').each(function (i) {
+          if ($(this).hasClass('field-group-accordion-active')) {
+            active_index = i;
+          }
+        });
+
+        wrapper.accordion({
+          heightStyle: 'content',
+          active: active_index,
+          collapsible: true,
+          changestart: function (event, ui) {
+            if ($(this).hasClass('effect-none')) {
+              ui.options.animated = false;
+            }
+            else {
+              ui.options.animated = 'slide';
+            }
+          }
+        });
+
+        if (group_info.context === 'form') {
+
+          var $firstErrorItem = false;
+
+          // Add required fields mark to any element containing required fields
+          wrapper.find('div.field-group-accordion-item').each(function (i) {
+
+            var $this = $(this);
+            if ($this.is('.required-fields') && ($this.find('[required]').length > 0 || $this.find('.form-required').length > 0)) {
+              $('h3.ui-accordion-header a').eq(i).addClass('form-required');
+            }
+            if ($('.error', $this).length) {
+              // Save first error item, for focussing it.
+              if (!$firstErrorItem) {
+                $firstErrorItem = $this.parent().accordion('activate', i);
+              }
+              $('h3.ui-accordion-header').eq(i).addClass('error');
+            }
+          });
+
+          // Save first error item, for focussing it.
+          if (!$firstErrorItem) {
+            $('.ui-accordion-content-active', $firstErrorItem).css({height: 'auto', width: 'auto', display: 'block'});
+          }
+
+        }
+      });
+    }
+  };
+
+})(jQuery);

+ 23 - 0
sites/all/modules/contrib/fields/field_group/formatters/details/details.js

@@ -0,0 +1,23 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * This script adds the required and error classes to the details wrapper.
+   */
+  Drupal.behaviors.fieldGroupDetails = {
+    attach: function (context) {
+      $(context).find('.field-group-details').once('field-group-details').each(function () {
+        var $this = $(this);
+
+        if ($this.is('.required-fields') && ($this.find('[required]').length > 0 || $this.find('.form-required').length > 0)) {
+          $('summary', $this).first().addClass('form-required');
+        }
+      });
+    }
+  };
+
+})(jQuery);

+ 24 - 0
sites/all/modules/contrib/fields/field_group/formatters/fieldset/fieldset.js

@@ -0,0 +1,24 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * This script adds the required and error classes to the fieldset wrapper.
+   */
+  Drupal.behaviors.fieldGroupDFieldset = {
+    attach: function (context) {
+
+      $(context).find('.field-group-fieldset').once('field-group-fieldset').each(function () {
+        var $this = $(this);
+
+        if ($this.is('.required-fields') && ($this.find('[required]').length > 0 || $this.find('.form-required').length > 0)) {
+          $('legend', $this).first().addClass('form-required');
+        }
+      });
+    }
+  };
+
+})(jQuery);

+ 69 - 0
sites/all/modules/contrib/fields/field_group/formatters/html_element/html-element.js

@@ -0,0 +1,69 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * Implements Drupal.FieldGroup.processHook().
+   */
+  Drupal.FieldGroup.Effects.processHtml_element = {
+    execute: function (context, settings, group_info) {
+
+      $('.field-group-html-element', context).once('fieldgroup-effects').each(function () {
+        var $wrapper = $(this);
+
+        if ($wrapper.hasClass('fieldgroup-collapsible')) {
+          Drupal.FieldGroup.Effects.processHtml_element.renderCollapsible($wrapper);
+        }
+        else {
+
+          // Add required field markers if needed
+          if (group_info.settings.show_label && $wrapper.is('.required-fields') && ($wrapper.find('[required]').length > 0 || $wrapper.find('.form-required').length > 0)) {
+            $wrapper.find(group_info.settings.label_element + ':first').addClass('form-required');
+          }
+        }
+
+      });
+    },
+    renderCollapsible: function($wrapper) {
+
+      // Turn the legend into a clickable link, but retain span.field-group-format-toggler
+      // for CSS positioning.
+      var $toggler = $('.field-group-toggler:first', $wrapper);
+      var $link = $('<a class="field-group-title" href="#"></a>');
+      $link.prepend($toggler.contents());
+
+      // Add required field markers if needed
+      if ($wrapper.is('.required-fields') && ($wrapper.find('[required]').length > 0 || $wrapper.find('.form-required').length > 0)) {
+        $link.addClass('form-required');
+      }
+
+      $link.appendTo($toggler);
+
+      // .wrapInner() does not retain bound events.
+      $link.click(function () {
+        var wrapper = $wrapper.get(0);
+        // Don't animate multiple times.
+        if (!wrapper.animating) {
+          wrapper.animating = true;
+          var speed = $wrapper.hasClass('speed-fast') ? 300 : 1000;
+          if ($wrapper.hasClass('effect-none') && $wrapper.hasClass('speed-none')) {
+            $('> .field-group-wrapper', wrapper).toggle();
+          }
+          else if ($wrapper.hasClass('effect-blind')) {
+            $('> .field-group-wrapper', wrapper).toggle('blind', {}, speed);
+          }
+          else {
+            $('> .field-group-wrapper', wrapper).toggle(speed);
+          }
+          wrapper.animating = false;
+        }
+        $wrapper.toggleClass('collapsed');
+        return false;
+      });
+    }
+  };
+
+})(jQuery);

+ 129 - 0
sites/all/modules/contrib/fields/field_group/formatters/tabs/horizontal-tabs.css

@@ -0,0 +1,129 @@
+.horizontal-tabs {
+  margin: 0 0 1em 0; /* LTR */
+  padding: 0;
+  border: 1px solid #ccc;
+  position: relative; /* IE6/7 */
+}
+
+[dir="rtl"] .horizontal-tabs {
+    margin: 0 0 1em 0;
+}
+
+.horizontal-tabs .horizontal-tabs-list {
+  display: inline-block;
+  margin: 0;
+  border: 0;
+  padding: 0px;
+  list-style: none;
+  background-color: #dedede;
+  border-right: 1px solid #dedede; /* LTR */
+  width: 100%;
+  height: auto;
+  clear: both;
+}
+
+[dir="rtl"] .horizontal-tabs .horizontal-tabs-list {
+  border-right: 0;
+  border-left: 1px solid #dedede;
+}
+
+.horizontal-tabs-pane {
+  padding: 0 1em;
+  border: 0;
+}
+
+.horizontal-tabs-pane > summary {
+  display: none;
+}
+
+/* Layout of each tab */
+.horizontal-tabs  .horizontal-tab-button {
+  background: #eee;
+  border-right: 1px solid #ccc; /* LTR */
+  padding: 1px;
+  padding-top: 0;
+  margin: 0;
+  min-width: 5em; /* IE7 */
+  float: left; /* LTR */
+}
+
+[dir="rtl"] .horizontal-tabs .horizontal-tab-button {
+  border-right: 0;
+  border-left: 1px solid #ccc;
+  float: right;
+}
+
+.horizontal-tabs .horizontal-tab-button a {
+  display: block;
+  text-decoration: none;
+  padding: 0.5em 0.6em;
+}
+
+.horizontal-tabs .horizontal-tab-button a:hover {
+  outline: none;
+  background-color: #ededdd;
+}
+
+.horizontal-tabs .horizontal-tab-button li:hover,
+.horizontal-tabs .horizontal-tab-button li:focus {
+  background-color: #ddd;
+}
+
+.horizontal-tabs ul.horizontal-tabs-list :focus {
+  outline: none;
+}
+
+.horizontal-tab-button a:focus strong,
+.horizontal-tab-button a:active strong,
+.horizontal-tab-button a:hover strong {
+  text-decoration: none;
+  outline: none;
+}
+
+.horizontal-tab-button.selected {
+  background-color: #fff;
+  padding: 0 0 1px 0;
+}
+
+[dir="rtl"] .horizontal-tab-button.selected {
+  border-left-width: 0;
+  border-right-width: 1px;
+}
+
+.horizontal-tabs ul.horizontal-tabs-list li a,
+.horizontal-tabs ul.horizontal-tabs-list li.selected a {
+  display: block;
+  text-decoration: none;
+  padding: 0.5em 0.6em 0.3em 0.6em;
+  position:relative;
+  top: 0px;
+}
+
+.horizontal-tab-button .selected strong {
+  color: #000;
+}
+
+.horizontal-tab-button .summary {
+  display: block;
+}
+
+.horizontal-tab-button .summary {
+  line-height: normal;
+  margin-bottom: 0;
+}
+
+/**
+ * tab content
+ */
+div.field-group-htabs-wrapper .field-group-format-wrapper {
+  clear: both;
+  padding: 0 0 0.6em;
+}
+
+/*hide*/
+.horizontal-tabs .horizontal-tab-hidden {
+  display: block;
+  position: absolute;
+  top: -100000px;
+  width: 100%;
+}

+ 248 - 0
sites/all/modules/contrib/fields/field_group/formatters/tabs/horizontal-tabs.js

@@ -0,0 +1,248 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * This script transforms a set of fieldsets into a stack of horizontal
+   * tabs. Another tab pane can be selected by clicking on the respective
+   * tab.
+   *
+   * Each tab may have a summary which can be updated by another
+   * script. For that to work, each fieldset has an associated
+   * 'horizontalTabCallback' (with jQuery.data() attached to the fieldset),
+   * which is called every time the user performs an update to a form
+   * element inside the tab pane.
+   */
+  Drupal.behaviors.horizontalTabs = {
+    attach: function (context) {
+
+      var width = drupalSettings.widthBreakpoint || 640;
+      var mq = '(max-width: ' + width + 'px)';
+
+      if (window.matchMedia(mq).matches) {
+        return;
+      }
+
+      $(context).find('[data-horizontal-tabs-panes]').once('horizontal-tabs').each(function () {
+
+        var $this = $(this).addClass('horizontal-tabs-panes');
+        var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
+        var tab_focus;
+
+        // Check if there are some details that can be converted to horizontal-tabs
+        var $details = $this.find('> details');
+        if ($details.length === 0) {
+          return;
+        }
+
+        // If collapse.js did not do his work yet, call it directly.
+        if (!$($details[0]).hasClass('.collapse-processed')) {
+          Drupal.behaviors.collapse.attach(context);
+        }
+
+        // Create the tab column.
+        var tab_list = $('<ul class="horizontal-tabs-list"></ul>');
+        $(this).wrap('<div class="horizontal-tabs clearfix"></div>').before(tab_list);
+
+        // Transform each details into a tab.
+        $details.each(function (i) {
+          var $this = $(this);
+          var summaryElement = $this.find('> summary .details-title');
+
+          if (!summaryElement.length) {
+            summaryElement = $this.find('> summary');
+          }
+
+          var summary = summaryElement.clone().children().remove().end().text();
+          var horizontal_tab = new Drupal.horizontalTab({
+            title: $.trim(summary),
+            details: $this
+          });
+          horizontal_tab.item.addClass('horizontal-tab-button-' + i);
+          tab_list.append(horizontal_tab.item);
+          $this
+            .removeClass('collapsed')
+            // prop() can't be used on browsers not supporting details element,
+            // the style won't apply to them if prop() is used.
+            .attr('open', true)
+            .addClass('horizontal-tabs-pane')
+            .data('horizontalTab', horizontal_tab);
+          if (this.id === focusID) {
+            tab_focus = $this;
+          }
+        });
+
+        $(tab_list).find('> li:first').addClass('first');
+        $(tab_list).find('> li:last').addClass('last');
+
+        if (!tab_focus) {
+          // If the current URL has a fragment and one of the tabs contains an
+          // element that matches the URL fragment, activate that tab.
+          var hash = window.location.hash.replace(/[=%;,\/]/g, '');
+          if (hash !== '#' && $(hash, this).length) {
+            tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane');
+          }
+          else {
+            tab_focus = $this.find('> .horizontal-tabs-pane:first');
+          }
+        }
+        if (tab_focus.length) {
+          tab_focus.data('horizontalTab').focus();
+        }
+      });
+    }
+  };
+
+  /**
+   * The horizontal tab object represents a single tab within a tab group.
+   *
+   * @param {object} settings
+   *   An object with the following keys:
+   *   - title: The name of the tab.
+   *   - details: The jQuery object of the details element that is the tab pane.
+   */
+  Drupal.horizontalTab = function (settings) {
+    var self = this;
+    $.extend(this, settings, Drupal.theme('horizontalTab', settings));
+
+    this.link.attr('href', '#' + settings.details.attr('id'));
+
+    this.link.on('click', function (e) {
+      e.preventDefault();
+      self.focus();
+    });
+
+    // Keyboard events added:
+    // Pressing the Enter key will open the tab pane.
+    this.link.on('keydown', function (event) {
+      event.preventDefault();
+      if (event.keyCode === 13) {
+        self.focus();
+        // Set focus on the first input field of the visible details/tab pane.
+        $('.horizontal-tabs-pane :input:visible:enabled:first').trigger('focus');
+      }
+    });
+
+    // Only bind update summary on forms.
+    if (this.details.drupalGetSummary) {
+      this.details
+        .on('summaryUpdated', function () {
+          self.updateSummary();
+        })
+        .trigger('summaryUpdated');
+    }
+
+  };
+
+  Drupal.horizontalTab.prototype = {
+
+    /**
+     * Displays the tab's content pane.
+     */
+    focus: function () {
+      this.details
+        .removeClass('horizontal-tab-hidden')
+        .siblings('.horizontal-tabs-pane')
+        .each(function () {
+          var tab = $(this).data('horizontalTab');
+          tab.details.addClass('horizontal-tab-hidden');
+          tab.item.removeClass('selected');
+        })
+        .end()
+        .siblings(':hidden.horizontal-tabs-active-tab')
+        .val(this.details.attr('id'));
+      this.item.addClass('selected');
+      // Mark the active tab for screen readers.
+      $('#active-horizontal-tab').remove();
+      this.link.append('<span id="active-horizontal-tab" class="visually-hidden">' + Drupal.t('(active tab)') + '</span>');
+    },
+
+    /**
+     * Updates the tab's summary.
+     */
+    updateSummary: function () {
+      this.summary.html(this.details.drupalGetSummary());
+    },
+
+    /**
+     * Shows a horizontal tab pane.
+     *
+     * @return {Drupal.horizontalTab} The current horizontal tab.
+     */
+    tabShow: function () {
+      // Display the tab.
+      this.item.removeClass('horizontal-tab-hidden');
+      // Update .first marker for items. We need recurse from parent to retain the
+      // actual DOM element order as jQuery implements sortOrder, but not as public
+      // method.
+      this.item.parent().children('.horizontal-tab-button').removeClass('first')
+        .filter(':visible:first').addClass('first');
+      // Display the details element.
+      this.details.removeClass('horizontal-tab-hidden');
+      // Focus this tab.
+      this.focus();
+      return this;
+    },
+
+    /**
+     * Hides a horizontal tab pane.
+     *
+     * @return {Drupal.horizontalTab} The current horizontal tab.
+     */
+    tabHide: function () {
+      // Hide this tab.
+      this.item.addClass('horizontal-tab-hidden');
+      // Update .first marker for items. We need recurse from parent to retain the
+      // actual DOM element order as jQuery implements sortOrder, but not as public
+      // method.
+      this.item.parent().children('.horizontal-tab-button').removeClass('first')
+        .filter(':visible:first').addClass('first');
+      // Hide the details element.
+      this.details.addClass('horizontal-tab-hidden');
+      // Focus the first visible tab (if there is one).
+      var $firstTab = this.details.siblings('.horizontal-tabs-pane:not(.horizontal-tab-hidden):first');
+      if ($firstTab.length) {
+        $firstTab.data('horizontalTab').focus();
+      }
+      else {
+        // Hide the vertical tabs (if no tabs remain).
+        this.item.closest('.form-type-horizontal-tabs').hide();
+      }
+      return this;
+    }
+  };
+
+  /**
+   * Theme function for a horizontal tab.
+   *
+   * @param {object} settings
+   *   An object with the following keys:
+   *   - title: The name of the tab.
+   * @return {object}
+   *   This function has to return an object with at least these keys:
+   *   - item: The root tab jQuery element
+   *   - link: The anchor tag that acts as the clickable area of the tab
+   *       (jQuery version)
+   *   - summary: The jQuery element that contains the tab summary
+   */
+  Drupal.theme.horizontalTab = function (settings) {
+    var tab = {};
+    var idAttr = settings.details.attr('id');
+
+    tab.item = $('<li class="horizontal-tab-button" tabindex="-1"></li>')
+      .append(tab.link = $('<a href="#' + idAttr + '"></a>')
+        .append(tab.title = $('<strong></strong>').text(settings.title))
+      );
+
+    // No need to add summary on frontend.
+    if (settings.details.drupalGetSummary) {
+      tab.link.append(tab.summary = $('<span class="summary"></span>'));
+    }
+
+    return tab;
+  };
+
+})(jQuery, Modernizr);

+ 39 - 0
sites/all/modules/contrib/fields/field_group/formatters/tabs/tabs.js

@@ -0,0 +1,39 @@
+(function ($) {
+
+  'use strict';
+
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+
+  /**
+   * Implements Drupal.FieldGroup.processHook().
+   */
+  Drupal.FieldGroup.Effects.processTabs = {
+    execute: function (context, settings, group_info) {
+
+      if (group_info.context === 'form') {
+
+        // Add required fields mark to any element containing required fields
+        var direction = group_info.settings.direction;
+        $(context).find('[data-' + direction + '-tabs-panes] details').once('fieldgroup-effects').each(function () {
+
+          var $this = $(this);
+          if (typeof $(this).data(direction + 'Tab') !== 'undefined') {
+
+            if ($this.is('.required-fields') && ($this.find('[required]').length > 0 || $this.find('.form-required').length > 0)) {
+              $this.data(direction + 'Tab').link.find('strong:first').addClass('form-required');
+            }
+
+            if ($('.error', $this).length) {
+              $this.data(direction + 'Tab').link.parent().addClass('error');
+              Drupal.FieldGroup.setGroupWithfocus($this);
+              $this.data(direction + 'Tab').focus();
+            }
+          }
+        });
+
+      }
+    }
+  };
+
+})(jQuery, Modernizr);

Разница между файлами не показана из-за своего большого размера
+ 368 - 0
sites/all/modules/contrib/fields/field_group/includes/field_ui.inc


+ 64 - 0
sites/all/modules/contrib/fields/field_group/includes/helpers.inc

@@ -0,0 +1,64 @@
+<?php
+
+use Drupal\Core\Form\FormStateInterface;
+
+/*
+ * @file
+ * Helper functions for the fieldgroup module.
+ */
+
+/**
+ * Get the default formatter settings for a given formatter and context.
+ */
+function _field_group_get_default_formatter_settings($format_type, $context) {
+  $manager = Drupal::service('plugin.manager.field_group.formatters');
+  return $manager->getDefaultSettings($format_type, $context);
+}
+
+/**
+ * Return an array of field_group_formatter options.
+ */
+function field_group_field_formatter_options($type) {
+  $options = &drupal_static(__FUNCTION__);
+
+  if (!isset($options)) {
+    $options = array();
+
+    $manager = Drupal::service('plugin.manager.field_group.formatters');
+    $formatters = $manager->getDefinitions();
+
+    foreach ($formatters as $formatter) {
+      if (in_array($type, $formatter['supported_contexts'])) {
+        $options[$formatter['id']] = $formatter['label'];
+      }
+    }
+  }
+
+  return $options;
+}
+
+/**
+ * Validate the entered css class from the submitted format settings.
+ * @param Array $element The validated element
+ * @param FormStateInterface $form_state The state of the form.
+ */
+function field_group_validate_css_class($element, FormStateInterface $form_state) {
+  $form_state_values = $form_state->getValues();
+  $plugin_name = $form_state->get('plugin_settings_edit');
+  if (!empty($form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['classes']) && !preg_match('!^[A-Za-z0-9-_ ]+$!', $form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['classes'])) {
+    $form_state->setError($element, $form_state, t('The css class must include only letters, numbers, underscores and dashes.'));
+  }
+}
+
+/**
+ * Validate the entered id attribute from the submitted format settings.
+ * @param Array $element The validated element
+ * @param FormStateInterface $form_state The state of the form.
+ */
+function field_group_validate_id($element, FormStateInterface $form_state) {
+  $form_state_values = $form_state->getValues();
+  $plugin_name = $form_state->get('plugin_settings_edit');
+  if (!empty($form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['id']) && !preg_match('!^[A-Za-z0-9-_]+$!', $form_state_values['fields'][$plugin_name]['settings_edit_form']['settings']['id'])) {
+    Drupal::formBuilder()->setError($element, $form_state, t('The id must include only letters, numbers, underscores and dashes.'));
+  }
+}

+ 123 - 0
sites/all/modules/contrib/fields/field_group/js/field_group.field_ui.js

@@ -0,0 +1,123 @@
+(function ($) {
+
+  'use strict';
+  Drupal.behaviors.fieldUIFieldsOverview = {
+    attach: function (context, settings) {
+      $('table#field-overview', context).once('field-field-overview', function () {
+        Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIFieldOverview);
+      });
+    }
+  };
+
+  /**
+   * Row handlers for the 'Manage fields' screen.
+   */
+  Drupal.fieldUIFieldOverview = Drupal.fieldUIFieldOverview || {};
+
+  Drupal.fieldUIFieldOverview.group = function (row, data) {
+    this.row = row;
+    this.name = data.name;
+    this.region = data.region;
+    this.tableDrag = data.tableDrag;
+
+    // Attach change listener to the 'group format' select.
+    this.$formatSelect = $('select.field-group-type', row);
+    this.$formatSelect.change(Drupal.fieldUIOverview.onChange);
+
+    return this;
+  };
+
+  Drupal.fieldUIFieldOverview.group.prototype = {
+    getRegion: function () {
+      return 'main';
+    },
+
+    regionChange: function (region, recurse) {
+      return {};
+    },
+
+    regionChangeFields: function (region, element, refreshRows) {
+
+      // Create a new tabledrag rowObject, that will compute the group's child
+      // rows for us.
+      var tableDrag = element.tableDrag;
+      var rowObject = new tableDrag.row(element.row, 'mouse', true);
+      // Skip the main row, we handled it above.
+      rowObject.group.shift();
+
+      // Let child rows handlers deal with the region change - without recursing
+      // on nested group rows, we are handling them all here.
+      $.each(rowObject.group, function () {
+        var childRow = this;
+        var childRowHandler = $(childRow).data('fieldUIRowHandler');
+        $.extend(refreshRows, childRowHandler.regionChange(region, false));
+      });
+    }
+  };
+
+
+  /**
+   * Row handlers for the 'Manage display' screen.
+   */
+  Drupal.fieldUIDisplayOverview = Drupal.fieldUIDisplayOverview || {};
+
+  Drupal.fieldUIDisplayOverview.group = function (row, data) {
+    this.row = row;
+    this.name = data.name;
+    this.region = data.region;
+    this.tableDrag = data.tableDrag;
+
+    // Attach change listener to the 'group format' select.
+    this.$regionSelect = $(row).find('select.field-region');
+    this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange);
+
+    return this;
+  };
+
+  Drupal.fieldUIDisplayOverview.group.prototype = {
+    getRegion: function getRegion() {
+      return this.$regionSelect.val();
+    },
+
+    regionChange: function (region, recurse) {
+
+      // Default recurse to true.
+      recurse = (typeof recurse === 'undefined') || recurse;
+
+      // When triggered by a row drag, the 'region' select needs to be adjusted to
+      // the new region.
+      region = region.replace(/-/g, '_');
+      this.$regionSelect.val(region);
+
+      var refreshRows = {};
+      refreshRows[this.name] = this.$regionSelect.get(0);
+
+      if (recurse) {
+        this.regionChangeFields(region, this, refreshRows);
+      }
+
+      return refreshRows;
+    },
+
+    regionChangeFields: function (region, element, refreshRows) {
+
+      // Create a new tabledrag rowObject, that will compute the group's child
+      // rows for us.
+      var tableDrag = element.tableDrag;
+      var rowObject = new tableDrag.row(element.row, 'mouse', true);
+      // Skip the main row, we handled it above.
+      rowObject.group.shift();
+
+      // Let child rows handlers deal with the region change - without recursing
+      // on nested group rows, we are handling them all here.
+      $.each(rowObject.group, function () {
+        var childRow = this;
+        var childRowHandler = $(childRow).data('fieldUIRowHandler');
+        $.extend(refreshRows, childRowHandler.regionChange(region, false));
+      });
+
+    }
+
+  };
+
+})(jQuery);

+ 58 - 0
sites/all/modules/contrib/fields/field_group/js/field_group.js

@@ -0,0 +1,58 @@
+(function ($) {
+
+  'use strict';
+
+  /**
+   * Drupal FieldGroup object.
+   */
+  Drupal.FieldGroup = Drupal.FieldGroup || {};
+  Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
+  Drupal.FieldGroup.groupWithfocus = null;
+
+  Drupal.FieldGroup.setGroupWithfocus = function (element) {
+    element.css({display: 'block'});
+    Drupal.FieldGroup.groupWithfocus = element;
+  };
+
+  /**
+   * Behaviors.
+   */
+  Drupal.behaviors.fieldGroup = {
+    attach: function (context, settings) {
+
+      settings.field_group = settings.field_group || drupalSettings.field_group;
+      if (typeof settings.field_group === 'undefined') {
+        return;
+      }
+
+      // Execute all of them.
+      $.each(Drupal.FieldGroup.Effects, function (func) {
+        // We check for a wrapper function in Drupal.field_group as
+        // alternative for dynamic string function calls.
+        var type = func.toLowerCase().replace('process', '');
+        if (typeof settings.field_group[type] !== 'undefined' && $.isFunction(this.execute)) {
+          this.execute(context, settings, settings.field_group[type]);
+        }
+      });
+
+      // Add a new ID to each fieldset.
+      $('.group-wrapper fieldset').each(function () {
+        // Tats bad, but we have to keep the actual id to prevent layouts to break.
+        var fieldgroupID = 'field_group-' + $(this).attr('id') + ' ' + $(this).attr('id');
+        $(this).attr('id', fieldgroupID);
+      });
+
+      // Set the hash in url to remember last userselection.
+      $('.group-wrapper ul li').each(function () {
+        var fieldGroupNavigationListIndex = $(this).index();
+        $(this).children('a').click(function () {
+          var fieldset = $('.group-wrapper fieldset').get(fieldGroupNavigationListIndex);
+          // Grab the first id, holding the wanted hashurl.
+          var hashUrl = $(fieldset).attr('id').replace(/^field_group-/, '').split(' ')[0];
+          window.location.hash = hashUrl;
+        });
+      });
+    }
+  };
+
+})(jQuery);

+ 80 - 0
sites/all/modules/contrib/fields/field_group/src/Annotation/FieldGroupFormatter.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\field_group\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a FieldGroupFormatter annotation object.
+ *
+ * Formatters handle the display of fieldgroups.
+ *
+ * Additional annotation keys for formatters can be defined in
+ * hook_field_group_formatter_info_alter().
+ *
+ * @Annotation
+ *
+ * @see \Drupal\field_group\FieldGroupFormatterPluginManager
+ * @see \Drupal\field_group\FieldGroupFormatterInterface
+ *
+ * @ingroup field_formatter
+ */
+class FieldGroupFormatter extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the formatter type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * A short description of the formatter type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description;
+
+  /**
+   * The name of the fieldgroup formatter class.
+   *
+   * This is not provided manually, it will be added by the discovery mechanism.
+   *
+   * @var string
+   */
+  public $class;
+
+  /**
+   * An array of contexts the formatter supports (form / view).
+   *
+   * @var array
+   */
+  public $supported_contexts = array();
+
+  /**
+   * The different format types available for this formatter.
+   *
+   * @var array
+   */
+  public $format_types = array();
+
+  /**
+   * An integer to determine the weight of this formatter relative to other
+   * formatter in the Field UI when selecting a formatter for a given group.
+   *
+   * @var int optional
+   */
+  public $weight = NULL;
+
+}

+ 57 - 0
sites/all/modules/contrib/fields/field_group/src/Element/Accordion.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\field_group\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\RenderElement;
+
+/**
+ * Provides a render element for an accordion.
+ *
+ * @FormElement("field_group_accordion")
+ */
+class Accordion extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+
+    return array(
+      '#process' => array(
+        array($class, 'processAccordion'),
+      ),
+      '#theme_wrappers' => array('field_group_accordion'),
+    );
+  }
+
+  /**
+   * Process the accordion item.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   details element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processAccordion(&$element, FormStateInterface $form_state) {
+
+    // Add the jQuery UI accordion.
+    $element['#attached']['library'][] = 'field_group/formatter.accordion';
+
+    // Add the effect class.
+    if (isset($element['#effect'])) {
+      if (!isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array();
+      }
+      $element['#attributes']['class'][] = 'effect-' . $element['#effect'];
+    }
+
+    return $element;
+  }
+
+}

+ 24 - 0
sites/all/modules/contrib/fields/field_group/src/Element/AccordionItem.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\field_group\Element;
+
+use Drupal\Core\Render\Element\RenderElement;
+
+/**
+ * Provides a render element for an accordion item.
+ *
+ * @FormElement("field_group_accordion_item")
+ */
+class AccordionItem extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    return array(
+      '#open' => TRUE,
+      '#theme_wrappers' => array('field_group_accordion_item'),
+    );
+  }
+
+}

+ 88 - 0
sites/all/modules/contrib/fields/field_group/src/Element/HorizontalTabs.php

@@ -0,0 +1,88 @@
+<?php
+
+namespace Drupal\field_group\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\RenderElement;
+
+/**
+ * Provides a render element for horizontal tabs.
+ *
+ * Formats all child details and all non-child details whose #group is
+ * assigned this element's name as horizontal tabs.
+ *
+ * @FormElement("horizontal_tabs")
+ */
+class HorizontalTabs extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+
+    return array(
+      '#default_tab' => '',
+      '#process' => array(
+        array($class, 'processHorizontalTabs'),
+      ),
+      '#theme_wrappers' => array('horizontal_tabs'),
+    );
+  }
+
+  /**
+   * Creates a group formatted as horizontal tabs.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   details element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param bool $on_form
+   *   Are the tabs rendered on a form or not.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processHorizontalTabs(&$element, FormStateInterface $form_state, $on_form = TRUE) {
+
+    // Inject a new details as child, so that form_process_details() processes
+    // this details element like any other details.
+    $element['group'] = array(
+      '#type' => 'details',
+      '#theme_wrappers' => array(),
+      '#parents' => $element['#parents'],
+    );
+
+    // Add an invisible label for accessibility.
+    if (!isset($element['#title'])) {
+      $element['#title'] = t('Horizontal Tabs');
+      $element['#title_display'] = 'invisible';
+    }
+
+    // Add required JavaScript and Stylesheet.
+    $element['#attached']['library'][] = 'field_group/element.horizontal_tabs';
+
+    // Only add forms library on forms.
+    if ($on_form) {
+      $element['#attached']['library'][] = 'core/drupal.form';
+    }
+
+    // The JavaScript stores the currently selected tab in this hidden
+    // field so that the active tab can be restored the next time the
+    // form is rendered, e.g. on preview pages or when form validation
+    // fails.
+    $name = implode('__', $element['#parents']);
+    if ($form_state->hasValue($name . '__active_tab')){
+      $element['#default_tab'] = $form_state->getValue($name . '__active_tab');
+    }
+    $element[$name . '__active_tab'] = array(
+      '#type' => 'hidden',
+      '#default_value' => $element['#default_tab'],
+      '#attributes' => array('class' => array('horizontal-tabs-active-tab')),
+    );
+
+    return $element;
+  }
+
+}

+ 67 - 0
sites/all/modules/contrib/fields/field_group/src/Element/HtmlElement.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace Drupal\field_group\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\RenderElement;
+
+/**
+ * Provides a render element for a html element.
+ *
+ * @FormElement("field_group_html_element")
+ */
+class HtmlElement extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+
+    return array(
+      '#process' => array(
+        array($class, 'processHtmlElement'),
+      ),
+      '#theme_wrappers' => array('field_group_html_element'),
+    );
+  }
+
+  /**
+   * Process a html element
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   details element.
+   * @param FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function processHtmlElement(&$element, FormStateInterface $form_state) {
+
+    // If an effect is set, we need to load extra js.
+    if (!empty($element['#effect']) && $element['#effect'] !== 'none') {
+
+      $element['#attached']['library'][] = 'field_group/formatter.html_element';
+      $element['#attached']['library'][] = 'field_group/core';
+
+      // Add the required classes for the js.
+      $element['#attributes']['class'][] = 'field-group-html-element';
+      $element['#attributes']['class'][] = 'fieldgroup-collapsible';
+      $element['#attributes']['class'][] = 'effect-' . $element['#effect'];
+      if (!empty($element['#speed'])) {
+        $element['#attributes']['class'][] = 'speed-' . $element['#speed'];
+      }
+
+      // Add jquery ui effects library for the blind effect.
+      if ($element['#effect'] == 'blind') {
+        $element['#attached']['library'][] = 'core/jquery.ui.effects.blind';
+      }
+
+    }
+
+    return $element;
+  }
+
+}

+ 179 - 0
sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterBase.php

@@ -0,0 +1,179 @@
+<?php
+
+namespace Drupal\field_group;
+
+use Drupal\Core\Field\PluginSettingsBase;
+
+/**
+ * Base class for 'Fieldgroup formatter' plugin implementations.
+ *
+ * @ingroup field_group_formatter
+ */
+abstract class FieldGroupFormatterBase extends PluginSettingsBase implements FieldGroupFormatterInterface {
+
+  /**
+   * The group this formatter needs to render.
+   * @var stdClass
+   */
+  protected $group;
+
+  /**
+   * The formatter settings.
+   *
+   * @var array
+   */
+  protected $settings;
+
+  /**
+   * The label display setting.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * The view mode.
+   *
+   * @var string
+   */
+  protected $viewMode;
+
+  /**
+   * The context mode.
+   *
+   * @var string
+   */
+  protected $context;
+
+  /**
+   * Constructs a FieldGroupFormatterBase object.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the formatter.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param $group
+   *   The group object.
+   * @param array $settings
+   *   The formatter settings.
+   * @param string $label
+   *   The formatter label.
+   */
+  public function __construct($plugin_id, $plugin_definition, $group, array $settings, $label) {
+    parent::__construct(array(), $plugin_id, $plugin_definition);
+
+    $this->group = $group;
+    $this->settings = $settings;
+    $this->label = $label;
+    $this->context = $group->context;
+  }
+
+  /**
+   * Get the current label.
+   * @return string
+   */
+  public function getLabel() {
+    return $this->label;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = array();
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field group label'),
+      '#default_value' => $this->label,
+      '#weight' => -5,
+    );
+
+    $form['id'] = array(
+      '#title' => t('ID'),
+      '#type' => 'textfield',
+      '#default_value' => $this->getSetting('id'),
+      '#weight' => 10,
+      '#element_validate' => array('field_group_validate_id'),
+    );
+
+    $form['classes'] = array(
+      '#title' => t('Extra CSS classes'),
+      '#type' => 'textfield',
+      '#default_value' => $this->getSetting('classes'),
+      '#weight' => 11,
+      '#element_validate' => array('field_group_validate_css_class'),
+    );
+
+    return $form;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = array();
+
+    if ($this->getSetting('formatter')) {
+      $summary[] = $this->pluginDefinition['label'] . ': ' . $this->getSetting('formatter');
+    }
+
+    if ($this->getSetting('id')) {
+      $summary[] = $this->t('Id: @id', array('@id' => $this->getSetting('id')));
+    }
+
+    if ($this->getSetting('classes')) {
+      $summary[] = \Drupal::translation()->translate('Extra CSS classes: @classes', array('@classes' => $this->getSetting('classes')));
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return self::defaultContextSettings('view');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    return array(
+      'classes' => '',
+      'id' => '',
+    );
+  }
+
+  /**
+   * Get the classes to add to the group.
+   */
+  protected function getClasses() {
+
+    $classes = array();
+    // Add a required-fields class to trigger the js.
+    if ($this->getSetting('required_fields')) {
+      $classes[] = 'required-fields';
+      $classes[] = 'field-group-' . str_replace('_', '-', $this->getBaseId());
+    }
+
+    if ($this->getSetting('classes')) {
+      $classes = array_merge($classes, explode(' ', trim($this->getSetting('classes'))));
+    }
+
+    return $classes;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    $element['#group_name'] = $this->group->group_name;
+    $element['#entity_type'] = $this->group->entity_type;
+    $element['#bundle'] = $this->group->bundle;
+  }
+
+}

+ 58 - 0
sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterInterface.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\field_group;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
+/**
+ * Interface definition for fieldgroup formatter plugins.
+ *
+ * @ingroup field_group_formatter
+ */
+interface FieldGroupFormatterInterface extends PluginInspectionInterface {
+
+  /**
+   * Allows the field group formatter to manipulate the field group array and attach the formatters rendering element.
+   *
+   * @param array $element
+   *   The field group render array.
+   * @param object $rendering_object
+   *   The object / entity beïng rendered.
+   */
+  public function preRender(&$element, $rendering_object);
+
+  /**
+   * Returns a form to configure settings for the formatter.
+   *
+   * Invoked in field_group_field_ui_display_form_alter to allow
+   * administrators to configure the formatter. The field_group module takes care
+   * of handling submitted form values.
+   *
+   * @return array
+   *   The form elements for the formatter settings.
+   */
+  public function settingsForm();
+
+  /**
+   * Returns a short summary for the current formatter settings.
+   *
+   * If an empty result is returned, a UI can still be provided to display
+   * a settings form in case the formatter has configurable settings.
+   *
+   * @return array()
+   *   A short summary of the formatter settings.
+   */
+  public function settingsSummary();
+
+  /**
+   * Defines the default settings for this plugin.
+   *
+   * @param string $context
+   *   The context to get the default settings for.
+   *
+   * @return array
+   *   A list of default settings, keyed by the setting name.
+   */
+  public static function defaultContextSettings($context);
+
+}

+ 143 - 0
sites/all/modules/contrib/fields/field_group/src/FieldGroupFormatterPluginManager.php

@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\field_group;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Plugin type manager for all fieldgroup formatters.
+ */
+class FieldGroupFormatterPluginManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new FieldGroupFormatterPluginManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/field_group/FieldGroupFormatter', $namespaces, $module_handler, 'Drupal\field_group\FieldGroupFormatterInterface', 'Drupal\field_group\Annotation\FieldGroupFormatter');
+
+    $this->alterInfo('field_group_formatter_info');
+    $this->setCacheBackend($cache_backend, 'field_group_formatter_info');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createInstance($plugin_id, array $configuration = array()) {
+    $plugin_definition = $this->getDefinition($plugin_id);
+    $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
+
+    // If the plugin provides a factory method, pass the container to it.
+    if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
+      return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
+    }
+
+    return new $plugin_class($plugin_id, $plugin_definition, $configuration['group'], $configuration['settings'], $configuration['label']);
+  }
+
+  /**
+   * Overrides PluginManagerBase::getInstance().
+   *
+   * @param array $options
+   *   An array with the following key/value pairs:
+   *   - format_type: The current format type.
+   *   - group: The current group.
+   *   - prepare: (bool, optional) Whether default values should get merged in
+   *     the 'configuration' array. Defaults to TRUE.
+   *   - configuration: (array) the configuration for the formatter. The
+   *     following key value pairs are allowed, and are all optional if
+   *     'prepare' is TRUE:
+   *     - label: (string) Position of the label. The default 'field' theme
+   *       implementation supports the values 'inline', 'above' and 'hidden'.
+   *       Defaults to 'above'.
+   *     - settings: (array) Settings specific to the formatter. Each setting
+   *       defaults to the default value specified in the formatter definition.
+   *
+   * @return \Drupal\field_group\FieldGroupFormatterInterface|null
+   *   A formatter object or NULL when plugin is not found.
+   */
+  public function getInstance(array $options) {
+    $configuration = $options['configuration'];
+    $format_type = $options['format_type'];
+    $context = $options['group']->context;
+
+    // Fill in default configuration if needed.
+    if (!isset($options['prepare']) || $options['prepare'] == TRUE) {
+      $configuration = $this->prepareConfiguration($format_type, $context, $configuration);
+    }
+
+    $plugin_id = $format_type;
+
+    // Validate if plugin exists and it's allowed for current context.
+    $definition = $this->getDefinition($format_type, FALSE);
+    if (!isset($definition['class']) || !in_array($context, $definition['supported_contexts'])) {
+      return NULL;
+    }
+
+    $configuration += array(
+      'group' => $options['group'],
+    );
+
+    return $this->createInstance($plugin_id, $configuration);
+  }
+
+  /**
+   * Merges default values for formatter configuration.
+   *
+   * @param string $format_type
+   *   The format type
+   * @param string $context
+   *   The context to prepare configuration for.
+   * @param array $properties
+   *   An array of formatter configuration.
+   *
+   * @return array
+   *   The display properties with defaults added.
+   */
+  public function prepareConfiguration($format_type, $context, array $configuration) {
+    // Fill in defaults for missing properties.
+    $configuration += array(
+      'label' => '',
+      'settings' => array(),
+    );
+
+    // Fill in default settings values for the formatter.
+    $configuration['settings'] += $this->getDefaultSettings($format_type, $context);
+
+    return $configuration;
+  }
+
+  /**
+   * Returns the default settings of a field_group formatter.
+   *
+   * @param string $type
+   *   A formatter type name.
+   * @param string $context
+   *   The context to get default values for.
+   *
+   * @return array
+   *   The formatter type's default settings, as provided by the plugin
+   *   definition, or an empty array if type or settings are undefined.
+   */
+  public function getDefaultSettings($type, $context) {
+    $plugin_definition = $this->getDefinition($type, FALSE);
+    if (!empty($plugin_definition['class'])) {
+      $plugin_class = DefaultFactory::getPluginClass($type, $plugin_definition);
+      return $plugin_class::defaultContextSettings($context);
+    }
+    return array();
+  }
+
+}

+ 97 - 0
sites/all/modules/contrib/fields/field_group/src/FieldgroupUi.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\field_group;
+use Drupal\Core\Url;
+use Drupal\field_ui\FieldUI;
+
+/**
+ * Static methods for fieldgroup UI.
+ */
+class FieldgroupUi {
+
+  /**
+   * Get the field ui route that should be used for given arguments.
+   * @param stdClass $group
+   *   The group to get the field ui route for.
+   *
+   * @return \Drupal\Core\Url
+   *   A URL object.
+   */
+  public static function getFieldUiRoute($group) {
+
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($group->entity_type);
+    if ($entity_type->get('field_ui_base_route')) {
+
+      $context_route_name = "";
+      $mode_route_name = "default";
+      $route_parameters = FieldUI::getRouteBundleParameter($entity_type, $group->bundle);
+
+      // Get correct route name based on context and mode.
+      if ($group->context == 'form') {
+        $context_route_name = 'entity_form_display';
+
+        if ($group->mode != 'default') {
+          $mode_route_name = 'form_mode';
+          $route_parameters['form_mode_name'] = $group->mode;
+        }
+
+      }
+      else {
+        $context_route_name = 'entity_view_display';
+
+        if ($group->mode != 'default') {
+          $mode_route_name = 'view_mode';
+          $route_parameters['view_mode_name'] = $group->mode;
+        }
+
+      }
+
+      return new Url("entity.{$context_route_name}.{$group->entity_type}.{$mode_route_name}", $route_parameters);
+    }
+  }
+
+  /**
+   * Get the field group delete route for a given group.
+   * @param \stdClass $group
+   *
+   * @return \Drupal\Core\Url
+   *   A URL object.
+   */
+  public static function getDeleteRoute($group) {
+
+    $entity_type_id = $group->entity_type;
+    $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+    if ($entity_type->get('field_ui_base_route')) {
+
+      $mode_route_name = '';
+      $route_parameters = FieldUI::getRouteBundleParameter($entity_type, $group->bundle);
+      $route_parameters['field_group_name'] = $group->group_name;
+
+      // Get correct route name based on context and mode.
+      if ($group->context == 'form') {
+
+        $context_route_name = 'form_display';
+        if ($group->mode != 'default') {
+          $mode_route_name = '.form_mode';
+          $route_parameters['form_mode_name'] = $group->mode;
+        }
+
+      }
+      else {
+
+        $context_route_name = 'display';
+        if ($group->mode != 'default') {
+          $mode_route_name = '.view_mode';
+          $route_parameters['view_mode_name'] = $group->mode;
+        }
+
+      }
+
+      return new Url('field_ui.field_group_delete_' . $entity_type_id . '.' . $context_route_name . $mode_route_name, $route_parameters);
+    }
+
+    throw new \InvalidArgumentException('The given group is not a valid.');
+
+  }
+
+}

+ 276 - 0
sites/all/modules/contrib/fields/field_group/src/Form/FieldGroupAddForm.php

@@ -0,0 +1,276 @@
+<?php
+
+namespace Drupal\field_group\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\field_group\FieldgroupUi;
+
+/**
+ * Provides a form for adding a fieldgroup to a bundle.
+ */
+class FieldGroupAddForm extends FormBase {
+
+  /**
+   * The prefix for groups.
+   *
+   * @var string
+   */
+  const GROUP_PREFIX = 'group_';
+
+  /**
+   * The name of the entity type.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The entity bundle.
+   *
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * The context for the group.
+   *
+   * @var string
+   */
+  protected $context;
+
+  /**
+   * The mode for the group.
+   *
+   * @var string
+   */
+  protected $mode;
+
+  /**
+   * Current step of the form.
+   *
+   * @var string
+   */
+  protected $currentStep;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'field_group_add_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, $bundle = NULL, $context = NULL) {
+
+    $this->entityTypeId = $entity_type_id;
+    $this->bundle = $bundle;
+    $this->context = $context;
+
+    if ($context == 'form') {
+      $this->mode = \Drupal::request()->get('form_mode_name');
+    }
+    else {
+      $this->mode = \Drupal::request()->get('view_mode_name');
+    }
+
+    if (empty($this->mode)) {
+      $this->mode = 'default';
+    }
+
+    if (!$form_state->get('step')) {
+      $form_state->set('step', 'formatter');
+    }
+    $this->currentStep = $form_state->get('step');
+
+    if ($this->currentStep == 'formatter') {
+      $this->buildFormatterSelectionForm($form, $form_state);
+    }
+    else {
+      $this->buildConfigurationForm($form, $form_state);
+    }
+
+    return $form;
+  }
+
+  /**
+   * Build the formatter selection step.
+   */
+  function buildFormatterSelectionForm(array &$form, FormStateInterface $form_state) {
+
+    // Gather group formatters.
+    $formatter_options = \field_group_field_formatter_options($this->context);
+    $form['add'] = array(
+      '#type' => 'container',
+      '#attributes' => array('class' => array('form--inline', 'clearfix')),
+    );
+
+    $form['add']['group_formatter'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Add a new group'),
+      '#options' => $formatter_options,
+      '#empty_option' => $this->t('- Select a group type -'),
+      '#required' => TRUE,
+    );
+
+    // Field label and field_name.
+    $form['new_group_wrapper'] = array(
+      '#type' => 'container',
+      '#states' => array(
+        '!visible' => array(
+          ':input[name="group_formatter"]' => array('value' => ''),
+        ),
+      ),
+    );
+    $form['new_group_wrapper']['label'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Label'),
+      '#size' => 15,
+      '#required' => TRUE,
+    );
+
+    $form['new_group_wrapper']['group_name'] = array(
+      '#type' => 'machine_name',
+      '#size' => 15,
+      // This field should stay LTR even for RTL languages.
+      '#field_prefix' => '<span dir="ltr">' . self::GROUP_PREFIX,
+      '#field_suffix' => '</span>&lrm;',
+      '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'),
+      '#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen(self::GROUP_PREFIX),
+      '#machine_name' => array(
+        'source' => array('new_group_wrapper', 'label'),
+        'exists' => array($this, 'groupNameExists'),
+      ),
+      '#required' => TRUE,
+    );
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Save and continue'),
+      '#button_type' => 'primary',
+      '#validate' => array(
+        array($this, 'validateFormatterSelection')
+      ),
+    );
+
+    $form['#attached']['library'][] = 'field_ui/drupal.field_ui';
+  }
+
+  /**
+   * Build the formatter configuration form.
+   */
+  function buildConfigurationForm(array &$form, FormStateInterface $form_state) {
+
+    $group = new \stdClass();
+    $group->context = $this->context;
+    $group->entity_type = $this->entityTypeId;
+    $group->bundle = $this->bundle;
+    $group->mode = $this->mode;
+
+    $manager = \Drupal::service('plugin.manager.field_group.formatters');
+    $plugin = $manager->getInstance(array(
+      'format_type' => $form_state->getValue('group_formatter'),
+      'configuration' => [
+        'label' => $form_state->getValue('label'),
+      ],
+      'group' => $group,
+    ));
+
+    $form['format_settings'] = array(
+      '#type' => 'container',
+      '#tree' => TRUE,
+    );
+
+    $form['format_settings'] += $plugin->settingsForm();
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Create group'),
+      '#button_type' => 'primary',
+    );
+
+  }
+
+  /**
+   * Validate the formatter selection step.
+   */
+  public function validateFormatterSelection(array &$form, FormStateInterface $form_state) {
+
+    $group_name = self::GROUP_PREFIX . $form_state->getValue('group_name');
+
+    // Add the prefix.
+    $form_state->setValueForElement($form['new_group_wrapper']['group_name'], $group_name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+
+    if ($form_state->get('step') == 'formatter') {
+      $form_state->set('step', 'configuration');
+      $form_state->set('group_label', $form_state->getValue('label'));
+      $form_state->set('group_name', $form_state->getValue('group_name'));
+      $form_state->set('group_formatter', $form_state->getValue('group_formatter'));
+      $form_state->setRebuild();
+    }
+    else {
+
+      $new_group = (object) array(
+        'group_name' => $form_state->get('group_name'),
+        'entity_type' => $this->entityTypeId,
+        'bundle' => $this->bundle,
+        'mode' => $this->mode,
+        'context' => $this->context,
+        'children' =>[],
+        'parent_name' => '',
+        'weight' => 20,
+        'format_type' => $form_state->get('group_formatter'),
+      );
+
+      $new_group->format_settings = $form_state->getValue('format_settings');
+      $new_group->label = $new_group->format_settings['label'];
+      unset($new_group->format_settings['label']);
+      $new_group->format_settings += _field_group_get_default_formatter_settings($form_state->get('group_formatter'), $this->context);
+
+      field_group_group_save($new_group);
+
+      // Store new group information for any additional submit handlers.
+      $groups_added = $form_state->get('groups_added');
+      $groups_added['_add_new_group'] = $new_group->group_name;
+      drupal_set_message(t('New group %label successfully created.', array('%label' => $new_group->label)));
+
+      $form_state->setRedirectUrl(FieldgroupUi::getFieldUiRoute($new_group));
+      \Drupal::cache()->invalidate('field_groups');
+
+    }
+
+  }
+
+  /**
+   * Checks if a group machine name is taken.
+   *
+   * @param string $value
+   *   The machine name, not prefixed.
+   * @param array $element
+   *   An array containing the structure of the 'group_name' element.
+   * @param FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return bool
+   *   Whether or not the group machine name is taken.
+   */
+  public function groupNameExists($value, $element, FormStateInterface $form_state) {
+
+    // Add the prefix.
+    $group_name = self::GROUP_PREFIX . $value;
+
+    return field_group_exists($group_name, $this->entityTypeId, $this->bundle, $this->context, $this->mode);
+  }
+
+}

+ 82 - 0
sites/all/modules/contrib/fields/field_group/src/Form/FieldGroupDeleteForm.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\field_group\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\field_group\FieldgroupUi;
+
+/**
+ * Provides a form for removing a fieldgroup from a bundle.
+ */
+class FieldGroupDeleteForm extends ConfirmFormBase {
+
+  /**
+   * The fieldgroup to delete.
+   *
+   * @var stdClass
+   */
+  protected $fieldGroup;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'field_group_delete_form';
+  }
+
+  public function buildForm(array $form, FormStateInterface $form_state, $field_group_name = NULL, $entity_type_id = NULL, $bundle = NULL, $context = NULL) {
+
+    if ($context == 'form') {
+      $mode = $this->getRequest()->attributes->get('form_mode_name');
+    }
+    else {
+      $mode = $this->getRequest()->attributes->get('view_mode_name');
+    }
+
+    if (empty($mode)) {
+      $mode = 'default';
+    }
+
+    $this->fieldGroup = field_group_load_field_group($field_group_name, $entity_type_id, $bundle, $context, $mode);
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $bundles = \Drupal::service('entity_type.bundle.info')->getAllBundleInfo();
+    $bundle_label = $bundles[$this->fieldGroup->entity_type][$this->fieldGroup->bundle]['label'];
+
+    field_group_group_delete($this->fieldGroup);
+
+    drupal_set_message(t('The group %group has been deleted from the %type content type.', array('%group' => t($this->fieldGroup->label), '%type' => $bundle_label)));
+
+    // Redirect.
+    $form_state->setRedirectUrl($this->getCancelUrl());
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete the group %group?', array('%group' => t($this->fieldGroup->label)));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return FieldgroupUi::getFieldUiRoute($this->fieldGroup);
+  }
+
+}

+ 106 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/Derivative/FieldGroupLocalAction.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\field_group\Plugin\Derivative;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+
+/**
+ * Provides local action definitions for all entity bundles.
+ */
+class FieldGroupLocalAction extends DeriverBase implements ContainerDeriverInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The route provider.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a FieldUiLocalAction object.
+   *
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider to load routes by name.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(RouteProviderInterface $route_provider, EntityTypeManagerInterface $entity_type_manager) {
+    $this->routeProvider = $route_provider;
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('router.route_provider'),
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $this->derivatives = array();
+
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      if ($entity_type->get('field_ui_base_route')) {
+
+        $default_options = [
+          'title' => $this->t('Add group'),
+        ];
+
+        $this->derivatives['field_group_add_' . $entity_type_id . '_form_display'] = [
+          'route_name' => "field_ui.field_group_add_$entity_type_id.form_display",
+          'appears_on' => [
+            "entity.entity_form_display.{$entity_type_id}.default",
+          ],
+        ] + $default_options;
+
+        $this->derivatives['field_group_add_' . $entity_type_id . '_form_display_form_mode'] = [
+          'route_name' => "field_ui.field_group_add_$entity_type_id.form_display.form_mode",
+          'appears_on' => [
+            "entity.entity_form_display.{$entity_type_id}.form_mode",
+          ],
+        ] + $default_options;
+
+        $this->derivatives['field_group_add_' . $entity_type_id . '_display'] = [
+          'route_name' => "field_ui.field_group_add_$entity_type_id.display",
+          'appears_on' => [
+            "entity.entity_view_display.{$entity_type_id}.default",
+          ],
+        ] + $default_options;
+
+        $this->derivatives['field_group_add_' . $entity_type_id . '_display_view_mode'] = [
+          'route_name' => "field_ui.field_group_add_$entity_type_id.display.view_mode",
+          'appears_on' => [
+            "entity.entity_view_display.{$entity_type_id}.view_mode",
+          ],
+        ] + $default_options;
+      }
+    }
+
+    foreach ($this->derivatives as &$entry) {
+      $entry += $base_plugin_definition;
+    }
+
+    return $this->derivatives;
+  }
+
+}

+ 89 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Accordion.php

@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormState;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'accordion' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "accordion",
+ *   label = @Translation("Accordion"),
+ *   description = @Translation("This fieldgroup renders child groups as jQuery accordion."),
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   }
+ * )
+ */
+class Accordion extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $form_state = new FormState();
+
+    $element += array(
+      '#type' => 'field_group_accordion',
+      '#effect' => $this->getSetting('effect'),
+    );
+
+    if ($this->getSetting('id')) {
+      $element['#id'] = Html::getId($this->getSetting('id'));
+    }
+
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      $element += array('#attributes' => array('class' => $classes));
+    }
+
+    \Drupal\field_group\Element\Accordion::processAccordion($element, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['effect'] = array(
+      '#title' => $this->t('Effect'),
+      '#type' => 'select',
+      '#options' => array('none' => $this->t('None'), 'bounceslide' => $this->t('Bounce slide')),
+      '#default_value' => $this->getSetting('effect'),
+      '#weight' => 2,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = array();
+    $summary[] = $this->t('Effect : @effect',
+      array('@effect' => $this->getSetting('effect'))
+    );
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    return array(
+      'effect' => 'none',
+    ) + parent::defaultSettings($context);
+  }
+
+}

+ 119 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php

@@ -0,0 +1,119 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal;
+use Drupal\Component\Utility\Html;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'accordion_item' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "accordion_item",
+ *   label = @Translation("Accordion Item"),
+ *   description = @Translation("This fieldgroup renders the content in a div, part of accordion group."),
+ *   format_types = {
+ *     "open",
+ *     "closed",
+ *   },
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   },
+ * )
+ */
+class AccordionItem extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $element += array(
+      '#type' => 'field_group_accordion_item',
+      '#open' => $this->getSetting('formatter') == 'open' ? TRUE : FALSE,
+      '#description' => $this->getSetting('description'),
+      '#title' => Drupal::translation()->translate($this->getLabel()),
+    );
+
+    if ($this->getSetting('id')) {
+      $element['#id'] = Html::getId($this->getSetting('id'));
+    }
+
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      $element += array('#attributes' => array('class' => $classes));
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $element['#attached']['library'][] = 'field_group/formatter.details';
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['formatter'] = array(
+      '#title' => $this->t('Default state'),
+      '#type' => 'select',
+      '#options' => array_combine($this->pluginDefinition['format_types'], $this->pluginDefinition['format_types']),
+      '#default_value' => $this->getSetting('formatter'),
+      '#weight' => -4,
+    );
+
+    if ($this->context == 'form') {
+      $form['required_fields'] = array(
+        '#type' => 'checkbox',
+        '#title' => $this->t('Mark group as required if it contains required fields.'),
+        '#default_value' => $this->getSetting('required_fields'),
+        '#weight' => 2,
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = array();
+
+    if ($this->getSetting('required_fields')) {
+      $summary[] = $this->t('Mark as required');
+    }
+
+    if ($this->getSetting('description')) {
+      $summary[] = $this->t('Description : @description',
+        array('@description' => $this->getSetting('description'))
+      );
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    $defaults = array(
+      'formatter' => 'closed',
+      'description' => '',
+    ) + parent::defaultSettings($context);
+
+    if ($context == 'form') {
+      $defaults['required_fields'] = 1;
+    }
+
+    return $defaults;
+  }
+
+}

+ 118 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Details.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Details element.
+ *
+ * @FieldGroupFormatter(
+ *   id = "details",
+ *   label = @Translation("Details"),
+ *   description = @Translation("Add a details element"),
+ *   supported_contexts = {
+ *     "form",
+ *     "view"
+ *   }
+ * )
+ */
+class Details extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $element += array(
+      '#type' => 'details',
+      '#title' => Html::escape($this->t($this->getLabel())),
+      '#open' => $this->getSetting('open')
+    );
+
+    if ($this->getSetting('id')) {
+      $element['#id'] = Html::getId($this->getSetting('id'));
+    }
+
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      $element += array(
+        '#attributes' => array('class' => $classes),
+      );
+    }
+
+    if ($this->getSetting('description')) {
+      $element += array(
+        '#description' => $this->getSetting('description'),
+      );
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $element['#attached']['library'][] = 'field_group/formatter.details';
+      $element['#attached']['library'][] = 'field_group/core';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+    $form = parent::settingsForm();
+
+    $form['open'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Display element open by default.'),
+      '#default_value' => $this->getSetting('open'),
+    );
+
+    if ($this->context == 'form') {
+      $form['required_fields'] = array(
+        '#type' => 'checkbox',
+        '#title' => $this->t('Mark group as required if it contains required fields.'),
+        '#default_value' => $this->getSetting('required_fields'),
+        '#weight' => 2,
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = array();
+    if ($this->getSetting('open')) {
+      $summary[] = $this->t('Default state open');
+    }
+    else {
+      $summary[] = $this->t('Default state closed');
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $summary[] = $this->t('Mark as required');
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    $defaults = array(
+      'open' => FALSE,
+      'required_fields' => $context == 'form',
+    ) + parent::defaultSettings($context);
+
+    if ($context == 'form') {
+      $defaults['required_fields'] = 1;
+    }
+
+    return $defaults;
+  }
+
+}

+ 123 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'fieldset' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "fieldset",
+ *   label = @Translation("Fieldset"),
+ *   description = @Translation("This fieldgroup renders the inner content in a fieldset with the title as legend."),
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   }
+ * )
+ */
+class Fieldset extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+
+    $element += array(
+      '#type' => 'fieldset',
+      '#title' => Html::escape($this->t($this->getLabel())),
+      '#pre_render' => array(),
+      '#attributes' => array(),
+    );
+
+    if ($this->getSetting('description')) {
+      $element += array(
+        '#description' => $this->getSetting('description'),
+      );
+
+      // When a fieldset has a description, an id is required.
+      if (!$this->getSetting('id')) {
+        $element['#id'] = Html::getId($this->group->group_name);
+      }
+
+    }
+
+    if ($this->getSetting('id')) {
+      $element['#id'] = Html::getId($this->getSetting('id'));
+    }
+
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      $element['#attributes'] += array('class' => $classes);
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $element['#attached']['library'][] = 'field_group/formatter.fieldset';
+      $element['#attached']['library'][] = 'field_group/core';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['description'] = array(
+      '#title' => $this->t('Description'),
+      '#type' => 'textarea',
+      '#default_value' => $this->getSetting('description'),
+      '#weight' => -4,
+    );
+
+    if ($this->context == 'form') {
+      $form['required_fields'] = array(
+        '#type' => 'checkbox',
+        '#title' => $this->t('Mark group as required if it contains required fields.'),
+        '#default_value' => $this->getSetting('required_fields'),
+        '#weight' => 2,
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = parent::settingsSummary();
+
+    if ($this->getSetting('required_fields')) {
+      $summary[] = $this->t('Mark as required');
+    }
+
+    if ($this->getSetting('description')) {
+      $summary[] = $this->t('Description : @description',
+        array('@description' => $this->getSetting('description'))
+      );
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    $defaults = array(
+      'description' => '',
+    ) + parent::defaultSettings($context);
+
+    if ($context == 'form') {
+      $defaults['required_fields'] = 1;
+    }
+
+    return $defaults;
+  }
+
+}

+ 220 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php

@@ -0,0 +1,220 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Template\Attribute;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'html_element' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "html_element",
+ *   label = @Translation("HTML element"),
+ *   description = @Translation("This fieldgroup renders the inner content in a HTML element with classes and attributes."),
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   }
+ * )
+ */
+class HtmlElement extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $element_attributes = new Attribute();
+
+    if ($this->getSetting('attributes')) {
+
+      // This regex split the attributes string so that we can pass that
+      // later to drupal_attributes().
+      preg_match_all('/([^\s=]+)="([^"]+)"/', $this->getSetting('attributes'), $matches);
+
+      // Put the attribute and the value together.
+      foreach ($matches[1] as $key => $attribute) {
+        $element_attributes[$attribute] = $matches[2][$key];
+      }
+
+    }
+
+    // Add the id to the attributes array.
+    if ($this->getSetting('id')) {
+      $element_attributes['id'] = Html::getId($this->getSetting('id'));
+    }
+
+    // Add the classes to the attributes array.
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      if (!isset($element_attributes['class'])) {
+        $element_attributes['class'] = array();
+      }
+      // If user also entered class in the attributes textfield, force it to an array.
+      else {
+        $element_attributes['class'] = array($element_attributes['class']);
+      }
+      $element_attributes['class'] = array_merge($classes, $element_attributes['class']->value());
+    }
+
+    $element['#effect'] = $this->getSetting('effect');
+    $element['#speed'] = $this->getSetting('speed');
+    $element['#type'] = 'field_group_html_element';
+    $element['#wrapper_element'] = $this->getSetting('element');
+    $element['#attributes'] = $element_attributes;
+    if ($this->getSetting('show_label')) {
+      $element['#title_element'] = $this->getSetting('label_element');
+      $element['#title'] = Html::escape($this->t($this->getLabel()));
+    }
+
+    $form_state = new FormState();
+    \Drupal\field_group\Element\HtmlElement::processHtmlElement($element, $form_state);
+
+    if ($this->getSetting('required_fields')) {
+      $element['#attributes']['class'][] = 'field-group-html-element';
+      $element['#attached']['library'][] = 'field_group/formatter.html_element';
+      $element['#attached']['library'][] = 'field_group/core';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['element'] = array(
+      '#title' => $this->t('Element'),
+      '#type' => 'textfield',
+      '#default_value' => $this->getSetting('element'),
+      '#description' => $this->t('E.g. div, section, aside etc.'),
+      '#weight' => 1,
+    );
+
+    $form['show_label'] = array(
+      '#title' => $this->t('Show label'),
+      '#type' => 'select',
+      '#options' => array(0 => $this->t('No'), 1 => $this->t('Yes')),
+      '#default_value' => $this->getSetting('show_label'),
+      '#weight' => 2,
+      '#attributes' => array(
+        'data-fieldgroup-selector' => 'show_label'
+      ),
+    );
+
+    $form['label_element'] = array(
+      '#title' => $this->t('Label element'),
+      '#type' => 'textfield',
+      '#default_value' => $this->getSetting('label_element'),
+      '#weight' => 3,
+      '#states' => array(
+        'visible' => array(
+          ':input[data-fieldgroup-selector="show_label"]' => array('value' => 1),
+        ),
+      ),
+    );
+
+    if ($this->context == 'form') {
+      $form['required_fields'] = array(
+        '#title' => $this->t('Mark group as required if it contains required fields.'),
+        '#type' => 'checkbox',
+        '#default_value' => $this->getSetting('required_fields'),
+        '#weight' => 4,
+      );
+    }
+
+    $form['attributes'] = array(
+      '#title' => $this->t('Attributes'),
+      '#type' => 'textfield',
+      '#default_value' => $this->getSetting('attributes'),
+      '#description' => $this->t('E.g. name="anchor"'),
+      '#weight' => 5,
+    );
+
+    $form['effect'] = array(
+      '#title' => $this->t('Effect'),
+      '#type' => 'select',
+      '#options' => array(
+        'none' => $this->t('None'),
+        'collapsible' => $this->t('Collapsible'),
+        'blind' => $this->t('Blind')
+      ),
+      '#default_value' => $this->getSetting('effect'),
+      '#weight' => 6,
+      '#attributes' => array(
+        'data-fieldgroup-selector' => 'effect'
+      ),
+    );
+
+    $form['speed'] = array(
+      '#title' => $this->t('Speed'),
+      '#type' => 'select',
+      '#options' => array('slow' => $this->t('Slow'), 'fast' => $this->t('Fast')),
+      '#default_value' => $this->getSetting('speed'),
+      '#weight' => 7,
+      '#states' => array(
+        '!visible' => array(
+          ':input[data-fieldgroup-selector="effect"]' => array('value' => 'none'),
+        ),
+      ),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = parent::settingsSummary();
+    $summary[] = $this->t('Element: @element',
+      array('@element' => $this->getSetting('element'))
+    );
+
+    if ($this->getSetting('show_label')) {
+      $summary[] = $this->t('Label element: @element',
+        array('@element' => $this->getSetting('label_element'))
+      );
+    }
+
+    if ($this->getSetting('attributes')) {
+      $summary[] = $this->t('Attributes: @attributes',
+        array('@attributes' => $this->getSetting('attributes'))
+      );
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $summary[] = $this->t('Mark as required');
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    $defaults = array(
+      'element' => 'div',
+      'show_label' => 0,
+      'label_element' => 'h3',
+      'effect' => 'none',
+      'speed' => 'fast',
+      'attributes' => '',
+    ) + parent::defaultSettings($context);
+
+    if ($context == 'form') {
+      $defaults['required_fields'] = 1;
+    }
+
+    return $defaults;
+
+  }
+
+}

+ 123 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Tab.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'tab' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "tab",
+ *   label = @Translation("Tab"),
+ *   description = @Translation("This fieldgroup renders the content as a tab."),
+ *   format_types = {
+ *     "open",
+ *     "closed",
+ *   },
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   },
+ * )
+ */
+class Tab extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $add = array(
+      '#type' => 'details',
+      '#title' => Html::escape($this->t($this->getLabel())),
+      '#description' => $this->getSetting('description'),
+    );
+
+    if ($this->getSetting('id')) {
+      $add['#id'] = Html::getId($this->getSetting('id'));
+    }
+    else {
+      $add['#id'] = Html::getId('edit-' . $this->group->group_name);
+    }
+
+    $classes = $this->getClasses();
+    if (!empty($classes)) {
+      $element += array(
+        '#attributes' => array('class' => $classes),
+      );
+    }
+
+    if ($this->getSetting('formatter') == 'open') {
+      $element['#open'] = TRUE;
+    }
+
+    // Front-end and back-end on configuration will lead
+    // to vertical tabs nested in a separate vertical group.
+    if (!empty($this->group->parent_name)) {
+      $add['#group'] = $this->group->parent_name;
+      $add['#parents'] = array($add['#group']);
+    }
+
+    if ($this->getSetting('required_fields')) {
+      $element['#attached']['library'][] = 'field_group/formatter.tabs';
+      $element['#attached']['library'][] = 'field_group/core';
+    }
+
+    $element += $add;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['formatter'] = array(
+      '#title' => $this->t('Default state'),
+      '#type' => 'select',
+      '#options' => array_combine($this->pluginDefinition['format_types'], $this->pluginDefinition['format_types']),
+      '#default_value' => $this->getSetting('formatter'),
+      '#weight' => -4,
+    );
+
+    $form['description'] = array(
+      '#title' => $this->t('Description'),
+      '#type' => 'textarea',
+      '#default_value' => $this->getSetting('description'),
+      '#weight' => -4,
+    );
+
+    if ($this->context == 'form') {
+      $form['required_fields'] = array(
+        '#type' => 'checkbox',
+        '#title' => $this->t('Mark group as required if it contains required fields.'),
+        '#default_value' => $this->getSetting('required_fields'),
+        '#weight' => 2,
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    $defaults = array(
+      'formatter' => 'closed',
+      'description' => '',
+    ) + parent::defaultSettings($context);
+
+    if ($context == 'form') {
+      $defaults['required_fields'] = 1;
+    }
+
+    return $defaults;
+  }
+
+}

+ 139 - 0
sites/all/modules/contrib/fields/field_group/src/Plugin/field_group/FieldGroupFormatter/Tabs.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace Drupal\field_group\Plugin\field_group\FieldGroupFormatter;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Element\VerticalTabs;
+use Drupal\field_group\Element\HorizontalTabs;
+use Drupal\field_group\FieldGroupFormatterBase;
+
+/**
+ * Plugin implementation of the 'horizontal_tabs' formatter.
+ *
+ * @FieldGroupFormatter(
+ *   id = "tabs",
+ *   label = @Translation("Tabs"),
+ *   description = @Translation("This fieldgroup renders child groups in its own tabs wrapper."),
+ *   supported_contexts = {
+ *     "form",
+ *     "view",
+ *   }
+ * )
+ */
+class Tabs extends FieldGroupFormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$element, $rendering_object) {
+    parent::preRender($element, $rendering_object);
+
+    $element += array(
+      '#prefix' => '<div class=" ' . implode(' ' , $this->getClasses()) . '">',
+      '#suffix' => '</div>',
+      '#tree' => TRUE,
+      '#parents' => array($this->group->group_name),
+      '#default_tab' => '',
+    );
+
+    if ($this->getSetting('id')) {
+      $element['#id'] = Html::getId($this->getSetting('id'));
+    }
+
+    // By default tabs don't have titles but you can override it in the theme.
+    if ($this->getLabel()) {
+      $element['#title'] = Html::escape($this->getLabel());
+    }
+
+    $form_state = new FormState();
+
+    if ($this->getSetting('direction') == 'vertical') {
+
+      $element += array(
+        '#type' => 'vertical_tabs',
+        '#theme_wrappers' => array('vertical_tabs'),
+      );
+      $complete_form = array();
+      $element = VerticalTabs::processVerticalTabs($element, $form_state, $complete_form);
+    }
+    else {
+      $element += array(
+        '#type' => 'horizontal_tabs',
+        '#theme_wrappers' => array('horizontal_tabs'),
+      );
+      $on_form = $this->context == 'form';
+      $element = HorizontalTabs::processHorizontalTabs($element, $form_state, $on_form);
+    }
+
+    // Make sure the group has 1 child. This is needed to succeed at form_pre_render_vertical_tabs().
+    // Skipping this would force us to move all child groups to this array, making it an un-nestable.
+    $element['group']['#groups'][$this->group->group_name] = array(0 => array());
+    $element['group']['#groups'][$this->group->group_name]['#group_exists'] = TRUE;
+
+    // Search for a tab that was marked as open. First one wins.
+    foreach (Element::children($element) as $tab_name) {
+      if (!empty($element[$tab_name]['#open'])) {
+        $element[$this->group->group_name . '__active_tab']['#default_value'] = $tab_name;
+        break;
+      }
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm() {
+
+    $form = parent::settingsForm();
+
+    $form['direction'] = array(
+      '#title' => $this->t('Direction'),
+      '#type' => 'select',
+      '#options' => array(
+        'vertical' => $this->t('Vertical'),
+        'horizontal' => $this->t('Horizontal'),
+      ),
+      '#default_value' => $this->getSetting('direction'),
+      '#weight' => 1,
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+
+    $summary = parent::settingsSummary();
+    $summary[] = $this->t('Direction: @direction',
+      array('@direction' => $this->getSetting('direction'))
+    );
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultContextSettings($context) {
+    return array(
+      'direction' => 'vertical',
+    ) + parent::defaultContextSettings($context);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClasses() {
+
+    $classes = parent::getClasses();
+    $classes[] = 'field-group-' . $this->group->format_type . '-wrapper';
+
+    return $classes;
+  }
+
+}

+ 32 - 0
sites/all/modules/contrib/fields/field_group/src/Routing/FieldGroupConverter.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\field_group\Routing;
+
+use Drupal\Core\ParamConverter\ParamConverterInterface;
+
+/**
+ * Parameter converter for upcasting fieldgroup config ids to fieldgroup object.
+ */
+class FieldGroupConverter implements ParamConverterInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($definition, $name, \Symfony\Component\Routing\Route $route) {
+    return isset($definition['type']) && $definition['type'] == 'field_group';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convert($value, $definition, $name, array $defaults) {
+    $identifiers = explode('.', $value);
+    if (count($identifiers) != 5) {
+      return;
+    }
+
+    return field_group_load_field_group($identifiers[4], $identifiers[0], $identifiers[1], $identifiers[2], $identifiers[3]);
+  }
+
+
+}

+ 161 - 0
sites/all/modules/contrib/fields/field_group/src/Routing/RouteSubscriber.php

@@ -0,0 +1,161 @@
+<?php
+
+namespace Drupal\field_group\Routing;
+
+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 Field group routes.
+ */
+class RouteSubscriber extends RouteSubscriberBase {
+
+  /**
+   * The entity type manager
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a RouteSubscriber object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function alterRoutes(RouteCollection $collection) {
+
+    // Create fieldgroup routes for every entity.
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      $defaults = array();
+      if ($route_name = $entity_type->get('field_ui_base_route')) {
+        // Try to get the route from the current collection.
+        if (!$entity_route = $collection->get($route_name)) {
+          continue;
+        }
+        $path = $entity_route->getPath();
+
+        $options = $entity_route->getOptions();
+
+        // Special parameter used to easily recognize all Field UI routes.
+        $options['_field_ui'] = TRUE;
+
+        if (($bundle_entity_type = $entity_type->getBundleEntityType()) && $bundle_entity_type !== 'bundle') {
+          $options['parameters'][$entity_type->getBundleEntityType()] = array(
+            'type' => 'entity:' . $entity_type->getBundleEntityType(),
+          );
+        }
+
+        $options['parameters']['field_group'] = array(
+          'type' => 'field_group',
+          'entity_type' => $entity_type->getBundleEntityType(),
+        );
+
+        $defaults_delete = [
+          'entity_type_id' => $entity_type_id,
+          '_form' => '\Drupal\field_group\Form\FieldGroupDeleteForm',
+        ];
+        $defaults_add = [
+          'entity_type_id' => $entity_type_id,
+          '_form' => '\Drupal\field_group\Form\FieldGroupAddForm',
+          '_title' => 'Add group',
+        ];
+
+        // If the entity type has no bundles and it doesn't use {bundle} in its
+        // admin path, use the entity type.
+        if (strpos($path, '{bundle}') === FALSE) {
+          $defaults_add['bundle'] = !$entity_type->hasKey('bundle') ? $entity_type_id : '';
+          $defaults_delete['bundle'] = $defaults_add['bundle'];
+        }
+
+        // Routes to delete field groups.
+        $route = new Route(
+          "$path/form-display/{field_group_name}/delete",
+          ['context' => 'form'] + $defaults_delete,
+          array('_permission' => 'administer ' . $entity_type_id . ' form display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_delete_$entity_type_id.form_display", $route);
+
+        $route = new Route(
+          "$path/form-display/{form_mode_name}/{field_group_name}/delete",
+          ['context' => 'form'] + $defaults_delete,
+          array('_permission' => 'administer ' . $entity_type_id . ' form display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_delete_$entity_type_id.form_display.form_mode", $route);
+
+        $route = new Route(
+          "$path/display/{field_group_name}/delete",
+          ['context' => 'view'] + $defaults_delete,
+          array('_permission' => 'administer ' . $entity_type_id . ' display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_delete_$entity_type_id.display", $route);
+
+        $route = new Route(
+          "$path/display/{view_mode_name}/{field_group_name}/delete",
+          ['context' => 'view'] + $defaults_delete,
+          array('_permission' => 'administer ' . $entity_type_id . ' display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_delete_$entity_type_id.display.view_mode", $route);
+
+        // Routes to add field groups.
+        $route = new Route(
+          "$path/form-display/add-group",
+          ['context' => 'form'] + $defaults_add,
+          array('_permission' => 'administer ' . $entity_type_id . ' form display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_add_$entity_type_id.form_display", $route);
+
+        $route = new Route(
+          "$path/form-display/{form_mode_name}/add-group",
+          ['context' => 'form'] + $defaults_add,
+          array('_permission' => 'administer ' . $entity_type_id . ' form display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_add_$entity_type_id.form_display.form_mode", $route);
+
+        $route = new Route(
+          "$path/display/add-group",
+          ['context' => 'view'] + $defaults_add,
+          array('_permission' => 'administer ' . $entity_type_id . ' display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_add_$entity_type_id.display", $route);
+
+        $route = new Route(
+          "$path/display/{view_mode_name}/add-group",
+          ['context' => 'view'] + $defaults_add,
+          array('_permission' => 'administer ' . $entity_type_id . ' display'),
+          $options
+        );
+        $collection->add("field_ui.field_group_add_$entity_type_id.display.view_mode", $route);
+
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    //$events = parent::getSubscribedEvents();
+    // Come after field_ui, config_translation.
+    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -210);
+    return $events;
+  }
+
+}

+ 39 - 0
sites/all/modules/contrib/fields/field_group/templates/field-group-accordion-item.html.twig

@@ -0,0 +1,39 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a fieldgroup accordion item.
+ *
+ * Available variables:
+ * - title: Title of the group.
+ * - children: The children of the group.
+ * - label_attributes: A list of HTML attributes for the label.
+ * - attributes: A list of HTML attributes for the group wrapper.
+ *
+ * @see template_preprocess_field_group_accordion()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+
+  set label_classes = [
+    'field-group-format-toggler',
+    'accordion-item',
+    open ? 'field-group-accordion-active',
+  ]
+
+%}
+
+{%
+  set classes = [
+    'field-group-format-wrapper',
+  ]
+%}
+
+<h3 {{ label_attributes.addClass(label_classes) }}>
+  <a href="#">{{ title }}</a>
+</h3>
+<div {{ attributes.addClass(classes) }}>
+  {% if description %}<div class="description"></div>{% endif %}
+  {{children}}
+</div>

+ 21 - 0
sites/all/modules/contrib/fields/field_group/templates/field-group-accordion.html.twig

@@ -0,0 +1,21 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a fieldgroup accordion item.
+ *
+ * Available variables:
+ * - children: The children of the group.
+ * - attributes: A list of HTML attributes for the group wrapper.
+ *
+ * @see template_preprocess_field_group_accordion()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'field-group-accordion-wrapper',
+  ]
+%}
+
+<div {{ attributes.addClass(classes) }}>{{ children }}</div>

+ 30 - 0
sites/all/modules/contrib/fields/field_group/templates/field-group-html-element.html.twig

@@ -0,0 +1,30 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a fieldgroup html element.
+ *
+ * Available variables:
+ * - title: Title of the group.
+ * - title_element: Element to wrap the title.
+ * - children: The children of the group.
+ * - wrapper_element: The html element to use
+ * - attributes: A list of HTML attributes for the group wrapper.
+ *
+ * @see template_preprocess_field_group_html_element()
+ *
+ * @ingroup themeable
+ */
+#}
+
+<{{ wrapper_element }} {{ attributes }}>
+  {% if title %}
+  <{{ title_element }}{% if collapsible %} class="field-group-toggler"{% endif %}>{{ title }}</{{ title_element }}>
+  {% endif %}
+  {% if collapsible %}
+  <div class="field-group-wrapper">
+  {% endif %}
+  {{children}}
+  {% if collapsible %}
+  </div>
+  {% endif %}
+</{{ wrapper_element }}>

+ 15 - 0
sites/all/modules/contrib/fields/field_group/templates/horizontal-tabs.html.twig

@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for horizontal tabs.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The rendered children.
+ *
+ * @see template_preprocess_horizontal_tabs()
+ *
+ * @ingroup themeable
+ */
+#}
+<div data-horizontal-tabs-panes{{ attributes }}>{{ children }}</div>

+ 94 - 0
sites/all/modules/contrib/fields/field_group/templates/theme.inc

@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Preprocessors for fieldgroup elements.
+ */
+
+/**
+ * Prepares variables for horizontal tabs templates.
+ *
+ * Default template: horizontal-tabs.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the details element. Properties used: #children.
+ *
+ */
+function template_preprocess_horizontal_tabs(&$variables) {
+  $element = $variables['element'];
+  $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
+}
+
+/**
+ * Prepares variables for fieldgroup accordion templates.
+ *
+ * Default template: field-group-accordion.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the accordion element. Properties used: #children.
+ *
+ */
+function template_preprocess_field_group_accordion(&$variables) {
+  $element = $variables['element'];
+  $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
+}
+
+/**
+ * Prepares variables for fieldgroup accordion item templates.
+ *
+ * Default template: field-group-accordion-item.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the accordion item element.
+ *
+ */
+function template_preprocess_field_group_accordion_item(&$variables) {
+
+  $element = $variables['element'];
+
+  if (!empty($element['#title'])) {
+    $variables['title'] = $element['#title'];
+  }
+
+  if (!empty($element['#description'])) {
+    $variables['description'] = $element['#description'];
+  }
+
+  $variables['open'] = $element['#open'];
+  $variables['label_attributes'] = new \Drupal\Core\Template\Attribute();
+  $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
+
+}
+
+/**
+ * Prepares variables for fieldgroup html element templates.
+ *
+ * Default template: field-group-html-element.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the html element.
+ *
+ */
+function template_preprocess_field_group_html_element(&$variables) {
+
+  $element = $variables['element'];
+
+  if (!empty($element['#title']) && !empty($element['#title_element'])) {
+    $variables['title_element'] = $element['#title_element'];
+    $variables['title'] = $element['#title'];
+  }
+
+  $variables['collapsible'] = (!empty($element['#effect']) && $element['#effect'] !== 'none');
+  $variables['wrapper_element'] = $element['#wrapper_element'];
+  $variables['attributes'] = $element['#attributes'];
+  $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
+
+}

+ 11 - 0
sites/all/modules/contrib/fields/field_group/tests/modules/field_group_test/field_group_test.info.yml

@@ -0,0 +1,11 @@
+name: 'Field Group Test'
+description: 'Test module for Field Group'
+# core: 8.x
+package: 'Fields'
+type: module
+hidden: TRUE
+# Information added by Drupal.org packaging script on 2017-11-10
+version: '8.x-3.0-beta1'
+core: '8.x'
+project: 'field_group'
+datestamp: 1510352294

+ 27 - 0
sites/all/modules/contrib/fields/field_group/tests/modules/field_group_test/field_group_test.module

@@ -0,0 +1,27 @@
+<?php
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * @file
+ * Fieldgroup test module.
+ */
+
+/**
+ * Implements hook_entity_field_access().
+ */
+function field_group_test_entity_field_access($operation, FieldDefinitionInterface $field_definition,
+  AccountInterface $account, FieldItemListInterface $items = NULL) {
+
+  // Set access to false for field_no_access.
+  if ($operation == 'view' && $field_definition->getName() == 'field_no_access') {
+    return AccessResult::forbidden();
+  }
+
+  return AccessResult::neutral();
+
+}
+

+ 346 - 0
sites/all/modules/contrib/fields/field_group/tests/src/Functional/EntityDisplayTest.php

@@ -0,0 +1,346 @@
+<?php
+
+namespace Drupal\Tests\field_group\Functional;
+
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests for displaying entities.
+ *
+ * @group field_group
+ */
+class EntityDisplayTest extends BrowserTestBase {
+
+  use FieldGroupTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'field_test',
+    'field_ui',
+    'field_group',
+    'field_group_test',
+  ];
+
+  /**
+   * The node type id.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * A node to use for testing.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $node;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Create test user.
+    $admin_user = $this->drupalCreateUser([
+      'access content',
+      'administer content types',
+      'administer node fields',
+      'administer node form display',
+      'administer node display',
+      'bypass node access'
+    ]);
+    $this->drupalLogin($admin_user);
+
+    // Create content type, with underscores.
+    $type_name = strtolower($this->randomMachineName(8)) . '_test';
+    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+    $this->type = $type->id();
+    /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
+    $display = \Drupal::entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->load('node' . '.' . $type_name . '.' . 'default');
+
+    // Create a node.
+    $node_values = array('type' => $type_name);
+
+    // Create test fields.
+    foreach (['field_test', 'field_test_2', 'field_no_access'] as $field_name) {
+      $field_storage = FieldStorageConfig::create([
+        'field_name' => $field_name,
+        'entity_type' => 'node',
+        'type' => 'test_field',
+      ]);
+      $field_storage->save();
+
+      $instance = FieldConfig::create([
+        'field_storage' => $field_storage,
+        'bundle' => $type_name,
+        'label' => $this->randomMachineName(),
+      ]);
+      $instance->save();
+
+      // Assign a test value for the field.
+      $node_values[$field_name][0]['value'] = mt_rand(1, 127);
+
+      // Set the field visible on the display object.
+      $display_options = array(
+        'label' => 'above',
+        'type' => 'field_test_default',
+        'settings' => array(
+          'test_formatter_setting' => $this->randomMachineName(),
+        ),
+      );
+      $display->setComponent($field_name, $display_options);
+    }
+
+    // Save display + create node.
+    $display->save();
+    $this->node = $this->drupalCreateNode($node_values);
+  }
+
+  /**
+   * Test field access for field groups.
+   */
+  public function testFieldAccess() {
+    $data = array(
+      'label' => 'Wrapper',
+      'children' => array(
+        0 => 'field_no_access',
+      ),
+      'format_type' => 'html_element',
+      'format_settings' => array(
+        'element' => 'div',
+        'id' => 'wrapper-id',
+      ),
+    );
+
+    $this->createGroup('node', $this->type, 'view', 'default', $data);
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test if group is not shown.
+    $this->assertNoFieldByXPath("//div[contains(@id, 'wrapper-id')]", NULL, t('Div that contains fields with no access is not shown.'));
+  }
+
+  /**
+   * Test the html element formatter.
+   */
+  public function testHtmlElement() {
+    $data = array(
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test',
+        1 => 'body',
+      ),
+      'label' => 'Link',
+      'format_type' => 'html_element',
+      'format_settings' => array(
+        'label' => 'Link',
+        'element' => 'div',
+        'id' => 'wrapper-id',
+        'classes' => 'test-class',
+      ),
+    );
+    $group = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    //$groups = field_group_info_groups('node', 'article', 'view', 'default', TRUE);
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test group ids and classes.
+    $this->assertFieldByXPath("//div[contains(@id, 'wrapper-id')]", NULL, 'Wrapper id set on wrapper div');
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, 'Test class set on wrapper div, class="' . $group->group_name . ' test-class');
+
+    // Test group label.
+    $this->assertNoRaw('<h3><span>' . $data['label'] . '</span></h3>');
+
+    // Set show label to true.
+    $group->format_settings['show_label'] = TRUE;
+    $group->format_settings['label_element'] = 'h3';
+    field_group_group_save($group);
+
+    $this->drupalGet('node/' . $this->node->id());
+    $this->assertRaw('<h3>' . $data['label'] . '</h3>');
+
+    // Change to collapsible with blink effect.
+    $group->format_settings['effect'] = 'blink';
+    $group->format_settings['speed'] = 'fast';
+    field_group_group_save($group);
+
+    $this->drupalGet('node/' . $this->node->id());
+    $this->assertFieldByXPath("//div[contains(@class, 'speed-fast')]", NULL, 'Speed class is set');
+    $this->assertFieldByXPath("//div[contains(@class, 'effect-blink')]", NULL, 'Effect class is set');
+  }
+
+  /**
+   * Test the fieldset formatter.
+   */
+  public function testFieldset() {
+    $data = array(
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test',
+        1 => 'body',
+      ),
+      'label' => 'Test Fieldset',
+      'format_type' => 'fieldset',
+      'format_settings' => array(
+        'id' => 'fieldset-id',
+        'classes' => 'test-class',
+        'description' => 'test description',
+      ),
+    );
+    $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test group ids and classes.
+    $this->assertFieldByXPath("//fieldset[contains(@id, 'fieldset-id')]", NULL, 'Correct id set on the fieldset');
+    $this->assertFieldByXPath("//fieldset[contains(@class, 'test-class')]", NULL, 'Test class set on the fieldset');
+  }
+
+  /**
+   * Test the tabs formatter.
+   */
+  public function testTabs() {
+    $data = array(
+      'label' => 'Tab 1',
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test',
+      ),
+      'format_type' => 'tab',
+      'format_settings' => array(
+        'label' => 'Tab 1',
+        'classes' => 'test-class',
+        'description' => '',
+        'formatter' => 'open',
+      ),
+    );
+    $first_tab = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $data = array(
+      'label' => 'Tab 2',
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test_2',
+      ),
+      'format_type' => 'tab',
+      'format_settings' => array(
+        'label' => 'Tab 1',
+        'classes' => 'test-class-2',
+        'description' => 'description of second tab',
+        'formatter' => 'closed',
+      ),
+    );
+    $second_tab = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $data = array(
+      'label' => 'Tabs',
+      'weight' => '1',
+      'children' => array(
+        0 => $first_tab->group_name,
+        1 => $second_tab->group_name,
+      ),
+      'format_type' => 'tabs',
+      'format_settings' => array(
+        'direction' => 'vertical',
+        'label' => 'Tab 1',
+        'classes' => 'test-class-wrapper',
+      ),
+    );
+    $tabs_group = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test properties.
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, 'Test class set on tabs wrapper');
+    $this->assertFieldByXPath("//details[contains(@class, 'test-class-2')]", NULL, 'Test class set on second tab');
+    $this->assertRaw('<div class="details-description">description of second tab</div>');
+
+    // Test if correctly nested.
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class')]", NULL, 'First tab is displayed as child of the wrapper.');
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class-2')]", NULL, 'Second tab is displayed as child of the wrapper.');
+
+    // Test if it's a vertical tab.
+    $this->assertFieldByXPath('//div[@data-vertical-tabs-panes=""]', NULL, 'Tabs are shown vertical.');
+
+    // Switch to horizontal
+    $tabs_group->format_settings['direction'] = 'horizontal';
+    field_group_group_save($tabs_group);
+
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test if it's a horizontal tab.
+    $this->assertFieldByXPath('//div[@data-horizontal-tabs-panes=""]', NULL, 'Tabs are shown horizontal.');
+  }
+
+  /**
+   * Test the accordion formatter.
+   */
+  public function testAccordion() {
+    $data = array(
+      'label' => 'Accordion item 1',
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test',
+      ),
+      'format_type' => 'accordion_item',
+      'format_settings' => array(
+        'label' => 'Accordion item 1',
+        'classes' => 'test-class',
+        'formatter' => 'closed',
+      ),
+    );
+    $first_item = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $data = array(
+      'label' => 'Accordion item 2',
+      'weight' => '1',
+      'children' => array(
+        0 => 'field_test_2',
+      ),
+      'format_type' => 'accordion_item',
+      'format_settings' => array(
+        'label' => 'Tab 2',
+        'classes' => 'test-class-2',
+        'formatter' => 'open',
+      ),
+    );
+    $second_item = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $data = array(
+      'label' => 'Accordion',
+      'weight' => '1',
+      'children' => array(
+        0 => $first_item->group_name,
+        1 => $second_item->group_name,
+      ),
+      'format_type' => 'accordion',
+      'format_settings' => array(
+        'label' => 'Tab 1',
+        'classes' => 'test-class-wrapper',
+        'effect' => 'bounceslide'
+      ),
+    );
+    $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $this->drupalGet('node/' . $this->node->id());
+
+    // Test properties.
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, 'Test class set on tabs wrapper');
+    $this->assertFieldByXPath("//div[contains(@class, 'effect-bounceslide')]", NULL, 'Correct effect is set on the accordion');
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, 'Accordion item with test-class is shown');
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-2')]", NULL, 'Accordion item with test-class-2 is shown');
+    $this->assertFieldByXPath("//h3[contains(@class, 'field-group-accordion-active')]", NULL, 'Accordion item 2 was set active');
+
+    // Test if correctly nested
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class')]", NULL, 'First item is displayed as child of the wrapper.');
+    $this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class-2')]", NULL, 'Second item is displayed as child of the wrapper.');
+  }
+
+}

+ 59 - 0
sites/all/modules/contrib/fields/field_group/tests/src/Functional/FieldGroupTestTrait.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\Tests\field_group\Functional;
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Provides common functionality for the FieldGroup test classes.
+ */
+trait FieldGroupTestTrait {
+
+  /**
+   * Create a new group.
+   *
+   * @param string $entity_type
+   *   The entity type as string.
+   * @param string $bundle
+   *   The bundle of the enity type
+   * @param string $context
+   *   The context for the group.
+   * @param string $mode
+   *   The view/form mode.
+   * @param array $data
+   *   Data for the field group.
+   *
+   * @return \stdClass
+   *   An object that represents the field group.
+   */
+  protected function createGroup($entity_type, $bundle, $context, $mode, array $data) {
+
+    if (!isset($data['format_settings'])) {
+      $data['format_settings'] = array();
+    }
+
+    $data['format_settings'] += _field_group_get_default_formatter_settings($data['format_type'], $context);
+
+    $group_name = 'group_' . Unicode::strtolower($this->randomMachineName());
+
+    $field_group = (object) array(
+      'group_name' => $group_name,
+      'entity_type' => $entity_type,
+      'bundle' => $bundle,
+      'mode' => $mode,
+      'context' => $context,
+      'children' => isset($data['children']) ? $data['children'] : array(),
+      'parent_name' => isset($data['parent']) ? $data['parent'] : '',
+      'weight' => isset($data['weight']) ? $data['weight'] : 0,
+      'label' => isset($data['label']) ? $data['label'] : $this->randomString(8),
+      'format_type' => $data['format_type'],
+      'format_settings' => $data['format_settings'],
+      'region' => 'content',
+    );
+
+    field_group_group_save($field_group);
+
+    return $field_group;
+  }
+
+}

+ 30 - 0
sites/all/modules/contrib/fields/field_group/tests/src/Functional/FieldGroupWithoutFieldUiTest.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\Tests\field_group\Functional;
+
+use Drupal\Core\Url;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Test field_group without field_ui.
+ *
+ * @group field_group
+ */
+class FieldGroupWithoutFieldUiTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['field_group', 'block'];
+
+  /**
+   * Test that local actions show up without field ui enabled.
+   */
+  public function testLocalActions() {
+    // Local actions of field_group should not depend on field_ui
+    // @see https://www.drupal.org/node/2719569
+    $this->placeBlock('local_actions_block', ['id' => 'local_actions_block']);
+    $this->drupalGet(Url::fromRoute('user.login'));
+  }
+
+}

+ 158 - 0
sites/all/modules/contrib/fields/field_group/tests/src/Functional/ManageDisplayTest.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace Drupal\Tests\field_group\Functional;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests for managing display of entities.
+ *
+ * @group field_group
+ */
+class ManageDisplayTest extends BrowserTestBase {
+
+  use FieldGroupTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('node', 'field_ui', 'field_group');
+
+  /**
+   * Content type id.
+   *
+   * @var string
+   */
+  protected $type;
+
+  protected $strictConfigSchema = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    // Create test user.
+    $admin_user = $this->drupalCreateUser([
+      'access content',
+      'administer content types',
+      'administer node fields',
+      'administer node form display',
+      'administer node display',
+      'bypass node access',
+    ]);
+    $this->drupalLogin($admin_user);
+
+    // Create content type, with underscores.
+    $type_name = 'll4ma_test';
+    $type = $this->drupalCreateContentType(['name' => $type_name, 'type' => $type_name]);
+    $this->type = $type->id();
+
+  }
+
+  /**
+   * Test the creation a group on the article content type.
+   */
+  public function testCreateGroup() {
+    // Create random group name.
+    $group_label = $this->randomString(8);
+    $group_name_input = Unicode::strtolower($this->randomMachineName());
+    $group_name = 'group_' . $group_name_input;
+    $group_formatter = 'details';
+
+    // Setup new group.
+    $group = array(
+      'group_formatter' => $group_formatter,
+      'label' => $group_label,
+    );
+
+    $add_form_display = 'admin/structure/types/manage/' . $this->type . '/form-display/add-group';
+    $this->drupalPostForm($add_form_display, $group, 'Save and continue');
+    $this->assertSession()->pageTextContains('Machine-readable name field is required.');
+
+    // Add required field to form.
+    $group['group_name'] = $group_name_input;
+
+    // Add new group on the 'Manage form display' page.
+    $this->drupalPostForm($add_form_display, $group, 'Save and continue');
+    $this->drupalPostForm(NULL, [], 'Create group');
+
+    $this->assertSession()->responseContains(t('New group %label successfully created.', array('%label' => $group_label)));
+
+    // Test if group is in the $groups array.
+    $this->group = field_group_load_field_group($group_name, 'node', $this->type, 'form', 'default');
+    $this->assertNotNull($group, 'Group was loaded');
+
+    // Add new group on the 'Manage display' page.
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display/add-group', $group, 'Save and continue');
+    $this->drupalPostForm(NULL, [], 'Create group');
+
+    $this->assertSession()->responseContains(t('New group %label successfully created.', array('%label' => $group_label)));
+
+    // Test if group is in the $groups array.
+    $loaded_group = field_group_load_field_group($group_name, 'node', $this->type, 'view', 'default');
+    $this->assertNotNull($loaded_group, 'Group was loaded');
+  }
+
+  /**
+   * Delete a group.
+   */
+  public function testDeleteGroup() {
+    $data = array(
+      'format_type' => 'fieldset',
+      'label' => 'testing',
+    );
+
+    $group = $this->createGroup('node', $this->type, 'form', 'default', $data);
+
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/form-display/' . $group->group_name . '/delete', array(), 'Delete');
+    $this->assertSession()->responseContains(t('The group %label has been deleted from the %type content type.', array('%label' => $group->label, '%type' => $this->type)));
+
+    // Test that group is not in the $groups array.
+    \Drupal::entityTypeManager()
+      ->getStorage('entity_form_display')
+      ->resetCache();
+    $loaded_group = field_group_load_field_group($group->group_name, 'node', $this->type, 'form', 'default');
+    $this->assertNull($loaded_group, 'Group not found after deleting');
+
+    $data = array(
+      'format_type' => 'fieldset',
+      'label' => 'testing',
+    );
+
+    $group = $this->createGroup('node', $this->type, 'view', 'default', $data);
+
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/display/' . $group->group_name . '/delete', array(), t('Delete'));
+    $this->assertRaw(t('The group %label has been deleted from the %type content type.', array('%label' => $group->label, '%type' => $this->type)));
+
+    // Test that group is not in the $groups array.
+    \Drupal::entityTypeManager()
+      ->getStorage('entity_view_display')
+      ->resetCache();
+    $loaded_group = field_group_load_field_group($group->group_name, 'node', $this->type, 'view', 'default');
+    $this->assertNull($loaded_group, 'Group not found after deleting');
+  }
+
+  /**
+   * Nest a field underneath a group.
+   */
+  public function testNestField() {
+    $data = array(
+      'format_type' => 'fieldset',
+    );
+
+    $group = $this->createGroup('node', $this->type, 'form', 'default', $data);
+
+    $edit = array(
+      'fields[body][parent]' => $group->group_name,
+    );
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->type . '/form-display', $edit, 'Save');
+    $this->assertRaw('Your settings have been saved.');
+
+    $group = field_group_load_field_group($group->group_name, 'node', $this->type, 'form', 'default');
+    $this->assertTrue(in_array('body', $group->children), t('Body is a child of %group', array('%group' => $group->group_name)));
+  }
+
+}

+ 105 - 0
sites/all/modules/contrib/fields/field_group/tests/src/FunctionalJavascript/FieldGroupUiTest.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Drupal\Tests\field_group\FunctionalJavascript;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\node\Entity\NodeType;
+use Drupal\Tests\field_group\Functional\FieldGroupTestTrait;
+
+/**
+ * Test field_group user interface.
+ *
+ * @group field_group
+ */
+class FieldGroupUiTest extends JavascriptTestBase {
+
+  use FieldGroupTestTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'field_ui', 'field_group');
+
+  /**
+   * @var string
+   */
+  protected $nodeType;
+
+  public function setUp() {
+    parent::setUp();
+
+    // Create test user.
+    $admin_user = $this->drupalCreateUser([
+      'access content',
+      'administer content types',
+      'administer node fields',
+      'administer node form display',
+      'administer node display',
+      'bypass node access',
+    ]);
+    $this->drupalLogin($admin_user);
+
+    // Create content type, with underscores.
+    $type_name =  Unicode::strtolower($this->randomMachineName(8)) . '_test';
+    $type = NodeType::create([
+      'name' => $type_name,
+      'type' => $type_name,
+    ]);
+    $type->save();
+    $this->nodeType = $type->id();
+  }
+
+  /**
+   * Test creation and editing trough the UI.
+   */
+  public function testCreateAndEdit() {
+    foreach (['test_1', 'test_2'] as $name) {
+      $group = array(
+        'group_formatter' => 'details',
+        'label' => 'Test 1',
+        'group_name' => $name,
+      );
+
+      // Add new group on the 'Manage form display' page.
+      $this->drupalPostForm('admin/structure/types/manage/' . $this->nodeType . '/form-display/add-group', $group, 'Save and continue');
+      $this->drupalPostForm(NULL, [], 'Create group');
+    }
+
+    // Update title in group 1
+    $page = $this->getSession()->getPage();
+    $page->pressButton('group_test_1_group_settings_edit');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $page->fillField('fields[group_test_1][settings_edit_form][settings][label]', 'Test 1 - Update');
+    $page->pressButton('Update');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    // Update title in group 2
+    $page->pressButton('group_test_2_group_settings_edit');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $page->fillField('fields[group_test_2][settings_edit_form][settings][label]', 'Test 2 - Update');
+    $page->pressButton('Update');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    // Open group 1 again
+    $page->pressButton('group_test_1_group_settings_edit');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+    $this->assertSession()->fieldValueEquals('fields[group_test_1][settings_edit_form][settings][label]', 'Test 1 - Update');
+    $page->pressButton('Cancel');
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    $page->pressButton('Save');
+
+    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display */
+    $display = EntityFormDisplay::load("node.{$this->nodeType}.default");
+    $this->assertSame('Test 1 - Update', $display->getThirdPartySetting('field_group', 'group_test_1')['label']);
+    $this->assertSame('Test 1 - Update', $display->getThirdPartySetting('field_group', 'group_test_1')['format_settings']['label']);
+
+    $this->assertSame('Test 2 - Update', $display->getThirdPartySetting('field_group', 'group_test_2')['label']);
+    $this->assertSame('Test 2 - Update', $display->getThirdPartySetting('field_group', 'group_test_2')['format_settings']['label']);
+  }
+
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов