first import
This commit is contained in:
339
sites/all/modules/menu_link/LICENSE.txt
Normal file
339
sites/all/modules/menu_link/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.
|
48
sites/all/modules/menu_link/README.txt
Normal file
48
sites/all/modules/menu_link/README.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
-- SUMMARY --
|
||||
|
||||
The Menu link module defines a menu link field type.
|
||||
|
||||
Drupal's core Menu module allows nodes to place menu links (linking to the node)
|
||||
into the menu. The Menu link module however allows entities of any type to place
|
||||
menu links into the menu using a field.
|
||||
|
||||
-- Menu link as a field --
|
||||
|
||||
* One of the benefits of fields is that they automatically are available to
|
||||
other modules (like Views).
|
||||
* The Field API also provides the possibility to use separate widget and
|
||||
formatters per entity type (and bundle). Contrib modules can provide all kinds
|
||||
of new widgets and formatters.
|
||||
* Another benefit of the Menu link module is that it provides revisioning for
|
||||
menu links. Their title and location in the menu for example are stored per
|
||||
revision.
|
||||
|
||||
-- Menu module integration --
|
||||
|
||||
The Menu link module integrates seamlessly into the Menu module. The UI for node
|
||||
types, to configure which menus are available, isn't altered.
|
||||
|
||||
The "Menu settings" tab on node edit forms is however removed and replaced by a
|
||||
field widget (To put the widget into a vertical tab use the Field group module).
|
||||
|
||||
Now that menu links are stored as a field it is thus possible to use different
|
||||
widgets per node type.
|
||||
|
||||
-- DEVELOPERS --
|
||||
|
||||
* Relations between menu links and entities
|
||||
|
||||
The Menu link module provides one predefined field stored in a fixed database
|
||||
table (using field_sql_storage) named {field_data_menu_link}. Using a fixed
|
||||
table allows other modules to use this table to retrieve and build upon
|
||||
relations between menu links and entities (This field is also being used for
|
||||
the integration with the Menu module).
|
||||
|
||||
* Menu API additions
|
||||
|
||||
To speed up saving/deleting of menu links, the Menu link provides a couple of
|
||||
additions to the Menu API. Those may be used by other modules.
|
||||
|
||||
* menu_link_load_multiple(array $mlids, array $conditions = array())
|
||||
* menu_link_delete_multiple(array $mlids)
|
11
sites/all/modules/menu_link/menu_link.field.css
Normal file
11
sites/all/modules/menu_link/menu_link.field.css
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
.field-widget-menu-link-default .menu-link-item .form-item {
|
||||
float: left;
|
||||
margin: 0 1em 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.field-widget-menu-link-default .menu-link-item .form-item.form-type-textarea {
|
||||
float: none;
|
||||
clear: both;
|
||||
}
|
619
sites/all/modules/menu_link/menu_link.field.inc
Normal file
619
sites/all/modules/menu_link/menu_link.field.inc
Normal file
@@ -0,0 +1,619 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines a menu link field type.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_info().
|
||||
*
|
||||
* Field settings:
|
||||
* - link_path_field: Boolean whether or not the link path field is available
|
||||
* to users. If set to FALSE enity_uri() is used to determine the link path.
|
||||
* Instance settings:
|
||||
* - menu_options: The menus available to place links in for this field.
|
||||
*/
|
||||
function menu_link_field_info() {
|
||||
return array(
|
||||
'menu_link' => array(
|
||||
'label' => t('Menu link'),
|
||||
'description' => t('This field stores a reference to a menu link in the database.'),
|
||||
'settings' => array(
|
||||
'link_path_field' => FALSE,
|
||||
),
|
||||
'instance_settings' => array(
|
||||
'menu_options' => array('main-menu'),
|
||||
),
|
||||
'default_widget' => 'menu_link_default',
|
||||
'default_formatter' => 'menu_link_link',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_settings_form().
|
||||
*/
|
||||
function menu_link_field_settings_form($field, $instance, $has_data) {
|
||||
$settings = $field['settings'];
|
||||
|
||||
$form = array();
|
||||
|
||||
/* @todo Allow menu link fields to be used as a reference field? http://drupal.org/node/1028344
|
||||
$form['link_path_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Path</em> field'),
|
||||
'#default_value' => $settings['link_path_field'],
|
||||
'#description' => t('Allow users to set the path of menu links. When not checked the uri of the entity being edited is used.'),
|
||||
);*/
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_instance_settings_form().
|
||||
*/
|
||||
function menu_link_field_instance_settings_form($field, $instance) {
|
||||
$settings = $instance['settings'];
|
||||
|
||||
$form['menu_options'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Available menus'),
|
||||
'#default_value' => $settings['menu_options'],
|
||||
'#options' => menu_get_menus(),
|
||||
'#description' => t('The menus available to place links in for this field.'),
|
||||
'#required' => TRUE,
|
||||
'#weight' => -5,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_load().
|
||||
*/
|
||||
function menu_link_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
|
||||
foreach ($entities as $id => $entity) {
|
||||
foreach ($items[$id] as $delta => &$item) {
|
||||
$item['options'] = !empty($item['options']) ? unserialize($item['options']) : array();
|
||||
|
||||
$item['external'] = (!empty($item['link_path']) && $item['link_path'] != '<front>' && url_is_external($item['link_path'])) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_validate().
|
||||
*
|
||||
* Possible error codes:
|
||||
* - 'menu_link_duplicate': A menu link can only be referenced once per entity
|
||||
* per field.
|
||||
* - 'menu_link_invalid_parent': Selected parent menu link does not exist or may
|
||||
* not be selected.
|
||||
* - 'menu_link_no_entity_uri': No link path is provided while entity being
|
||||
* edited has no URI of its own.
|
||||
*/
|
||||
function menu_link_field_validate($entity_type, $entity, $field, $instance, $langcode, &$items, &$errors) {
|
||||
if (!empty($entity)) {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
}
|
||||
|
||||
// Build an array of existing menu link IDs so they can be loaded with
|
||||
// menu_link_load_multiple();
|
||||
$mlids = array();
|
||||
foreach ($items as $delta => &$item) {
|
||||
if (menu_link_field_is_empty($item, $field)) {
|
||||
continue;
|
||||
}
|
||||
if (!empty($item['mlid']) && (int)$item['mlid'] > 0) {
|
||||
$mlids[] = (int)$item['mlid'];
|
||||
}
|
||||
if (!empty($item['plid']) && (int)$item['plid'] > 0) {
|
||||
$mlids[] = (int)$item['plid'];
|
||||
}
|
||||
}
|
||||
$menu_links = !empty($mlids) ? menu_link_load_multiple($mlids) : array();
|
||||
|
||||
$mlids = array();
|
||||
foreach ($items as $delta => &$item) {
|
||||
if (menu_link_field_is_empty($item, $field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($item['menu_name']) || !in_array($item['menu_name'], $instance['settings']['menu_options'])) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'menu_link_invalid_parent',
|
||||
'message' => t('%name: Invalid menu name.', array('%name' => t($instance['label']))),
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($item['plid'])) {
|
||||
if (!isset($menu_links[$item['plid']]) || !in_array($menu_links[$item['plid']]['menu_name'], $instance['settings']['menu_options'])) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'menu_link_invalid_parent',
|
||||
'message' => t('%name: Invalid parent menu link.', array('%name' => t($instance['label']))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($item['mlid'])) {
|
||||
if (isset($mlids[$item['mlid']])) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'menu_link_duplicate',
|
||||
'message' => t('%name: A menu link can only be referenced once per entity per field.', array('%name' => t($instance['label']))),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$mlids[$item['mlid']] = $item['mlid'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($field['settings']['link_path_field']) && empty($item['link_path']) && !empty($id)) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
if ($uri === NULL) {
|
||||
$errors[$field['field_name']][$langcode][$delta][] = array(
|
||||
'error' => 'menu_link_no_entity_uri',
|
||||
'message' => t('%name: No link path is provided while entity being edited has no URI of its own.', array('%name' => t($instance['label']))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_presave().
|
||||
*
|
||||
* @see menu_link_menu_link_update()
|
||||
*/
|
||||
function menu_link_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
foreach ($items as $delta => &$item) {
|
||||
if (empty($item['plid'])) {
|
||||
$item['plid'] = 0;
|
||||
}
|
||||
|
||||
$item += array(
|
||||
'options' => array(),
|
||||
'hidden' => 0,
|
||||
'expanded' => 0,
|
||||
'weight' => 0,
|
||||
);
|
||||
|
||||
// Add a key to indicate we are saving menu links from within a field. This
|
||||
// key is not stored in the database and will only be available during the
|
||||
// current request.
|
||||
// @see menu_link_menu_link_update()
|
||||
$item['menu_link_field_save'] = $field['field_name'];
|
||||
|
||||
// TODO This could override the module column!
|
||||
$item['module'] = 'menu_link';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_insert().
|
||||
*/
|
||||
function menu_link_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
$path = _menu_link_path($entity_type, $entity, $langcode);
|
||||
|
||||
foreach ($items as $delta => &$item) {
|
||||
if (empty($field['settings']['link_path_field']) || empty($item['link_path'])) {
|
||||
$item['link_path'] = $path;
|
||||
}
|
||||
|
||||
if (!menu_link_save($item)) {
|
||||
drupal_set_message(t('There was an error saving the menu link.'), 'error');
|
||||
}
|
||||
|
||||
$item['options'] = serialize($item['options']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update().
|
||||
*/
|
||||
function menu_link_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
// Load the field items as they are stored in the database before update.
|
||||
$original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
|
||||
field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, $options = array('field_id' => $field['id']));
|
||||
|
||||
// Initially asume that all links are being deleted; later on in this function,
|
||||
// links that are kept are removed from this array.
|
||||
$delete_links = array();
|
||||
if (!empty($original->{$instance['field_name']}[$langcode])) {
|
||||
foreach($original->{$instance['field_name']}[$langcode] as $item) {
|
||||
$delete_links[$item['mlid']] = $item['mlid'];
|
||||
}
|
||||
}
|
||||
|
||||
$path = _menu_link_path($entity_type, $entity, $langcode);
|
||||
|
||||
foreach ($items as $delta => &$item) {
|
||||
if (empty($field['settings']['link_path_field']) || empty($item['link_path'])) {
|
||||
$item['link_path'] = $path;
|
||||
}
|
||||
|
||||
if (!menu_link_save($item)) {
|
||||
drupal_set_message(t('There was an error saving the menu link.'), 'error');
|
||||
// TODO what to do?
|
||||
}
|
||||
|
||||
$item['options'] = serialize($item['options']);
|
||||
|
||||
// Don't remove menu links that are being kept.
|
||||
unset($delete_links[$item['mlid']]);
|
||||
}
|
||||
|
||||
// Delete any menu links that are no longer used.
|
||||
if (!empty($delete_links)) {
|
||||
menu_link_delete_multiple($delete_links);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build a menu link path based on an entity.
|
||||
*/
|
||||
function _menu_link_path($entity_type, $entity, $langcode) {
|
||||
$uri = entity_uri($entity_type, $entity);
|
||||
if (url_is_external($uri['path'])) {
|
||||
$path = url($uri['path'], $uri['options']);
|
||||
}
|
||||
else {
|
||||
$path = drupal_get_normal_path($uri['path'], $langcode);
|
||||
if (!empty($uri['options']['query'])) {
|
||||
$path .= (strpos($path, '?') !== FALSE ? '&' : '?') . drupal_http_build_query($uri['options']['query']);
|
||||
}
|
||||
if (!empty($uri['options']['fragment'])) {
|
||||
$path .= '#' . $uri['options']['fragment'];
|
||||
}
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_delete().
|
||||
*/
|
||||
function menu_link_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
|
||||
$mlids = array();
|
||||
foreach ($items as $delta => $item) {
|
||||
$mlids[] = $item['mlid'];
|
||||
}
|
||||
|
||||
if (!empty($mlids)) {
|
||||
// Only delete menu links that are (still) owned by the Menu link module.
|
||||
$mlids = db_select('menu_links')
|
||||
->fields('menu_links', array('mlid'))
|
||||
->condition('module', 'menu_link')
|
||||
->condition('mlid', $mlids)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
if (!empty($mlids)) {
|
||||
menu_link_delete_multiple($mlids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_is_empty().
|
||||
*/
|
||||
function menu_link_field_is_empty($item, $field) {
|
||||
if (!empty($item['delete']) || !trim($item['link_title']) || empty($item['menu_name']) || (empty($item['plid']) && $item['plid'] != '0')) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_info().
|
||||
*/
|
||||
function menu_link_field_formatter_info() {
|
||||
return array(
|
||||
'menu_link_link' => array(
|
||||
'label' => t('Link'),
|
||||
'field types' => array('menu_link'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_prepare_view().
|
||||
*/
|
||||
function menu_link_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
|
||||
$mlids = array();
|
||||
|
||||
// Collect every possible menu link attached to any of the fieldable entities.
|
||||
foreach ($entities as $id => $entity) {
|
||||
foreach ($items[$id] as $delta => $item) {
|
||||
// Prevent doing things twice.
|
||||
if (empty($item['title'])) {
|
||||
// Force the array key to prevent duplicates.
|
||||
$mlids[$item['mlid']] = $item['mlid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($mlids)) {
|
||||
$menu_links = menu_link_load_multiple($mlids);
|
||||
|
||||
// Iterate through the fieldable entities again to attach the loaded menu
|
||||
// link data.
|
||||
foreach ($entities as $id => $entity) {
|
||||
$rekey = FALSE;
|
||||
|
||||
foreach ($items[$id] as $delta => $item) {
|
||||
// Check whether the menu link field instance value could be loaded.
|
||||
if (isset($menu_links[$item['mlid']])) {
|
||||
// Replace the instance value with the menu link data.
|
||||
$items[$id][$delta] = $menu_links[$item['mlid']];
|
||||
}
|
||||
// Otherwise, unset the instance value, since the menu link does not exist.
|
||||
else {
|
||||
unset($items[$id][$delta]);
|
||||
$rekey = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if ($rekey) {
|
||||
// Rekey the items array.
|
||||
$items[$id] = array_values($items[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_formatter_view().
|
||||
*/
|
||||
function menu_link_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
|
||||
$element = array();
|
||||
|
||||
// If the default formatter is set to "hidden" but the field is being
|
||||
// displayed using this formatter, some module don't properly invoke
|
||||
// hook_field_formatter_prepare_view() which is essential for this formatter.
|
||||
// Let's make sure it has been invoked.
|
||||
// TODO remove if resolved.
|
||||
if ($instance['display']['default']['type'] == 'hidden') {
|
||||
list($id, , ) = entity_extract_ids($entity_type, $entity);
|
||||
$items = array($id => &$items);
|
||||
menu_link_field_formatter_prepare_view(
|
||||
$entity_type,
|
||||
array($id => $entity),
|
||||
$field,
|
||||
array($id => $instance),
|
||||
$langcode,
|
||||
$items,
|
||||
array($id => $display)
|
||||
);
|
||||
$items = $items[$id];
|
||||
}
|
||||
|
||||
switch ($display['type']) {
|
||||
case 'menu_link_link':
|
||||
foreach ($items as $delta => $item) {
|
||||
$element[$delta] = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $item['title'],
|
||||
'#href' => $item['href'],
|
||||
'#options' => $item['localized_options'],
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_info().
|
||||
*/
|
||||
function menu_link_field_widget_info() {
|
||||
return array(
|
||||
'menu_link_default' => array(
|
||||
'label' => t('Select list'),
|
||||
'field types' => array('menu_link'),
|
||||
'settings' => array(
|
||||
'description_field' => TRUE,
|
||||
'expanded_field' => FALSE
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_settings_form().
|
||||
*/
|
||||
function menu_link_field_widget_settings_form($field, $instance) {
|
||||
$widget = $instance['widget'];
|
||||
$settings = $widget['settings'];
|
||||
|
||||
$form['description_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Description</em> field'),
|
||||
'#default_value' => $settings['description_field'],
|
||||
'#description' => t('The description field is used as a tooltip when the mouse hovers over the menu link.'),
|
||||
'#weight' => 5,
|
||||
);
|
||||
$form['expanded_field'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Enable <em>Expanded</em> field'),
|
||||
'#default_value' => $settings['expanded_field'],
|
||||
'#description' => t('The expanded field is a checkbox which, if selected and the menu link has children, makes the menu link always appear expanded.'),
|
||||
'#weight' => 5,
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_form().
|
||||
*/
|
||||
function menu_link_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
|
||||
$module_path = drupal_get_path('module', 'menu_link');
|
||||
|
||||
$element += array(
|
||||
'#input' => TRUE,
|
||||
'#type' => ($field['cardinality'] == 1) ? 'fieldset' : 'container',
|
||||
'#element_validate' => array('menu_link_field_widget_validate'),
|
||||
'#attached' => array(
|
||||
'js' => array($module_path . '/menu_link.field.js'),
|
||||
'css' => array($module_path . '/menu_link.field.css'),
|
||||
),
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item', 'menu-link-auto-title', 'menu-link-auto-fieldset-summary'),
|
||||
)
|
||||
);
|
||||
|
||||
// Populate the element with the link data.
|
||||
foreach (array('mlid', 'link_path', 'options', 'hidden', 'expanded') as $key) {
|
||||
if (isset($items[$delta][$key])) {
|
||||
$element[$key] = array('#type' => 'value', '#value' => $items[$delta][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$menus = menu_get_menus();
|
||||
$available_menus = array_combine($instance['settings']['menu_options'], $instance['settings']['menu_options']);
|
||||
$options = _menu_get_options($menus, $available_menus, array('mlid' => 0));
|
||||
|
||||
$element['parent'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Parent menu item'),
|
||||
'#options' => $options,
|
||||
'#empty_value' => '_none',
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-parent'),
|
||||
'title' => t('Choose the menu item to be the parent for this link.'),
|
||||
),
|
||||
'#required' => !empty($element['#required']),
|
||||
);
|
||||
if (isset($items[$delta]['menu_name'], $items[$delta]['plid'])) {
|
||||
$element['parent']['#default_value'] = $items[$delta]['menu_name'] . ':' . $items[$delta]['plid'];
|
||||
}
|
||||
|
||||
$element['weight'] = array(
|
||||
'#type' => 'weight',
|
||||
'#title' => t('Weight'),
|
||||
'#delta' => 50,
|
||||
'#default_value' => isset($items[$delta]['weight']) ? $items[$delta]['weight'] : 0,
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-weight'),
|
||||
'title' => t('Menu links with smaller weights are displayed before links with larger weights.'),
|
||||
),
|
||||
);
|
||||
|
||||
$element['link_title'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Title'),
|
||||
'#default_value' => isset($items[$delta]['link_title']) ? $items[$delta]['link_title'] : '',
|
||||
'#size' => 50,
|
||||
'#maxlength' => 255,
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-title'),
|
||||
),
|
||||
'#description' => t('The text to be used for this link in the menu.'),
|
||||
'#required' => !empty($element['#required']),
|
||||
);
|
||||
|
||||
if (!empty($field['settings']['link_path_field'])) {
|
||||
$element['link_path'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Path'),
|
||||
'#default_value' => isset($items[$delta]['link_path']) ? $items[$delta]['link_path'] : '',
|
||||
'#size' => 50,
|
||||
'#maxlength' => 255,
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-path'),
|
||||
),
|
||||
'#description' => t('The path for this menu link.'),
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($instance['widget']['settings']['fragment_field'])) {
|
||||
$element['fragment'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('URL Fragment'),
|
||||
'#field_prefix' => '#',
|
||||
'#default_value' => isset($items[$delta]['options']['attributes']['fragment']) ? $items[$delta]['options']['attributes']['fragment'] : '',
|
||||
'#size' => 10,
|
||||
'#maxlength' => 255,
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-fragment'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($instance['widget']['settings']['expanded_field'])) {
|
||||
$element['expanded'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Expanded'),
|
||||
'#title_display' => 'before',
|
||||
'#default_value' => isset($items[$delta]['expanded']) ? $items[$delta]['expanded'] : 0,
|
||||
'#attributes' => array(
|
||||
'class' => array('menu-link-item-expanded'),
|
||||
'title' => t('If selected and this menu link has children, the menu will always appear expanded.'),
|
||||
),
|
||||
'#weight' => 5,
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($instance['widget']['settings']['description_field'])) {
|
||||
$element['description'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Description'),
|
||||
'#default_value' => isset($items[$delta]['options']['attributes']['title']) ? $items[$delta]['options']['attributes']['title'] : '',
|
||||
'#rows' => 1,
|
||||
'#description' => t('Shown when hovering over the menu link.'),
|
||||
'#attributes' => array('class' => array('menu-link-item-description')),
|
||||
'#weight' => 10,
|
||||
);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form element validate handler for menu link field widget.
|
||||
*/
|
||||
function menu_link_field_widget_validate($element, &$form_state) {
|
||||
$item = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
|
||||
if (!empty($item['parent']) && $item['parent'] != '_none') {
|
||||
list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
|
||||
}
|
||||
|
||||
$item['link_title'] = trim($item['link_title']);
|
||||
|
||||
if (!empty($item['description']) && trim($item['description'])) {
|
||||
$item['options']['attributes']['title'] = trim($item['description']);
|
||||
}
|
||||
else {
|
||||
// If the description field was left empty, remove the title attribute
|
||||
// from the menu link.
|
||||
unset($item['options']['attributes']['title']);
|
||||
}
|
||||
|
||||
if (!empty($item['fragment']) && trim($item['fragment'])) {
|
||||
$item['options']['fragment'] = trim($item['fragment']);
|
||||
}
|
||||
else {
|
||||
unset($item['options']['fragment']);
|
||||
}
|
||||
|
||||
form_set_value($element, $item, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_error().
|
||||
*/
|
||||
function menu_link_field_widget_error($element, $error, $form, &$form_state) {
|
||||
switch ($error['error']) {
|
||||
case 'menu_link_invalid_parent':
|
||||
$error_element = $element['parent'];
|
||||
break;
|
||||
|
||||
default:
|
||||
$error_element = $element['link_title'];
|
||||
break;
|
||||
}
|
||||
|
||||
form_error($error_element, $error['message']);
|
||||
}
|
89
sites/all/modules/menu_link/menu_link.field.js
Normal file
89
sites/all/modules/menu_link/menu_link.field.js
Normal file
@@ -0,0 +1,89 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* Automatically fill in a menu link title, if possible.
|
||||
*/
|
||||
Drupal.behaviors.menuLinkFieldAutoTitle = {
|
||||
attach: function (context) {
|
||||
$('.field-type-menu-link .menu-link-item.menu-link-auto-title', context).each(function () {
|
||||
// Try to find menu link item elements as well as a entity 'title' field
|
||||
// in the form, but play nicely with user permissions and form alterations.
|
||||
var $enabled = $('.menu-link-item-enabled', this),
|
||||
$link_title = $('.menu-link-item-title', this),
|
||||
$title = $(this).closest('form').find('.form-item-title input'),
|
||||
required = $link_title.hasClass('required'),
|
||||
link_title = $link_title.val();
|
||||
|
||||
// Bail out if we do not have all required fields.
|
||||
if (!$link_title.length || !$title.length || !($enabled.length || required)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a link title already, mark it as overridden. The user expects
|
||||
// that toggling the checkbox twice will take over the node's title.
|
||||
if (link_title.length ) {
|
||||
if ($enabled.is(':checked')) {
|
||||
$link_title.data('menuLinkFieldAutomaticTitleOverridden', true);
|
||||
}
|
||||
else if (!$enabled.length && required && link_title != $title.val()) {
|
||||
$link_title.data('menuLinkFieldAutomaticTitleOverridden', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Take over any title change.
|
||||
$title.keyup(function () {
|
||||
if (!$link_title.data('menuLinkFieldAutomaticTitleOverridden') && ($enabled.length ? $enabled.is(':checked') : required)) {
|
||||
$link_title.val($title.val());
|
||||
$link_title.val($title.val()).trigger('formUpdated');
|
||||
}
|
||||
});
|
||||
// Whenever the value is changed manually, disable this behavior.
|
||||
$link_title.keyup(function () {
|
||||
$link_title.data('menuLinkFieldAutomaticTitleOverridden', true);
|
||||
});
|
||||
|
||||
// Global trigger on checkbox (do not fill-in a value when disabled).
|
||||
$enabled.change(function () {
|
||||
if ($enabled.is(':checked')) {
|
||||
if (!$link_title.data('menuLinkFieldAutomaticTitleOverridden')) {
|
||||
$link_title.val($title.val());
|
||||
}
|
||||
}
|
||||
else {
|
||||
$link_title.val('');
|
||||
$link_title.removeData('menuLinkFieldAutomaticTitleOverridden');
|
||||
}
|
||||
$enabled.closest('fieldset.vertical-tabs-pane').trigger('summaryUpdated');
|
||||
$enabled.trigger('formUpdated');
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Automatically update summary of fieldsets containging menu link fields.
|
||||
*
|
||||
* To enable this behavior enable the Field group module and put your menu link
|
||||
* field into a field group.
|
||||
*
|
||||
* @link http://drupal.org/project/field_group
|
||||
*/
|
||||
Drupal.behaviors.menuLinkFieldAutoFieldsetSummary = {
|
||||
attach: function (context) {
|
||||
$('fieldset.vertical-tabs-pane .field-type-menu-link', context).each(function () {
|
||||
$(this).closest('fieldset.vertical-tabs-pane').drupalSetSummary(function (context) {
|
||||
var $item = $('.menu-link-item.menu-link-auto-fieldset-summary:first', context);
|
||||
var $enabled = $item.find('.menu-link-item-enabled');
|
||||
var $title = $item.find('.menu-link-item-title');
|
||||
if (($enabled.length && $enabled.is(':checked')) || $title.val()) {
|
||||
return Drupal.checkPlain($title.val());
|
||||
}
|
||||
else {
|
||||
return Drupal.t('Not in menu');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
14
sites/all/modules/menu_link/menu_link.info
Normal file
14
sites/all/modules/menu_link/menu_link.info
Normal file
@@ -0,0 +1,14 @@
|
||||
name = Menu link
|
||||
description = Defines a menu link field type.
|
||||
package = Fields
|
||||
core = 7.x
|
||||
|
||||
files[] = menu_link.test
|
||||
files[] = views/menu_link_handler_sort_weight.inc
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-07-03
|
||||
version = "7.x-1.x-dev"
|
||||
core = "7.x"
|
||||
project = "menu_link"
|
||||
datestamp = "1341318595"
|
||||
|
58
sites/all/modules/menu_link/menu_link.install
Normal file
58
sites/all/modules/menu_link/menu_link.install
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Menu link module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_schema().
|
||||
*/
|
||||
function menu_link_field_schema($field) {
|
||||
$menu_links = drupal_get_schema_unprocessed('system', 'menu_links');
|
||||
|
||||
$schema = array(
|
||||
'columns' => array(
|
||||
'mlid' => array(
|
||||
'description' => 'The {menu_links}.mlid being referenced in this field.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
|
||||
// Columns below are being used for revision control.
|
||||
'menu_name' => $menu_links['fields']['menu_name'],
|
||||
'plid' => $menu_links['fields']['plid'],
|
||||
'link_path' => $menu_links['fields']['link_path'],
|
||||
'link_title' => $menu_links['fields']['link_title'],
|
||||
'options' => $menu_links['fields']['options'],
|
||||
'hidden' => $menu_links['fields']['hidden'],
|
||||
'expanded' => $menu_links['fields']['expanded'],
|
||||
'weight' => $menu_links['fields']['weight'],
|
||||
),
|
||||
'indexes' => array(
|
||||
'mlid' => array('mlid'),
|
||||
'menu_plid' => array('menu_name', 'plid'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'menu_link' => array(
|
||||
'table' => 'menu_links',
|
||||
'columns' => array('mlid' => 'mlid'),
|
||||
),
|
||||
'parent_menu_link' => array(
|
||||
'table' => 'menu_links',
|
||||
'columns' => array('plid' => 'mlid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (module_exists('menu')) {
|
||||
$schema['foreign keys']['menu'] = array(
|
||||
'table' => 'menu_custom',
|
||||
'columns' => array('menu_name' => 'menu_name'),
|
||||
);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
303
sites/all/modules/menu_link/menu_link.module
Normal file
303
sites/all/modules/menu_link/menu_link.module
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Defines a menu link field type.
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/menu_link.field.inc';
|
||||
|
||||
|
||||
/**
|
||||
* Name of the fixed Menu link field.
|
||||
*
|
||||
* This module provides one Menu link field by default. This field cannot be
|
||||
* deleted and its storage backend is restricted to field_sql_storage. This way
|
||||
* other modules can use its database table {field_data_menu_link}, to include
|
||||
* the {menu_links} table in entity queries.
|
||||
*/
|
||||
define('MENU_LINK_DEFAULT_FIELD', 'menu_link');
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function menu_link_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#menu_link':
|
||||
$output = '';
|
||||
$output .= '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t("The Menu link module defines a menu link field type for the Field module. A menu link field may be used to place links into a menu that link to it's entity. See the <a href='@field-help'>Field module help page</a> for more information about fields.", array('@field-help' => url('admin/help/field'), '@filter-help' => url('admin/help/filter'))) . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Load multiple menu links, access checked and link translated for rendering.
|
||||
*
|
||||
* This function should never be called from within node_load() or any other
|
||||
* function used as a menu object load function since an infinite recursion may
|
||||
* occur.
|
||||
*
|
||||
* @param $mlids array
|
||||
* An array of menu link IDs.
|
||||
* @param $conditions array
|
||||
* An associative array of conditions on the {menu_links}
|
||||
* table, where the keys are the database fields and the values are the
|
||||
* values those fields must have.
|
||||
*
|
||||
* @return
|
||||
* An array of menu links indexed by mlid.
|
||||
*
|
||||
* @see menu_link_load()
|
||||
*
|
||||
* @todo Remove this function when http://drupal.org/node/1034732 lands.
|
||||
*/
|
||||
function menu_link_load_multiple(array $mlids, array $conditions = array()) {
|
||||
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
|
||||
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
|
||||
$query->fields('ml');
|
||||
// Weight should be taken from {menu_links}, not {menu_router}.
|
||||
$query->addField('ml', 'weight', 'link_weight');
|
||||
$query->fields('m');
|
||||
|
||||
if (!empty($mlids)) {
|
||||
$query->condition('ml.mlid', $mlids, 'IN');
|
||||
}
|
||||
if (!empty($conditions)) {
|
||||
foreach ($conditions as $field => $value) {
|
||||
$query->condition('ml.' . $field, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$items = array();
|
||||
foreach ($query->execute() as $item) {
|
||||
$item['weight'] = $item['link_weight'];
|
||||
$items[$item['mlid']] = $item;
|
||||
_menu_link_translate($items[$item['mlid']]);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple menu links.
|
||||
*
|
||||
* @param $mlids array
|
||||
* An array of menu link IDs.
|
||||
* @param $force boolean
|
||||
* Forces deletion. Internal use only, setting to TRUE is discouraged.
|
||||
*
|
||||
* @see menu_link_delete()
|
||||
*
|
||||
* @todo Remove this function when http://drupal.org/node/1034732 lands.
|
||||
*/
|
||||
function menu_link_delete_multiple(array $mlids, $force = FALSE) {
|
||||
if (!empty($mlids)) {
|
||||
$query = db_select('menu_links')
|
||||
->fields('menu_links')
|
||||
->condition('mlid', $mlids, 'IN');
|
||||
if (!$force) {
|
||||
// Exclude links belonging to system module except if they are marked
|
||||
// updated (generated during update from Drupal 5).
|
||||
$query->condition(db_or()->condition('module', 'system', '<>')->condition('updated', 0, '<>'));
|
||||
}
|
||||
$links_to_delete = $query->execute()->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);
|
||||
|
||||
if (!empty($links_to_delete)) {
|
||||
$links_with_children = array();
|
||||
$parent_mlids = array();
|
||||
$affected_menus = array();
|
||||
foreach ($links_to_delete as $item) {
|
||||
if ($item['has_children']) {
|
||||
$links_with_children[$item['mlid']] = $item['mlid'];
|
||||
}
|
||||
$parent_mlids[$item['plid']] = $item['plid'];
|
||||
$affected_menus[$item['menu_name']] = $item['menu_name'];
|
||||
}
|
||||
$parent_mlids = array_diff_key($parent_mlids, array(0 => 0) + array_keys($links_to_delete));
|
||||
|
||||
// Re-parent any children to it's closest parent that is not deleted.
|
||||
if (!empty($links_with_children)) {
|
||||
$children = menu_link_load_multiple(array(), array('plid' => $links_with_children));
|
||||
foreach ($children as $item) {
|
||||
while (isset($links_to_delete[$item['plid']])) {
|
||||
$item['plid'] = $links_to_delete[$item['plid']]['plid'];
|
||||
}
|
||||
menu_link_save($item);
|
||||
}
|
||||
}
|
||||
|
||||
db_delete('menu_links')->condition('mlid', array_keys($links_to_delete), 'IN')->execute();
|
||||
|
||||
foreach ($links_to_delete as $item) {
|
||||
// Notify modules we have deleted the item.
|
||||
module_invoke_all('menu_link_delete', $item);
|
||||
|
||||
// Update the has_children status of the parent.
|
||||
_menu_update_parental_status($item);
|
||||
}
|
||||
|
||||
// Update the has_children status of parents of deleted links.
|
||||
// @todo fix query und use this instead of _menu_update_parental_status($item);
|
||||
/*if (!empty($parent_mlids)) {
|
||||
$exists_query = db_select('menu_links', 'child')
|
||||
->fields('child', array('mlid'))
|
||||
->condition('child.hidden', 0)
|
||||
->where('child.plid = parent.mlid')
|
||||
->where('child.menu_name = parent.menu_name')
|
||||
->range(0, 1);
|
||||
|
||||
db_update('menu_links', 'parent')
|
||||
->fields(array('has_children' => 0))
|
||||
->condition('has_children', 1)
|
||||
->condition('mlid', $parent_mlids, 'IN')
|
||||
->notExists($exists_query)
|
||||
->execute();
|
||||
}*/
|
||||
|
||||
// Clear caches.
|
||||
foreach ($affected_menus as $menu_name) {
|
||||
menu_cache_clear($menu_name);
|
||||
}
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_menu_delete().
|
||||
*/
|
||||
function menu_link_menu_delete($menu) {
|
||||
// Menu should not be removed from settings of menu_link field instances; menus
|
||||
// have a machine name so they can be recreated. Non existant menus won't be
|
||||
// available in the field widgets.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_alter().
|
||||
*
|
||||
* @see http://drupal.org/node/1087888
|
||||
* Add $prior_link to hook_menu_link_update().
|
||||
*/
|
||||
function menu_link_menu_link_alter($item = NULL) {
|
||||
static $existing_item;
|
||||
if (isset($item)) {
|
||||
$existing_item = FALSE;
|
||||
if (isset($item['mlid'])) {
|
||||
if ($existing_item = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item['mlid']))->fetchAssoc()) {
|
||||
$existing_item['options'] = unserialize($existing_item['options']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($existing_item['module'] == 'menu_link') {
|
||||
|
||||
}
|
||||
}
|
||||
return $existing_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_update().
|
||||
*
|
||||
* Synchronize menu_link fields with the updated menu link.
|
||||
*/
|
||||
function menu_link_menu_link_update($item) {
|
||||
$prior_item = menu_link_menu_link_alter();
|
||||
|
||||
if ($item['module'] == 'menu_link' && empty($item['menu_link_field_save'])) {
|
||||
static $entity_paths;
|
||||
|
||||
if (empty($entity_paths)) {
|
||||
$entity_get_info = entity_get_info();
|
||||
foreach ($entity_get_info as $entity_type => $entity_info) {
|
||||
if (isset($entity_info['path'])) {
|
||||
$entity_path = str_replace('%' . $entity_type, '%', $entity_info['path']);
|
||||
$entity_paths[$entity_path] = $entity_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($entity_paths[$item['router_path']])) {
|
||||
// Get entity type
|
||||
$entity_type = $entity_paths[$item['router_path']];
|
||||
// Get path to entity without wildcard
|
||||
$router_path = str_replace('%','',$item['router_path']);
|
||||
// Get Entity ID
|
||||
$entity_id = str_replace($router_path, '', $item['link_path']);
|
||||
// Load entity
|
||||
$entity = array_shift(entity_load($entity_type, array($entity_id)));
|
||||
// Get bundle
|
||||
list( , , $bundle) = entity_extract_ids($entity_type, $entity);
|
||||
|
||||
$save_entity = FALSE;
|
||||
foreach (field_info_instances($entity_type, $bundle) as $instance) {
|
||||
$field_name = $instance['field_name'];
|
||||
$field = field_info_field($field_name);
|
||||
if ($field['module'] == 'menu_link') {
|
||||
|
||||
// Check if field exist on this entity
|
||||
if (isset($entity->{$field_name}) && !empty($entity->{$field_name})) {
|
||||
// Check if content is the same as current path for each language
|
||||
foreach ($entity->{$field_name} as $lang => $items) {
|
||||
// for each contents of fields
|
||||
foreach ($items as $i => $field_item) {
|
||||
// Check if it's current item
|
||||
if ($field_item['mlid'] == $item['mlid']) {
|
||||
// So give it updated options
|
||||
foreach ($entity->{$field_name}[$lang][$i] as $option_name => $option_value) {
|
||||
$entity->{$field_name}[$lang][$i][$option_name] = $item[$option_name];
|
||||
}
|
||||
$save_entity = TRUE;
|
||||
break; // We found item in field content so break to next field
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entity has been edited so save it
|
||||
if ($save_entity) {
|
||||
entity_save($entity_name, $entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu_link_delete().
|
||||
*
|
||||
* Remove link from all menu_link fields. Note that this module disables the
|
||||
* possibility to delete menu links through the Admin > Structure > Menus
|
||||
* interface that are used in a menu_link field (by storing menu links under its
|
||||
* own module instead of system). So this hook may not be neccessary at all.
|
||||
*/
|
||||
function menu_link_menu_link_delete($item) {
|
||||
if ($item['module'] == 'menu_link' && empty($item['menu_link_field_save'])) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update_forbid().
|
||||
*/
|
||||
function menu_link_field_update_forbid($field, $prior_field, $has_data) {
|
||||
if ($field['field_name'] == MENU_LINK_DEFAULT_FIELD) {
|
||||
if (!empty($field['settings']['link_path_field'])) {
|
||||
throw new FieldUpdateForbiddenException(t('The link path cannot not be exposed for the ":menu_link_field" field.', array(':menu_link_field' => MENU_LINK_DEFAULT_FIELD)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_views_api().
|
||||
*/
|
||||
function menu_link_views_api() {
|
||||
return array(
|
||||
'api' => 3,
|
||||
'path' => drupal_get_path('module', 'menu_link'),
|
||||
);
|
||||
}
|
90
sites/all/modules/menu_link/menu_link.test
Normal file
90
sites/all/modules/menu_link/menu_link.test
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests for Menu link module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for menu link field and formatter.
|
||||
*/
|
||||
class MenuLinkFieldTestCase extends DrupalWebTestCase {
|
||||
protected $instance;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Menu link field',
|
||||
'description' => 'Test the creation of menu link fields.',
|
||||
'group' => 'Menu link',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test');
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Setup a field and instance.
|
||||
$this->field_name = drupal_strtolower($this->randomName());
|
||||
$this->field = array(
|
||||
'field_name' => $this->field_name,
|
||||
'type' => 'menu_link',
|
||||
);
|
||||
field_create_field($this->field);
|
||||
$this->instance = array(
|
||||
'field_name' => $this->field_name,
|
||||
'entity_type' => 'test_entity',
|
||||
'bundle' => 'test_bundle',
|
||||
'setings' => array(
|
||||
'menu_options' => array('main-menu'),
|
||||
),
|
||||
'widget' => array(
|
||||
'type' => 'menu_link_default',
|
||||
),
|
||||
'display' => array(
|
||||
'full' => array(
|
||||
'type' => 'menu_link_link',
|
||||
),
|
||||
),
|
||||
);
|
||||
field_create_instance($this->instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test menu link field validation.
|
||||
*/
|
||||
function testMenuLinkFieldValidation() {
|
||||
// Test valid and invalid values with field_attach_validate().
|
||||
$langcode = LANGUAGE_NONE;
|
||||
$entity = field_test_create_stub_entity();
|
||||
$entity->uri = array('path' => 'test', 'options' => array());
|
||||
$entity->{$this->field_name}[$langcode][0] = array(
|
||||
'menu_name' => 'main-menu',
|
||||
'plid' => 0,
|
||||
'link_title' => 'Title'
|
||||
);
|
||||
try {
|
||||
field_attach_validate('test_entity', $entity);
|
||||
$this->pass(t('Correct menu link does not cause validation error'));
|
||||
}
|
||||
catch (FieldValidationException $e) {
|
||||
$this->fail(t('Correct menu link does not cause validation error'));
|
||||
}
|
||||
|
||||
$entity = field_test_create_stub_entity();
|
||||
$entity->uri = array('path' => 'test', 'options' => array());
|
||||
$entity->{$this->field_name}[$langcode][0] = array(
|
||||
'menu_name' => 'nonexistant-menu',
|
||||
'plid' => 0,
|
||||
'link_title' => 'Title'
|
||||
);
|
||||
try {
|
||||
field_attach_validate('test_entity', $entity);
|
||||
$this->fail(t('Menu link with an invalid menu name causes validation error'));
|
||||
}
|
||||
catch (FieldValidationException $e) {
|
||||
$this->pass(t('Menu link with an invalid menu name causes validation error'));
|
||||
}
|
||||
}
|
||||
}
|
6
sites/all/modules/menu_link/menu_link.views.inc
Normal file
6
sites/all/modules/menu_link/menu_link.views.inc
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
function menu_link_views_data_alter(&$data) {
|
||||
unset($data['field_data_menu_link']['menu_link_weight']['sort']['handler']);
|
||||
$data['field_data_menu_link']['menu_link_weight']['sort']['handler'] = 'menu_link_handler_sort_weight';
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Menu callback: confirm rebuilding of relations.
|
||||
*/
|
||||
function menu_link_node_menu_admin_populate_confirm() {
|
||||
return confirm_form(array(), t('Are you sure you want to populate the menu link field of nodes?'),
|
||||
'admin/reports/status', t('This action populates the default menu link field on nodes with existing menu links, and may be a lengthy process.'), t('Populate'), t('Cancel'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit handler that populates the default menu link field on nodes with existing menu links.
|
||||
*/
|
||||
function menu_link_node_menu_admin_populate_confirm_submit($form, &$form_state) {
|
||||
$operations = array();
|
||||
foreach (node_type_get_types() as $info) {
|
||||
$instance = field_read_instance('node', MENU_LINK_DEFAULT_FIELD, $info->type);
|
||||
if (!empty($instance)) {
|
||||
$operations[] = array('menu_link_node_menu_admin_populate_batch_operation', array($instance));
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($operations)) {
|
||||
$batch = array(
|
||||
'title' => t('Populating the ":menu_link_field" field.', array(':menu_link_field' => MENU_LINK_DEFAULT_FIELD)),
|
||||
'file' => drupal_get_path('module', 'menu_link_node_menu') . '/menu_link_node_menu.admin.inc',
|
||||
'operations' => $operations,
|
||||
);
|
||||
batch_set($batch);
|
||||
}
|
||||
$form_state['redirect'] = 'admin/reports/status';
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch operation: seize and store menu links of nodes in the MENU_LINK_DEFAULT_FIELD.
|
||||
*
|
||||
* This is a multistep operation, iterating all nodes with a menu link by
|
||||
* packs of 20.
|
||||
*/
|
||||
function menu_link_node_menu_admin_populate_batch_operation($instance, &$context) {
|
||||
$default_menu = !empty($instance['default value'][0]['menu_name']) ? $instance['default value'][0]['menu_name'] : 'main-menu';
|
||||
|
||||
$query = db_select('menu_links', 'ml');
|
||||
$query->fields('ml', array('mlid', 'menu_name', 'plid', 'link_path', 'link_title', 'options', 'hidden', 'expanded', 'weight'));
|
||||
$query->addExpression('ml.menu_name = :default_menu', 'default_menu', array(':default_menu' => $default_menu));
|
||||
$query->addField('n', 'nid');
|
||||
$query->innerJoin('node', 'n', "ml.link_path = CONCAT('node/', n.nid)");
|
||||
$query->condition('ml.module', 'menu');
|
||||
$query->condition('ml.menu_name', $instance['settings']['menu_options']);
|
||||
$query->condition('ml.router_path', 'node/%');
|
||||
$query->condition('n.type', $instance['bundle']);
|
||||
$query->groupBy('ml.link_path');
|
||||
$query->orderBy('default_menu', 'DESC');
|
||||
$query->orderBy('ml.mlid');
|
||||
|
||||
if (empty($context['sandbox'])) {
|
||||
// Initiate multistep processing.
|
||||
$context['sandbox']['count'] = 0;
|
||||
$context['sandbox']['current'] = 0;
|
||||
$context['sandbox']['progress'] = 0;
|
||||
$context['sandbox']['max'] = $query->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
// Process the next 20 menu links/nodes.
|
||||
$limit = 20;
|
||||
$menu_links = $query
|
||||
->condition('ml.mlid', $context['sandbox']['current'], '>')
|
||||
->range(0, $limit)
|
||||
->execute()->fetchAllAssoc('nid', PDO::FETCH_ASSOC);
|
||||
|
||||
$nodes = node_load_multiple(array_keys($menu_links));
|
||||
foreach ($nodes as $nid => $node) {
|
||||
// To preserve database integrity, only populate the menu link field if the
|
||||
// node loads successfully.
|
||||
if (!empty($node) && empty($node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE])) {
|
||||
$menu_links[$nid]['options'] = unserialize($menu_links[$nid]['options']);
|
||||
$node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE][0] = $menu_links[$nid];
|
||||
|
||||
node_save($node);
|
||||
$context['sandbox']['count']++;
|
||||
}
|
||||
$context['sandbox']['current'] = $menu_links[$nid]['mlid'];
|
||||
}
|
||||
$context['sandbox']['progress'] += $limit;
|
||||
|
||||
// Multistep processing: report progress.
|
||||
if ($context['sandbox']['progress'] < $context['sandbox']['max']) {
|
||||
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
|
||||
}
|
||||
else {
|
||||
$types = node_type_get_names();
|
||||
drupal_set_message(t('Populated the ":menu_link_field" field of :count posts of type :node_type.', array(':menu_link_field' => MENU_LINK_DEFAULT_FIELD, ':count' => $context['sandbox']['count'], ':node_type' => $types[$instance['bundle']])));
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
name = Menu link - Node menu
|
||||
description = Use a menu link field for core.
|
||||
package = Fields
|
||||
core = 7.x
|
||||
dependencies[] = menu_link
|
||||
dependencies[] = menu
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-07-03
|
||||
version = "7.x-1.x-dev"
|
||||
core = "7.x"
|
||||
project = "menu_link"
|
||||
datestamp = "1341318595"
|
||||
|
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the Menu link module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ensures availability of the MENU_LINK_DEFAULT_FIELD constant.
|
||||
*/
|
||||
require_once dirname(__FILE__) . '/../menu_link.module';
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function menu_link_node_menu_requirements($phase) {
|
||||
$requirements = array();
|
||||
|
||||
if ($phase == 'install') {
|
||||
$prior_field = field_read_field(MENU_LINK_DEFAULT_FIELD, array('include_inactive' => TRUE));
|
||||
if (!empty($prior_field) && !($prior_field['type'] == 'menu_link')) {
|
||||
$t = get_t();
|
||||
|
||||
$requirements['menu_link_default_field'] = array(
|
||||
'title' => $t('Field name already in use'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => $t('The field name ":menu_link_field" is already in use. This field name is required for the Menu link module. Please rename or remove the prior field before installing the Menu link module.', array(':menu_link_field' => MENU_LINK_DEFAULT_FIELD)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($phase == 'runtime') {
|
||||
$requirements['menu_link_node_menu'] = array(
|
||||
'title' => t('Node Menu links'),
|
||||
'value' => 'Synchronized',
|
||||
'description' => l(t('Populate the menu link field of nodes.'), 'admin/reports/status/rebuild/node-menu-link'),
|
||||
);
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_install().
|
||||
*/
|
||||
function menu_link_node_menu_install() {
|
||||
// Make sure the Menu link field type is available.
|
||||
field_info_cache_clear();
|
||||
|
||||
$prior_field = field_read_field(MENU_LINK_DEFAULT_FIELD, array('include_inactive' => TRUE));
|
||||
if (empty($prior_field)) {
|
||||
// Create the default menu link field.
|
||||
field_create_field(array(
|
||||
'field_name' => MENU_LINK_DEFAULT_FIELD,
|
||||
'type' => 'menu_link',
|
||||
'storage' => array('type' => 'field_sql_storage'))
|
||||
);
|
||||
}
|
||||
elseif (!$prior_field['active']) {
|
||||
// Activate the default menu link field.
|
||||
$prior_field['active'] = 1;
|
||||
field_update_field($prior_field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*
|
||||
* @see menu_link_node_menu_disable()
|
||||
* Passed ownership of menu links in the default menu link field to the Menu
|
||||
* module.
|
||||
* @see menu_link_field_delete()
|
||||
* Deletes menu links when a menu link field is deleted, but only if they are
|
||||
* owned by the Menu link module.
|
||||
*/
|
||||
function menu_link_node_menu_uninstall() {
|
||||
// Remove instances of the default menu link field from all content types.
|
||||
// Menu links won't be deleted as their ownership is passed to the Menu module
|
||||
// in menu_link_node_menu_disable()
|
||||
foreach (field_read_instances(array('entity_type' => 'node', 'field_name' => MENU_LINK_DEFAULT_FIELD)) as $instance) {
|
||||
field_delete_instance($instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_enable().
|
||||
*
|
||||
* @see menu_link_node_type_update()
|
||||
* Creates instances of the MENU_LINK_DEFAULT_FIELD for content types that
|
||||
* allow nodes to be placed in the menu.
|
||||
*/
|
||||
function menu_link_node_menu_enable() {
|
||||
foreach (node_type_get_types() as $info) {
|
||||
// Invokes menu_link_node_type_update().
|
||||
node_type_save($info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_disable().
|
||||
*/
|
||||
function menu_link_node_menu_disable() {
|
||||
$instances = field_read_instances(array('entity_type' => 'node', 'field_name' => MENU_LINK_DEFAULT_FIELD));
|
||||
if (!empty($instances)) {
|
||||
$bundles = array();
|
||||
foreach ($instances as $instance) {
|
||||
$bundles[] = $instance['bundle'];
|
||||
}
|
||||
|
||||
// Pass on ownership of menu links previously owned by the Menu link module
|
||||
// to the Menu module. We don't need to use menu_link_save() as an ownership
|
||||
// change doesn't have to trigger hooks.
|
||||
db_update('menu_links')
|
||||
->fields(array('module' => 'menu'))
|
||||
->condition('module', 'menu_link')
|
||||
->exists(db_select('field_data_' . MENU_LINK_DEFAULT_FIELD, 'f')
|
||||
->fields('f')
|
||||
->condition('f.entity_type', 'node')
|
||||
->condition('f.bundle', $bundles)
|
||||
->where('f.' . MENU_LINK_DEFAULT_FIELD . '_mlid = mlid')
|
||||
)
|
||||
->execute();
|
||||
|
||||
// De-activate instances of the default menu link field on content types.
|
||||
foreach ($instances as $instance) {
|
||||
$instance['active'] = 0;
|
||||
field_update_instance($instance);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Use a menu link field for core.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function menu_link_node_menu_menu() {
|
||||
$items['admin/reports/status/rebuild/node-menu-link'] = array(
|
||||
'title' => 'Populate the default menu link field of nodes with existing menu links.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('menu_link_node_menu_admin_populate_confirm'),
|
||||
'access arguments' => array('access administration pages'),
|
||||
'type' => MENU_CALLBACK,
|
||||
'file' => 'menu_link_node_menu.admin.inc',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_module_implements_alter().
|
||||
*
|
||||
* Prevent Menu module from adding a menu link form to node forms.
|
||||
*/
|
||||
function menu_link_node_menu_module_implements_alter(&$implementations, $hook) {
|
||||
// Disable certain hook implementations of the Menu module.
|
||||
if (in_array($hook, array('node_insert', 'node_update', 'node_delete', 'node_prepare', 'form_node_form_alter', 'node_submit'))) {
|
||||
unset($implementations['menu']);
|
||||
}
|
||||
}
|
||||
|
||||
class MenuLinkNodeMenu implements ArrayAccess {
|
||||
private $node = array();
|
||||
private $menu;
|
||||
public function __construct($node) {
|
||||
$this->node = $node;
|
||||
}
|
||||
private function loadMenu() {
|
||||
$item = array();
|
||||
// Prepare the node for the edit form so that $node->menu always exists.
|
||||
$menu_name = strtok(variable_get('menu_parent_' . $this->node->type, 'main-menu:0'), ':');
|
||||
if (!empty($this->node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE][0]['mlid'])) {
|
||||
$mlid = $this->node->{MENU_LINK_DEFAULT_FIELD}[LANGUAGE_NONE][0]['mlid'];
|
||||
$item = menu_link_load($mlid);
|
||||
}
|
||||
|
||||
// Set default values.
|
||||
$this->menu = $item + array(
|
||||
'link_title' => '',
|
||||
'mlid' => 0,
|
||||
'plid' => 0,
|
||||
'menu_name' => $menu_name,
|
||||
'weight' => 0,
|
||||
'options' => array(),
|
||||
'module' => 'menu',
|
||||
'expanded' => 0,
|
||||
'hidden' => 0,
|
||||
'has_children' => 0,
|
||||
'customized' => 0,
|
||||
);
|
||||
}
|
||||
public function offsetSet($offset, $value) {
|
||||
if (empty($this->menu)) {
|
||||
$this->loadMenu();
|
||||
}
|
||||
if ($offset === NULL) {
|
||||
$this->menu[] = $value;
|
||||
}
|
||||
else {
|
||||
$this->menu[$offset] = $value;
|
||||
}
|
||||
}
|
||||
public function offsetExists($offset) {
|
||||
if (empty($this->menu)) {
|
||||
$this->loadMenu();
|
||||
}
|
||||
return isset($this->menu[$offset]);
|
||||
}
|
||||
public function offsetUnset($offset) {
|
||||
if (empty($this->menu)) {
|
||||
$this->loadMenu();
|
||||
}
|
||||
unset($this->menu[$offset]);
|
||||
}
|
||||
public function offsetGet($offset) {
|
||||
if (empty($this->menu)) {
|
||||
$this->loadMenu();
|
||||
}
|
||||
return $this->menu[$offset];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_load().
|
||||
*
|
||||
* @see menu_node_prepare()
|
||||
* Loads node menu only if not set yet.
|
||||
*/
|
||||
function menu_link_node_menu_node_load($nodes, $types) {
|
||||
foreach ($nodes as $node) {
|
||||
$node->menu = new MenuLinkNodeMenu($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_prepare().
|
||||
*/
|
||||
function menu_link_node_menu_node_prepare($node) {
|
||||
$node->menu = new MenuLinkNodeMenu($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_create_instance().
|
||||
*/
|
||||
function menu_link_node_menu_field_create_instance($instance) {
|
||||
_menu_link_node_menu_update_type($instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_update_instance().
|
||||
*/
|
||||
function menu_link_node_menu_field_update_instance($instance, $prior_instance) {
|
||||
_menu_link_node_menu_update_type($instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_type_insert().
|
||||
*/
|
||||
function menu_link_node_menu_node_type_insert($info) {
|
||||
_menu_link_node_menu_update_field($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_type_update().
|
||||
*/
|
||||
function menu_link_node_menu_node_type_update($info) {
|
||||
_menu_link_node_menu_update_field($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instance of the default menu link field based on a content type's menu settings.
|
||||
*
|
||||
* @param $info object
|
||||
* The content type object.
|
||||
*
|
||||
* @return array
|
||||
* An associative array representing an instance structure.
|
||||
*/
|
||||
function _menu_link_node_menu_instance($info) {
|
||||
$menu_options = variable_get('menu_options_' . $info->type, array('main-menu'));
|
||||
$default = variable_get('menu_parent_' . $info->type, 'main-menu:0');
|
||||
list($default_menu_name, $default_plid) = explode(':', $default);
|
||||
|
||||
return array(
|
||||
'field_name' => MENU_LINK_DEFAULT_FIELD,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $info->type,
|
||||
'label' => t('Menu'),
|
||||
'settings' => array(
|
||||
'menu_options' => $menu_options,
|
||||
),
|
||||
'default_value' => array(
|
||||
0 => array(
|
||||
'mlid' => NULL,
|
||||
'menu_name' => $default_menu_name,
|
||||
'plid' => $default_plid,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds, updates or deletes the default menu link field from a content type based on its menu settings.
|
||||
*
|
||||
* @param $info object
|
||||
* The content type object.
|
||||
*/
|
||||
function _menu_link_node_menu_update_field($info) {
|
||||
$prior_instance = field_read_instance('node', MENU_LINK_DEFAULT_FIELD, $info->type, array('include_inactive' => TRUE));
|
||||
$menu_options = variable_get('menu_options_' . $info->type, array('main-menu'));
|
||||
if (!empty($menu_options)) {
|
||||
$instance = _menu_link_node_menu_instance($info);
|
||||
$t_args = array('%label' => $instance['label'], '%name' => $info->name);
|
||||
|
||||
if (empty($prior_instance)) {
|
||||
try {
|
||||
field_create_instance($instance);
|
||||
drupal_set_message(t('The %label field has been added to the %name content type.', $t_args));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
drupal_set_message(t('There was a problem creating field %label: @message.', array('%label' => $instance['label'], '@message' => $e->getMessage())));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO recursive?
|
||||
// TODO only update field if actually changed.
|
||||
// TODO reactivate inactivate field for new content types using a machine name that has been used before?
|
||||
$instance = $prior_instance + $instance;
|
||||
try {
|
||||
field_update_instance($instance);
|
||||
//drupal_set_message(t('The %label field has updated.', $t_args));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
drupal_set_message(t('There was a problem creating field %label: @message.', array('%label' => $instance['label'], '@message' => $e->getMessage())));
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (!empty($prior_instance)) {
|
||||
$t_args = array('%label' => $prior_instance['label'], '%name' => $info->name);
|
||||
|
||||
field_delete_instance($prior_instance);
|
||||
drupal_set_message(t('The %label field has been removed from the %name content type.', $t_args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes content type's menu settings with those of the default menu link field.
|
||||
*
|
||||
* @param $instance array
|
||||
* A field instance definition array.
|
||||
*/
|
||||
function _menu_link_node_menu_update_type($instance) {
|
||||
if ($instance['entity_type'] == 'node' && $instance['field_name'] == MENU_LINK_DEFAULT_FIELD) {
|
||||
variable_set('menu_options_' . $instance['bundle'], $instance['settings']['menu_options']);
|
||||
|
||||
if (isset($instance['default_value'][0])) {
|
||||
$default = $instance['default_value'][0]['menu_name'] . ':' . $instance['default_value'][0]['plid'];
|
||||
}
|
||||
elseif (isset($instance['settings']['menu_options']['main-menu'])) {
|
||||
$default = 'main-menu:0';
|
||||
}
|
||||
else {
|
||||
$default = reset($instance['settings']['menu_options']) . ':0';
|
||||
}
|
||||
|
||||
variable_set('menu_parent_' . $instance['bundle'], $default);
|
||||
}
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Handler for menu weight field of menu_link module
|
||||
*/
|
||||
class menu_link_handler_sort_weight extends views_handler_sort {
|
||||
|
||||
function option_definition() {
|
||||
$options = parent::option_definition();
|
||||
|
||||
$options['lowest'] = array('default' => 1);
|
||||
$options['highest'] = array('default' => 3);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
function options_form(&$form, &$form_state) {
|
||||
parent::options_form($form, $form_state);
|
||||
|
||||
$level_vals = array();
|
||||
for ($i = 1; $i < 10; $i++) {
|
||||
$level_vals[$i] = $i;
|
||||
}
|
||||
|
||||
$form['lowest'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $level_vals,
|
||||
'#default_value' => isset($this->options['lowest']) ? $this->options['lowest'] : 1,
|
||||
'#title' => t('Start level'),
|
||||
'#description' => t('This tells the sort handler from which value the sort will begin.'),
|
||||
);
|
||||
|
||||
$form['highest'] = array(
|
||||
'#type' => 'select',
|
||||
'#options' => $level_vals,
|
||||
'#default_value' => isset($this->options['highest']) ? $this->options['highest'] : 3,
|
||||
'#title' => t('End level'),
|
||||
'#description' => t('Set the deepest / highest level the sort will attempt to sort'),
|
||||
);
|
||||
}
|
||||
|
||||
function options_validate(&$form, &$form_state) {
|
||||
parent::options_validate($form, $form_state);
|
||||
|
||||
$vals = $form_state['values']['options'];
|
||||
|
||||
// the lowest limit has to be bigger than the selected highest
|
||||
if ($vals['lowest'] < $this->vals['highest']) {
|
||||
form_set_error('lowest', 'The lowest level has to be bigger than the highest value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to add additional OrderBy statements to the query.
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
|
||||
$params = array(
|
||||
'function' => $this->options['group_type'],
|
||||
);
|
||||
|
||||
$highest = $this->options['highest'];
|
||||
|
||||
$join = new views_join();
|
||||
$join->definition = array(
|
||||
'left_field' => 'menu_link_mlid',
|
||||
'field' => 'mlid',
|
||||
'table' => 'menu_links',
|
||||
'left_table' => 'field_data_menu_link',
|
||||
);
|
||||
$join->table = 'menu_links';
|
||||
$join->left_table = 'field_data_menu_link';
|
||||
$join->left_field = 'menu_link_mlid';
|
||||
$join->field = 'mlid';
|
||||
$join->type = 'LEFT';
|
||||
|
||||
$this->query->add_relationship('menu_links', $join, 'menu_links');
|
||||
$this->query->add_table('menu_links', 'menu_links', $join);
|
||||
|
||||
$from = 1;
|
||||
if (isset($this->options['lowest']) && ($this->options['lowest'] > 0)) {
|
||||
$from = $this->options['lowest'];
|
||||
}
|
||||
|
||||
for ($i = $from; $i <= $highest; $i++) {
|
||||
$index = $i;
|
||||
$addjoin = new views_join();
|
||||
$addjoin->definition = array(
|
||||
'left_field' => 'p' .$index,
|
||||
'field' => 'mlid',
|
||||
'table' => 'menu_links',
|
||||
'left_table' => 'menu_links',
|
||||
);
|
||||
$addjoin->table = 'menu_links';
|
||||
$addjoin->left_table = 'menu_links';
|
||||
$addjoin->left_field = 'p' .$index;
|
||||
$addjoin->field = 'mlid';
|
||||
$addjoin->type = 'LEFT';
|
||||
|
||||
$alias = 'nml_p' .$index;
|
||||
$this->query->add_relationship('nml_p' .$index, $addjoin, 'nml_p' .$index);
|
||||
$this->query->add_table('menu_links', $alias, $addjoin);
|
||||
$this->query->add_orderby(NULL, $alias . '.weight', $this->options['order'], NULL, $params);
|
||||
}
|
||||
}
|
||||
|
||||
function ui_name($short = FALSE) {
|
||||
return $this->get_field(parent::ui_name($short));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user