first import 1.3
This commit is contained in:
commit
16ed68abf6
40
CHANGELOG.txt
Normal file
40
CHANGELOG.txt
Normal file
@ -0,0 +1,40 @@
|
||||
/* $Id*/
|
||||
CHANGELOG for field_group for Drupal 7
|
||||
|
||||
Field_group 7.x-1.x-dev
|
||||
o Issue #1095316: Field Groups disappear when Content Type is renamed.
|
||||
o Issue #1095316 by swentel: Support for Entity API.
|
||||
o Issue #1095002 by animelion: Upgrading removes all existing field groups.
|
||||
o Issue #1095130 by willvincent: Features export not working with rc2.
|
||||
|
||||
Field_group 7.x-1.0-rc2
|
||||
o Ran through coder, minor.
|
||||
o Issue #1033036 by Stalski, swentel: Create a field_group.api.php.
|
||||
o Made the summary descriptions more human readable.
|
||||
o Issue #1086450: Cannot see red star on some field groups even required fields are set to 1.
|
||||
o #1072292 by shadow_jh, stalski: Using on user settings page but need to hid on registration page.
|
||||
o #1092360 by dww: Move field_group_update_7000 functionality to hook_install().
|
||||
o #1061228 Rewrite the field_group_field_group_is_empty function.
|
||||
o Added ID's to fieldgroups.
|
||||
o Removed unused field_group.admin.inc + menu item. Required asterix moving to field_group setting.
|
||||
o #1045526 by stalski: Make formatter options more user-friendly and logical.
|
||||
o #1041880 by robertgarrigos: duplicated entries in field_group table.
|
||||
o #1043834 by amsri: Field Group module just does not work with profiles 2.
|
||||
|
||||
Field_group 7.x-1.0-rc1
|
||||
o #1006464 Change #groups to #fieldgroups because of name collapsing with form_process_fieldset
|
||||
o #1024184 fix collapsible when mode is set to open
|
||||
o #1020278 by mori: Update fails.
|
||||
o #1020116 by mikegfx: Confusing verbage across group types.
|
||||
o #1018012 by mikegfx: Adding required asterisk to group tabs that have required fields.
|
||||
o #960916 fixed reference warnings.
|
||||
o No label anymore with div>open.
|
||||
o #969258 Added check for fields and extra_fields.
|
||||
o #960916 Fixed notice on for reference on group in field_group_settings.
|
||||
o #961106 Fixed notice on entity type and bundle check.
|
||||
o #962072 by mori: Improve CSS for horizontal tabs & accordion.
|
||||
o Changed Fieldgroup API: defaults and instance_settings are now merged.
|
||||
o Changed save action so everything is gathered during form_state to
|
||||
postpone saving until the save button is hit.
|
||||
o Changed some important variable name, so it makes more sense and easier to read.
|
||||
o Add basic crud functions.
|
339
LICENSE.txt
Normal file
339
LICENSE.txt
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
47
README.txt
Normal file
47
README.txt
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
History:
|
||||
Field_group was written for Drupal 7. 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 Drupal7,
|
||||
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 has API functions 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:
|
||||
http://drupal.org/node/1017838
|
||||
http://drupal.org/node/1017962
|
||||
|
||||
Available group types:
|
||||
- Fieldsets
|
||||
- Horizontal tabs
|
||||
- Vertical tabs
|
||||
- Accordions
|
||||
- Divs
|
||||
- Multipage steps: <strong>Note: This is only client side.
|
||||
- HTML5 group type
|
||||
- Html element
|
||||
|
||||
To submit bug reports and feature suggestions, or to track changes:
|
||||
http://drupal.org/project/issues/field_group
|
||||
|
||||
-- MAINTAINERS --
|
||||
|
||||
stalski - http://drupal.org/user/322618
|
||||
swentel - http://drupal.org/user/107403
|
||||
zuuperman - http://drupal.org/user/361625
|
||||
|
||||
-- INSPIRATORS --
|
||||
|
||||
yched - http://drupal.org/user/39567
|
7
field_group-rtl.css
Normal file
7
field_group-rtl.css
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Override the accordion default style for view_modes.
|
||||
*/
|
||||
form .ui-accordion h3, form .ui-accordion h3.ui-state-active {
|
||||
padding-left: 0;
|
||||
padding-right: 2em;
|
||||
}
|
460
field_group.api.php
Normal file
460
field_group.api.php
Normal file
@ -0,0 +1,460 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Field group module.
|
||||
*
|
||||
* Fieldgroup is a module that will wrap fields and other fieldgroups. Nothing more, nothing less.
|
||||
* For this there are formatters we can create on forms and view modes.
|
||||
*
|
||||
* Some of the elements defined in fieldgroup will be ported to the elements module.
|
||||
*
|
||||
* DEVELOPERS NOTES
|
||||
*
|
||||
* - Fieldgroup uses a ''#fieldgroups' property to know what fieldgroups are to be pre_rendered and
|
||||
* rendered by the field_group module. This means we need to be sure our groups are in #fieldgroups.
|
||||
* #fieldgroups is later merged with the normal #groups that can be used by any other module.
|
||||
* This is done to be sure fieldgroup is not taking fieldsets from profile2, commerce line items,
|
||||
* commerce user profiles, ... .
|
||||
* When trying to merge a programmatically created field wrapper (div, markup, fieldset, ...) into
|
||||
* groups, you might consider adding it in #field_groups as well if you want the element processed
|
||||
* by fieldgroup.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Javascript hooks
|
||||
*
|
||||
* Drupal.FieldGroup.Effects.processHook.execute()
|
||||
* See field_group.js for the examples for all implemented formatters.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_formatter_info().
|
||||
*
|
||||
* Define the information on formatters. The formatters are
|
||||
* separated by view mode type. We have "form" for all form elements
|
||||
* and "display" will be the real view modes (full, teaser, sticky, ...)
|
||||
*
|
||||
* structure:
|
||||
* @code
|
||||
* array(
|
||||
* 'form' => array(
|
||||
* 'fieldset' => array(
|
||||
* // required, String with the name of the formatter type.
|
||||
* 'label' => t('Fieldset'),
|
||||
* // optional, String description of the formatter type.
|
||||
* 'description' => t('This is field group that ...'),
|
||||
* // required, Array of available formatter options.
|
||||
* 'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
* // required, String with default value of the style.
|
||||
'default_formatter' => 'collapsible',
|
||||
* // optional, Array with key => default_value pairs.
|
||||
* 'instance_settings' => array('key' => 'value'),
|
||||
* ),
|
||||
* ),
|
||||
* 'display' => array(
|
||||
* 'fieldset' => array(
|
||||
* // required, String with the name of the formatter type.
|
||||
* 'label' => t('Fieldset'),
|
||||
* // optional, String description of the formatter type.
|
||||
* 'description' => t('This is field group that ...'),
|
||||
* // required, Array of available formatter options.
|
||||
* 'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
* // required, String with default value of the style.
|
||||
'default_formatter' => 'collapsible',
|
||||
* // optional, Array with key => default_value pairs.
|
||||
* 'instance_settings' => array('key' => 'value'),
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* @endcode
|
||||
*
|
||||
* @return $formatters
|
||||
* A collection of available formatting html controls for form
|
||||
* and display overview type.
|
||||
*
|
||||
* @see field_group_field_group_formatter_info()
|
||||
*/
|
||||
function hook_field_group_formatter_info() {
|
||||
return array(
|
||||
'form' => array(
|
||||
'fieldset' => array(
|
||||
'label' => t('Fieldset'),
|
||||
'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'),
|
||||
'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
'instance_settings' => array('classes' => ''),
|
||||
'default_formatter' => 'collapsible',
|
||||
),
|
||||
),
|
||||
'display' => array(
|
||||
'div' => array(
|
||||
'label' => t('Div'),
|
||||
'description' => t('This fieldgroup renders the inner content in a simple div with the title as legend.'),
|
||||
'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
'instance_settings' => array('effect' => 'none', 'speed' => 'fast', 'classes' => ''),
|
||||
'default_formatter' => 'collapsible',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_format_settings().
|
||||
*
|
||||
* Defines configuration widget for the settings on a field group
|
||||
* formatter. Eache formatter can have different elements and storage.
|
||||
*
|
||||
* @params Object $group The group object.
|
||||
* @return Array $form The form element for the format settings.
|
||||
*/
|
||||
function hook_field_group_format_settings($group) {
|
||||
// Add a wrapper for extra settings to use by others.
|
||||
$form = array(
|
||||
'instance_settings' => array(
|
||||
'#tree' => TRUE,
|
||||
'#weight' => 2,
|
||||
),
|
||||
);
|
||||
|
||||
$field_group_types = field_group_formatter_info();
|
||||
$mode = $group->mode == 'form' ? 'form' : 'display';
|
||||
$formatter = $field_group_types[$mode][$group->format_type];
|
||||
|
||||
// Add the required formatter type selector.
|
||||
if (isset($formatter['format_types'])) {
|
||||
$form['formatter'] = array(
|
||||
'#title' => t('Fieldgroup settings'),
|
||||
'#type' => 'select',
|
||||
'#options' => drupal_map_assoc($formatter['format_types']),
|
||||
'#default_value' => isset($group->format_settings['formatter']) ? $group->format_settings['formatter'] : $formatter['default_formatter'],
|
||||
'#weight' => 1,
|
||||
);
|
||||
}
|
||||
if ($mode == 'form') {
|
||||
$form['instance_settings']['required_fields'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Mark group for required fields.'),
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['required_fields']) ? $group->format_settings['instance_settings']['required_fields'] : (isset($formatter['instance_settings']['required_fields']) ? $formatter['instance_settings']['required_fields'] : ''),
|
||||
'#weight' => 2,
|
||||
);
|
||||
}
|
||||
$form['instance_settings']['classes'] = array(
|
||||
'#title' => t('Extra CSS classes'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['classes']) ? $group->format_settings['instance_settings']['classes'] : (isset($formatter['instance_settings']['classes']) ? $formatter['instance_settings']['classes'] : ''),
|
||||
'#weight' => 3,
|
||||
'#element_validate' => array('field_group_validate_css_class'),
|
||||
);
|
||||
$form['instance_settings']['description'] = array(
|
||||
'#title' => t('Description'),
|
||||
'#type' => 'textarea',
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['description']) ? $group->format_settings['instance_settings']['description'] : (isset($formatter['instance_settings']['description']) ? $formatter['instance_settings']['description'] : ''),
|
||||
'#weight' => 0,
|
||||
);
|
||||
|
||||
// Add optional instance_settings.
|
||||
switch ($group->format_type) {
|
||||
case 'div':
|
||||
$form['instance_settings']['effect'] = array(
|
||||
'#title' => t('Effect'),
|
||||
'#type' => 'select',
|
||||
'#options' => array('none' => t('None'), 'blind' => t('Blind')),
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : $formatter['instance_settings']['effect'],
|
||||
'#weight' => 2,
|
||||
);
|
||||
$form['instance_settings']['speed'] = array(
|
||||
'#title' => t('Speed'),
|
||||
'#type' => 'select',
|
||||
'#options' => array('none' => t('None'), 'slow' => t('Slow'), 'fast' => t('Fast')),
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['speed']) ? $group->format_settings['instance_settings']['speed'] : $formatter['instance_settings']['speed'],
|
||||
'#weight' => 3,
|
||||
);
|
||||
break;
|
||||
case 'fieldset':
|
||||
$form['instance_settings']['classes'] = array(
|
||||
'#title' => t('Extra CSS classes'),
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => isset($group->format_settings['instance_settings']['classes']) ? $group->format_settings['instance_settings']['classes'] : $formatter['instance_settings']['classes'],
|
||||
'#weight' => 3,
|
||||
'#element_validate' => array('field_group_validate_css_class'),
|
||||
);
|
||||
break;
|
||||
case 'tabs':
|
||||
case 'htabs':
|
||||
case 'accordion':
|
||||
unset($form['instance_settings']['description']);
|
||||
if (isset($form['instance_settings']['required_fields'])) {
|
||||
unset($form['instance_settings']['required_fields']);
|
||||
}
|
||||
break;
|
||||
case 'tab':
|
||||
case 'htab':
|
||||
case 'accordion-item':
|
||||
default:
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_pre_render().
|
||||
*
|
||||
* This function gives you the oppertunity to create the given
|
||||
* wrapper element that can contain the fields.
|
||||
* In the example beneath, some variables are prepared and used when building the
|
||||
* actual wrapper element. All elements in drupal fapi can be used.
|
||||
*
|
||||
* Note that at this point, the field group has no notion of the fields in it.
|
||||
*
|
||||
* There is also an alternative way of handling this. The default implementation
|
||||
* within field_group calls "field_group_pre_render_<format_type>".
|
||||
* @see field_group_pre_render_fieldset.
|
||||
*
|
||||
* @param Array $elements by address.
|
||||
* @param Object $group The Field group info.
|
||||
*/
|
||||
function hook_field_group_pre_render(& $element, $group, & $form) {
|
||||
|
||||
// You can prepare some variables to use in the logic.
|
||||
$view_mode = isset($form['#view_mode']) ? $form['#view_mode'] : 'form';
|
||||
$id = $form['#entity_type'] . '_' . $form['#bundle'] . '_' . $view_mode . '_' . $group->group_name;
|
||||
|
||||
// Each formatter type can have whole different set of element properties.
|
||||
switch ($group->format_type) {
|
||||
|
||||
// Normal or collapsible div.
|
||||
case 'div':
|
||||
$effect = isset($group->format_settings['instance_settings']['effect']) ? $group->format_settings['instance_settings']['effect'] : 'none';
|
||||
$speed = isset($group->format_settings['instance_settings']['speed']) ? $group->format_settings['instance_settings']['speed'] : 'none';
|
||||
$add = array(
|
||||
'#type' => 'markup',
|
||||
'#weight' => $group->weight,
|
||||
'#id' => $id,
|
||||
);
|
||||
$classes .= " speed-$speed effect-$effect";
|
||||
if ($group->format_settings['formatter'] != 'open') {
|
||||
$add['#prefix'] = '<div class="field-group-format ' . $classes . '">
|
||||
<span class="field-group-format-toggler">' . check_plain(t($group->label)) . '</span>
|
||||
<div class="field-group-format-wrapper" style="display: none;">';
|
||||
$add['#suffix'] = '</div></div>';
|
||||
}
|
||||
else {
|
||||
$add['#prefix'] = '<div class="field-group-format ' . $group->group_name . ' ' . $classes . '">';
|
||||
$add['#suffix'] = '</div>';
|
||||
}
|
||||
if (!empty($description)) {
|
||||
$add['#prefix'] .= '<div class="description">' . $description . '</div>';
|
||||
}
|
||||
$element += $add;
|
||||
|
||||
if ($effect == 'blind') {
|
||||
drupal_add_library('system', 'effects.blind');
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_pre_render().
|
||||
*
|
||||
* Function that fungates as last resort to alter the pre_render build.
|
||||
*/
|
||||
function hook_field_group_pre_render_alter(&$element, $group, & $form) {
|
||||
|
||||
if ($group->format_type == 'htab') {
|
||||
$element['#theme_wrappers'] = array('my_horizontal_tab');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_build_pre_render_alter().
|
||||
*
|
||||
* Function that fungates as last resort where you can alter things. It is
|
||||
* expected that when you need this function, you have most likely a very custom
|
||||
* case or it is a fix that can be put in field_group core.
|
||||
*
|
||||
* @param Array $elements by address.
|
||||
*/
|
||||
function hook_field_group_build_pre_render_alter(& $element) {
|
||||
|
||||
// Prepare variables.
|
||||
$display = isset($element['#view_mode']);
|
||||
$groups = array_keys($element['#groups']);
|
||||
|
||||
// Example from field_group itself to unset empty elements.
|
||||
if ($display) {
|
||||
foreach (element_children($element) as $name) {
|
||||
if (in_array($name, $groups)) {
|
||||
if (field_group_field_group_is_empty($element[$name], $groups)) {
|
||||
unset($element[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// You might include additional javascript files and stylesheets.
|
||||
$element['#attached']['js'][] = drupal_get_path('module', 'field_group') . '/field_group.js';
|
||||
$element['#attached']['css'][] = drupal_get_path('module', 'field_group') . '/field_group.css';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_format_summary().
|
||||
*
|
||||
* Place to override or change default summary behavior. In most
|
||||
* cases the implementation of field group itself will be enough.
|
||||
*
|
||||
* TODO It might be better to change this hook with already created summaries,
|
||||
* giving the ability to alter or add it later on.
|
||||
*/
|
||||
function hook_field_group_format_summary($group) {
|
||||
$output = '';
|
||||
// Create additional summary or change the default setting.
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement hook_ctools_plugin_api().
|
||||
* This hook is needed to let ctools know about exportables.
|
||||
* If you create field groups by using hook_field_group_info, you
|
||||
* will need to include the ctools api hook as well.
|
||||
*/
|
||||
function hook_ctools_plugin_api($module, $api) {
|
||||
if ($module == 'field_group' && $api == 'field_group') {
|
||||
return array('version' => 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_info().
|
||||
* Don't forget to include the ctools hook to notify that
|
||||
* your modules has field group exports.
|
||||
* @see hook_ctools_plugin_api.
|
||||
*/
|
||||
function hook_field_group_info() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the field group definitions provided by other modules.
|
||||
*
|
||||
* @param array $groups
|
||||
* Reference to an array of field group definition objects.
|
||||
*/
|
||||
function hook_field_group_info_alter(&$groups) {
|
||||
if (!empty($groups['group_issue_metadata|node|project_issue|form'])) {
|
||||
$groups['group_issue_metadata|node|project_issue|form']->data['children'][] = 'taxonomy_vocabulary_9';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_update_field_group().
|
||||
*
|
||||
* This hook is invoked by ctools export API.
|
||||
* Note that this is used by ctools and the group could occasional be
|
||||
* the group ID.
|
||||
*
|
||||
* @param $object $group
|
||||
* The FieldGroup object.
|
||||
*/
|
||||
function hook_field_group_update_field_group($group) {
|
||||
// Delete extra data depending on the group.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_delete_field_group().
|
||||
*
|
||||
* This hook is invoked by ctools export API.
|
||||
*
|
||||
* @param $object $group
|
||||
* The FieldGroup object.
|
||||
*/
|
||||
function hook_field_group_delete_field_group($group) {
|
||||
// Delete extra data depending on the group.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_create_field_group().
|
||||
*
|
||||
* This hook is invoked by ctools export API.
|
||||
*
|
||||
* @param $object $group
|
||||
* The FieldGroup object.
|
||||
*/
|
||||
function hook_field_group_create_field_group($group) {
|
||||
// Create extra data depending on the group.
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @addtogroup utility functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the groups for a given entity type, bundle and view mode.
|
||||
*
|
||||
* @param String $entity_type
|
||||
* The Entity type where field groups are requested.
|
||||
* @param String $bundle
|
||||
* The entity bundle for the field groups.
|
||||
* @param String $view_mode
|
||||
* The view mode scope for the field groups.
|
||||
*
|
||||
* @see field_group_read_groups()
|
||||
* @see ctools_export_crud_load()
|
||||
* @see ctools_export_crud_load_all()
|
||||
* @see ctools_export_crud_delete()
|
||||
* @see ctools_export_crud_save()
|
||||
*/
|
||||
function field_group_info_groups($entity_type = NULL, $bundle = NULL, $view_mode = NULL, $reset = FALSE) {
|
||||
// This function caches the result and delegates to field_group_read_groups.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the groups for the given parameters, uncached.
|
||||
*
|
||||
* @param Array $params
|
||||
* The Entity type where field groups are requested.
|
||||
* @param $enabled
|
||||
* Return enabled or disabled groups.*
|
||||
*
|
||||
* @see field_group_info_groups()
|
||||
* @see ctools_export_load_object()
|
||||
*/
|
||||
function field_group_read_groups($conditions = array(), $enabled = TRUE) {
|
||||
// This function loads the requested groups through ctools export api.
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides field groups including children in a render array.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array. Can be a form, node, user, ...
|
||||
* @param array $group_names
|
||||
* An array of field group names that should be hidden.
|
||||
*/
|
||||
function field_group_hide_field_groups(&$element, $group_names) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup utility functions".
|
||||
*/
|
||||
|
23
field_group.css
Normal file
23
field_group.css
Normal file
@ -0,0 +1,23 @@
|
||||
/* $Id: field_group.css,v 1.1.2.12 2010/12/22 22:22:35 stalski Exp $ */
|
||||
|
||||
/**
|
||||
* Fix for fieldsets in vertical tabs.
|
||||
* Note that this can only be hardcoded to the Seven theme
|
||||
* where people who override this, are in trouble.
|
||||
* This can be removed in next d7 release.
|
||||
*/
|
||||
.vertical-tabs fieldset.default-fallback,
|
||||
div.field-group-tabs-wrapper div.field-type-image fieldset,
|
||||
div.field-group-tabs-wrapper div.field-type-file fieldset,
|
||||
div.field-group-tabs-wrapper div.field-type-datetime fieldset {
|
||||
border: 1px solid #CCCCCC;
|
||||
margin: 1em 0;
|
||||
padding: 2.5em 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.field-group-tabs-wrapper div.field-type-image legend,
|
||||
div.field-group-tabs-wrapper div.field-type-file legend,
|
||||
div.field-group-tabs-wrapper div.field-type-datetime legend {
|
||||
display: block;
|
||||
}
|
70
field_group.features.inc
Normal file
70
field_group.features.inc
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_features_export_alter().
|
||||
*
|
||||
* For a given feature, add field groups that contain any fields that
|
||||
* are a part of this feature. Also, add parent groups of any groups
|
||||
* that are a part of this feature.
|
||||
*/
|
||||
function field_group_features_export_alter(&$export, $module_name) {
|
||||
// Make sure we have fresh data by loading directly.
|
||||
ctools_include('export');
|
||||
$field_groups = ctools_export_load_object('field_group');
|
||||
|
||||
// Support the separate field base -vs- field instance structure that was
|
||||
// added in Features v7.x-2.0-beta2.
|
||||
if (function_exists('field_instance_features_export')) {
|
||||
$export_var = 'field_instance';
|
||||
}
|
||||
else {
|
||||
$export_var = 'field';
|
||||
}
|
||||
|
||||
// Add fieldgroups based on the fields that are present.
|
||||
if (!empty($export['features'][$export_var])) {
|
||||
if (!isset($export['features']['field_group'])) {
|
||||
$export['features']['field_group'] = array();
|
||||
}
|
||||
foreach ($export['features'][$export_var] as $field) {
|
||||
list($entity_type, $bundle, $field_name) = explode('-', $field);
|
||||
|
||||
foreach ($field_groups as $group_id => $group) {
|
||||
|
||||
if ($group->entity_type == $entity_type && $group->bundle == $bundle && in_array($field_name, $group->data['children']) && !in_array($group->identifier, $export['features']['field_group'])) {
|
||||
if (isset($group->export_module) && $group->export_module != $module_name) {
|
||||
$export['dependencies'][$group->export_module] = $group->export_module;
|
||||
}
|
||||
else {
|
||||
$export['features']['field_group'][$group_id] = $group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any parent field groups that haven't been selected.
|
||||
if (!empty($export['features']['field_group'])) {
|
||||
foreach ($export['features']['field_group'] as $id) {
|
||||
$group = isset($field_groups[$id]) ? $field_groups[$id] : FALSE;
|
||||
|
||||
if ($group && !empty($group->parent_name)) {
|
||||
$parent_id = $group->parent_name . '|' . $group->entity_type . '|' . $group->bundle . '|' . $group->mode;
|
||||
$parent_group = isset($field_groups[$parent_id]) ? $field_groups[$parent_id] : FALSE;
|
||||
|
||||
if ($parent_group && !isset($export['features']['field_group'][$parent_id])) {
|
||||
if (isset($parent_group->export_module) && $parent_group->export_module != $module_name) {
|
||||
$export['dependencies'][$parent_group->export_module] = $parent_group->export_module;
|
||||
}
|
||||
else {
|
||||
$export['features']['field_group'][$parent_id] = $parent_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(empty($export['dependencies']['field_group'])) {
|
||||
$export['dependencies']['field_group'] = 'field_group';
|
||||
}
|
||||
}
|
||||
}
|
14
field_group.field_ui.css
Normal file
14
field_group.field_ui.css
Normal file
@ -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;
|
||||
}
|
1013
field_group.field_ui.inc
Normal file
1013
field_group.field_ui.inc
Normal file
File diff suppressed because it is too large
Load Diff
137
field_group.field_ui.js
Normal file
137
field_group.field_ui.js
Normal file
@ -0,0 +1,137 @@
|
||||
|
||||
(function($) {
|
||||
|
||||
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;
|
||||
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.$formatSelect = $('select.field-group-type', row);
|
||||
this.$formatSelect.change(Drupal.fieldUIOverview.onChange);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Drupal.fieldUIDisplayOverview.group.prototype = {
|
||||
getRegion: function () {
|
||||
return (this.$formatSelect.val() == 'hidden') ? 'hidden' : 'visible';
|
||||
},
|
||||
|
||||
regionChange: function (region, recurse) {
|
||||
|
||||
// Default recurse to true.
|
||||
recurse = (recurse == undefined) || recurse;
|
||||
|
||||
// When triggered by a row drag, the 'format' select needs to be adjusted to
|
||||
// the new region.
|
||||
var currentValue = this.$formatSelect.val();
|
||||
switch (region) {
|
||||
case 'visible':
|
||||
if (currentValue == 'hidden') {
|
||||
// Restore the group format back to 'fieldset'.
|
||||
var value = 'fieldset';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
var value = 'hidden';
|
||||
break;
|
||||
}
|
||||
if (value != undefined) {
|
||||
this.$formatSelect.val(value);
|
||||
}
|
||||
|
||||
var refreshRows = {};
|
||||
refreshRows[this.name] = this.$formatSelect.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;
|
||||
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);
|
19
field_group.info
Normal file
19
field_group.info
Normal file
@ -0,0 +1,19 @@
|
||||
name = Fieldgroup
|
||||
description = Fieldgroup
|
||||
package = Fields
|
||||
dependencies[] = field
|
||||
dependencies[] = ctools
|
||||
core = 7.x
|
||||
files[] = field_group.install
|
||||
files[] = field_group.module
|
||||
files[] = field_group.field_ui.inc
|
||||
files[] = field_group.form.inc
|
||||
files[] = field_group.features.inc
|
||||
files[] = tests/field_group.ui.test
|
||||
files[] = tests/field_group.display.test
|
||||
; Information added by drupal.org packaging script on 2013-09-25
|
||||
version = "7.x-1.3"
|
||||
core = "7.x"
|
||||
project = "field_group"
|
||||
datestamp = "1380124361"
|
||||
|
327
field_group.install
Normal file
327
field_group.install
Normal file
@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Fieldgroup module install file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function field_group_schema() {
|
||||
$schema['field_group'] = array(
|
||||
'description' => t('Table that contains field group entries and settings.'),
|
||||
|
||||
// CTools export definitions.
|
||||
'export' => array(
|
||||
'key' => 'identifier',
|
||||
'identifier' => 'field_group',
|
||||
'default hook' => 'field_group_info',
|
||||
'save callback' => 'field_group_group_save',
|
||||
'delete callback' => 'field_group_group_export_delete',
|
||||
'can disable' => TRUE,
|
||||
'api' => array(
|
||||
'owner' => 'field_group',
|
||||
'api' => 'field_group',
|
||||
'minimum_version' => 1,
|
||||
'current_version' => 1,
|
||||
),
|
||||
),
|
||||
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The primary identifier for a group',
|
||||
'no export' => TRUE,
|
||||
),
|
||||
'identifier' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The unique string identifier for a group.',
|
||||
),
|
||||
'group_name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The name of this group.',
|
||||
),
|
||||
'entity_type' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'bundle' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => ''
|
||||
),
|
||||
'mode' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => ''
|
||||
),
|
||||
// @todo 'parent_name' is redundant with the data in the 'children'
|
||||
// entry, brings a risk of inconsistent data. This should be removed from
|
||||
// the schema and pre-computed it if needed in field_group_get_groups().
|
||||
'parent_name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The parent name for a group',
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'not null' => TRUE,
|
||||
'serialize' => TRUE,
|
||||
'description' => 'Serialized data containing the group properties that do not warrant a dedicated column.',
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
'indexes' => array(
|
||||
'group_name' => array('group_name'),
|
||||
),
|
||||
'unique keys' => array(
|
||||
'identifier' => array('identifier'),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function: fetch all the field_group definitions from the database.
|
||||
*/
|
||||
function _field_group_install_read_groups() {
|
||||
$groups = array();
|
||||
if (db_table_exists('content_group')) {
|
||||
$query = db_select('content_group', 'cg', array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('cg')
|
||||
// We only want non-multigroups.
|
||||
->condition('group_type', 'standard');
|
||||
foreach ($query->execute() as $record) {
|
||||
$record['settings'] = unserialize($record['settings']);
|
||||
$groups[$record['group_name'] . '-' . $record['type_name']] = $record;
|
||||
}
|
||||
foreach ($groups as $key => $group) {
|
||||
$query2 = db_select('content_group_fields', 'cgf', array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('cgf')
|
||||
->condition('group_name', $group['group_name']);
|
||||
foreach ($query2->execute() as $field) {
|
||||
$groups[$field['group_name'] . '-' . $field['type_name']]['children'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements of hook_install().
|
||||
*
|
||||
* Because this is a new module in D7, hook_update_N() doesn't help D6
|
||||
* users who upgrade to run the migration path. So, we try that here as
|
||||
* the module is being installed.
|
||||
*/
|
||||
function field_group_install() {
|
||||
|
||||
$groups = _field_group_install_read_groups();
|
||||
module_load_include('module', 'field_group');
|
||||
|
||||
if (!empty($groups)) {
|
||||
|
||||
module_load_include('module', 'ctools');
|
||||
ctools_include('export');
|
||||
|
||||
foreach ($groups as $group) {
|
||||
|
||||
$group = (object) $group;
|
||||
|
||||
$new = new stdClass();
|
||||
$new->group_name = $group->group_name;
|
||||
$new->entity_type = 'node';
|
||||
$new->bundle = $group->type_name;
|
||||
$new->label = $group->label;
|
||||
$new->parent_name = '';
|
||||
$new->children = array();
|
||||
foreach ($group->children as $child) {
|
||||
$new->children[] = $child['field_name'];
|
||||
}
|
||||
|
||||
// The form.
|
||||
$new->id = NULL;
|
||||
$new->weight = $group->weight;
|
||||
$new->mode = 'form';
|
||||
$new->format_type = 'fieldset';
|
||||
$new->format_settings = array(
|
||||
'formatter' => preg_match("/fieldset/", $group->settings['form']['style']) ? 'collapsible' : 'collapsed',
|
||||
'instance_settings' => array(),
|
||||
);
|
||||
$new->identifier = $new->group_name . '|' . $new->entity_type . '|' . $new->bundle . '|' . $new->mode;
|
||||
ctools_export_crud_save('field_group', $new);
|
||||
|
||||
// The full node.
|
||||
$new->id = NULL;
|
||||
$new->weight = $group->weight;
|
||||
$new->mode = 'default';
|
||||
$new->format_type = $group->settings['display']['full']['format'];
|
||||
$new->format_settings = array(
|
||||
'formatter' => 'collapsible',
|
||||
'instance_settings' => array(),
|
||||
);
|
||||
$new->identifier = $new->group_name . '|' . $new->entity_type . '|' . $new->bundle . '|' . $new->mode;
|
||||
ctools_export_crud_save('field_group', $new);
|
||||
|
||||
// The teaser node.
|
||||
$new->id = NULL;
|
||||
$new->weight = $group->weight;
|
||||
$new->mode = 'teaser';
|
||||
$new->format_type = $group->settings['display']['teaser']['format'];
|
||||
$new->format_settings = array(
|
||||
'formatter' => 'collapsible',
|
||||
'instance_settings' => array(),
|
||||
);
|
||||
$new->identifier = $new->group_name . '|' . $new->entity_type . '|' . $new->bundle . '|' . $new->mode;
|
||||
ctools_export_crud_save('field_group', $new);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set weight to 1.
|
||||
db_update('system')
|
||||
->fields(array('weight' => 1))
|
||||
->condition('name', 'field_group')
|
||||
->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hook on the field_group table to add an unique identifier.
|
||||
*/
|
||||
function field_group_update_7001() {
|
||||
|
||||
if (!db_field_exists('field_group', 'identifier')) {
|
||||
// Add the new string identifier field for ctools.
|
||||
db_add_field('field_group', 'identifier', array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'description' => 'The unique string identifier for a group.',
|
||||
));
|
||||
// Force drupal's schema to be rebuilt
|
||||
drupal_get_schema('field_group', TRUE);
|
||||
|
||||
module_load_include('module', 'field_group');
|
||||
_field_group_recreate_identifiers();
|
||||
|
||||
}
|
||||
|
||||
db_update('system')
|
||||
->fields(array('weight' => 1))
|
||||
->condition('name', 'field_group')
|
||||
->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hook to clear cache for new changes to take effect.
|
||||
*/
|
||||
function field_group_update_7002() {
|
||||
|
||||
module_load_include('module', 'field_group');
|
||||
|
||||
// This hook is called to satify people with older version of field_group.
|
||||
// This will recreate all identifiers for the field_groups known in database.
|
||||
// At the moment, we only trigger field_groups that are stored in the database, where
|
||||
// we should maybe get all field_groups as ctools has registered them.
|
||||
// See http://drupal.org/node/1169146.
|
||||
// See http://drupal.org/node/1018550.
|
||||
_field_group_recreate_identifiers();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hook to recreate identifiers.
|
||||
* @see function field_group_update_7002.
|
||||
*/
|
||||
function field_group_update_7003() {
|
||||
|
||||
module_load_include('module', 'field_group');
|
||||
_field_group_recreate_identifiers();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hook to make sure identifier is set as unique key.
|
||||
*/
|
||||
function field_group_update_7004() {
|
||||
db_drop_unique_key('field_group', 'identifier');
|
||||
db_add_unique_key('field_group', 'identifier', array('identifier'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all existing groups and removes optional HTML classes
|
||||
* while adding them as extra classes.
|
||||
*/
|
||||
function field_group_update_7005() {
|
||||
|
||||
// Migrate the field groups so they have a unique identifier.
|
||||
$result = db_select('field_group', 'fg')
|
||||
->fields('fg')
|
||||
->execute();
|
||||
$rows = array();
|
||||
foreach($result as $row) {
|
||||
//$row->identifier = $row->group_name . '|' . $row->entity_type . '|' . $row->bundle . '|' . $row->mode;
|
||||
$row->data = unserialize($row->data);
|
||||
$classes = explode(" ", $row->data['format_settings']['instance_settings']['classes']);
|
||||
$optional_classes = array(str_replace("_", "-", $row->group_name), 'field-group-' . $row->data['format_type']);
|
||||
foreach ($optional_classes as $optional_class) {
|
||||
if (!in_array($optional_class, $classes)) {
|
||||
$classes[] = $optional_class;
|
||||
}
|
||||
}
|
||||
$row->data['format_settings']['instance_settings']['classes'] = implode(" ", $classes);
|
||||
$rows[] = $row;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
drupal_write_record('field_group', $row, array('id'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all optional HTML classes for fieldgroups located in features.
|
||||
* If you need the optional classes, recreate feature after this update.
|
||||
* If not, you can revert it.
|
||||
*/
|
||||
function field_group_update_7006() {
|
||||
ctools_include("export");
|
||||
// Migrate the field groups so they have a unique identifier.
|
||||
$field_groups = ctools_export_load_object("field_group");
|
||||
foreach ($field_groups as $row) {
|
||||
|
||||
// Only update feature field_groups this time.
|
||||
// Don't touch the fieldgroups in db.
|
||||
if ($row->export_type == EXPORT_IN_CODE) {
|
||||
$classes = explode(" ", $row->data['format_settings']['instance_settings']['classes']);
|
||||
$optional_classes = array(str_replace("_", "-", $row->group_name), 'field-group-' . $row->data['format_type']);
|
||||
foreach ($optional_classes as $optional_class) {
|
||||
if (!in_array($optional_class, $classes)) {
|
||||
$classes[] = $optional_class;
|
||||
}
|
||||
}
|
||||
$row->data['format_settings']['instance_settings']['classes'] = implode(" ", $classes);
|
||||
unset($row->id);
|
||||
drupal_write_record('field_group', $row);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
224
field_group.js
Normal file
224
field_group.js
Normal file
@ -0,0 +1,224 @@
|
||||
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processFieldset = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
// Add required fields mark to any fieldsets containing required fields
|
||||
$('fieldset.fieldset', context).once('fieldgroup-effects', function(i) {
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$('legend span.fieldset-legend', $(this)).eq(0).append(' ').append($('.form-required').eq(0).clone());
|
||||
}
|
||||
if ($('.error', $(this)).length) {
|
||||
$('legend span.fieldset-legend', $(this)).eq(0).addClass('error');
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processAccordion = {
|
||||
execute: function (context, settings, type) {
|
||||
$('div.field-group-accordion-wrapper', context).once('fieldgroup-effects', function () {
|
||||
var wrapper = $(this);
|
||||
|
||||
wrapper.accordion({
|
||||
autoHeight: false,
|
||||
active: '.field-group-accordion-active',
|
||||
collapsible: true,
|
||||
changestart: function(event, ui) {
|
||||
if ($(this).hasClass('effect-none')) {
|
||||
ui.options.animated = false;
|
||||
}
|
||||
else {
|
||||
ui.options.animated = 'slide';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (type == 'form') {
|
||||
|
||||
var $firstErrorItem = false;
|
||||
|
||||
// Add required fields mark to any element containing required fields
|
||||
wrapper.find('div.field-group-accordion-item').each(function(i) {
|
||||
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$('h3.ui-accordion-header a').eq(i).append(' ').append($('.form-required').eq(0).clone());
|
||||
}
|
||||
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'});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processHtabs = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
// Add required fields mark to any element containing required fields
|
||||
$('fieldset.horizontal-tabs-pane', context).once('fieldgroup-effects', function(i) {
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$(this).data('horizontalTab').link.find('strong:first').after($('.form-required').eq(0).clone()).after(' ');
|
||||
}
|
||||
if ($('.error', $(this)).length) {
|
||||
$(this).data('horizontalTab').link.parent().addClass('error');
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('horizontalTab').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processTabs = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
// Add required fields mark to any fieldsets containing required fields
|
||||
$('fieldset.vertical-tabs-pane', context).once('fieldgroup-effects', function(i) {
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$(this).data('verticalTab').link.find('strong:first').after($('.form-required').eq(0).clone()).after(' ');
|
||||
}
|
||||
if ($('.error', $(this)).length) {
|
||||
$(this).data('verticalTab').link.parent().addClass('error');
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('verticalTab').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*
|
||||
* TODO clean this up meaning check if this is really
|
||||
* necessary.
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processDiv = {
|
||||
execute: function (context, settings, type) {
|
||||
|
||||
$('div.collapsible', context).once('fieldgroup-effects', function() {
|
||||
var $wrapper = $(this);
|
||||
|
||||
// Turn the legend into a clickable link, but retain span.field-group-format-toggler
|
||||
// for CSS positioning.
|
||||
|
||||
var $toggler = $('span.field-group-format-toggler:first', $wrapper);
|
||||
var $link = $('<a class="field-group-format-title" href="#"></a>');
|
||||
$link.prepend($toggler.contents());
|
||||
|
||||
// Add required field markers if needed
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$link.append(' ').append($('.form-required').eq(0).clone());
|
||||
}
|
||||
|
||||
$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-format-wrapper', wrapper).toggle();
|
||||
}
|
||||
else if ($wrapper.hasClass('effect-blind')) {
|
||||
$('> .field-group-format-wrapper', wrapper).toggle('blind', {}, speed);
|
||||
}
|
||||
else {
|
||||
$('> .field-group-format-wrapper', wrapper).toggle(speed);
|
||||
}
|
||||
wrapper.animating = false;
|
||||
}
|
||||
$wrapper.toggleClass('collapsed');
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Behaviors.
|
||||
*/
|
||||
Drupal.behaviors.fieldGroup = {
|
||||
attach: function (context, settings) {
|
||||
if (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 (settings.field_group[type] != undefined && $.isFunction(this.execute)) {
|
||||
this.execute(context, settings, settings.field_group[type]);
|
||||
}
|
||||
});
|
||||
|
||||
// Fixes css for fieldgroups under vertical tabs.
|
||||
$('.fieldset-wrapper .fieldset > legend').css({display: 'block'});
|
||||
$('.vertical-tabs fieldset.fieldset').addClass('default-fallback');
|
||||
|
||||
|
||||
// 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 fieldgorupID = 'field_group-' + $(this).attr('id') + ' ' + $(this).attr('id');
|
||||
$(this).attr('id', fieldgorupID);
|
||||
})
|
||||
// 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);
|
2139
field_group.module
Normal file
2139
field_group.module
Normal file
File diff suppressed because it is too large
Load Diff
15
horizontal-tabs/horizontal-tabs-rtl.css
Normal file
15
horizontal-tabs/horizontal-tabs-rtl.css
Normal file
@ -0,0 +1,15 @@
|
||||
div.horizontal-tabs {
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.horizontal-tabs ul.horizontal-tabs-list {
|
||||
border-right: 0;
|
||||
border-left: 1px solid #dedede;
|
||||
}
|
||||
|
||||
/* Layout of each tab */
|
||||
.horizontal-tabs ul.horizontal-tabs-list li {
|
||||
border-right: 0;
|
||||
border-left: 1px solid #ccc;
|
||||
float: right;
|
||||
}
|
101
horizontal-tabs/horizontal-tabs.css
Normal file
101
horizontal-tabs/horizontal-tabs.css
Normal file
@ -0,0 +1,101 @@
|
||||
div.horizontal-tabs {
|
||||
margin: 0 0 1em 0; /* LTR */
|
||||
padding: 0;
|
||||
border: 1px solid #ccc;
|
||||
position: relative; /* IE6/7 */
|
||||
}
|
||||
|
||||
.horizontal-tabs ul.horizontal-tabs-list {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0px;
|
||||
position: relative; /* IE6 */
|
||||
list-style: none;
|
||||
list-style-image: none; /* IE6 */
|
||||
background-color: #dedede;
|
||||
border-right: 1px solid #dedede; /* LTR */
|
||||
width: 100%;
|
||||
height: auto;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.horizontal-tabs fieldset.horizontal-tabs-pane {
|
||||
padding: 0 1em;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
fieldset.horizontal-tabs-pane > legend,
|
||||
fieldset.vertical-tabs-pane fieldset.horizontal-tabs-pane > legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Layout of each tab */
|
||||
.horizontal-tabs ul.horizontal-tabs-list li {
|
||||
background: #eee;
|
||||
border-right: 1px solid #ccc; /* LTR */
|
||||
padding: 1px;
|
||||
padding-top: 0;
|
||||
margin: 0;
|
||||
min-width: 5em; /* IE7 */
|
||||
float: left; /* LTR */
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li.selected {
|
||||
background-color: #fff;
|
||||
padding: 0 0 1px 0;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 0.5em 0.6em;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:hover {
|
||||
outline: none;
|
||||
background-color: #ededdd;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li:hover,
|
||||
.horizontal-tabs ul.horizontal-tabs-list li:focus {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list :focus {
|
||||
outline: none;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:focus strong,
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:active strong,
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:hover strong {
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
.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-tabs ul.horizontal-tabs-list .selected strong {
|
||||
color: #000;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list .summary {
|
||||
display: block;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs ul.horizontal-tabs-list .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%;
|
||||
}
|
203
horizontal-tabs/horizontal-tabs.js
Normal file
203
horizontal-tabs/horizontal-tabs.js
Normal file
@ -0,0 +1,203 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$('.horizontal-tabs-panes', context).once('horizontal-tabs', function () {
|
||||
var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
|
||||
var tab_focus;
|
||||
|
||||
// Check if there are some fieldsets that can be converted to horizontal-tabs
|
||||
var $fieldsets = $('> fieldset', this);
|
||||
if ($fieldsets.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 fieldset into a tab.
|
||||
$fieldsets.each(function (i) {
|
||||
var horizontal_tab = new Drupal.horizontalTab({
|
||||
title: $('> legend', this).text(),
|
||||
fieldset: $(this)
|
||||
});
|
||||
horizontal_tab.item.addClass('horizontal-tab-button-' + i);
|
||||
tab_list.append(horizontal_tab.item);
|
||||
$(this)
|
||||
.removeClass('collapsible collapsed')
|
||||
.addClass('horizontal-tabs-pane')
|
||||
.data('horizontalTab', horizontal_tab);
|
||||
if (this.id == focusID) {
|
||||
tab_focus = $(this);
|
||||
}
|
||||
});
|
||||
|
||||
$('> li:first', tab_list).addClass('first');
|
||||
$('> li:last', tab_list).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.
|
||||
if (window.location.hash && window.location.hash !== '#' && $(window.location.hash, this).length) {
|
||||
tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane');
|
||||
}
|
||||
else {
|
||||
tab_focus = $('> .horizontal-tabs-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (tab_focus.length) {
|
||||
tab_focus.data('horizontalTab').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The horizontal tab object represents a single tab within a tab group.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - fieldset: The jQuery object of the fieldset that is the tab pane.
|
||||
*/
|
||||
Drupal.horizontalTab = function (settings) {
|
||||
var self = this;
|
||||
$.extend(this, settings, Drupal.theme('horizontalTab', settings));
|
||||
|
||||
this.link.click(function () {
|
||||
self.focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.link.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible fieldset/tab pane.
|
||||
$("fieldset.horizontal-tabs-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Only bind update summary on forms.
|
||||
if (this.fieldset.drupalGetSummary) {
|
||||
this.fieldset.bind('summaryUpdated', function() {
|
||||
self.updateSummary();
|
||||
}).trigger('summaryUpdated');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Drupal.horizontalTab.prototype = {
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.fieldset
|
||||
.removeClass('horizontal-tab-hidden')
|
||||
.siblings('fieldset.horizontal-tabs-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('horizontalTab');
|
||||
tab.fieldset.addClass('horizontal-tab-hidden');
|
||||
tab.item.removeClass('selected');
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.horizontal-tabs-active-tab')
|
||||
.val(this.fieldset.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="element-invisible">' + Drupal.t('(active tab)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the tab's summary.
|
||||
*/
|
||||
updateSummary: function () {
|
||||
this.summary.html(this.fieldset.drupalGetSummary());
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
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 fieldset.
|
||||
this.fieldset.removeClass('horizontal-tab-hidden');
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
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 fieldset.
|
||||
this.fieldset.addClass('horizontal-tab-hidden');
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.fieldset.siblings('.horizontal-tabs-pane:not(.horizontal-tab-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('horizontalTab').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a horizontal tab.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* 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.prototype.horizontalTab = function (settings) {
|
||||
var tab = {};
|
||||
var idAttr = settings.fieldset.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.fieldset.drupalGetSummary) {
|
||||
tab.link.append(tab.summary = $('<span class="summary"></span>'))
|
||||
}
|
||||
|
||||
return tab;
|
||||
};
|
||||
|
||||
})(jQuery);
|
13
multipage/multipage-rtl.css
Normal file
13
multipage/multipage-rtl.css
Normal file
@ -0,0 +1,13 @@
|
||||
.multipage-controls-list #edit-actions {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.multipage-button {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.multipage-counter{
|
||||
float: left !important;
|
||||
margin-right: 0 !important;
|
||||
margin-left: 5px !important;
|
||||
}
|
128
multipage/multipage.css
Normal file
128
multipage/multipage.css
Normal file
@ -0,0 +1,128 @@
|
||||
.multipage-controls-list #edit-actions {
|
||||
float: left; /* LTR */
|
||||
}
|
||||
|
||||
.multipage-button {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 0;
|
||||
float: left; /* LTR */
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.multipage-button a {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.multipage-counter {
|
||||
float: right; /* LTR */
|
||||
margin-right: 5px; /* LTR */
|
||||
height: 0;
|
||||
position: relative;
|
||||
top: 1.8em;
|
||||
line-height: 30px;
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
a.multipage-link-previous {
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color:#666;
|
||||
-webkit-transition: color 218ms;
|
||||
-moz-transition: color 218ms;
|
||||
-o-transition: color 218ms;
|
||||
transition: color 218ms;
|
||||
}
|
||||
|
||||
a.multipage-link-previous:hover {
|
||||
text-decoration:none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit {
|
||||
background:none;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
text-shadow: 0 1px 0 white;
|
||||
padding: 7px 12px;
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(whiteSmoke),to(#F1F1F1));
|
||||
-o-transition: border-top-color 0.218s,border-right-color 0.218s,border-bottom-color 0.218s,border-left-color .218s;
|
||||
-webkit-transition: border-color .218s;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit:hover {
|
||||
color:#333;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
border-color: #939393;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit:active {
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F1F1F1),to(whiteSmoke));
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit {
|
||||
background: #4D90FE; /* for non-css3 browsers */
|
||||
background-image: #4D90FE; /* for non-css3 browsers */
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#4787ed);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4D90FE', endColorstr='#4787ED'); /* for IE */
|
||||
background: -webkit-gradient(linear, center top, center bottom, from(#4D90FE), to(#4787ED)); /* for webkit browsers */
|
||||
background: -moz-linear-gradient(center top, #4D90FE, #4787ED); /* for firefox 3.6+ */
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
text-transform: uppercase;
|
||||
min-width: 79px;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit:hover {
|
||||
background-image: -moz-linear-gradient(top,#4d90fe,#357ae8);
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#357ae8);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit:active {
|
||||
background: #4D90FE;
|
||||
border-color: #2F5BB7;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#d14836);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#d14836);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836));
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid transparent;
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete:hover {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#c53727);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#c53727);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#c53727));
|
||||
border: 1px solid #B0281A!important;
|
||||
border-bottom: 1px solid #AF301F!important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete:active {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#b0281a);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#b0281a);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#b0281a));
|
||||
border: 1px solid #992A1b!important;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
}
|
268
multipage/multipage.js
Normal file
268
multipage/multipage.js
Normal file
@ -0,0 +1,268 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* This script transforms a set of wrappers into a stack of multipage pages.
|
||||
* Another pane can be entered by clicking next/previous.
|
||||
*
|
||||
*/
|
||||
Drupal.behaviors.MultiPage = {
|
||||
attach: function (context) {
|
||||
$('.multipage-panes', context).once('multipage', function () {
|
||||
|
||||
var focusID = $(':hidden.multipage-active-control', this).val();
|
||||
var paneWithFocus;
|
||||
|
||||
// Check if there are some wrappers that can be converted to multipages.
|
||||
var $panes = $('> div.field-group-multipage', this);
|
||||
var $form = $panes.parents('form');
|
||||
if ($panes.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the next/previous controls.
|
||||
var $controls;
|
||||
|
||||
// Transform each div.multipage-pane into a multipage with controls.
|
||||
$panes.each(function () {
|
||||
|
||||
$controls = $('<div class="multipage-controls-list clearfix"></div>');
|
||||
$(this).append($controls);
|
||||
|
||||
// Check if the submit button needs to move to the latest pane.
|
||||
if (Drupal.settings.field_group.multipage_move_submit && $('.form-actions').length) {
|
||||
$('.form-actions', $form).remove().appendTo($($controls, $panes.last()));
|
||||
}
|
||||
|
||||
var multipageControl = new Drupal.multipageControl({
|
||||
title: $('> .multipage-pane-title', this).text(),
|
||||
wrapper: $(this),
|
||||
has_next: $(this).next().length,
|
||||
has_previous: $(this).prev().length
|
||||
});
|
||||
|
||||
$controls.append(multipageControl.item);
|
||||
$(this)
|
||||
.addClass('multipage-pane')
|
||||
.data('multipageControl', multipageControl);
|
||||
|
||||
if (this.id == focusID) {
|
||||
paneWithFocus = $(this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (paneWithFocus === undefined) {
|
||||
// If the current URL has a fragment and one of the tabs contains an
|
||||
// element that matches the URL fragment, activate that tab.
|
||||
if (window.location.hash && window.location.hash !== '#' && $(window.location.hash, this).length) {
|
||||
paneWithFocus = $(window.location.hash, this).closest('.multipage-pane');
|
||||
}
|
||||
else {
|
||||
paneWithFocus = $('multipage-open', this).length ? $('multipage-open', this) : $('> .multipage-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (paneWithFocus !== undefined) {
|
||||
paneWithFocus.data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The multipagePane object represents a single div as a page.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - wrapper: The jQuery object of the <div> that is the tab pane.
|
||||
*/
|
||||
Drupal.multipageControl = function (settings) {
|
||||
var self = this;
|
||||
var controls = Drupal.theme('multipage', settings);
|
||||
$.extend(self, settings, controls);
|
||||
|
||||
this.nextLink.click(function () {
|
||||
self.nextPage();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.previousLink.click(function () {
|
||||
self.previousPage();
|
||||
return false;
|
||||
});
|
||||
|
||||
/*
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.nextLink.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible wrapper/tab pane.
|
||||
$("div.multipage-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Pressing the Enter key lets you leave the tab again.
|
||||
this.wrapper.keydown(function(event) {
|
||||
// Enter key should not trigger inside <textarea> to allow for multi-line entries.
|
||||
if (event.keyCode == 13 && event.target.nodeName != "TEXTAREA") {
|
||||
// Set focus on the selected tab button again.
|
||||
$(".multipage-tab-button.selected a").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
Drupal.multipageControl.prototype = {
|
||||
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.wrapper
|
||||
.show()
|
||||
.siblings('div.multipage-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('multipageControl');
|
||||
tab.wrapper.hide();
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.multipage-active-control')
|
||||
.val(this.wrapper.attr('id'));
|
||||
// Mark the active control for screen readers.
|
||||
$('#active-multipage-control').remove();
|
||||
this.nextLink.after('<span id="active-multipage-control" class="element-invisible">' + Drupal.t('(active page)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Continues to the next page or step in the form.
|
||||
*/
|
||||
nextPage: function () {
|
||||
this.wrapper.next().data('multipageControl').focus();
|
||||
$('html, body').scrollTop(this.wrapper.parents('.field-group-multipage-group-wrapper').offset().top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns to the previous page or step in the form.
|
||||
*/
|
||||
previousPage: function () {
|
||||
this.wrapper.prev().data('multipageControl').focus();
|
||||
$('html, body').scrollTop(this.wrapper.parents('.field-group-multipage-group-wrapper').offset().top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.show();
|
||||
// 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('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Display the wrapper.
|
||||
this.wrapper.removeClass('multipage-control-hidden').show();
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
tabHide: function () {
|
||||
// Hide this tab.
|
||||
this.item.hide();
|
||||
// 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('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Hide the wrapper.
|
||||
this.wrapper.addClass('horizontal-tab-hidden').hide();
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.wrapper.siblings('.multipage-pane:not(.multipage-control-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('multipageControl').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a multipage control.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* This function has to return an object with at least these keys:
|
||||
* - item: The root tab jQuery element
|
||||
* - nextLink: The anchor tag that acts as the clickable area of the control
|
||||
* - nextTitle: The jQuery element that contains the group title
|
||||
* - previousLink: The anchor tag that acts as the clickable area of the control
|
||||
* - previousTitle: The jQuery element that contains the group title
|
||||
*/
|
||||
Drupal.theme.prototype.multipage = function (settings) {
|
||||
|
||||
var controls = {};
|
||||
controls.item = $('<span class="multipage-button"></span>');
|
||||
|
||||
controls.previousLink = $('<input type="button" class="form-submit multipage-link-previous" value="" />');
|
||||
controls.previousTitle = Drupal.t('Previous page');
|
||||
controls.item.append(controls.previousLink.val(controls.previousTitle));
|
||||
|
||||
controls.nextLink = $('<input type="button" class="form-submit multipage-link-next" value="" />');
|
||||
controls.nextTitle = Drupal.t('Next page');
|
||||
controls.item.append(controls.nextLink.val(controls.nextTitle));
|
||||
|
||||
if (!settings.has_next) {
|
||||
controls.nextLink.hide();
|
||||
}
|
||||
if (!settings.has_previous) {
|
||||
controls.previousLink.hide();
|
||||
}
|
||||
|
||||
return controls;
|
||||
};
|
||||
|
||||
|
||||
Drupal.FieldGroup = Drupal.FieldGroup || {};
|
||||
Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processMultipage = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
|
||||
var $firstErrorItem = false;
|
||||
|
||||
// Add required fields mark to any element containing required fields
|
||||
$('div.multipage-pane').each(function(i){
|
||||
if ($('.error', $(this)).length) {
|
||||
|
||||
// Save first error item, for focussing it.
|
||||
if (!$firstErrorItem) {
|
||||
$firstErrorItem = $(this).data('multipageControl');
|
||||
}
|
||||
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Focus on first multipage that has an error.
|
||||
if ($firstErrorItem) {
|
||||
$firstErrorItem.focus();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
416
tests/field_group.display.test
Normal file
416
tests/field_group.display.test
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test file for fieldgroup display.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group display tests
|
||||
*/
|
||||
class GroupDisplayTestCase extends DrupalWebTestCase {
|
||||
|
||||
protected $node;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Display tests',
|
||||
'description' => 'Test the field group display.',
|
||||
'group' => 'Field group',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
|
||||
parent::setUp('field_test', 'field_group', 'field_group_test');
|
||||
|
||||
$node = new stdClass();
|
||||
$node->type = 'article';
|
||||
$node->title = $this->randomName();
|
||||
$node->status = 1;
|
||||
|
||||
// Create test fields.
|
||||
$test_fields = array('field_test', 'field_test_2', 'field_no_access');
|
||||
foreach ($test_fields as $field_name) {
|
||||
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
);
|
||||
$instance = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'label' => $this->randomName(),
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomName(),
|
||||
),
|
||||
),
|
||||
'teaser' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomName(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
field_create_field($field);
|
||||
field_create_instance($instance);
|
||||
|
||||
$node->{$field_name}[LANGUAGE_NONE][0]['value'] = mt_rand(1, 127);
|
||||
}
|
||||
|
||||
node_save($node);
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new group.
|
||||
* @param array $data
|
||||
* Data for the field group.
|
||||
*/
|
||||
function createGroup($mode, array $data) {
|
||||
|
||||
$group_name = 'group_' . drupal_strtolower($this->randomName(8));
|
||||
$identifier = $group_name . '|node|article|' . $mode;
|
||||
|
||||
$field_group = new stdClass;
|
||||
$field_group->disabled = FALSE;
|
||||
$field_group->api_version = 1;
|
||||
$field_group->identifier = $identifier;
|
||||
$field_group->group_name = $group_name;
|
||||
$field_group->entity_type = 'node';
|
||||
$field_group->bundle = 'article';
|
||||
$field_group->mode = $mode;
|
||||
$field_group->parent_name = '';
|
||||
$field_group->children = $data['children'];
|
||||
$field_group->data = $data;
|
||||
drupal_write_record('field_group', $field_group);
|
||||
ctools_export_crud_enable('field_group', $field_group->identifier);
|
||||
|
||||
return $field_group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if an empty formatter.
|
||||
*/
|
||||
function testFieldAccess() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Wrapper',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_no_access',
|
||||
),
|
||||
'format_type' => 'div',
|
||||
'format_settings' => array(
|
||||
'label' => 'Link',
|
||||
'instance_settings' => array(
|
||||
'required_fields' => 0,
|
||||
'id' => 'wrapper-id',
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
'show_label' => FALSE,
|
||||
'label_element' => 'h3',
|
||||
'effect' => 'blink',
|
||||
'speed' => 'fast',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$group = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// 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 div formatter.
|
||||
*/
|
||||
function testDiv() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Wrapper',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'div',
|
||||
'format_settings' => array(
|
||||
'label' => 'Link',
|
||||
'instance_settings' => array(
|
||||
'required_fields' => 0,
|
||||
'id' => 'wrapper-id',
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
'show_label' => FALSE,
|
||||
'label_element' => 'h3',
|
||||
'effect' => 'blink',
|
||||
'speed' => 'fast',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$group = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test group ids and classes.
|
||||
$this->assertFieldByXPath("//div[contains(@id, 'wrapper-id')]", NULL, t('Wrapper id set on wrapper div'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, t('Test class set on wrapper div') . 'class="' . $group->group_name . ' test-class');
|
||||
|
||||
// Test group label.
|
||||
$this->assertNoRaw('<h3><span>' . $data['label'] . '</span></h3>', t('Label is not shown'));
|
||||
|
||||
// Set show label to true.
|
||||
$group->data['format_settings']['instance_settings']['show_label'] = TRUE;
|
||||
|
||||
drupal_write_record('field_group', $group, array('identifier'));
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
$this->assertRaw('<h3><span>' . $data['label'] . '</span></h3>', t('Label is shown'));
|
||||
|
||||
// Change to collapsible
|
||||
$group->data['format_settings']['formatter'] = 'collapsible';
|
||||
drupal_write_record('field_group', $group, array('identifier'));
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'speed-fast')]", NULL, t('Speed class is set'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'effect-blink')]", NULL, t('Effect class is set'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the horizontal tabs formatter.
|
||||
*/
|
||||
function testHorizontalTabs() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 1',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'htab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$first_tab = $this->createGroup('default', $data);
|
||||
$first_tab_id = 'node_article_full_' . $first_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'htab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
'description' => 'description of second tab',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$second_tab = $this->createGroup('default', $data);
|
||||
$second_tab_id = 'node_article_full_' . $first_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tabs',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => $first_tab->group_name,
|
||||
1 => $second_tab->group_name,
|
||||
),
|
||||
'format_type' => 'htabs',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
),
|
||||
),
|
||||
);
|
||||
$tabs = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//fieldset[contains(@class, 'test-class-2')]", NULL, t('Test class set on second tab'));
|
||||
$this->assertRaw('<div class="fieldset-description">description of second tab</div>', t('Description of tab is shown'));
|
||||
$this->assertRaw('class="collapsible collapsed test-class-2', t('Second tab is default collapsed'));
|
||||
|
||||
// Test if correctly nested
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$first_tab_id')]", NULL, 'First tab is displayed as child of the wrapper.');
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$second_tab_id')]", NULL, 'Second tab is displayed as child of the wrapper.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the vertical tabs formatter.
|
||||
*/
|
||||
function testVerticalTabs() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 1',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'tab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$first_tab = $this->createGroup('default', $data);
|
||||
$first_tab_id = 'node_article_full_' . $first_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'tab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
'description' => 'description of second tab',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$second_tab = $this->createGroup('default', $data);
|
||||
$second_tab_id = 'node_article_full_' . $first_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tabs',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => $first_tab->group_name,
|
||||
1 => $second_tab->group_name,
|
||||
),
|
||||
'format_type' => 'tabs',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
),
|
||||
),
|
||||
);
|
||||
$tabs = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//fieldset[contains(@class, 'test-class-2')]", NULL, t('Test class set on second tab'));
|
||||
$this->assertRaw('<div class="fieldset-description">description of second tab</div>', t('Description of tab is shown'));
|
||||
$this->assertRaw('class="collapsible collapsed test-class-2', t('Second tab is default collapsed'));
|
||||
|
||||
// Test if correctly nested
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$first_tab_id')]", NULL, 'First tab is displayed as child of the wrapper.');
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$second_tab_id')]", NULL, 'Second tab is displayed as child of the wrapper.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the accordion formatter.
|
||||
*/
|
||||
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',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$first_item = $this->createGroup('default', $data);
|
||||
$first_item_id = 'node_article_full_' . $first_item->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Accordion item 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'accordion-item',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 2',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$second_item = $this->createGroup('default', $data);
|
||||
$second_item_id = 'node_article_full_' . $second_item->group_name;
|
||||
|
||||
$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',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
'effect' => 'bounceslide'
|
||||
),
|
||||
),
|
||||
);
|
||||
$accordion = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'effect-bounceslide')]", NULL, t('Correct effect is set on the accordion'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, t('Accordion item with test-class is shown'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-2')]", NULL, t('Accordion item with test-class-2 is shown'));
|
||||
$this->assertFieldByXPath("//h3[contains(@class, 'field-group-accordion-active')]", NULL, t('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.');
|
||||
}
|
||||
|
||||
}
|
109
tests/field_group.ui.test
Normal file
109
tests/field_group.ui.test
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test file for fieldgroup UI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group UI tests.
|
||||
*/
|
||||
class GroupUITestCase extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'UI tests',
|
||||
'description' => 'Test the field group UI.',
|
||||
'group' => 'Field group',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test', 'field_group', 'field_group_test');
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'access administration pages', 'bypass node access'));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the creation a group on the article content type.
|
||||
*/
|
||||
function createGroup() {
|
||||
|
||||
// Create random group name.
|
||||
$this->group_label = $this->randomName(8);
|
||||
$this->group_name_input = drupal_strtolower($this->randomName(8));
|
||||
$this->group_name = 'group_' . $this->group_name_input;
|
||||
|
||||
// Setup new group.
|
||||
$group = array(
|
||||
'fields[_add_new_group][label]' => $this->group_label,
|
||||
'fields[_add_new_group][group_name]' => $this->group_name_input,
|
||||
);
|
||||
|
||||
// Add new group on the 'Manage fields' page.
|
||||
$this->drupalPost('admin/structure/types/manage/article/fields', $group, t('Save'));
|
||||
|
||||
$this->assertRaw(t('New group %label successfully created.', array('%label' => $this->group_label)), t('Group message displayed on screen.'));
|
||||
|
||||
// Test if group is in the $groups array.
|
||||
$groups = field_group_info_groups('node', 'article', 'form', TRUE);
|
||||
$this->assertTrue(array_key_exists($this->group_name, $groups), t('Group found in groups array'));
|
||||
|
||||
// Add new group on the 'Manage display' page.
|
||||
$this->drupalPost('admin/structure/types/manage/article/display', $group, t('Save'));
|
||||
$this->assertRaw(t('New group %label successfully created.', array('%label' => $this->group_label)), t('Group message displayed on screen.'));
|
||||
|
||||
// Test if group is in the $groups array.
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->assertTrue(array_key_exists($this->group_name, $groups), t('Group found in groups array'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a group.
|
||||
*/
|
||||
function deleteGroup() {
|
||||
|
||||
$this->drupalPost('admin/structure/types/manage/article/groups/' . $this->group_name . '/delete/form', array(), t('Delete'));
|
||||
$this->assertRaw(t('The group %label has been deleted from the %article content type.', array('%label' => $this->group_label, '%article' => 'Article')), t('Group removal message displayed on screen.'));
|
||||
|
||||
// Test that group is not in the $groups array.
|
||||
$groups = field_group_info_groups('node', 'article', 'form', TRUE);
|
||||
$this->assertFalse(array_key_exists($this->group_name, $groups), t('Group not found in groups array while deleting'));
|
||||
|
||||
$this->drupalPost('admin/structure/types/manage/article/groups/' . $this->group_name . '/delete/default', array(), t('Delete'));
|
||||
$this->assertRaw(t('The group %label has been deleted from the %article content type.', array('%label' => $this->group_label, '%article' => 'Article')), t('Group removal message displayed on screen.'));
|
||||
|
||||
// Test that group is not in the $groups array.
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->assertFalse(array_key_exists($this->group_name, $groups), t('Group not found in groups array while deleting'));
|
||||
}
|
||||
|
||||
/**
|
||||
* General CRUD.
|
||||
*/
|
||||
function testCRUDGroup() {
|
||||
$this->createGroup();
|
||||
$this->deleteGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest a field underneath a group.
|
||||
*/
|
||||
function testNestField() {
|
||||
|
||||
$this->createGroup();
|
||||
|
||||
$edit = array(
|
||||
'fields[field_image][parent]' => $this->group_name,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/article/fields', $edit, t('Save'));
|
||||
$this->assertRaw(t('Your settings have been saved.'), t('Settings saved'));
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'form', TRUE);
|
||||
$this->assertTrue(in_array('field_image', $groups[$this->group_name]->children), t('Image is a child of %group', array('%group' => $this->group_name)));
|
||||
}
|
||||
|
||||
}
|
||||
|
12
tests/field_group_test.info
Normal file
12
tests/field_group_test.info
Normal file
@ -0,0 +1,12 @@
|
||||
name = "Fieldgroup Test"
|
||||
description = "Test module for fieldgroup"
|
||||
core = "7.x"
|
||||
package = "Fieldgroup"
|
||||
|
||||
|
||||
; Information added by drupal.org packaging script on 2013-09-25
|
||||
version = "7.x-1.3"
|
||||
core = "7.x"
|
||||
project = "field_group"
|
||||
datestamp = "1380124361"
|
||||
|
17
tests/field_group_test.module
Normal file
17
tests/field_group_test.module
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Fieldgroup test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_access().
|
||||
*/
|
||||
function field_group_test_field_access($op, $field, $entity_type, $entity, $account) {
|
||||
// Set access to false for field_no_access.
|
||||
if ($op == 'view' && $field['field_name'] == 'field_no_access') {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user