diff --git a/modules/custom_breadcrumbs/LICENSE.txt b/modules/custom_breadcrumbs/LICENSE.txt new file mode 100644 index 0000000..2c095c8 --- /dev/null +++ b/modules/custom_breadcrumbs/LICENSE.txt @@ -0,0 +1,274 @@ +GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, +Cambridge, MA 02139, 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 Library 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 diff --git a/modules/custom_breadcrumbs/README.txt b/modules/custom_breadcrumbs/README.txt new file mode 100644 index 0000000..34f0dae --- /dev/null +++ b/modules/custom_breadcrumbs/README.txt @@ -0,0 +1,407 @@ +/* $Id $ */ + +Custom Breadcrumbs 6.x-1.x (See below for 6.x-2.x) +-------------------------------------------------- +Summary +------- +* Enable the module +* Assign 'administer custom breadcrumbs' permission to those roles that should + be allowed to add/edit/delete custom breadcrumbs. +* Assign 'use php in custom breadcrumbs' to roles that should be allowed to use + php to determine breadcrumb visibility. +* Go to Administer > Site building > Custom breadcrumbs to add new breadcrumbs +* Click "Add a new custom breadcrumb" +* Choose the node type to create a breadcrumb trail for +* For the titles, put each "crumb" one line after another (There is no need to + put in "home"): + + Item 1 + SubItem A + SuperSubItem X + +* For the paths, put the path to each crumb starting after the domain name. + Don't include a leading or trailing slash. + + item1 + item-1/subitem-a + item-1/subitem-a/supersubitem-x + +* Click save to save the breadcrumb +* Visit the page and your breadcrumb should appear! + +Description +----------- +As the name suggests, Custom Breadcrumbs allows you to create and modify your +own breadcrumbs based on node type. After enabling the module, click on +Administer > Site building > Custom breadcrumbs. You'll be abel to add new +breadcrumbs from this page. + +Clicking on that link, you have the option to select the node type the +breadcrumb will apply to. There are two text fields below-- "Titles" and +"Paths." When creating a breadcrumb, you're simply creating a link. In the +custom breadcrumbs interface "Titles" describes the text of the breadcrumb +while "Paths" describes the Drupal path the breadcrumb links to. Each Title +must have a corresponding Path. + +To give a very simple example of how to use this module, let's say I have a +blog on my web site called "Deep Thoughts." To create this, I use the Views +module to create a page at /blog that displays all the node types "blog post." +Whenever a user views a blog post I want the breadcrumb to show +Home > Deep Thoughts instead of simply Home. To do this I would simply type +"Deep Thoughts" in the "Titles" field and and "blog" in the "Paths" field and +save my breadcrumb. + +Using the Tokens module, the Custom breadcrumbs module becomes much more +flexible because breadcrumbs can become dynamic. You can create a breadcrumb +like Home > Deep Thoughts > [Month of Blog Post] [Year of Blog Post], where +"Deep Thoughts" links to my main blog page and "[Month of Blog Post] +[Year of Blog Post]" links to a view that shows only blog posts from the month +and year the blog post was created (e.g. June 2007). For this, you would do +the following: + +Node Type: +Blog Post + +Titles: +Deep Thoughts +[month] [yyyy] + +Paths: +blog +blog/[mm]_[yyyy] + +(where of course, blog/[mm]_[yyyy] is the path to the view of blog posts from +that month and year). So if you created a blog pos on June 13, 2007 your +breadcrumb would show Home > Deep Thoughts > June 2007 and "June 2007" links +to "blog/06_2007" which is a view of all blog posts from June 2007. + +Also, note that Custom Breadcrumbs doesn't actually check to be sure that a +particular path exists, so you'll have to check yourself to avoid 404 errors. + +Only users with 'administer custom breadcrumbs' permission will be allowed to +create or modify custom breadcrumbs. + +Breadcrumb Visibility +--------------------- +Users given 'use php in custom breadcrumbs' permission can include a php code +snippet that returns TRUE or FALSE to control whether or not the breadcrumb is +displayed. Note that this code has access to the $node variable, and can check +its type or any other property. Tokens should not be used in the visibility +code snippet, since they will not be replaced. + +Special Identifiers +------------------- +The following identifiers can be used to achieve a special behavior: + - will clean any path using the current pathauto module settings, + if that module is installed. + - can be used as a path to have a breadcrumb element that is not + hyperlinked. + +Identifiers should be added to the paths area in the following format: +identifier|path. To be recognized, the identifier must be enclosed in angular +brackets, and proceed any part of the path: + +For example: |[ogname-raw] + + +Custom Breadcrumbs 2.0 +---------------------- + +Summary +------- +* Enable the module and any option submodules (see below for details) +* Assign 'administer custom breadcrumbs' permission to those roles that should + be allowed to add/edit/delete custom breadcrumbs. +* Assign 'use php in custom breadcrumbs' to roles that should be allowed to use + php to determine breadcrumb visibility. +* Go to Administer > Site Configuration > Custom breadcrumbs Settings to select + the 'Home' breacrumb text and possibly other global settings. +* Go to Administer > Site building > Custom breadcrumbs to add new breadcrumbs +* To add a breadcrumb, click on one of the tabs at the top of the page. For + example, click 'Node' to create a custom breadcrumb based on node type. +* Fill in the required information for the breadcrumb (varies depending on + breadcrumb type, see below). +* For the titles, put each "crumb" one line after another (There is no need to + put in "home") +* (optional) For each crumb title you can specify a title attribute ("tooltip") + to add to the link. Separate the crumb title and the title attribute with a + pipe (|) symbol: + + Item 1 + SubItem A|Title attribute for SubItemA (optional) + SuperSubItem X + +* For the paths, put the path to each crumb starting after the domain name. + Don't include a leading or trailing slash. + + item1 + item-1/subitem-a + item-1/subitem-a/supersubitem-x + +* Click save to save the breadcrumb +* Visit the page and your breadcrumb should appear! + +New Features +------------ +In the 6.x-2.x release, custom breadcrumbs has many new features which are +available through optional modules in the custom breadcrumbs package. The base +module, required by all the others, is still custom_breadcrumbs. This module +handles custom breadcrumbs based on node type as described above. The following +optional modules can also be installed to provide custom breadcrumbs in a +variety of situations: + ++ custom_breadcrumbs_views provides custom breadcrumbs on views pages. + Once this module is enabled, a new "Views" tab will appear at + admin/build/custom_breadcrumbs. To add a views page breadcrumb, click on the + tab and then select the view from list of available views. Fill in the + visibility, title and paths sections as described above, and your breadcrumb + should appear on your views page. Note that token substitution is possible + with global and user tokens only. The $view object is available for use in + the php_visibility section. + ++ custom_breadcrumbs_paths provides custom breadcrumbs on nodes and views at + a specified path (url). Once this module is enabled, a new "Path" tab will + appear at admin/build/custom_breadcrumbs. To add a breadcrumb for a node + or a view at a specific path, just enter the Drupal path in the Specific + Path section. Fill in the visibility, title and paths sections as + described above, and save the breadcrumb. Now your breadcrumb should appear + on the node or view at the specific path that you selected. Note that custom + breadcrumbs does not check the validity of the entered path. When entering a + path for a particular language (see below), do not specify the two-letter + language prefix. Custom breadcrumbs will assume the correct prefix according + to the selected language. To use '*' as a wildcard, go to custom breadcrumbs + configuration page at /admin/settings/custom-breadcrumbs and select the + 'Use wildcard pattern matching in paths' option in the advanced settings + section. When this option is enabled, the breadcrumb that best matches the + path will be selected. The best match is determined by the depth at which + the first wildcard appears. For example, if the path is path/to/some/content + and breadcrumbs have been defined for path/to/* and path/to/some/*, the + latter will be chosen as the best match. + ++ custom_breadcrumbs_taxonomy provides custom breadcrumbs on taxonomy term + pages, views, and for nodes that are assigned a taxonomy vocabulary or term. + Once this module is enabled, two new tabs will appear appear at + admin/build/custom_breadcrumbs: Term and Vocabulary. Breadcrumb generation + can be handled in two different ways. If 'use the taxonomy term hierarchy' + is checked at custom breadcrumbs configuration page, then breadcrumbs will + be generated similarly to the taxonomy_breadcrumb module. Otherwise, + breadcrumb generation will be according to the standard custom breadcrumbs + approach. + + In taxonomy breadcrumb mode, the breadcrumb trail is automatically + constructed based on the taxonomy term hierarchy: + [HOME] >> [VOCABULARY] >> TERM >> [TERM] >> [TITLE]. In this mode the + breadcrumb titles are the term and vocabulary names. The paths these titles + are linked to can be assigned via the Term and Vocabulary tabs at + admin/build/custom_breadcrumbs. Providing a path for a vocabulary will enable + the [VOCABULARY] portion of the breadcrumb. The path for a term can + similarly be set, but if one is not provided the default taxonomy/term/tid + (where tid is a number, the taxonomy term id) will be used. Select the types + of nodes to include or exclude at the custom breadcrumbs configuration + settings page. The option to add the node title at the end of the breadcrumb + trail can also be enabled on that page. There is also an option to append + the current taxonomy term to the breadcrumb on taxonomy term pages + (defined to be any page with drupal path */taxonomy/term/*). + + In the standard custom breadcrumbs mode, you can provide the titles and paths + for constructing the breadcrumb trail on nodes that have defined taxonomy + terms. Note that if a node has more than one term, the lightest term in the + lightest vocabulary with a defined custom breadcrumb will be used. + + Note: do not use this module and the taxonomy_breadcrumb module at the same + time. Custom_breadcrumbs_taxonomy has extended the functionality of + taxonomy_breadcrumb, so that module is not needed if you are using + custom_breadcrumbs. + + While at admin/settings/custom-breadcrumbs, go ahead and enable any + additional taxonomy breadcrumb options that suits your needs. If you are + using views to override taxonomy term pages, then be sure to enable the + "Use taxonomy breadcrumbs for views" option. + ++ custom_breadcrumbsapi provides a simple api that allows custom breadcrumbs to + be defined for module pages implementing the api. Module developers need to + provide a modulename_custom_breadcrumbsapi() function that returns an array + containing the names of the module pages for which custom breadcrumbs may be + defined. + + The following is an example that could be used with the forum module. + + /** + * Implementation of hook_custom_breadcrumbsapi(). + * Allow custom breadcrumbs for the following module pages. + */ + function forum_custom_breadcrumbsapi() { + return array('forum listing'); + } + + Then, in the callback functions for each of those pages, the following line + must be inserted within the function (preferably after defining $breadcrumb + but before setting the breadcrumb): + + drupal_alter('breadcrumb', $breadcrumb, 'module_page_name'); + + Continuing with the forum module example, 'module_page_name' would be + replaced with 'forum listing'. + + custom_breadcrumbsapi can also provide custom breadcrumbs for modules + implementing theme templates (e.g. files ending in .tpl.php). To add a + custom breadcrumb when a specific theme template file is called, click + on the module page tab at admin/build/custom_breadcrumbs. Select the + template file from the list of theme templates (determined from the + theme registry). Then fill in the usual custom breadcrumbs information + such as titles as paths. If using a php snippet for breadcrumb visibility + or to specify titles and paths (see below), you have access to the template + variables through $variables, an associative array defined by the module + providing the template. See the documentation in the template file for + details. For example, if a template file uses the variable $foo, then + access to that variable would be through $variables['foo']. + +User Interface +-------------- +The user interface has been modified for Custom Breadcrumbs 2.0. Breadcrumbs +from all custom breadcrumbs modules are tabulated at +admin/build/custom_breadcrumbs. The table can be sorted according to +breadcrumb name, type, language (if locale is enabled) by clicking on the +column headers. The table can also be filtered to display breadcrumbs of a +specific type, language, or combination of the two. + +A new custom breadcrumbs fieldset has been added to node edit pages. All +defined breadcrumbs for a particular node are displayed here, with an option to +edit each. If no breadcrumbs have been defined for a particular node, then a +link can be followed back to admin/build/custom_breacrumbs to add a custom +breadcrumb. + +Languages +--------- +If the core Locale module is enabled, then an additional option to specify the +language for the breadcrumb is available when constructing the breadcrumb trail +(with any of the custom breadcrumb modules). + +HOME breadcrumb +--------------- +The text to display at beginning of the breadcrumb trail can be assigned from +the custom breadcrumb configuration settings page. Typically this is Home or +your site name. You can leave it blank to have no home breadcrumb. As with +normal crumb titles, you can optionally specify a title attribute ("tooltip") +for the crumb. Just separate the crumb text and the title attribute text with a +pipe (|) symbol (i.e. Home crumb text|attribute title text). There is +also an advanced setting to set the Home breadcrumb text on ALL pages, not +just those with defined custom breadcrumbs. You can also use this feature to +remove the home breadcrumb on all pages on the site - just enable the advanced +setting and then leave the home breadcrumb text blank. + +It is possible to translate the home reference title from custom breadcrumbs +using the i18n module. Just put this in your settings.php: + + $conf['i18n_variables'] = array( + //custom breadcrumbs + 'custom_breadcrumb_home', + ); + +Then you can change it for each language at +http://example.com/#lang-prefix#/admin/settings/custom-breadcrumbs. + +See http://drupal.org/node/313272 for additional information. + +Use PHP in breadcrumb titles and paths +-------------------------------------- +If this advanced option is enabled at admin/settings/custom-breadcrumbs, then +users given 'use php in custom breadcrumbs' permission can include small php +code snippets (less than 250 characters)in the titles and/or paths fields of +the add breadcrumb form. Be careful when enabling this option, as the incorrect +use of php can break your site. + +There are a couple of ways to use php in breadcrumbs and titles. One way is to +return an array of breadcrumb titles in the titles text field and a +corresponding array of breadcrumb paths in the paths text field such as + +Titles: + + +Paths: + + +Sometimes, it may be more convient to assign the titles and paths in the same +code snippet, so you can also return an associate array with elements 'titles' +and 'paths' that contain the titles and paths arrays, respectively. +For example, + +Titles: + $titles, 'paths' => $paths); ?> + +(In this case, the paths text field will be ignored, so you can leave it +empty). + +When defined, appropriate objects such as $node, $term, or $view, will be +available for these code snippets. Note that if this option is enabled and an +array is not returned, then the module defaults to the standard operation of +using each line of the titles and paths text fields to define a part of the +breadcrumb. + +For longer code snippets (greater than 250 characters), you can save your code +snippet in an include file and use a php require_once statement in the titles +and/or paths section of your custom breadcrumb to include and evaluate your +code. See http://drupal.org/node/654766 for an example of this. + +Add CSS classes to custom breadcrumb elements +--------------------------------------------- + +You can enable this feature on the custom breadcrumbs configuration screen +under the HTML element identifiers section. There are several options that +provide html class identifiers for theming custom breadcrumb links, including +add a 'custom-breadcrumbs-home' ID attribute to the home breadcrumb item, +adding numbered class attributes 'custom-breadcrumbs-item-N' for each +breadcrumb item, adding even and odd classes to breadcrumb items and storing an +identifier that is unique for each defined custom breadcrumbs. Using this +last option requires modifying your sites phptemplate_breadcrumb (or theme +override) function to actually add the class name to the breadcrumb container. +The class name is returned as a string by the function +custom_breadcrumbs_unique_breadcrumb_id(). The identifier will be of the form +'custom-breadcrumbs-type-id'where type is the breadcrumb type (node, panels, +path, views or taxonomy) and id is the breadcrumb id number. +See http://drupal.org/node/643796#comment-2532998 for more information on this +feature. + +Special Identifiers +------------------- +In Custom Breadcrumbs 2.0, Special identifiers are now provided as a separate, +optional module - custom_breadcrumbs_identifiers. At present, this module +provides the following identifiers: + + - Produces a plain text crumb. This identifier should not + be used with the pipe (|) symbol. + - Cleans the given path using your pathauto replacement + rules. + - Provides crumbs for each parent node of a book page. + Whatever is placed in the corresponding position of the + title area will be ignored. It should not be used with + the pipe (|) symbol. + - Provides a plain text crumb using the page title. Whatever + is placed in the corresponding position of the title area + will be ignored. It should not be used with the pipe (|) + symbol. + - Produces crumbs for each parent item for the given path. + The title information for this line will be ignored + because the menu link titles are used. If a path is not + provided following the pipe (|) symbol, the current path + with be used. + +Additional special identifiers can be developed and added by contributed +modules that implement hook_cb_identifier_list(), to provide a description of +the identifer, and hook_cb_identifier_values(), to prepare the appropriate +crumb items. See the custom_breadcrumbs_identifiers.module for examples of +how to do this. + +Identifiers should be added to the paths area in the following format: +identifier|path. To be recognized, the identifier must be enclosed in angular +brackets, and proceed any part of the path: + +For example: |[ogname-raw] + +Note that not all identifiers require the use of |path. + +Authors +------- +bennybobw, dbabbage, Michelle, MGN diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs.admin.inc b/modules/custom_breadcrumbs/custom_breadcrumbs.admin.inc new file mode 100644 index 0000000..4280f59 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs.admin.inc @@ -0,0 +1,855 @@ + array('name' => 'asc', 'breadcrumb_type' => 'asc', 'language' => 'asc'), + 'column' => 'name')); + $sort = $custom_breadcrumbs_sort['direction']; + $multilingual = _custom_breadcrumbs_multilingual(); + $breadcrumbs = array(); + // Read session variable to load breadcrumbs of type and language selected by user. + if (empty($_SESSION['custom_breadcrumbs_overview_filter'])) { + $breadcrumbs = _custom_breadcrumbs_load_all_breadcrumbs(TRUE); + } + else { + // Determine a list of selected languages, and a list of modules to load them from. + $modules = array(); + $tables = array(); + $languages = array(); + foreach ($_SESSION['custom_breadcrumbs_overview_filter'] as $filter) { + if ($filter[0] == 'type') { + list($module, $table) = explode('-', $filter[1]); + $modules[] = $module; + $tables[] = $table; + } + elseif ($filter[0] == 'language') { + $languages[] = $filter[1]; + } + } + if (empty($modules)) { + $modules = module_implements('cb_breadcrumb_info'); + } + foreach ($modules as $key => $module) { + $more = custom_breadcrumbs_load_breadcrumbs($module, $tables[$key], NULL, $languages); + if (!empty($more)) { + $breadcrumbs = array_merge($breadcrumbs, $more); + } + } + } + // Sort the breadcrumbs according to $sort and $order. + $columns = array('name' => 'Name', 'breadcrumb_type' => 'Breadcrumb Type'); + if ($multilingual) { + $columns['language'] = 'Language'; + } + $order = (isset($_GET['order']) && in_array($_GET['order'], array_keys($columns))) ? $_GET['order'] : 'name'; + $sort[$order] = (isset($_GET['sort']) && ($_GET['sort'] == 'desc')) ? 'desc' : 'asc'; + variable_set('custom_breadcrumbs_sort', array('direction' => $sort, 'column' => $order)); + usort($breadcrumbs, '_custom_breadcrumbs_sort_cmp'); + + // Make the breadcrumb list table sortable by name, type and language. + $path = 'admin/build/custom_breadcrumbs'; + $sort[$order] = ($sort[$order] == 'asc') ? 'desc' : 'asc'; + + // Add an arrow indicating sort direction. + $image = array($order => theme('tablesort_indicator', $sort[$order])); + + $headers = array(); + foreach ($columns as $key => $title) { + $options = array('attributes' => array('title' => t('sort by @s', array('@s' => $title)))); + $options['query'] = 'order=' . $key . '&sort=' . $sort[$key]; + if (isset($image[$key])) { + $options['html'] = TRUE; + $title .= $image[$key]; + } + $headers[] = array('data' => l($title, $path, $options)); + } + $headers[] = t('Operations'); + $rows = array(); + foreach ($breadcrumbs as $breadcrumb) { + $row = array(); + $name = $breadcrumb->name; + $type = $breadcrumb->breadcrumb_type; + $row['name'] = array('data' => $name . (!empty($breadcrumb->visibility_php) ? ' '. t('with PHP snippet') : '')); + $row['breadcrumb_type'] = array('data' => $type); + if ($multilingual) { + $row['language'] = array('data' => module_invoke('locale', 'language_name', $breadcrumb->language)); + } + $row['edit'] = l(t('edit'), $path .'/'. $breadcrumb->breadcrumb_type .'/edit/'. $breadcrumb->bid); + $row[$order]['class'] = 'active'; + $rows[] = $row; + } + if (count($rows) == 0) { + $rows[] = array(array('data' => t('No custom breadcrumbs have been defined.'), 'colspan' => 2 + (int)$multilingual)); + } + + $output = drupal_get_form('custom_breadcrumbs_filter_form'); + $output .= theme('table', $headers, $rows); + return $output; +} + +/** + * Sorts two custom_breadcrumbs objects by name, type, or language. + * + * Each column can be independently sorted as asc or desc. + * + * @param $bc1 + * First breadcrumb object. + * @param $bc2 + * Second breadcrumb object. + * + * @return + * 0 if the two objects have equal ranking, + * 1 if the first object is greater than the second, + * -1 if the second object is greater than the first, + */ +function _custom_breadcrumbs_sort_cmp($bc1, $bc2) { + $custom_breadcrumbs_sort = variable_get('custom_breadcrumbs_sort', array( + 'direction' => array('name' => 'asc', 'breadcrumb_type' => 'asc', 'language' => 'asc'), + 'column' => 'name') + ); + $options = array('name', 'breadcrumb_type', 'language'); + $first = $custom_breadcrumbs_sort['column']; + $keys = array_keys($options, $first); + $key = array_pop($keys); + unset($options[$key]); + // Reindex the array. + $options = array_values($options); + $sortdir = array(); + foreach ($custom_breadcrumbs_sort['direction'] as $key => $sort) { + $sortdir[$key] = ($sort == 'asc') ? 1 : -1; + } + if ($bc1->$first == $bc2->$first) { + if ($bc1->$options[0] == $bc2->$options[0]) { + if ($bc1->$options[1] == $bc2->$options[1]) { + return 0; + } + return ($bc1->$options[1] > $bc2->$options[1]) ? $sortdir[$options[1]] : -1*$sortdir[$options[1]]; + } + return ($bc1->$options[0] > $bc2->$options[0]) ? $sortdir[$options[0]] : -1*$sortdir[$options[0]]; + } + return ($bc1->$first > $bc2->$first) ? $sortdir[$first] : -1*$sortdir[$first]; +} + +/** + * Form builder to edit a custom breadcrumb record. + * + * @param $type + * The type of custom breadcrumb to edit. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + * @see custom_breadcrumbs_form_delete() + * @see custom_breadcrumbs_form_cancel() + */ +function custom_breadcrumbs_form(&$form_state, $type) { + $form = array(); + $breadcrumb = NULL; + $bid = arg(5); + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for Node')); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs', NULL, array('bid' => $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Node')); + } + $options = array(); + foreach (node_get_types('names') as $type => $name) { + $options[$type] = $name; + } + $form['node_type'] = array( + '#type' => 'select', + '#title' => t('Node type'), + '#required' => TRUE, + '#options' => $options, + '#description' => t('The node type this custom breadcrumb trail will apply to.'), + '#default_value' => isset($breadcrumb->node_type) ? $breadcrumb->node_type : NULL, + '#weight' => -10, + ); + // Store information needed to save this breadcrumb. + $form['#module'] = 'custom_breadcrumbs'; + $form['#infokey'] = 'node'; + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + + return $form; +} + +/** + * Form validation handler for custom_breadcrumbs_form(). + * + * @see custom_breadcrumbs_form() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbs_form_validate($form, &$form_state) { + // Check to make sure there are an equal number of paths and titles. + $check = TRUE; + if (!user_access('use php in custom breadcrumbs') || !variable_get('custom_breadcrumbs_use_php_in_titles', FALSE)) { + // Strip any PHP tags from paths and titles since they are not allowed. + $elements = array('titles', 'paths'); + foreach ($elements as $element) { + if (strpos($form_state['values'][$element], ''), '', $form_state['values'][$element]); + form_set_error($element, t('Use of PHP in !element is not permitted and will be filtered out.', array('!element' => $element))); + } + } + } + else { + // If PHP is used, don't validate number of paths and titles. + if ((strpos($form_state['values']['titles'], ' $path_count, '!titles' => $title_count))); + } + } +} + +/** + * Form submission handler for custom_breadcrumbs_form(). + * + * @see custom_breadcrumbs_form() + * @see custom_breadcrumbs_form_validate() + */ +function custom_breadcrumbs_form_submit($form, &$form_state) { + $breadcrumb = (object)$form_state['values']; + _custom_breadcrumbs_save_breadcrumb($form['#module'], $form['#infokey'], $breadcrumb); + $form_state['redirect'] = 'admin/build/custom_breadcrumbs'; +} + +function custom_breadcrumbs_form_delete($form, &$form_state) { + _custom_breadcrumbs_delete_breadcrumb($form['#module'], $form['#infokey'], $form_state['values']['bid']); + $form_state['redirect'] = 'admin/build/custom_breadcrumbs'; +} + +function custom_breadcrumbs_form_cancel($form, &$form_state) { + $form_state['redirect'] = 'admin/build/custom_breadcrumbs'; +} + +/** + * Provides form elements commonly used by custom breadcrumbs submodules. + * + * @param $bid + * The breadcrumb id. + * @param $breadcrumb; + * The breadcrumb object. + * + * @return $form + * A form array with common custom breadcrumb elements. + */ +function custom_breadcrumbs_common_form_elements($bid, $breadcrumb) { + // TODO: consider removing the $bid parameter since its part of the breadcrumb object. + // TODO: or should $breadcrumb default to NULL? + $form = array(); + $form['bid'] = array('#type' => 'hidden', '#value' => $bid, ); + + $form['name'] = array( + '#type' => 'textfield', + '#title' => t('Breadcrumb Name'), + '#default_value' => isset($breadcrumb->name) ? $breadcrumb->name : '', + '#description' => t('This name will only appear when listing custom breadcrumbs. If left blank, a name for this breadcrumb will be generated for you.'), + '#weight' => -50, + ); + if (isset($breadcrumb)) { + $cbid = custom_breadcrumbs_unique_breadcrumb_id($breadcrumb->breadcrumb_type, $breadcrumb->bid); + } + if (isset($cbid)) { + $form['classid'] = array( + '#value' => ''. t('The CSS class name for this breadcrumb is !name', array('!name' => $cbid)) .'', + ); + } + + $multilingual = _custom_breadcrumbs_multilingual(); + if ($multilingual) { + $form['language'] = array( + '#type' => 'select', + '#title' => t('Language'), + '#options' => array('' => t('All languages')) + locale_language_list('name'), + '#default_value' => isset($breadcrumb->language) ? $breadcrumb->language : NULL, + '#description' => t('A breadcrumb set for a specific language will always be used when displaying a node in that language, and takes precedence over breadcrumbs set for All languages.'), + ); + } + else { + $form['language'] = array('#type' => 'value', '#value' => '', ); + } + $form['visibility_php'] = array( + '#type' => 'textarea', + '#title' => t('Breadcrumb visibility'), + '#access' => user_access('use php in custom breadcrumbs'), + '#description' => t('Determine whether this breadcrumb should be displayed by using a PHP snippet to return TRUE or FALSE. Note that this code has access to the $node variable, and can check its type or any other property. Do not use opening and closing php tags.'), + '#default_value' => isset($breadcrumb->visibility_php) ? $breadcrumb->visibility_php : '', + ); + + $description = t('A list of titles for the breadcrumb links, one on each line. For each crumb title you can also specify a title attribute to add to the link. Separate the crumb title and the title attribute with a pipe (|) symbol.'); + if (variable_get('custom_breadcrumbs_use_php_in_titles', FALSE) && user_access('use php in custom breadcrumbs')) { + $description .= ' '. t("Or, you can enter a small PHP code snippet (less than 250 characters) returning either an array of text strings for the breadcrumb titles, with one title per array element, or an associative array with elements 'titles' and 'paths' each containing an array of text strings for the breadcrumb titles and paths, respectively. Include the snippet between %php. Use with caution since incorrect PHP code can break your Drupal site. Token replacement will occur after any PHP is evaluated.", array('%php' => '')); + } + + $form['titles'] = array( + '#type' => 'textarea', + '#title' => t('Titles'), + '#required' => TRUE, + '#description' => $description, + '#default_value' => isset($breadcrumb->titles) ? $breadcrumb->titles : NULL, + ); + + $description = t('A list of Drupal paths for the breadcrumb links, one on each line.'); + if (variable_get('custom_breadcrumbs_use_php_in_titles', FALSE) && user_access('use php in custom breadcrumbs')) { + $description .= ' '. t("You can also enter a small PHP code snippet (less than 250 characters) returning an array of drupal paths for the breadcrumb links, with one path per array element. Include the snippet between %php. Use with caution since incorrect PHP code can break your Drupal site. Token replacement will occur after any PHP is evaluated.", array('%php' => '')); + } + $form['paths'] = array( + '#type' => 'textarea', + '#title' => t('Paths'), + '#required' => FALSE, + '#description' => $description, + '#default_value' => isset($breadcrumb->paths) ? $breadcrumb->paths : NULL, + ); + $form['help'] = array( + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Placeholder tokens'), + '#description' => t("The following placeholder tokens can be used in both paths and titles. When used in a path or title, they will be replaced with the appropriate values."), + ); + if (module_exists('token')) { + // This code depends on a patch still under review at http://drupal.org/node/127072. + // $form['help']['tokens'] = array('#value' => theme('token_help', array('global', 'node', 'user')), ); + // This is overkill, but is necessary until this patch is committed. + $form['help']['tokens'] = array('#value' => theme('token_help', 'all'), ); + } + else { + $form['help']['#description'] = t("To use dynamic placeholder tokens in your breadcrumb trails (the ID or title of the current node, for example), download and install the Token module from Drupal.org.", array('@token' => 'http://www.drupal.org/project/token')); + $form['help']['#collapsible'] = FALSE; + $form['help']['#collapsed'] = FALSE; + } + $form['help2'] = array( + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#title' => t('Special identifiers'), + '#description' => t("The following identifiers can be used to achieve a special behavior. Identifiers should be added to the paths area in the following format: identifier|path.
For example: %pathauto_id|[ogname-raw]", array('%pathauto_id' => '')), + ); + $form['help2']['tokens'] = array('#value' => theme('custom_breadcrumbs_help_identifiers'), ); + + $form['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + if (isset($bid)) { + $form['buttons']['delete'] = array( + '#type' => 'submit', + '#value' => t('Delete'), + '#submit' => array('custom_breadcrumbs_form_delete'), + ); + } + $form['buttons']['cancel'] = array( + '#type' => 'submit', + '#value' => t('Cancel'), + '#submit' => array('custom_breadcrumbs_form_cancel'), + ); + return $form; +} + +/** + * Form builder; Configure basic and advanced custom breadcrumbs settings for this site. + * + * @ingroup forms + * @see system_settings_form() + */ +function custom_breadcrumbs_admin_settings() { + $form = array(); + drupal_set_title(t('Custom Breadcrumbs Configuration')); + + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Basic settings'), + '#collapsible' => TRUE, + ); + + $form['settings']['custom_breadcrumb_home'] = array( + '#type' => 'textfield', + '#title' => t('Home breadcrumb text'), + '#default_value' => variable_get('custom_breadcrumb_home', t('Home')), + '#description' => t('This text will be displayed as the first item of the breadcrumb trail. Typically Home or your site name. Leave blank to have no home breadcrumb. You can also specify a title attribute (tooltip text) to add to the link. Just separate the crumb text and the title attribute text with a pipe (|) symbol (i.e. Home crumb text|attribute title text).'), + ); + + $form['settings']['custom_breadcrumbs_menu_structure'] = array( + '#type' => 'fieldset', + '#title' => t('Menu structure'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['settings']['custom_breadcrumbs_menu_structure']['custom_breadcrumbs_set_menu_breadcrumb'] = array( + '#type' => 'checkbox', + '#title' => t('Use the menu structure to set the breadcrumb trail'), + '#default_value' => variable_get('custom_breadcrumbs_set_menu_breadcrumb', FALSE), + '#description' => t("If enabled, the default breadcrumb trail will be patterned after the page's placement in the menu structure. Select the menus that this option will apply to below. Note that this default can be superceded by other custom breadcrumbs."), + '#weight' => -30, + ); + + $menu_options = array(); + foreach (menu_get_names() as $name) { + $menu_options[$name] = $name; + } + + $form['settings']['custom_breadcrumbs_menu_structure']['custom_breadcrumbs_menu_list'] = array( + '#type' => 'select', + '#title' => t('Menu name'), + '#options' => $menu_options, + '#default_value' => variable_get('custom_breadcrumbs_menu_list', FALSE), + '#description' => t("Pages within selected menus will have a default breadcrumb patterned after the menu structure."), + '#multiple' => TRUE, + '#weight' => -20, + ); + + $form['adv_settings'] = array( + '#type' => 'fieldset', + '#title' => t('Advanced settings'), + '#collapsible' => TRUE, + ); + + $form['adv_settings']['custom_breadcrumbs_set_global_home_breadcrumb'] = array( + '#type' => 'checkbox', + '#title' => t('Set the home breadcrumb text on all pages'), + '#default_value' => variable_get('custom_breadcrumbs_set_global_home_breadcrumb', FALSE), + '#description' => t("If enabled, the home breadcrumb text will be used on all pages, not just those with defined custom breadcrumbs. If you don't want a home breadcrumb to be displayed, just enable this option and make sure that the home breadcrumb text above is empty."), + '#weight' => -40, + ); + + $form['adv_settings']['custom_breadcrumbs_use_php_in_titles'] = array( + '#type' => 'checkbox', + '#title' => t('Allow the use of PHP to set titles and paths'), + '#default_value' => variable_get('custom_breadcrumbs_use_php_in_titles', FALSE), + '#description' => t("If enabled, small PHP code snippets (less than 250 characters) can be used in the titles and paths section of the create breadcrumb forms. Each should return an array with text strings for each part of the crumb. Alternatively, a PHP code snippet placed in the titles box can return an associative array with elements 'titles' and 'paths', each an array of text strings. If this is done, the paths text field will be ignored."), + '#weight' => -35, + ); + + $form['adv_settings']['custom_breadcrumbs_force_active_trail'] = array( + '#type' => 'checkbox', + '#title' => t('Force the active trail'), + '#description' => t('This options sets the active trail to match the custom breadcrumb trail and overrides the normal theme_links() implementation to add the active-trail class to links. This is experimental and may not work as expected. If this is of interest to you, please test and report back to the custom breadcrumbs issue queue.'), + '#default_value' => variable_get('custom_breadcrumbs_force_active_trail', FALSE), + '#weight' => -10, + ); + + $form['adv_settings']['custom_breadcrumbs_excluded'] = array( + '#type' => 'fieldset', + '#title' => t('Excluded paths'), + '#description' => t("If enabled, the custom breadcrumbs module will not modify breadcrumbs set on the paths defined below."), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['adv_settings']['custom_breadcrumbs_excluded']['custom_breadcrumbs_use_exclude_list'] = array( + '#type' => 'checkbox', + '#title' => t('Exclude custom breadcrumbs from some pages'), + '#default_value' => variable_get('custom_breadcrumbs_use_exclude_list', FALSE), + ); + + $default_exclude_paths = array(); + if (module_exists('admin_menu')) { + $default_exclude_paths[] = 'admin/user/user'; + $default_exclude_paths[] = 'user'; + } + $default = implode(',', $default_exclude_paths); + + $form['adv_settings']['custom_breadcrumbs_excluded']['custom_breadcrumbs_exclude_list'] = array( + '#type' => 'textfield', + '#title' => t('Do not set custom breadcrumbs on the following paths'), + '#default_value' => variable_get('custom_breadcrumbs_exclude_list', $default), + '#description' => t("A comma delimited set of paths to exclude from custom breadcrumbs. The '*' character can be used as a wildcard."), + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers'] = array( + '#type' => 'fieldset', + '#title' => t('HTML element identifiers'), + '#description' => t("Enabling one or more of these options will provide html identifiers (class or id) for theming custom breadcrumbs links. Style rules can then be defined in the styles.css file located in the site's theme directory."), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options'] = array( + '#type' => 'fieldset', + '#collapsible' => FALSE, + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_home_id'] = array( + '#type' => 'checkbox', + '#title' => t("Add a custom-breadcrumbs-home ID attribute to the home breadcrumb item."), + '#default_value' => variable_get('custom_breadcrumbs_home_id', FALSE), + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_parts_class'] = array( + '#type' => 'checkbox', + '#title' => t("Add a numbered class attribute for each breadcrumb item."), + '#description' => t("If enabled, a custom-breadcrumbs-item-N class attribute will be assigned for each item, where N designates the item number, starting with the first item after the home breadcrumb."), + '#default_value' => variable_get('custom_breadcrumbs_parts_class', FALSE), + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_even_odd_class'] = array( + '#type' => 'checkbox', + '#title' => t("Add even and odd classes to breadcrumb items."), + '#default_value' => variable_get('custom_breadcrumbs_even_odd_class', FALSE), + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_type_class'] = array( + '#type' => 'checkbox', + '#title' => t("Store an identifier for breadcrumb theming."), + '#description' => t("If enabled, the breadcrumb class 'custom-breadcrumbs-type' (where type is the breadcrumb type) can be retrieved and used in the breadcrumb theme override. The function custom_breadcrumbs_unique_breadcrumb_id() will return a string containing the class name."), + '#default_value' => variable_get('custom_breadcrumbs_type_class', FALSE), + ); + + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_append_bid_class'] = array( + '#type' => 'checkbox', + '#title' => t("Append a unique breadcrumb id number to the custom-breadcrumbs-type identifier."), + '#description' => t("If this option and 'Store an identifier for breadcrumb theming' are both enabled, the identifer will be custom-breadcrumbs-type-id, where id is the unique breadcrumb id number."), + '#default_value' => variable_get('custom_breadcrumbs_append_bid_class', FALSE), + ); + + $form['adv_settings']['module_weights'] = array( + '#type' => 'fieldset', + '#title' => t('Module weights'), + '#description' => t('If enabled, the relative module weights can be adjusted in the table below. Modules with lighter weights will provide custom breadcrumbs that can be modified, or overriden, by heavier modules.'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['adv_settings']['module_weights']['custom_breadcrumbs_adjust_module_weights'] = array( + '#type' => 'checkbox', + '#title' => t('Adjust module weights'), + '#default_value' => FALSE, + ); + + $form['adv_settings']['module_weights']['table'] = custom_breadcrumbs_module_weight(); + if (!empty($form['adv_settings']['module_weights']['table'])) { + $form['adv_settings']['module_weights']['table']['#theme'] = 'custom_breadcrumbs_module_weight'; + } + else { + unset($form['adv_settings']['module_weights']); + } + $form['#submit'][] = 'custom_breadcrumbs_admin_settings_submit'; + + return system_settings_form($form); +} + +/** + * Form submission handler for custom_breadcrumbs_admin_settings(). + * + * @see custom_breadcrumbs_admin_settings() + */ +function custom_breadcrumbs_admin_settings_submit($form, &$form_state) { + if (!empty($form['adv_settings']['module_weights']['table']) && $form_state['values']['custom_breadcrumbs_adjust_module_weights']) { + // Set the initial weight of the lightest module to zero. + $current_mod_weight = 0; + $table = array('module name' => array(), 'table weight' => array(), 'module weight' => array()); + // Prepare module table that can be sorted by table weight. + foreach ($form_state['values']['table'] as $module_id => $value) { + if (is_numeric($module_id)) { + $module = $form['adv_settings']['module_weights']['table'][$module_id]['#module']; + $table['table weight'][$module] = $form_state['values']['table'][$module_id]['weight']; + $table['module weight'][$module] = $form['adv_settings']['module_weights']['table'][$module_id]['#weight']; + } + } + // Sort module list by table weight. + array_multisort($table['table weight'], $table['module weight']); + foreach ($table['module weight'] as $module => $orig_weight) { + // Find the module's minimum weight determined by dependence on other modules. + $func = $module .'_minimum_module_weight'; + $min = function_exists($func) ? $func() : 0; + $weight = max($min, $current_mod_weight); + // Update module weight in the system table if it has changed. + if ($orig_weight != $weight) { + db_query("UPDATE {system} SET weight = %d WHERE name = '%s'", $weight, $module); + variable_set('menu_rebuild_needed', TRUE); + } + $current_mod_weight = $weight + 1; + } + } + + // Rebuild the theme registry if custom_breadcrumbs_force_active_trail has changed. + if ($form_state['values']['custom_breadcrumbs_force_active_trail'] != $form['adv_settings']['custom_breadcrumbs_force_active_trail']['#default_value']) { + drupal_rebuild_theme_registry(); + drupal_set_message(t('The theme registry has been rebuilt')); + } +} + +/** + * Lists and manages custom breadcrumb module weights. + * + * @see theme_custom_breadcrumbs_module_weight() + * @see custom_breadcrumbs_admin_settings_submit() + */ +function custom_breadcrumbs_module_weight() { + // Get a list of all custom_breadcrumbs submodules and their module weights. + $modules = module_implements('cb_breadcrumb_info'); + $form = array('#tree' => TRUE); + $module_id = 0; + $weights = _custom_breadcrumbs_get_module_weight($modules); + foreach ($weights as $module => $weight) { + $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info'); + $form[$module_id]['#weight'] = $weight; + $form[$module_id]['#module'] = $module; + $form[$module_id]['name'] = array('#value' => check_plain($info['name'])); + $form[$module_id]['weight'] = array('#type' => 'weight', '#delta' => 200, '#default_value' => $weight); + ++$module_id; + } + + // Only add this form if there is more than one module. + return ($module_id > 1) ? $form : array(); +} + +/** + * Returns HTML for the module weights as a sortable list. + * + * @ingroup themeable + * @see custom_breadcrumbs_module_weight() + */ +function theme_custom_breadcrumbs_module_weight($form) { + $rows = array(); + foreach (element_children($form) as $key) { + if (isset($form[$key]['name'])) { + $module = &$form[$key]; + $row = array(); + $row[] = drupal_render($module['name']); + if (isset($module['weight'])) { + $module['weight']['#attributes']['class'] = 'module-weight'; + $row[] = drupal_render($module['weight']); + } + $rows[] = array('data' => $row, 'class' => 'draggable'); + } + } + + $header = array(t('Module Name')); + $header[] = t('Weight'); + drupal_add_tabledrag('custom_breadcrumbs', 'order', 'self', 'module-weight'); + return theme('table', $header, $rows, array('id' => 'custom_breadcrumbs')) . drupal_render($form); +} + +/** + * Checks whether the administration interface should show multilingual features. + */ +function _custom_breadcrumbs_multilingual() { + return module_exists('locale'); +} + +/** + * Lists custom_breadcrumbs administration filters that can be applied. + */ +function custom_breadcrumbs_filters() { + $filters = array(); + $options = array(); + foreach (module_implements('cb_breadcrumb_info') as $module) { + $bc_info = module_invoke($module, 'cb_breadcrumb_info'); + foreach ($bc_info as $info) { + $options[$module .'-'. $info['table']] = $info['type']; + } + } + $filters['type'] = array( + 'title' => t('Type'), + 'options' => $options, + ); + if (_custom_breadcrumbs_multilingual()) { + $filters['language'] = array( + 'title' => t('Language'), + 'options' => array('' => t('All languages')) + locale_language_list('name'), + ); + } + return $filters; +} + +/** + * Form builder; Return form for custom_breadcrumbs administration filters. + * + * @ingroup forms + * @see custom_breadcrumbs_filter_form_submit() + */ +function custom_breadcrumbs_filter_form() { + $session = &$_SESSION['custom_breadcrumbs_overview_filter']; + $session = is_array($session) ? $session : array(); + $filters = custom_breadcrumbs_filters(); + + $i = 0; + $form['filters'] = array( + '#type' => 'fieldset', + '#title' => t('Show only custom_breadcrumbs where'), + '#theme' => 'custom_breadcrumbs_filters', + ); + foreach ($session as $filter) { + list($type, $value) = $filter; + // Merge an array of arrays into one if necessary. + $options = $filters[$type]['options']; + $params = array('%property' => $filters[$type]['title'] , '%value' => $options[$value]); + if ($i++ > 0) { + $form['filters']['current'][] = array('#value' => t('and where %property is %value', $params)); + } + else { + $form['filters']['current'][] = array('#value' => t('%property is %value', $params)); + } + unset($filters[$type]); + } + + foreach ($filters as $key => $filter) { + $names[$key] = $filter['title']; + $form['filters']['status'][$key] = array( + '#type' => 'select', + '#options' => $filter['options'], + ); + } + + $form['filters']['filter'] = array( + '#type' => 'radios', + '#options' => $names, + ); + + if (count($filters)) { + $form['filters']['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => (count($session) ? t('Refine') : t('Filter')), + ); + } + if (count($session)) { + $form['filters']['buttons']['undo'] = array( + '#type' => 'submit', + '#value' => t('Undo'), + ); + $form['filters']['buttons']['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset'), + ); + } + + drupal_add_js('misc/form.js', 'core'); + + return $form; +} + +/** + * Form submission handler for custom_breadcrumbs_filter_form(). + * + * @see custom_breadcrumbs_filter_form() + */ +function custom_breadcrumbs_filter_form_submit($form, &$form_state) { + $op = $form_state['values']['op']; + $filters = custom_breadcrumbs_filters(); + switch ($op) { + case t('Filter'): case t('Refine'): + if (isset($form_state['values']['filter'])) { + $filter = $form_state['values']['filter']; + // Merge an array of arrays into one if necessary. + $options = $filters[$filter]['options']; + if (isset($options[$form_state['values'][$filter]])) { + $_SESSION['custom_breadcrumbs_overview_filter'][] = array($filter, $form_state['values'][$filter]); + } + } + break; + case t('Undo'): + array_pop($_SESSION['custom_breadcrumbs_overview_filter']); + break; + case t('Reset'): + $_SESSION['custom_breadcrumbs_overview_filter'] = array(); + break; + case t('Update'): + return; + } + + $form_state['redirect'] = 'admin/build/custom_breadcrumbs'; + return; +} + +/** + * Returns HTML for the custom_breadcrumbs filter form. + * + * @ingroup themeable + */ +function theme_custom_breadcrumbs_filter_form($form) { + $output = '
'; + $output .= drupal_render($form['filters']); + $output .= '
'; + $output .= drupal_render($form); + return $output; +} + +/** + * Returns HTML for the custom_breadcrumbs filter selector. + * + * @ingroup themeable + */ +function theme_custom_breadcrumbs_filters($form) { + $output = '
    '; + if (!empty($form['current'])) { + foreach (element_children($form['current']) as $key) { + $output .= '
  • '. drupal_render($form['current'][$key]) .'
  • '; + } + } + $output .= '
  • '; + if (isset($form['buttons']['submit'])) { + $output .= '
    '. (!empty($form['current']) ? '
    '. t('and') .' '. t('where') .'
    ' : '') .'
    '; + foreach (element_children($form['filter']) as $key) { + $output .= drupal_render($form['filter'][$key]); + } + $output .= '
    '; + $output .= '
    '. t('is') .'
    '; + foreach (element_children($form['status']) as $key) { + $output .= drupal_render($form['status'][$key]); + } + $output .= '
    '; + $output .= '
    '; + } + else { + $output .= '
  • '; + } + $output .= '
    '. drupal_render($form['buttons']) .'
    '; + $output .= '
'; + + return $output; +} + +/** + * Provides a simple breadcrumb table for the node edit form. + * + * @param $breadcrumbs + * An array of breadcrumbs that apply to the node. + * + * @return + * A themed table of custom breadcrumbs. + */ +function custom_breadcrumbs_simple_breadcrumb_table($breadcrumbs) { + $header = array(t('Name'), t('Type'), t('Titles'), t('Paths')); + $multilingual = _custom_breadcrumbs_multilingual(); + if ($multilingual) { + $header[] = t('Language'); + } + $header[] = t('Operations'); + $rows = array(); + foreach ($breadcrumbs as $breadcrumb) { + $row = array(); + $row[] = array('data' => $breadcrumb->name . (!empty($breadcrumb->visibility_php) ? ' '. t('with PHP snippet') : '')); + $row[] = array('data' => $breadcrumb->breadcrumb_type); + $row[] = array('data' => check_plain($breadcrumb->titles)); + $row[] = array('data' => check_plain($breadcrumb->paths)); + if ($multilingual) { + $row[] = array('data' => module_invoke('locale', 'language_name', $breadcrumb->language)); + } + $row[] = l(t('edit'), 'admin/build/custom_breadcrumbs/'. $breadcrumb->breadcrumb_type .'/edit/'. $breadcrumb->bid); + $rows[] = $row; + } + if (count($rows) == 0) { + $rows[] = array(array('data' => t('You can define custom breadcrumbs at Custom Breadcrumbs Administration Page.', array('@link' => url('admin/build/custom_breadcrumbs'))), 'colspan' => 5 + (int)$multilingual)); + } + return theme('table', $header, $rows); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs.info b/modules/custom_breadcrumbs/custom_breadcrumbs.info new file mode 100644 index 0000000..6c42f1e --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs.info @@ -0,0 +1,15 @@ +; $Id: custom_breadcrumbs.info,v 1.6.4.1 2009/03/28 21:31:24 mgn Exp $ +name = Custom Breadcrumbs +package = Custom Breadcrumbs +description = Allows administrators to define custom breadcrumb trails for node types. +core = 6.x + + + + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs.install b/modules/custom_breadcrumbs/custom_breadcrumbs.install new file mode 100644 index 0000000..60e3c09 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs.install @@ -0,0 +1,219 @@ + 'Stores custom breadcrumb trail overrides.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumb}.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A return-delimited list of titles for the breadcrumb links.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A return-delimited list of url paths for the breadcrumb links.', + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumb} visibility.', + ), + 'node_type' => array( + 'type' => 'varchar', + 'length' => 64, + 'not null' => FALSE, + 'default' => 'AND', + 'description' => 'Node types the {custom_breadcrumb} should apply to.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'node_language' => array('node_type', 'language'), + ), + 'primary key' => array('bid'), + ); + return $schema; +} + +// Update old-style tokens from early versions of token.module. +// Most users aren't using them, but we might as well handle them +// properly. + +function custom_breadcrumbs_update_1() { + $stuff = array( + '%author_id' => '[author-uid]', + '%author_name' => '[author-name]', + '%user_id' => '[user-id]', + '%user_name' => '[user-name]', + '%node_id' => '[nid]', + '%node_type' => '[type]', + '%node_type_name' => '[type-name]', + '%top_term' => '[term]', + '%top_tname' => '[term-id]', + '%created_d' => '[dd]', + '%created_D' => '[ddd]', + '%created_j' => '[d]', + '%created_l' => '[day]', + '%created_F' => '[month]', + '%created_m' => '[mm]', + '%created_M' => '[mon]', + '%created_n' => '[m]', + '%created_y' => '[yy]', + '%created_Y' => '[yyyy]', + ); + + $search = array_keys($stuff); + $replace = array_values($stuff); + + $result = db_query("SELECT * FROM {custom_breadcrumb}"); + while ($crumb = db_fetch_object($result)) { + $crumb->titles = str_replace($search, $replace, $crumb->titles); + $crumb->paths = str_replace($search, $replace, $crumb->paths); + _custom_breadcrumbs_save_breadcrumb('custom_breadcrumb', $crumb); + } + + return array('success' => TRUE, 'query' => 'Custom Breadcrumb replacement strings updated for use with Token module.'); +} + +function custom_breadcrumbs_update_2() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + $ret[] = update_sql("ALTER TABLE {custom_breadcrumb} ADD visibility_php text NOT NULL default ''"); + break; + case 'pgsql': + db_add_column($ret, 'custom_breadcrumb', 'visibility_php', 'text', array('not null' => TRUE, 'default' => "''")); + break; + } + return $ret; +} + +// Ensure this module's weight is larger than other modules, like views, so custom breadcrumbs page callback is used. +function custom_breadcrumbs_update_3() { + $ret[] = update_sql("UPDATE {system} SET weight = 12 WHERE name = 'custom_breadcrumbs'"); + return $ret; +} + +// Add the menu flag. +function custom_breadcrumbs_update_6001() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumb', 'set_active_menu', array('type' => 'int', 'default' => 1, 'NOT NULL' => TRUE)); + return $ret; +} + + +/** + * Removes set_active_menu field because it is no longer used. + */ +function custom_breadcrumbs_update_6101() { + $ret = array(); + db_drop_field($ret, 'custom_breadcrumb', 'set_active_menu'); + return $ret; +} + +/** + * Adds language support to breadcrumbs. + */ +function custom_breadcrumbs_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumb', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '')); + return $ret; +} + +/** + * Adds indices to custom_breadcrumb table to improve performance. + */ +function custom_breadcrumbs_update_6201() { + $ret = array(); + db_add_index($ret, 'custom_breadcrumb', 'language', array('language')); + db_add_index($ret, 'custom_breadcrumb', 'node_language', array('node_type', 'language')); + return $ret; +} + +/** + * Adds name field for improved organization of breadcrumbs + */ +function custom_breadcrumbs_update_6202() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumb', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + return $ret; +} + +/** + * Enables custom_breadcrumbs_identifiers for legacy. + */ +function custom_breadcrumbs_update_6203() { + module_enable(array('custom_breadcrumbs_identifiers')); + $ret[] = array( + 'success' => module_exists('custom_breadcrumbs_identifiers'), + 'query' => 'Custom_breadcrumbs_identifiers was enabled for legacy reasons. Please disable it, if you do not use special identifiers in your breadcrumb settings.', + ); + return $ret; +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbs_uninstall() { + drupal_uninstall_schema('custom_breadcrumbs'); + + // Remove persistent variables. + variable_del('custom_breadcrumbs_sort'); + variable_del('custom_breadcrumb_home'); + variable_del('custom_breadcrumbs_set_global_home_breadcrumb'); + variable_del('custom_breadcrumbs_set_menu_breadcrumb'); + variable_del('custom_breadcrumbs_force_active_trail'); + variable_del('custom_breadcrumbs_menu_theme'); + variable_del('custom_breadcrumbs_adjust_module_weights'); + variable_del('custom_breadcrumbs_use_php_in_titles'); + variable_del('custom_breadcrumbs_use_exclude_list'); + variable_del('custom_breadcrumbs_menu_list'); + variable_del('custom_breadcrumbs_append_bid_class'); + variable_del('custom_breadcrumbs_type_class'); + variable_del('custom_breadcrumbs_even_odd_class'); + variable_del('custom_breadcrumbs_exclude_list'); + variable_del('custom_breadcrumbs_home_id'); + variable_del('custom_breadcrumbs_none_span'); + variable_del('custom_breadcrumbs_parts_class'); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs.module b/modules/custom_breadcrumbs/custom_breadcrumbs.module new file mode 100644 index 0000000..7087114 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs.module @@ -0,0 +1,1173 @@ + 'custom_breadcrumb', + 'field' => 'node_type', + 'type' => 'node', + 'name_constructor' => '_custom_breadcrumbs_breadcrumb_name', + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a default name to display in the admin screen. + */ +function _custom_breadcrumbs_breadcrumb_name($breadcrumb) { + if (isset($breadcrumb->node_type)) { + return $breadcrumb->node_type; + } +} + +/** + * Implements hook_theme(). + */ +function custom_breadcrumbs_theme() { + return array( + 'custom_breadcrumbs_filter_form' => array( + 'arguments' => array('form' => array()), + 'file' => 'custom_breadcrumbs.admin.inc', + ), + 'custom_breadcrumbs_filters' => array( + 'arguments' => array('form' => array()), + 'file' => 'custom_breadcrumbs.admin.inc', + ), + 'custom_breadcrumbs_help_identifiers' => array( + 'arguments' => array(), + ), + 'custom_breadcrumbs_module_weight' => array( + 'arguments' => array('form' => array()), + 'file' => 'custom_breadcrumbs.admin.inc', + ), + ); +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbs_menu() { + $items = array(); + $items['admin/build/custom_breadcrumbs'] = array( + 'title' => 'Custom Breadcrumbs', + 'description' => 'Customize the breadcrumb trail for pages on your site.', + 'page callback' => 'custom_breadcrumbs_page', + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs.admin.inc', + ); + $items['admin/build/custom_breadcrumbs/list'] = array( + 'title' => 'List', + 'page callback' => 'custom_breadcrumbs_page', + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs.admin.inc', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 0, + ); + + $items['admin/build/custom_breadcrumbs/node/add'] = array( + 'title' => 'Node', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_form', 'node'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 1, + ); + + $items['admin/build/custom_breadcrumbs/node/edit'] = array( + 'title' => 'Edit custom breadcrumb for nodes', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_form', 'node'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs.admin.inc', + 'type' => MENU_CALLBACK, + ); + + $items['admin/settings/custom-breadcrumbs'] = array( + 'title' => 'Custom Breadcrumb Settings', + 'description' => 'Manage sitewide configuration settings to customize the breadcrumb trail.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_admin_settings'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs.admin.inc', + 'type' => MENU_NORMAL_ITEM, + ); + return $items; +} + +/** + * Implements hook_preprocess_page(). + */ +function custom_breadcrumbs_preprocess_page(&$variables) { + if (!custom_breadcrumbs_exclude_path()) { + if (variable_get('custom_breadcrumbs_set_global_home_breadcrumb', FALSE)) { + $trail = drupal_get_breadcrumb(); + if (!empty($trail) && (strip_tags($trail[0]) == t('Home'))) { + // Replace the leading Home crumb. + array_shift($trail); + $cb_home = custom_breadcrumbs_home_crumb(); + if (!empty($cb_home)) { + array_unshift($trail, array_pop($cb_home)); + } + drupal_set_breadcrumb($trail); + $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb()); + } + } + } +} + +/** + * Implements hook_init(). + */ +function custom_breadcrumbs_init() { + if (variable_get('custom_breadcrumbs_set_menu_breadcrumb', FALSE)) { + // Use selected menu structure to set the breadcrumb. + if (custom_breadcrumbs_set_menu_breadcrumb()) { + $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb()); + } + } +} + +/** + * Implements hook_perm(). + */ +function custom_breadcrumbs_perm() { + return array('administer custom breadcrumbs', 'use php in custom breadcrumbs'); +} + +/** + * Implements hook_help(). + */ +function custom_breadcrumbs_help($path, $arg) { + switch ($path) { + case 'admin/help#custom_breadcrumbs': + $output = '

' . t("Custom Breadcrumbs allows you to create and modify your own breadcrumbs based on node type. After enabling the module, click on Administer > Site building > Custom breadcrumbs. Select Node Type at the top of the page to create breadcrumbs according to node type. Other types (such as views, paths, and taxonomy) may also be present, depending on what modules have been installed.") . '

'; + $output .= '

' . t("On the Node Type page, select the node type the breadcrumb will apply to. There are two text fields below-- 'Titles' and 'Paths.' When creating a breadcrumb, you are simply creating a link. In the custom breadcrumbs interface 'Titles' describes the text of the breadcrumb while 'Paths' describes the Drupal path the breadcrumb links to. Each Title must have a corresponding Path.") . '

'; + $output .= '

' . t("To give a very simple example of how to use this module, let's say I have a blog on my web site called 'Deep Thoughts.' To create this, I use the Views module to create a page at /blog that displays all the node types 'blog post.' Whenever a user views a blog post I want the breadcrumb to show Home > Deep Thoughts instead of simply Home. To do this I would simply type 'Deep Thoughts' in the 'Titles' field and and 'blog' in the 'Paths' field and save my breadcrumb.") . '

'; + $output .= '

' . t("Using the Tokens module, the Custom breadcrumbs module becomes much more flexible because breadcrumbs can become dynamic. You can create a breadcrumb like Home > Deep Thoughts > [Month of Blog Post] [Year of Blog Post], where 'Deep Thoughts' links to my main blog page and '[Month of Blog Post] [Year of Blog Post]' links to a view that shows only blog posts from the month and year the blog post was created (e.g. June 2007). For this, you would do the following:") . '

'; + $output .= '

' . t("Node Type:
Blog Post

Titles:
Deep Thoughts
[month] [yyyy]

Paths:
blog
blog/[mm]_[yyyy]
(where of course, blog/[mm]_[yyyy] is the path to the view of blog posts from that month and year). So if you created a blog post on June 13, 2007 your breadcrumb would show Home > Deep Thoughts > June 2007 and 'June 2007' links to 'blog/06_2007' which is a view of all blog posts from June 2007.") . '

'; + $output .= '

' . t("Also, note that Custom Breadcrumbs doesn't actually check to be sure that a particular path exists, so you'll have to check yourself to avoid 404 errors.") . '

'; + $output .= '

' . t("Only users with 'administer custom breadcrumbs' permission will be allowed to create or modify custom breadcrumbs.") . '

'; + $output .= '

' . t("Breadcrumb Visibility") . '

'; + $output .= '

' . t("Users given 'use php in custom breadcrumbs' permission can include php code snippet that returns TRUE or FALSE to control whether or not the breadcrumb is displayed. Note that this code has access to the %node variable, and can check its type or any other property.", array('%node' => '$node')) . '

'; + $output .= '

' . t("Special Identifiers") . '

'; + $output .= '

' . t("Special identifiers are now provided through optional modules implementing hook_cb_identifier_list(), to provide a description of the identifer, and hook_cb_identifier_values(), to prepare the appropriate crumb items. See the custom_breadcrumbs_identifiers.module file for examples of how to do this.") . '

'; + $output .= '

' . t("Currently, the following identifiers can be used to achieve a special behavior:") . '

'; + $output .= theme('custom_breadcrumbs_help_identifiers'); + $output .= '

' . t("Identifiers should be added to the paths area in the following format: identifier|path. To be recognized, the identifier must be enclosed in angular brackets and proceed any part of the path. For example: %pathauto|[ogname-raw]", array('%pathauto' => '')) . '

'; + return $output; + case 'admin/build/custom_breadcrumbs': + $output = '

' . t("To create a custom breadcrumb, choose one of the breadcrumb types listed above. The following table lists all of the custom breadcrumbs that have been defined. The list can be filtered by breadcrumb type or language, or sorted by clicking on one of the column headings.") . '

'; + $output .= '

' . t('You can configure Custom Breadcrumb settings at admin/settings/custom-breadcrumbs.', array('@link' => url('admin/settings/custom-breadcrumbs'))) . '

'; + return $output; + } +} + +/** + * Implements hook_nodeapi(). + */ +function custom_breadcrumbs_nodeapi($node, $op, $teaser, $page) { + if ($op == 'alter' && empty($teaser) && !empty($page)) { + // Check for breadcrumb for this node type. + global $language; + $languages = array('language' => $language->language, 'all' => ''); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs', NULL, array('node_type' => $node->type), $languages); + if (!empty($breadcrumbs)) { + $objs = array('node' => $node); + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs); + } + } + } +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_form_alter(&$form, $form_state, $form_id) { + // Provide custom breadcrumbs for comment forms associated with nodes. + if ($form_id == 'comment_form' && isset($form['nid']['#value'])) { + $node = node_load($form['nid']['#value']); + // Call custom_breadcrumbs_nodeapi to provide a custom_breadcrumb for this comment. + custom_breadcrumbs_nodeapi($node, 'alter', array(), array(1)); + } + elseif (isset($form['#node']->type) && + variable_get('custom_breadcrumbs_show_form_table_' . $form['#node']->type, CUSTOM_BREADCRUMBS_SHOW_FORM_TABLE_DEFAULT) && + user_access('administer custom breadcrumbs') && + isset($form['type']) && + isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) { + // Provide a custom breadcrumbs fieldset for node edit forms. + $node = $form['#node']; + // Load all custom breadcrumbs for this node type. + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs', 'custom_breadcrumb', array('node_type' => $form['type']['#value'])); + foreach (module_implements('cb_node_form_table') as $module) { + $func = $module .'_cb_node_form_table'; + $more = $func($node); + if (!empty($more)) { + $breadcrumbs = array_merge($breadcrumbs, $more); + } + } + + $output = NULL; + if (count($breadcrumbs) > 0) { + $output = '

' . t('Custom breadcrumbs have been created for this %type page. Use the Custom Breadcrumbs Administration Page to add additional breadcrumbs, or follow the links in the table below to edit or delete existing custom breadcrumbs.', array('%type' => $form['type']['#value'], '@link' => url('admin/build/custom_breadcrumbs'))) . '

'; + } + // Show a table of custom breadcrumbs with links to the edit form. + module_load_include('inc', 'custom_breadcrumbs', 'custom_breadcrumbs.admin'); + + $output .= custom_breadcrumbs_simple_breadcrumb_table($breadcrumbs); + $form['custom_breadcrumbs'] = array( + '#type' => 'fieldset', + '#title' => t('Custom Breadcrumbs'), + '#access' => user_access('administer custom breadcrumbs'), + '#group' => 'additional_settings', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => (function_exists('content_extra_field_weight') && isset($form['type'])) ? content_extra_field_weight($form['type']['#value'], 'custom_breadcrumbs') : CUSTOM_BREADCRUMBS_TYPE_FIELDS_WEIGHT, + ); + $form['custom_breadcrumbs']['breadcrumb_table'] = array('#value' => $output, ); + } + if (user_access('administer custom breadcrumbs') && ($form_id == 'node_type_form')) { + $form['custom_breadcrumbs'] = array( + '#type' => 'fieldset', + '#title' => t('Custom Breadcrumbs'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => (function_exists('content_extra_field_weight') && isset($form['type'])) ? content_extra_field_weight($form['type']['#value'], 'custom_breadcrumbs') : CUSTOM_BREADCRUMBS_TYPE_FIELDS_WEIGHT, + ); + $form['custom_breadcrumbs']['custom_breadcrumbs_show_form_table'] = array( + '#type' => 'checkbox', + '#title' => t('Display defined custom breadcrumbs on node edit form'), + '#description' => t('Access is limited to users with administer custom breadcrumbs permission.'), + '#default_value' => variable_get('custom_breadcrumbs_show_form_table_' . $form['#node_type']->type, CUSTOM_BREADCRUMBS_TYPE_SHOW_FORM_TABLE), + ); + } +} + +/** + * Implements hook_content_extra_fields(). + */ +function custom_breadcrumbs_content_extra_fields($type_name) { + $fields['custom_breadcrumbs'] = array( + 'label' => t('Custom Breadcrumbs'), + 'description' => t('Custom Breadcrumbs module form.'), + 'weight' => CUSTOM_BREADCRUMBS_TYPE_FIELDS_WEIGHT, + ); + return $fields; +} + +/** + * Selects a breadcrumb from an array of breadcrumbs. + * + * @param $breadcrumbs + * The array of breadcrumb objects that the breadcrumb will be selected from. + * @param $objs + * An array of optional object (node, view, ...) to aid in the selection process. + * + * @return $breadcrumb + * The selected breadcrumb object. + */ +function custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs = array()) { + while ($breadcrumb = array_pop($breadcrumbs)) { + if (custom_breadcrumbs_is_visible($breadcrumb, $objs)) { + return $breadcrumb; + } + } +} + +/** + * Sets the custom breadcrumb. + * + * This can be used by submodules, but they could also provide their own function. + * @param $breadcrumb + * The breadcrumb object + * @param $objs + * An array of objects (if available) for building token substituions. + */ +function custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs = array()) { + if ($breadcrumb && !custom_breadcrumbs_exclude_path()) { + $locations = array(); + $trail = _custom_breadcrumbs_get_breadcrumb($breadcrumb, $objs, $locations); + if (variable_get('custom_breadcrumbs_force_active_trail', FALSE)) { + menu_set_active_trail($locations); + } + drupal_set_breadcrumb($trail); + // Optionally save the unique breadcrumb id of the last set breadcrumb. + custom_breadcrumbs_unique_breadcrumb_id($breadcrumb->breadcrumb_type, $breadcrumb->bid); + } +} + +/** + * Gets the custom breadcrumb. + * + * This function is used to retrieve the breadcrumb trail before actually setting it. + * @see custom_breadcrumbs_get_breadcrumb(). + * + * @param $breadcrumb + * The breadcrumb object. + * @param $objs + * An array of object (if available) for building token substituions. + * @param $locations + * Locations array to be able to set active menu trail - passed by reference. + * + * @return + * array of html crumbs + */ +function _custom_breadcrumbs_get_breadcrumb($breadcrumb, $objs, &$locations) { + // Assure locations is an array. + if (!is_array($locations)) { + $locations = array(); + } + // Avoid invalid nesting. + elseif (isset($locations['title']) || isset($locations['href'])) { + $locations = array($locations); + } + + $trail = custom_breadcrumbs_home_crumb(); + if (!empty($trail)) { + $title = variable_get('custom_breadcrumb_home', t('Home')); + $locations[] = array('title' => variable_get('custom_breadcrumb_home', t('Home')), 'href' => '', 'localized_options' => array()); + } + + if (variable_get('custom_breadcrumbs_use_php_in_titles', FALSE)) { + $titles = extract_php($breadcrumb->titles, $objs); + // Titles and paths arrays can also be provided as elements of an associative array. + if (isset($titles['titles']) && is_array($titles['titles']) && isset($titles['paths']) && is_array($titles['paths'])) { + $paths = $titles['paths']; + $titles = $titles['titles']; + } + else { + $paths = extract_php($breadcrumb->paths, $objs); + } + } + if (!isset($titles) || is_null($titles)) { + $titles = preg_split("/[\n]+/", $breadcrumb->titles); + } + if (!isset($paths) || is_null($paths)) { + $paths = preg_split("/[\n]+/", $breadcrumb->paths); + } + + // Token replacement for titles and paths + if (module_exists('token')) { + // Do token replacement. + $types = custom_breadcrumbs_token_types($objs); + $titles = token_replace_multiple($titles, $types); + $paths = token_replace_multiple($paths, $types); + } + $items = _custom_breadcrumbs_get_trail_items($breadcrumb, $titles, $paths); + + // Use the returned items to set the trail. + foreach ($items as $item) { + if ($item['crumb']) { + $trail[] = $item['crumb']; + } + if (variable_get('custom_breadcrumbs_force_active_trail', FALSE)) { + $locations[] = array('title' => $item['title'], 'href' => drupal_get_normal_path(trim($item['href']))); + } + } + + return $trail; +} + +/** + * Builds the trail items for a given breadcrumb specification. + * + * @param $breadcrumb + * The breadcrumb object. + * @param $titles + * An array of titles (after token replacement). + * @param $paths + * An array of paths (after token replacement) that may contain special identifiers. + * + * @return + * An associative array of trail items with keys + * 'title' - the title of the item + * 'href' - the path of the item used to set the active trail + * 'crumb'- the html crumb for use in the breadcrumb + */ +function _custom_breadcrumbs_get_trail_items($breadcrumb, $titles, $paths) { + $trail_items = array(); + + for ($i = 0; $i < count($titles); $i++) { + $title = trim($titles[$i]); + + if (($title != '') && ($title != '')) { + // Create a breadcrumb only if there is a title. + // Include optional html attributes. + $options = _custom_breadcrumbs_identifiers_option($i+1, $breadcrumb->bid); + $crumb_items = _custom_breadcrumbs_create_crumb_items($title, trim($paths[$i]), $options); + $trail_items = array_merge($trail_items, $crumb_items); + } + } + return $trail_items; +} + +/** + * Sets or gets the unique breadcrumb id. + * + * @param $type + * The breadcrumb type, used to set the unique breadcrumb id. + * @param $bid + * The breadcrumb id, used to set the unique breadcrumb id. + * + * @return + * A string containing the unique id for this breadcrumb. + */ +function custom_breadcrumbs_unique_breadcrumb_id($type = NULL, $bid = NULL) { + static $stored_breadcrumb_id; + if (variable_get('custom_breadcrumbs_type_class', FALSE)) { + if (isset($type)) { + $base = 'custom-breadcrumbs'; + $cbid = $base . '-' . $type; + if (variable_get('custom_breadcrumbs_append_bid_class', FALSE) && isset($bid)) { + $cbid .= '-' . $bid; + } + $stored_breadcrumb_id = $cbid; + } + if (isset($stored_breadcrumb_id)) return $stored_breadcrumb_id; + } +} + +/** + * Prepares some common contexts for token substitution. + * + * @param $objs + * An array of objects to be used in token replacement. Array keys indicate type of object. + * + * @return $types + * An array of substitution classes for token_replace_multiple(). + */ +function custom_breadcrumbs_token_types($objs = array()) { + if (!isset($objs['user'])) { + global $user; + if ($user->uid) { + $user = user_load(array('uid' => $user->uid)); + } + $objs['user'] = $user; + } + $objs['global'] = NULL; + + return $objs; +} + +/** + * Saves the custom breadcrumb. + * + * @param $module + * The name of the custom breadcrumbs submodule that created the breadcrumb. + * @param $key + * The type of breadcrumb to save. + * @param $breadcrumb + * Any additional submodule function to call after breadcrumb has been saved. + */ +function _custom_breadcrumbs_save_breadcrumb($module, $key, $breadcrumb) { + if (is_array($breadcrumb->paths)) { + $breadcrumb->paths = implode("\n", $breadcrumb->paths); + } + if (is_array($breadcrumb->titles)) { + $breadcrumb->titles = implode("\n", $breadcrumb->titles); + } + $info = module_invoke($module, 'cb_breadcrumb_info'); + if (isset($info[$key])) { + if ((!isset($breadcrumb->name) || $breadcrumb->name == '') && isset($info[$key]['name_constructor']) && function_exists($info[$key]['name_constructor'])) { + $breadcrumb->name = $info[$key]['name_constructor']($breadcrumb); + } + if (isset($breadcrumb->bid)) { + drupal_write_record($info[$key]['table'], $breadcrumb, 'bid'); + } + else { + drupal_write_record($info[$key]['table'], $breadcrumb); + } + } +} + +/** + * Deletes the custom breadcrumb. + * + * @param $module + * The name of the custom breadcrumbs submodule that created the breadcrumb. + * @param $key + * An array key indicating the type of custom breadrumb that is to be deleted. + * @param $bid + * The id for the breadcrumb that is to be deleted. + */ +function _custom_breadcrumbs_delete_breadcrumb($module, $key, $bid) { + $info = module_invoke($module, 'cb_breadcrumb_info'); + if (isset($info[$key]['table'])) { + db_query('DELETE FROM {' . $info[$key]['table'] . '} WHERE bid = %d', $bid); + } +} + +/** + * Create the Home breadcrumb trail. + * + * @return + * The home breadcrumb item. + */ +function custom_breadcrumbs_home_crumb() { + $hometext = variable_get('custom_breadcrumb_home', t('Home')); + if ($hometext != '') { + // Add any html identifiers. + $options = _custom_breadcrumbs_identifiers_option(); + // Decode title to properly handle special characters. + $original_title = decode_entities($hometext); + // Extract title attribute, if present. + list($title, $title_attr) = explode("|", $original_title, 2); + if (isset($title_attr)) { + $options['attributes']['title'] = $title_attr; + } + $trail = array(l($title, '', $options)); + } + else { + $trail = array(); + } + return $trail; +} + +/** + * Creates one or more crumb items out of a custom breadcrumb definition line. + * + * @param $title + * Title string of the custom breadcrumb definition (after token replacement). + * @param $original_path + * Path string of the custom breadcrumb definition (after token replacment) + * which may contain a special identifier. + * @param $attributes + * An array of additional attributes for the breadcrumb item. + * @return + * An array of one or multiple crumb items. + * In most cases, especially without an identifier, it is only an array of one item. + */ +function _custom_breadcrumbs_create_crumb_items($title, $original_path, $attributes = array()) { + // The array to return. + $crumbs = array(); + + // Decode title to properly handle special characters. + $original_title = decode_entities($title); + // Extract title attribute, if present. + list($title, $title_attr) = explode("|", $original_title, 2); + if (isset($title_attr)) { + $attributes['attributes']['title'] = $title_attr; + } + // Collapse double slashes to one. + $original_path = preg_replace('/\/+/', '/', $original_path); + // Removing leading and trailing slashes. + $original_path = preg_replace('/^\/|\/+$/', '', $original_path); + list($identifier, $path) = explode("|", $original_path, 2); + $identifier = trim($identifier); + // Replace identifiers provided by modules implementing hook_cb_identifier_values. + $values = NULL; + $obj = array('title' => $title, 'path' => $path, 'attributes' => $attributes); + foreach (module_implements('cb_identifier_values') as $module) { + $values = module_invoke($module, 'cb_identifier_values', $identifier, $obj); + if (isset($values)) break; + } + if (isset($values)) { + // Ease return values for callbacks. + if (!is_array($values)) { + $crumbs[] = array('crumb' => $values, 'title' => $title, 'href' => $path); + } + elseif (isset($values['crumb']) || isset($values['title']) || isset($values['href'])) { + $crumbs[] = $values; + } + else { + $crumbs = $values; + } + } + else { + // Use original path if no pipe was given. + if (!isset($path)) { + $path = $original_path; + } + $options = parse_url($path); + $options = array_merge($options, $attributes); + $crumbs[] = array( + 'crumb' => l($title, $options['path'], $options), + 'title' => $title, + 'href' => $options['path'], + ); + } + + return $crumbs; +} + +/** + * Builds a table of identifiers and their behaviors. + * + * @ingroup themeable + */ +function theme_custom_breadcrumbs_help_identifiers() { + + $identifiers = module_invoke_all('cb_identifier_list'); + $headers = array(t('Identifier'), t('Behaviour')); + $rows = array(); + + if (!empty($identifiers)) { + foreach ($identifiers as $id => $description) { + $rows[] = array(check_plain($id), $description); + } + } + else { + $rows[] = array( + array( + 'data' => t('No special identifiers have been defined. You must enable the custom breadcrumbs identifiers module or another module that implements hook_cb_identifier_list and hook_cb_identifier_values to enable this feature.', array('@link' => url('admin/build/modules'))), + 'colspan' => 2, + ), + ); + } + return theme('table', $headers, $rows, array('class' => 'description')); +} + +/** + * Loads the custom breadcrumb from submodule table. + * + * @param $module + * The name of the custom breadcrumbs submodule managing the requested breadcrumb. + * @param $table + * The name of the table to limit the search to. This only needs to be provided if + * the submodule provides breadcrumbs from more than one table. + * @param $param + * An array of the form 'field' => $value used in the SQL WHERE clause. + * + * @return + * if $param is empty, all breadcrumbs from the table will be returned as an array + * otherwise a single breadcrumb object is be returned. + */ +function custom_breadcrumbs_load_breadcrumbs($module, $table = NULL, $param = array(), $languages = array()) { + static $breadcrumbs_cache = array(); + $breadcrumbs = array(); + $bc_info = module_invoke($module, 'cb_breadcrumb_info'); + foreach ($bc_info as $info) { + if (!isset($table) || ($info['table'] == $table)) { + $args = array(); + $cond = array(); + $cond_string = array(); + $sql = "SELECT * FROM {". $info['table'] ."}"; + if ($p = !empty($param)) { + $sql .= " WHERE "; + foreach ($param as $key => $value) { + $cond[] = db_escape_string($key) ." = '%s'"; + $args[] = $value; + $cond_string[] = $key . '_' . $value; + } + if (!empty($cond)) { + $sql .= implode(' AND ', $cond); + } + } + if (!empty($languages)) { + $sql .= ($p) ? " AND " : " WHERE "; + $sql .= "language IN (" . db_placeholders($languages, 'text') . ") ORDER BY language ASC"; + $args = array_merge($args, $languages); + } + + $ckey = "{$info['table']}-{" . implode('_', $cond_string) . "}-" . implode('_', $languages); + if (isset($breadcrumbs_cache[$ckey])) { + $breadcrumbs = $breadcrumbs_cache[$ckey]; + } + else { + $result = db_query($sql, $args); + while ($breadcrumb = db_fetch_object($result)) { + if (!isset($breadcrumb->name)) { + $breadcrumb->name = (isset($info['name_constructor'])) ? $info['name_constructor']($breadcrumb) : $breadcrumb->$info['field']; + } + $breadcrumb->breadcrumb_type = $info['type']; + $breadcrumbs[] = $breadcrumb; + } + $breadcrumbs_cache[$ckey] = $breadcrumbs; + } + } + } + return $breadcrumbs; +} + +/** + * Determines breadcrumb visibility by evaluating PHP code. + * + * @param $breadcrumb + * The breadcrumb object. + * @param $objs + * An array of objects (node, taxonomy, or view) that can be used in the php code. + * + * @return + * TRUE if the breadcrumb should be displayed, FALSE otherwise. + */ +function custom_breadcrumbs_is_visible($breadcrumb, $objs = array()) { + $visibility = TRUE; + if (isset($breadcrumb->visibility_php)) { + // Guard against hidden spaces. + $trimmed = trim($breadcrumb->visibility_php); + if ($trimmed != '') { + // Provide access to objects by standard variable names. + foreach ($objs as $key => $obj) { + $$key = is_object($obj) ? drupal_clone($obj) : $obj; + } + ob_start(); + $visibility = eval($trimmed); + ob_end_clean(); + } + } + return $visibility; +} + +/** + * Loads all breadcrumbs from all submodules. + * + * Current breadcrumbs are held as static variable. + * + * @param $refresh + * If set to TRUE, reload breadcrumbs from database. + * + * @return + * An array of breadcrumb objects. + */ +function _custom_breadcrumbs_load_all_breadcrumbs($refresh = FALSE) { + static $breadcrumbs; + if ($refresh || !isset($breadcrumbs)) { + $breadcrumbs = array(); + foreach (module_implements('cb_breadcrumb_info') as $module) { + $more = custom_breadcrumbs_load_breadcrumbs($module); + if (!empty($more)) { + $breadcrumbs = array_merge($more, $breadcrumbs); + } + } + } + return $breadcrumbs; +} + +/** + * Sets the breadcrumb trail to match the menu structure. + * + * This function uses the same approach as in the menu_breadcrumb module. + */ +function custom_breadcrumbs_set_menu_breadcrumb() { + static $menu_id_cache = array(); + $menu_item = menu_get_item(); + $ckey = $menu_item['href']; + if (!isset($menu_id_cache[$ckey])) { + $result = db_query("SELECT mlid, menu_name FROM {menu_links} WHERE link_path = '%s'", $menu_item['href']); + $menu_link_menus = array(); + while ($menu_link = db_fetch_array($result)) { + $menu_link_menus[$menu_link['mlid']] = $menu_link['menu_name']; + } + $menu_id_cache[$ckey] = $menu_link_menus; + } + $menu_links = $menu_id_cache[$ckey]; + $use_menus = variable_get('custom_breadcrumbs_menu_list', array()); + + foreach ($menu_links as $mlid => $menu_name) { + if (in_array($menu_name, $use_menus)) { + menu_set_active_menu_name($menu_name); + return TRUE; + } + } +} + +/** + * Implements hook_theme_registry_alter(). + */ +function custom_breadcrumbs_theme_registry_alter(&$theme_registry) { + if (variable_get('custom_breadcrumbs_force_active_trail', FALSE) && !empty($theme_registry['links'])) { + global $theme; + // Store the existing theme functions. + $themes = variable_get('custom_breadcrumbs_menu_theme', array()); + $themes[$theme] = array( + 'menu_item' => $theme_registry['menu_item']['function'], + 'menu_item_link' => $theme_registry['menu_item_link']['function'], + ); + variable_set('custom_breadcrumbs_menu_theme', $themes); + // Replace these with our own functions. We will call the original functions after call these override functions. + $theme_registry['links']['function'] = 'custom_breadcrumbs_override_links'; + $theme_registry['menu_item_link']['function'] = 'custom_breadcrumbs_theme_menu_item_link'; + $theme_registry['menu_item']['function'] = 'custom_breadcrumbs_theme_menu_item'; + } +} + +/** + * Determines if a link is in the active trail. + * + * @param $link + * A menu link. + * + * @return + * TRUE if the link is in the active trail, FALSE otherwise. + */ +function custom_breadcrumbs_in_active_trail($link) { + if (!isset($link) || !isset($link['href'])) { + return FALSE; + } + $trail = menu_get_active_trail(); + if (!isset($trail)) { + return FALSE; + } + foreach ($trail as $step) { + if (isset($step['href']) && ($step['href'] == $link['href'] || $step['href'] == drupal_get_path_alias($link['href']))) { + return TRUE; + } + } + + return FALSE; +} + +function custom_breadcrumbs_override_links($links, $attributes = array('class' => 'links')) { + $output = ''; + if (count($links) > 0) { + $output = ''; + + $num_links = count($links); + $i = 1; + + foreach ($links as $key => $link) { + $class = $key; + + // Add first, last and active classes to the list of links to help out themers. + if ($i == 1) { + $class .= ' first'; + } + if ($i == $num_links) { + $class .= ' last'; + } + if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '' && drupal_is_front_page()))) { + $class .= ' active'; + } + if (custom_breadcrumbs_in_active_trail($link) && ($link['href'] != '')) { + $class .= ' active-trail'; + } + $output .= ' $class)) . '>'; + + if (isset($link['href'])) { + // Pass in $link as $options, they share the same keys. + $output .= l($link['title'], $link['href'], $link); + } + elseif (!empty($link['title'])) { + // Some links are actually not links, but we wrap these in for adding title and class attributes. + if (empty($link['html'])) { + $link['title'] = check_plain($link['title']); + } + $span_attributes = ''; + if (isset($link['attributes'])) { + $span_attributes = drupal_attributes($link['attributes']); + } + $output .= '' . $link['title'] . ''; + } + + $i++; + $output .= "\n"; + } + + $output .= ''; + } + return $output; +} + +/* code cribbed from dhtml - modified to suit custom breadcrumbs */ + +/** + * Preprocessor for menu_item_link. + * + * Adds an ID attribute to menu links and helps the module + * follow the recursion of menu_tree_output(). + * + * @param $link + * A menu link. + */ +function custom_breadcrumbs_theme_menu_item_link($link) { + // Find out which theme function to dispatch to after preprocessing. + global $theme; + static $function; + if (!isset($function)) { + $registry = variable_get('custom_breadcrumbs_menu_theme', array()); + $function = isset($registry[$theme]) ? $registry[$theme]['menu_item_link'] : 'theme_menu_item_link'; + } + + if (isset($link['mlid'])) { + // Some themes use options, others use localized_options. Populate both. + $link['localized_options']['attributes']['id'] = 'custom_breadcrumbs_menu-' . _custom_breadcrumbs_menu_unique_id($link['mlid']); + $link['options']['attributes']['id'] = $link['localized_options']['attributes']['id']; + + // Each link in series is another level of recursion. Add it to the stack. + _custom_breadcrumbs_menu_stack($link); + if (custom_breadcrumbs_in_active_trail($link)) { + $link['localized_options']['attributes']['class'] = 'active'; + } + } + // Pass the altered variables to the normal menu themer. + return $function($link); +} + +function custom_breadcrumbs_theme_menu_item($link, $has_children, $menu = '', $in_active_trail = FALSE, $extra_class = NULL) { + global $theme; + static $function; + + if (!isset($function)) { + $registry = variable_get('custom_breadcrumbs_menu_theme', array()); + $function = isset($registry[$theme]) ? $registry[$theme]['menu_item'] : 'theme_menu_item'; + } + + /* When theme('menu_item') is called, the menu tree below it has been + * rendered already. Since we are done on this recursion level, + * one element must be popped off the stack. + */ + + $item = _custom_breadcrumbs_menu_stack(); + + // If there are children, but they were not loaded... + if ($has_children && !$menu) { + // Load the tree below the current position. + $tree = _custom_breadcrumbs_menu_subtree($item); + $force_active_trail = FALSE; + if (!empty($tree)) { + foreach ($tree as $sub => $data) { + if (custom_breadcrumbs_in_active_trail($data['link'])) { + $force_active_trail = TRUE; + } + else { + $belows = (array)$data['below']; + foreach ($belows as $id => $below) { + // Descend... + if (custom_breadcrumbs_in_active_trail($below['link'])) { + $force_active_trail = TRUE; + } + else { + unset($tree[$sub]['below'][$id]); + } + } + } + } + } + if ($force_active_trail) { + // Render it... + $menu = menu_tree_output($tree); + $in_active_trail = TRUE; + } + + // Sanitize tree. If we found no children, the item has none. + if (!$menu) { + $has_children = FALSE; + } + } + + // If the current item can expand, and is neither saved as open nor in the active trail, close it. + if ($menu && !$in_active_trail) { + $extra_class .= ' collapsed start-collapsed '; + } + // Pass the altered variables to the normal menu themer. + return $function($link, $has_children, $menu, $in_active_trail, $extra_class); +} + +/** + * Traverses the menu tree and returns the sub-tree of the item indicated by the parameter. + * + * @param $stack + * An array of menu item links that are nested in each other in the tree. + * + * @return + * The items below the lowest item in the stack. + */ +function _custom_breadcrumbs_menu_subtree($item) { + static $index = array(); + static $indexed = array(); + + // This looks expensive, but menu_tree_all_data uses static caching. + $tree = menu_tree_all_data($item['menu_name']); + if (!isset($indexed[$item['menu_name']])) { + $index += _custom_breadcrumbs_menu_index($tree); + $indexed[$item['menu_name']] = TRUE; + } + // Traverse the tree. + foreach ($index[$item['mlid']]['parents'] as $mlid) { + $key = $index[$mlid]['key']; + if (!isset($tree[$key])) { + return array(); + } + $tree = $tree[$key]['below']; + } + $key = $index[$item['mlid']]['key']; + return isset($tree[$key]) ? $tree[$key]['below'] : array(); +} + +/** + * Indexes the menu tree by mlid. This is needed to identify the items without relying on titles. + * + * This function is recursive. + * + * @param $tree + * A tree of menu items such as the return value of menu_tree_all_data(). + * + * @return + * An array associating mlid values with the internal keys of the menu tree. + */ +function _custom_breadcrumbs_menu_index($tree, $ancestors = array(), $parent = NULL) { + $index = array(); + if ($parent) $ancestors[] = $parent; + foreach ($tree as $key => $item) { + $index[$item['link']['mlid']] = array( + 'key' => $key, + 'parents' => $ancestors, + ); + if (!empty($item['below'])) { + $index += _custom_breadcrumbs_menu_index($item['below'], $ancestors, $item['link']['mlid']); + } + } + return $index; +} + +/** + * Tracks the ID attributes and add a suffix to make it unique (when necessary). + * + * @param $id + * The link id. + * + * @return + * The link id, rendered unique by a suffix as needed. + */ +function _custom_breadcrumbs_menu_unique_id($id) { + static $ids = array(); + if (!isset($ids[$id])) { + $ids[$id] = 1; + return $id; + } + else { + return $id . '-' . $ids[$id]++; + } +} + +/** + * Stores the recursion levels. + * + * @param $link + * If a menu item link is passed, it will be appended to the stack. + * If none is given, the stack will be returned and popped by one. + * + * @return + * The stack, if no parameter is given. + */ +function _custom_breadcrumbs_menu_stack($link = FALSE) { + static $stack = array(); + if ($link) { + array_push($stack, $link); + } + else { + return array_pop($stack); + } +} + +/** + * Retrieves active module weights from the database. + * + * @param $names + * An array of module names. + * + * @return + * An array of module weightsm indexed by module name and ordered by weight. + */ +function _custom_breadcrumbs_get_module_weight($names) { + $weights = array(); + $results = db_query("SELECT name, weight FROM {system} WHERE name IN (" . db_placeholders($names, 'text') . ") AND status = 1 ORDER BY weight ASC", $names); + while ($row = db_fetch_object($results)) { + $weights[$row->name] = (int) $row->weight; + } + + return $weights; +} + +/** + * Determines if a text string is php code and if it is, evaluate it. + * + * @param $text + * A potential code snippet to evaluate. + * @param $objs + * An optional array of objects to make available to the php code snippet. + * + * @return + * If the text string contains a php code snippet, it will be evaluated, and if + * the result is an array, it will be returned. Otherwise nothing is returned. + */ +function extract_php($text, $objs = array()) { + if (drupal_substr(trim($text), 0, 5) == ''), '', $text); + foreach ($objs as $key => $obj) { + $$key = is_object($obj) ? drupal_clone($obj) : $obj; + } + ob_start(); + $output = eval($text); + ob_end_clean(); + return (is_array($output)) ? $output : NULL; + } +} + +/** + * Determines if the current path is in the excluded list. + * + * @return + * TRUE if the current path is on the custom breadcrumbs excluded path list, + * FALSE otherwise. + */ +function custom_breadcrumbs_exclude_path() { + static $excluded; + if (variable_get('custom_breadcrumbs_use_exclude_list', FALSE)) { + if (!isset($excluded)) { + $excluded = explode(',', variable_get('custom_breadcrumbs_exclude_list', '')); + } + if (!empty($excluded)) { + module_load_include('inc', 'custom_breadcrumbs', 'custom_breadcrumbs_common'); + foreach ($excluded as $path) { + if (_custom_breadcrumbs_match_path($_REQUEST['q'], trim($path))) { + return TRUE; + } + } + } + } + return FALSE; +} + +/** + * Adds optional html identifiers to breadcrumb links. + * + * @param $part + * A postive integer indicating the breadcrumb segment (home crumb = 0). + * + * @param $bid + * The breadcrumb id. + * + * @return + * An associative array containing the HTML attributes to apply to the anchor tag. + */ +function _custom_breadcrumbs_identifiers_option($part = 0, $bid = NULL) { + $options = array('attributes' => array()); + $classes = array(); + $base = 'custom-breadcrumbs'; + if (variable_get('custom_breadcrumbs_home_id', FALSE) && ($part == 0)) { + $options['attributes']['id'] = $base . '-home'; + } + elseif (variable_get('custom_breadcrumbs_parts_class', FALSE) && ($part > 0)) { + $classes[] = $base . '-item-' . $part; + } + if (variable_get('custom_breadcrumbs_even_odd_class', FALSE)) { + $classes[] = ($part % 2 == 0) ? 'even' : 'odd'; + } + if (!empty($classes)) { + $options['attributes']['class'] = implode(' ', $classes); + } + return $options; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_common.inc b/modules/custom_breadcrumbs/custom_breadcrumbs_common.inc new file mode 100644 index 0000000..6b706be --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_common.inc @@ -0,0 +1,174 @@ +args array. + * + * @return $viewpath + * The complete path to the view. + */ +function _custom_breadcrumbs_construct_view_path($display) { + $bits = explode('/', $display->display_options['path']); + $args = arg(); + foreach ($bits as $pos => $bit) { + if (!empty($args)) { + $arg = array_shift($args); + if ($bit == '%') { + $bits[$pos] = $arg; + } + } + } + if (!empty($args)) { + // Add any additional arguments to end of path. + $bits = array_merge($bits, $args); + } + $viewpath = implode('/', $bits); + return $viewpath; +} + +/** + * Determines if a view display is appropriate for assigning a custom breadcrumb. + * + * @param $display + * The view $display object. + * + * @return + * TRUE if the display should be considered for a custom breadcrumb, FALSE otherwise. + */ +function _custom_breadcrumbs_allowed_display($display) { + $allowed_display_types = array('page', 'calendar', 'image_gallery'); + if (in_array($display->display_plugin, $allowed_display_types)) { + if (!(isset($display->handler->view->is_attachment) && $display->handler->view->is_attachment)) { + if (isset($display->display_options['path']) ) { + if (module_exists('panels') && panels_get_current_page_display()) { + return FALSE; + } + return TRUE; + } + } + } + return FALSE; +} + +/** + * Returns the appropriate type and value of each view argument. + * + * @param $display_argument_ids + * An array of ids for each argument of the view. + * @param $viewargs + * The $display->handler->view->args array. + * + * @return $arg_values + * An associative array of two elements, 'types' and 'values', each an array with elements + * corresponding to the views arguments. + */ +function _custom_breadcrumbs_views_parse_args($arguments, $viewargs) { + $arg_values = array('types' => array(), 'values' => array()); + foreach ($arguments as $arg_id => $argument) { + if (!empty($viewargs)) { + $arg = array_shift($viewargs); + $arg_id_3 = drupal_substr($arg_id, 0, 3); + if (($arg_id_3 == 'tid') || (drupal_substr($arg_id, 0, 19) == 'term_node_tid_depth')) { + $terms = taxonomy_terms_parse_string($arg); + $arg_values['types'][] = 'tid'; + $arg_values['values'][] = empty($terms['tids']) ? NULL : $terms['tids'][0]; + } + elseif (drupal_substr($arg_id, 0, 4) == 'name') { + if (drupal_substr($argument['table'], 0, 5) == 'term_') { + $terms = taxonomy_get_term_by_name($arg); + $arg_values['types'][] = 'tid'; + $arg_values['values'][] = empty($terms) ? NULL : $terms[0]->tid; + } + } + elseif (($arg_id_3 == 'vid') || ($arg_id_3 == 'uid') || ($arg_id_3 == 'nid')) { + $arg_values['types'][] = $arg_id_3; + $arg_values['values'][] = $arg; + } + } + } + return $arg_values; +} + +/** + * Obtains the appropriate objects for token type replacement for a view display. + * + * @param $display + * The view $display object. + * + * @return $objs + * An associate array of objects to use for token replacement. + */ +function _custom_breadcrumbs_views_token_types($display) { + $objs = array(); + // Check to see if the current display has overriden the default arguments. + $arguments = _custom_breadcrumbs_views_display_arguments($display); + if (isset($arguments) && !empty($arguments)) { + $viewargs = (isset($display->handler->view->args) && is_array($display->handler->view->args)) ? $display->handler->view->args : array(); + $arg_values = _custom_breadcrumbs_views_parse_args($arguments, $viewargs); + foreach ($arg_values['types'] as $key => $type) { + switch ($type) { + case 'tid': + $objs['taxonomy'] = taxonomy_get_term($arg_values['values'][$key]); + break; + case 'nid': + $objs['node'] = node_load(array('nid' => $arg_values['values'][$key])); + break; + case 'uid': + $objs['user'] = user_load(array('uid' => $arg_values['values'][$key])); + break; + } + } + } + return $objs; +} + +/** + * Extracts the views display option arguments array from the display. + * + * @param $display + * The view $display object. + * + * @return $arguments + * The view display option arguments array. + */ +function _custom_breadcrumbs_views_display_arguments($display) { + $arguments = NULL; + if (isset($display->handler->view->display[$display->id]->display_options['arguments'])) { + $arguments = $display->handler->view->display[$display->id]->display_options['arguments']; + } + if (!isset($arguments) && isset($display->handler->view->display['default']->display_options['arguments'])) { + $arguments = $display->handler->view->display['default']->display_options['arguments']; + } + return $arguments; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.info b/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.info new file mode 100644 index 0000000..b630dc3 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.info @@ -0,0 +1,13 @@ +; $Id: custom_breadcrumbs_identifiers.info,v 1.1.2.1 2010/04/25 19:50:50 mgn Exp $ +name = Custom Breadcrumbs Identifiers +package = Custom Breadcrumbs +description = Provides special identifiers for custom breadcrumbs. +dependencies[] = custom_breadcrumbs +core = 6.x + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.module b/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.module new file mode 100644 index 0000000..743f8cd --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_identifiers/custom_breadcrumbs_identifiers.module @@ -0,0 +1,201 @@ +'] = t('Produces a plain text crumb. This identifier should not be used with the pipe (|) symbol.'); + if (module_exists('pathauto')) { + $identifiers[''] = t('Cleans the given path using your pathauto replacement rules.'); + } + + // Additional identifiers can be added here. + $identifiers[''] = t('Provides crumbs for each parent node of a book page. Whatever is placed in the corresponding position of the title area will be ignored. It should not be used with the pipe (|) symbol.'); + $identifiers[''] = t('Provides a plain text crumb using the page title. Whatever is placed in the corresponding position of the title area will be ignored. It should not be used with the pipe (|) symbol.'); + $identifiers[''] = t('Produces crumbs for each parent item for the given path. The title information for this line will be ignored because the menu link titles are used. If a path is not provided following the pipe (|) symbol, the current path with be used.'); + return $identifiers; +} + +/** + * Implements hook_cb_identifier_values(). + * + * This function prepares an array of crumb items to replace an identifier. + * The identifier should be a string starting with '<' and ending with '>'. + * The function also requires an object to make the substitution. Usually, + * this object will include the crumb title and path, but may contain other + * properties that can be used. + * + * This function returns an array of crumb items. Each crumb item is an + * associative array with keys + * 'crumb' = the html crumb to use in the breadcrumb + * 'title' = the title of the crumb + * 'href' = the link path + */ +function custom_breadcrumbs_identifiers_cb_identifier_values($identifier, $obj) { + $crumb_items = NULL; + switch ($identifier) { + case '': + $title = check_plain($obj['title']); + // Optionally wrap plain text crumb in span tag with class identifiers. + if (variable_get('custom_breadcrumbs_none_span', FALSE)) { + $class = 'custom-breadcrumbs-none'; + $attributes = $obj['attributes']['attributes']; + if (!empty($attributes['class'])) { + $attributes['class'] .= ' ' . $class; + } + else { + $attributes['class'] = $class; + } + $title = '' . $title . ''; + } + $crumb_item = array( + 'crumb' => $title, + 'title' => $obj['title'], + ); + $crumb_items[] = $crumb_item; + break; + case '': + // Decode title to properly handle special characters. + $title = filter_xss(drupal_get_title()); + $crumb_item = array( + 'crumb' => $title, + 'title' => $title, + ); + $crumb_items[] = $crumb_item; + break; + case '': + $options = parse_url($obj['path']); + $options = array_merge($options, $obj['attributes']); + if (module_exists('pathauto')) { + module_load_include('inc', 'pathauto', 'pathauto'); + $patharray = explode('/', $options['path']); + foreach ($patharray as $k => $v) { + $patharray[$k] = pathauto_cleanstring($v); + } + $options['path'] = implode('/', $patharray); + $crumb = l($obj['title'], $options['path'], $options); + } + else { + $crumb = l($obj['title'], $options['path'], $options); + } + $crumb_item = array( + 'crumb' => $crumb, + 'title' => $obj['title'], + 'href' => $obj['path'], + ); + $crumb_items[] = $crumb_item; + break; + + // New identifiers can be added here. + + case '': + // Get the node object for the current page and make sure its a book page. + if (arg(0) == 'node' && is_numeric(arg(1)) && !arg(2)) { + $node = node_load(array('nid' => arg(1))); + do { + if (isset($node->book['plid']) && ($node->book['plid'] != 0) && (count($crumb_items) < 9)) { + $parent = book_link_load($node->book['plid']); + $node = node_load(array('nid' => $parent['nid'])); + $item = array( + 'crumb' => l($node->book['title'], $node->book['href']), + 'title' => $node->book['title'], + 'href' => $node->book['href'], + ); + $crumb_items[] = $item; + $ascend = TRUE; + } + else { + $ascend = FALSE; + } + } while ($ascend); + if (count($crumb_items) > 1) { + $crumb_items = array_reverse($crumb_items); + } + if (empty($crumb_items)) { + // Return an empty array. + $crumb_items[] = array(); + } + } + break; + + // Support for showing a paths parent menu link items as crumbs. + case '': + $title = $obj['title']; + $path = ($obj['path'] != '') ? $obj['path'] : $_GET['q']; + $attributes = $obj['attributes']; + // Search for both alias and normal path. + $normal_path = drupal_get_normal_path($path); + + $query = "SELECT * FROM {menu_links} WHERE link_path IN ('%s', '%s')"; + $menu_item = db_fetch_object(db_query_range($query, $normal_path, $path, 0, 1)); + + if ($menu_item) { + // Parent ids of menu item. + $pids = array( + $menu_item->plid, + $menu_item->p1, $menu_item->p2, $menu_item->p3, + $menu_item->p4, $menu_item->p5, $menu_item->p6, + $menu_item->p7, $menu_item->p8, $menu_item->p9, + ); + $pids = array_unique(array_filter($pids)); + + // Remove mlid. + $mlid_key = array_search($menu_item->mlid, $pids); + if ($mlid_key !== FALSE) unset($pids[$mlid_key]); + + // Return empty if no parents given. + if (!count($pids)) return array(); + + // Build the replacement string. + $s = implode(', ', array_fill(0, count($pids), "'%s'")); + // Query parent items. + $query = 'SELECT * FROM {menu_links} WHERE mlid IN (' . $s . ')'; + $result = db_query($query, $pids); + $trail = array(); + + while ($item = db_fetch_object($result)) { + $i = array_search($item->mlid, $pids); + $trail[$i] = array( + 'title' => $item->link_title, + 'href' => $item->link_path, + 'crumb' => l($item->link_title, $item->link_path, $attributes), + ); + } + return $trail; + } + // Return an empty array if no menu entry is given. + else { + return array(); + } + break; + } + return $crumb_items; +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_identifiers_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'custom_breadcrumbs_admin_settings') { + $form['adv_settings']['custom_breadcrumbs_identifiers']['cb_identifier_options']['custom_breadcrumbs_none_span'] = array( + '#type' => 'checkbox', + '#title' => t("Wrap plain text breadcrumbs in <span> tags."), + '#description' => t("If enabled, breadcrumbs that use <none> for the link will be wrapped in <span> tags with the custom-breadcrumbs-none class identifier and any other applicable classes."), + '#default_value' => variable_get('custom_breadcrumbs_none_span', FALSE), + ); + } +} + + + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.info b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.info new file mode 100644 index 0000000..1b0c846 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.info @@ -0,0 +1,19 @@ +; $Id: custom_breadcrumbs_panels.info,v 1.1.2.1 2009/08/02 23:18:48 mgn Exp $ +name = Custom Breadcrumbs for Panels +package = Custom Breadcrumbs +dependencies[] = custom_breadcrumbs +dependencies[] = panels +dependencies[] = ctools +dependencies[] = page_manager +description = Provides Custom Breadcrumbs for panels pages. +core = 6.x + + + + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.install b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.install new file mode 100644 index 0000000..6c92687 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.install @@ -0,0 +1,94 @@ + 'Stores custom breadcrumb trail overrides for panels pages.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbs_panels}.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A return-delimited list of titles for the breadcrumb links.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A return-delimited list of url paths for the breadcrumb links.', + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbs_panels} visibility.', + ), + 'panel_id' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'The Id of the panel for this custom breadcrumb.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'panelid_language' => array('panel_id', 'language'), + ), + 'primary key' => array('bid'), + ); + return $schema; +} + +/** + * Adds name field for improved organization of breadcrumbs + * Remove set_active_menu field because it is no longer used. + */ +function custom_breadcrumbs_panels_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumbs_panels', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + return $ret; +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbs_panels_uninstall() { + drupal_uninstall_schema('custom_breadcrumbs_panels'); + + // Remove persistent variables. + variable_del('custom_breadcrumbs_taxonomy_panels'); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.module b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.module new file mode 100644 index 0000000..0ca655a --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_panels/custom_breadcrumbs_panels.module @@ -0,0 +1,228 @@ + 'custom_breadcrumbs_panels', + 'field' => 'panel_id', + 'type' => 'panels', + 'name_constructor' => '_custom_breadcrumbs_panels_breadcrumb_name', + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a default name to display in the admin screen. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbs_panels_breadcrumb_name($breadcrumb) { + if (isset($breadcrumb->panel_id)) { + return $breadcrumb->panel_id; + } +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbs_panels_menu() { + $items = array(); + + $items['admin/build/custom_breadcrumbs/panels/add'] = array( + 'title' => 'Panels', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_panels_form', 'panels'), + 'access callback' => 'user_access', + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 5, + ); + + $items['admin/build/custom_breadcrumbs/panels/edit'] = array( + 'title' => 'Edit custom breadcrumb for panels', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_panels_form', 'panels'), + 'access callback' => 'user_access', + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Implements hook_nodeapi(). + */ +function custom_breadcrumbs_panels_nodeapi($node, $op, $teaser, $page) { + static $module_weights = array(); + if ($op == 'alter' && empty($teaser) && !empty($page) && $node->type == 'panel') { + // Loop through content objects and call hook_nodeapi for custom_breadcrumbs + // and its submodules for each node object. + $display = panels_load_display($node->panels_node['did']); + $contents = $display->content; + foreach ((array)$contents as $content) { + if (isset($content->configuration['nid'])) { + $node_context = node_load($content->configuration['nid']); + if (empty($module_weights)) { + $modules = module_implements('cb_breadcrumb_info'); + $module_weights = _custom_breadcrumbs_get_module_weight($modules); + unset($module_weights['custom_breadcrumbs_panels']); + } + foreach ($module_weights as $module_name => $weight) { + $func = $module_name . '_nodeapi'; + if (function_exists($func)) { + $func($node_context, 'alter', array(), array(1)); + } + } + } + } + } +} + +/** + * Implements hook_ctools_render_alter(). + */ +function custom_breadcrumbs_panels_ctools_render_alter($info, $page, $args, $contexts, $task, $subtask) { + // Don't really do anything with the panel. This is just a pretense to insert a breadcrumb. + static $module_weights = array(); + + // Is this a node template? + if (isset($task['admin path']) && ($task['admin path'] == "node/%node")) { + $context = array_pop($contexts); + if ($context->type == 'node' && isset ($context->data)) { + $node = $context->data; + // Call hook_nodeapi for each Custom Breadcrumbs submodule in order of the module's weight. + if (empty($module_weights)) { + $modules = module_implements('cb_breadcrumb_info'); + $module_weights = _custom_breadcrumbs_get_module_weight($modules); + unset($module_weights['custom_breadcrumbs_panels']); + } + foreach ($module_weights as $module_name => $weight) { + $func = $module_name . '_nodeapi'; + if (function_exists($func)) { + $func($node, 'alter', array(), array(1)); + } + } + return; + } + } + + // Is this a taxonomy term template? + if (isset($task['admin path']) && ($task['admin path'] == "taxonomy/term/%term") && module_exists('custom_breadcrumbs_taxonomy') && variable_get('custom_breadcrumbs_taxonomy_panels', FALSE)) { + module_load_include('inc', 'custom_breadcrumbs_taxonomy'); + foreach ($contexts as $context) { + if (isset ($context->data->tid)) { + $terms = array($context->data->tid => $context->data); + _custom_breadcrumbs_taxonomy_set_breadcrumb($context->data->tid, $context->data->vid, TRUE, array(), $terms); + return; + } + } + } + + if ($page) { + global $language; + $languages = array('language' => $language->language, 'all' => ''); + // Check to see if the panel ID matches any custom_breadcrumb panel_id. + $id = isset($subtask['name']) ? 'page-' . $subtask['name'] : $task['name']; + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_panels', NULL, array('panel_id' => $id), $languages); + if (!empty($breadcrumbs)) { + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, array('panel' => $info))) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, array('panel' => $info)); + return; + } + } + if (variable_get('custom_breadcrumbs_set_menu_breadcrumb', FALSE)) { + // If a panels breadcrumb has not been defined for this panel, then use the default menu structure. + custom_breadcrumbs_set_menu_breadcrumb(); + } + } +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_panels_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'custom_breadcrumbs_admin_settings') { + if (module_exists('custom_breadcrumbs_taxonomy')) { + $form['settings']['custom_breadcrumbs_panels'] = array( + '#type' => 'fieldset', + '#title' => t('Panels'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + $form['settings']['custom_breadcrumbs_panels']['custom_breadcrumbs_taxonomy_panels'] = array( + '#type' => 'checkbox', + '#title' => t('Use taxonomy breadcrumbs for panels'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_panels', FALSE), + '#description' => t('If enabled, the view argument and/or the taxonomy structure of nodes returned by panels will be used to form the taxonomy breadcrumb trail.'), + '#weight' => 45, + ); + } + } +} + +/** + * Form builder; Displays an edit form for a panels breadcrumb. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbs_panels_form(&$form_state, $type) { + $form = array(); + $breadcrumb = NULL; + $bid = arg(5); + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for Panel Page')); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_panels', NULL, array('bid' => $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Panel Page')); + } + $options = array(); + $tasks = page_manager_get_tasks_by_type('page'); + $pages = array('operations' => array()); + page_manager_get_pages($tasks, $pages); + foreach ($pages['rows'] as $id => $info) { + $options[$id] = $id; + } + $form['panel_id'] = array( + '#type' => 'select', + '#title' => t('Custom Panel Page Id'), + '#required' => TRUE, + '#options' => $options, + '#description' => t('The Panel that this custom breadcrumb trail will apply to.'), + '#default_value' => isset($breadcrumb->panel_id) ? $breadcrumb->panel_id : NULL, + '#weight' => -10, + ); + // Store information needed to save this breadcrumb. + $form['#module'] = 'custom_breadcrumbs_panels'; + $form['#infokey'] = 'panels'; + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + $form['visibility_php']['#description'] = t('Determine whether this breadcrumb should be displayed by using a snippet of PHP to return TRUE or FALSE.'); + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + + return $form; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.info b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.info new file mode 100644 index 0000000..31e4444 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.info @@ -0,0 +1,16 @@ +; $Id: custom_breadcrumbs_paths.info,v 1.1.2.1 2009/04/06 12:43:32 mgn Exp $ +name = Custom Breadcrumbs for Paths +package = Custom Breadcrumbs +dependencies[] = custom_breadcrumbs +description = Provides Custom Breadcrumbs for specific paths. +core = 6.x + + + + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.install b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.install new file mode 100644 index 0000000..9e0e961 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.install @@ -0,0 +1,127 @@ +paths, "\n"); + $specific_path = drupal_substr($breadcrumb->paths, 0, $start); + $title = drupal_substr($breadcrumb->titles, strpos($breadcrumb->titles, "\n") + 1); + $newpath = drupal_substr($breadcrumb->paths, strpos($breadcrumb->paths, "\n") + 1); + db_query("INSERT INTO {custom_breadcrumbs_paths} (titles, paths, visibility_php, specific_path, set_active_menu, language) VALUES ('%s', '%s', '%s', '%s', %d, '%s' )", $title, $newpath, $breadcrumb->visibility_php, $specific_path, $breadcrumb->set_active_menu, $breadcrumb->language); + drupal_set_message('Copied path '. $specific_path .' to {custom_breadcrumbs_paths}.' ); + ++$found; + } + if ($found > 0) { + drupal_set_message(format_plural($found, 'Copied 1 breadcrumb.', 'Copied @count breadcrumbs.')); + drupal_set_message(t('You can now delete the old Specify Path breadcrumb from admin/build/custom_breadcrumbs. They will be listed with title Specify Path and page type node.', array('@link' => url('admin/build/custom_breadcrumbs')))); + } + else { + drupal_set_message(t('No Specify Path breadcrumbs were found in {custom_breadcrumbs}')); + } +} + +/** + * Implements hook_schema(). + */ +function custom_breadcrumbs_paths_schema() { + $schema['custom_breadcrumbs_paths'] = array( + 'description' => 'Stores custom breadcrumb trails for specific paths.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbs_views}.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A return-delimited list of titles for the breadcrumb links.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A return-delimited list of url paths for the breadcrumb links.', + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbs_views} visibility.', + ), + 'specific_path' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Path to the view for this custom breadcrumb.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'path_language' => array('specific_path', 'language'), + ), + 'primary key' => array('bid'), + ); + return $schema; +} + +/** + * Adds indices to custom_breadcrumb table to improve performance. + */ +function custom_breadcrumbs_paths_update_6000() { + $ret = array(); + db_add_index($ret, 'custom_breadcrumbs_paths', 'language', array('language')); + db_add_index($ret, 'custom_breadcrumbs_paths', 'path_language', array('specific_path', 'language')); + return $ret; +} + +/** + * Adds name field for improved organization of breadcrumbs + * Remove set_active_menu field because it is no longer used. + */ +function custom_breadcrumbs_paths_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumbs_paths', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + db_drop_field($ret, 'custom_breadcrumbs_paths', 'set_active_menu'); + return $ret; +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbs_paths_uninstall() { + drupal_uninstall_schema('custom_breadcrumbs_paths'); + + // Remove persistent variables. + variable_del('custom_breadcrumbs_paths_allow_wildcards'); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.module b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.module new file mode 100644 index 0000000..1dcd80d --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_paths/custom_breadcrumbs_paths.module @@ -0,0 +1,303 @@ + 'custom_breadcrumbs_paths', + 'field' => 'specific_path', + 'type' => 'path', + 'name_constructor' => '_custom_breadcrumbs_paths_breadcrumb_name', + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a default name to display in the admin screen. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbs_paths_breadcrumb_name($breadcrumb) { + if (isset($breadcrumb->specific_path)) { + return $breadcrumb->specific_path; + } +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbs_paths_menu() { + $items = array(); + $items['admin/build/custom_breadcrumbs/path/add'] = array( + 'title' => 'Path', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_paths_form', 'path'), + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 5, + ); + $items['admin/build/custom_breadcrumbs/path/edit'] = array( + 'title' => 'Edit custom breadcrumb for path', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_paths_form', 'path'), + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Implements hook_preprocess_page(). + */ +function custom_breadcrumbs_paths_preprocess_page(&$variables) { + if (!custom_breadcrumbs_exclude_path()) { + $objs = (isset($variables) && is_array($variables)) ? $variables : array(); + // Check for a match to provide custom breadcrumbs on module callback pages. + if (_custom_breadcrumbs_paths_set_breadcrumb($objs)) { + $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb()); + } + } +} + +/** + * Implements hook_nodeapi(). + */ +function custom_breadcrumbs_paths_nodeapi($node, $op, $teaser, $page) { + if ($op == 'alter' && empty($teaser) && !empty($page)) { + // Check for breadcrumb at this path and set if a match is found. + // Nodes have higher priority than views and theme templates. + _custom_breadcrumbs_paths_set_breadcrumb(array('node' => $node), 3); + } +} + +/** + * Implements hook_views_pre_render(). + */ +function custom_breadcrumbs_paths_views_pre_render(&$view) { + // Don't really do anything with the view. This is just a pretense to insert a breadcrumb. + $curpath = drupal_get_normal_path($_GET['q']); + // Check to see if the view path matches the current path. + $viewpage = FALSE; + foreach ($view->display as $display) { + // We're only interested in main page views. + if (!_custom_breadcrumbs_allowed_display($display)) continue; + $viewpath = _custom_breadcrumbs_construct_view_path($display); + $viewpage = $viewpage || _custom_breadcrumbs_match_path($curpath, $viewpath); + } + if ($viewpage) { + // Check for breadcrumb at this path and set if a match is found. + // Views have a higher priority than theme templates, but lower than nodes. + _custom_breadcrumbs_paths_set_breadcrumb(array('view' => $view), 2); + } +} + +/** + * Checks for a custom breadcrumb at the current path and sets it if one is found. + * + * @param $objs + * An array of objects to be used in token replacement. Array keys indicate type of object. + * @param $priority + * An integer indicating whether this attempt should override previous attempts. + * The lowest priority is 1. Higher values are given priority over lower values. + */ +function _custom_breadcrumbs_paths_set_breadcrumb($objs = array(), $priority = 1) { + static $success; + // Allow a higher priority request to take precedence. + if (!isset($success) || ($priority > $success)) { + $matchpath = variable_get('custom_breadcrumbs_paths_allow_wildcards', FALSE); + $breadcrumbs = _custom_breadcrumbs_paths_get_breadcrumbs($matchpath); + if (!empty($breadcrumbs)) { + foreach ($breadcrumbs as $id => $breadcrumb) { + if ((!$matchpath) || _custom_breadcrumbs_paths_page_match($breadcrumb)) { + if (custom_breadcrumbs_is_visible($breadcrumb, $objs)) { + if ($matchpath) { + // Assume a longer path match is a better fit than a prior shorter match. + if (($pos = strrpos($breadcrumb->specific_path, '*')) !== FALSE) { + if (!isset($max) || (isset($max) && ($pos > $max))) { + $max = $pos; + $max_id = $id; + } + } + else { + // No wildcards in this breadcrumb, so its a direct match. + $max_id = $id; + // Don't check any others once a visible breadcrumb is found. + break; + } + } + else { + // Wildcards are not allowed, so this must be a direct match. + $max_id = $id; + // Don't check any others once a visible breadcrumb is found. + break; + } + } + } + } + if (isset($max_id)) { + custom_breadcrumbs_set_breadcrumb($breadcrumbs[$max_id], $objs); + $success = $priority; + return TRUE; + } + } + } + return FALSE; +} + +/** + * Gets the custom_breadcrumbs_paths breadcrumbs. + * + * @param $matchpath + * If TRUE, then load all paths breadcrumbs to allow wildcard matching, + * otherwise only the current path is queried. + * @param $path + * The drupal path to match against. + * + * @return $breadcrumbs + * An array of breadcrumb objects meeting the query criteria. + */ +function _custom_breadcrumbs_paths_get_breadcrumbs($matchpath = FALSE, $path = NULL) { + // Don't bother checking if we don't have a path to match against. + if (isset($_REQUEST['q']) || !is_null($path)) { + global $language; + $languages = array('language' => $language->language, 'all' => ''); + // Load all path breadcrumbs for wildcard matching. + $param = array(); + if (!$matchpath) { + // Check for path prefix and strip it out if its found. + $prefix = $language->language .'/'; + $path = is_null($path) ? str_replace($prefix, '', $_REQUEST['q']) : $path; + $param = array('specific_path' => $path); + } + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_paths', NULL, $param, $languages); + return $breadcrumbs; + } +} + +/** + * Determines if the current path matches the breadcrumb specific path. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return $page_match + * TRUE if the current path matches the breadcrumb specific path, FALSE otherwise. + */ +function _custom_breadcrumbs_paths_page_match($breadcrumb) { + $page_match = FALSE; + if (isset($_REQUEST['q'])) { + if (isset($breadcrumb->language) && $breadcrumb->language != '') { + // Check for a match on the prefixed path. + $path = $breadcrumb->language . '/' . $breadcrumb->specific_path; + $page_match = _custom_breadcrumbs_match_path($_REQUEST['q'], $path); + } + else { + // Append the current language if the breadcrumb language is 'All'. + global $language; + $path = $language->language . '/' . $breadcrumb->specific_path; + $page_match = _custom_breadcrumbs_match_path($_REQUEST['q'], $path); + } + if (!$page_match) { + // Check for a direct match. + $page_match = _custom_breadcrumbs_match_path($_REQUEST['q'], $breadcrumb->specific_path); + } + } + return $page_match; +} + +/** + * Implements hook_cb_node_form_table(). + * + * @param $node + * The node object being edited. + * + * @return $breadcrumbs + * An array of breadcrumb objects with the same path as the node + * used in the custom_breadcrumbs fieldset on the node edit page. + */ +function custom_breadcrumbs_paths_cb_node_form_table($node) { + $matchpath = variable_get('custom_breadcrumbs_paths_allow_wildcards', FALSE); + $param = ($matchpath) ? array() : array('specific_path' => $node->path); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_paths', NULL, $param); + foreach ($breadcrumbs as $key => $breadcrumb) { + if (!_custom_breadcrumbs_match_path($node->path, $breadcrumb->specific_path)) { + unset($breadcrumbs[$key]); + } + } + return $breadcrumbs; +} + +/** + * Form builder; Provide an edit form for a custom breadcrumb paths breadcrumb. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbs_paths_form(&$form_state, $type) { + $form = array(); + $bid = arg(5); + $breadcrumb = NULL; + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for Path')); + $breadcrumb = array_pop(custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_paths', NULL, array('bid' => $bid))); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Path')); + } + $description = t('The Drupal path that this custom breadcrumb trail will apply to.'); + if (variable_get('custom_breadcrumbs_paths_allow_wildcards', FALSE)) { + $description .= ' ' . t("The '*' character can be used as a wildcard to set a custom breadcrumb for all matching paths. For example, foo/bar* could be used to match every page with a path beginning with foo/bar. Do not include language prefixes in the path. This will be handled automatically according to the selected language."); + } + $form['specific_path'] = array( + '#type' => 'textfield', + '#title' => t('Specific Path'), + '#required' => TRUE, + '#description' => $description, + '#default_value' => isset($breadcrumb->specific_path) ? $breadcrumb->specific_path : NULL, + '#weight' => -10, + ); + + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + // Store the function to call to save this breadcrumb. + $form['#module'] = 'custom_breadcrumbs_paths'; + $form['#infokey'] = 'paths'; + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + return $form; +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_paths_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'custom_breadcrumbs_admin_settings') { + $form['adv_settings']['custom_breadcrumbs_paths_allow_wildcards'] = array( + '#type' => 'checkbox', + '#title' => t('Use wildcard pattern matching in paths'), + '#default_value' => variable_get('custom_breadcrumbs_paths_allow_wildcards', FALSE), + '#description' => t("If checked, the '*' character can be used as a wildcard to set a custom breadcrumb for all matching paths. For example, foo/bar/* could be used to match every page with a path beginning with foo/bar."), + '#weight' => -20, + ); + } +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.admin.inc b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.admin.inc new file mode 100644 index 0000000..b071f25 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.admin.inc @@ -0,0 +1,129 @@ + $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Taxonomy Term')); + } + $options = taxonomy_form_all(TRUE); + + $form['tid'] = array( + '#type' => 'select', + '#title' => t('Taxonomy Term'), + '#options' => $options, + '#required' => TRUE, + '#description' => t('Select the taxonomy term to apply this breadcrumb to'), + '#default_value' => isset($breadcrumb->tid) ? $breadcrumb->tid : NULL, + '#weight' => -10, + ); + + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + + $taxonomy_breadcrumbs_mode = variable_get('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE); + if ($taxonomy_breadcrumbs_mode) { + // Limit one title and path per taxonomy term. + $form['titles'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#required' => FALSE, + '#description' => t('Specify the Title to use for the term breadcrumb. Leave blank to use the term name. Disable the use taxonomy hierarchy option on the Custom Breadcrumbs Settings page if you would prefer to specify several titles/paths for the breadcrumb.', array('@link' => url('admin/settings/custom-breadcrumbs'))), + '#default_value' => isset($breadcrumb->titles) ? $breadcrumb->titles : NULL, + ); + $form['paths'] = array( + '#type' => 'textfield', + '#title' => t('Path'), + '#required' => TRUE, + '#description' => t('Specify the Drupal path that the taxonomy term will be linked to. Currently operating in taxonomy breadcrumbs mode. Disable the use taxonomy hierarchy option on the Custom Breadcrumbs Settings page if you would prefer to specify several titles/paths for the breadcrumb.', array('@link' => url('admin/settings/custom-breadcrumbs'))), + '#default_value' => isset($breadcrumb->paths) ? $breadcrumb->paths : NULL, + ); + } + + $form['#module'] = 'custom_breadcrumbs_taxonomy'; + $form['#infokey'] = 'taxonomy_term'; + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + return $form; +} + +/** + * Form builder; Displays an edit form for a custom breadcrumb taxonomy vocabulary record. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbs_taxonomy_vocabulary_form(&$form_state, $type) { + $form = array(); + $bid = arg(5); + $breadcrumb = NULL; + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for Taxonomy Vocabulary')); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_vocabulary', array('bid' => $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Taxonomy Vocabulary')); + } + + // Get a list of all vocabularies. + $vocabularies = taxonomy_get_vocabularies(); + $options = array(); + foreach ($vocabularies as $vid => $vocabulary) { + $options[$vid] = $vocabulary->name; + } + $form['vid'] = array( + '#type' => 'select', + '#title' => t('Vocabulary'), + '#options' => $options, + '#required' => TRUE, + '#description' => t('Select the taxonomy vocabulary to apply this breadcrumb to.'), + '#default_value' => isset($breadcrumb->vid) ? $breadcrumb->vid : NULL, + '#weight' => -10, + ); + + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + + $taxonomy_breadcrumbs_mode = variable_get('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE); + if ($taxonomy_breadcrumbs_mode) { + // Limit one title and path per taxonomy vocabulary. + $form['titles'] = array( + '#type' => 'textfield', + '#title' => t('Title'), + '#required' => FALSE, + '#description' => t('Specify the Title to use for the vocabulary breadcrumb. Leave blank to use the vocabulary name. Disable the use taxonomy hierarchy option on the Custom Breadcrumbs Settings page if you would prefer to specify several titles/paths for the breadcrumb.', array('@link' => url('admin/settings/custom-breadcrumbs'))), + '#default_value' => isset($breadcrumb->titles) ? $breadcrumb->titles : NULL, + ); + $form['paths'] = array( + '#type' => 'textfield', + '#title' => t('Path'), + '#required' => TRUE, + '#description' => t('Specify the Drupal path that the taxonomy vocabulary will be linked to. Currently operating in taxonomy breadcrumbs mode. Disable the use taxonomy hierarchy option on the Custom Breadcrumbs Settings page if you would prefer to specify several titles/paths for the breadcrumb.', array('@link' => url('admin/settings/custom-breadcrumbs'))), + '#default_value' => isset($breadcrumb->paths) ? $breadcrumb->paths : NULL, + ); + } + $form['#module'] = 'custom_breadcrumbs_taxonomy'; + $form['#infokey'] = 'taxonomy_vocabulary'; + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + return $form; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.inc b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.inc new file mode 100644 index 0000000..d8e7d25 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.inc @@ -0,0 +1,391 @@ +tid] = $term; + } + } + if (is_file($filepath . '/' . $file)) { + require_once($filepath . '/' . $file); + } + $output = call_user_func_array($callback, $args); + _custom_breadcrumbs_taxonomy_set_breadcrumb($tids['tids'][0], NULL, TRUE, array(), $terms); + + return $output; +} + +/** + * Sets the breadcrumb using a node's taxonomy. + * + * @param $tid + * A taxonomy id. + * @param $vid + * A taxonomy vocabulary id. + * @param $is_term_page + * TRUE if the breadcrumb is being prepared for the taxonomy term page, FALSE otherwise. + * @param $objs + * An optional array of objects to be used for token replacement. + * @param $terms + * An array of taxonomy terms to use (potentially) to construct the breadcrumb. + */ +function _custom_breadcrumbs_taxonomy_set_breadcrumb($tid, $vid = NULL, $is_term_page = FALSE, $objs = array(), $terms = array()) { + if (variable_get('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE) && (!custom_breadcrumbs_exclude_path())) { + $breadcrumb = custom_breadcrumbs_taxonomy_generate_breadcrumb($tid, $vid, $is_term_page, $objs); + if ($is_term_page) { + _custom_breadcrumbs_taxonomy_recent_term($tid); + } + if (variable_get('custom_breadcrumbs_taxonomy_include_node_title', FALSE) && isset($objs['node'])) { + $breadcrumb[] = check_plain($objs['node']->title); + } + drupal_set_breadcrumb($breadcrumb); + // Optionally save the unique breadcrumb id of the last set breadcrumb. + custom_breadcrumbs_unique_breadcrumb_id('taxonomy'); + } + else { + global $language; + $languages = array('language' => $language->language, 'all' => ''); + // Check each term to see if it has a custom breadcrumb. + $vids = array(); + if (!empty($terms)) { + foreach ($terms as $term) { + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_term', array('tid' => $term->tid), $languages); + $objs['taxonomy'] = $term; + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs); + _custom_breadcrumbs_taxonomy_recent_term($term->tid); + return; + } + if (!isset($vids[$term->vid])) { + $vids[$term->vid] = $term; + } + } + } + if (empty($vids) && !is_null($vid)) { + $vids[$vid] = NULL; + } + // No terms match, look for a match on the taxonomy vocabulary. + foreach ($vids as $vid => $term) { + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_vocabulary', array('vid' => $vid), $languages); + $objs['taxonomy'] = $term; + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs); + return; + } + } + } +} + +/** + * Returns the previous selected term or the lightest term for a given node. + * + * @param $node + * The node object. + * + * @return + * The taxonomy term object. + */ +function custom_breadcrumbs_taxonomy_node_get_term($node) { + // First try to see if a recently viewed term matches one of the node's terms. + $term = custom_breadcrumbs_taxonomy_node_get_recent_term($node); + // If not, then select the nodes lightest term. + if (is_null($term)) { + $term = custom_breadcrumbs_taxonomy_node_get_lightest_term($node); + } + return $term; +} + +/** + * Returns the most recently selected term for a given node. + * + * @param $node + * The node object. + * + * @return + * The previously selected taxonomy term object that also belongs to the node. + */ +function custom_breadcrumbs_taxonomy_node_get_recent_term($node) { + $terms = taxonomy_node_get_terms($node); + $tid = _custom_breadcrumbs_taxonomy_recent_term(); + if (is_array($terms) && !empty($terms) && !is_null($tid)) { + return (isset($terms[$tid])) ? taxonomy_get_term($tid) : NULL; + } +} + +/** + * Sets or returns the previous selected term id. + * + * @param $tid + * An optional term id to store in the session variable to establish a term history. + * + * @return + * If this function is called without a term id, then it will return the previously + * selected taxonomy term id, retrieved from the session variable. + */ +function _custom_breadcrumbs_taxonomy_recent_term($tid = NULL) { + if (!is_null($tid)) { + $_SESSION['custom_breadcrumbs_previous_term'] = $tid; + } + elseif (isset($_SESSION['custom_breadcrumbs_previous_term'])) { + return $_SESSION['custom_breadcrumbs_previous_term']; + } +} + +/** + * Returns the lightest term for a given node. + * + * If the term has parents, then the lightest parent's weight is used for the + * term weight. And if the parent has multiple child terms at different depths, + * the deepest child term will be returned. If the child terms have the same + * depth, then the lightest child term is returned. + * + * @param $node + * The node object. + * + * @return + * The taxonomy term object. + */ +function custom_breadcrumbs_taxonomy_node_get_lightest_term($node) { + $terms = taxonomy_node_get_terms($node); + if (!empty($terms)) { + if (count($terms) > 1) { + foreach ($terms as $term) { + // Only consider terms in the lightest vocabulary. + if (!isset($vid)) { + $vid = $term->vid; + } + elseif ($term->vid != $vid) continue; + // If the term has parents, the weight of the term is the weight of the lightest parent. + $parents = taxonomy_get_parents_all($term->tid); + $depth = count($parents); + if ($depth > 0) { + $parent = array_pop($parents); + $weight = $parent->weight; + } + else { + $weight = $term->weight; + } + if ((isset($lweight) && ($weight < $lweight)) || !isset($lweight)) { + $lterm = $term; + $lweight = $weight; + $ldepth = $depth; + } + elseif (isset($lweight) && ($weight == $lweight)) { + // If the node has multiple child terms with the same parent, choose the child with the greatest depth. + if ($depth > $ldepth) { + $lterm = $term; + $ldepth = $depth; + } + elseif ($depth == $ldepth) { + // If the terms have the same depth, pick the term with the lightest weight. + $lterm = ($lterm->weight < $term->weight) ? $lterm : $term; + } + } + } + return $lterm; + } + else { + return array_pop($terms); + } + } +} + +/** + * Generates a breadcrumb from the taxonomy hierarchy of the term id or vocab id. + * This will only be called if custom_breadcrumbs_taxonomy_use_hierarchy has been enabled. + * + * @param $tid + * A taxonomy id. + * @param $vid + * A taxonomy vocabulary id. + * @param $is_term_page + * TRUE if the breadcrumb is being prepared for the taxonomy term page, FALSE otherwise. + * @param $objs + * An optional array of objects to be used for token replacement. + * + * @return + * The breadcrumb trail. + */ +function custom_breadcrumbs_taxonomy_generate_breadcrumb($tid, $vid = NULL, $is_term_page = FALSE, $objs = NULL) { + $term = (is_null($tid)) ? NULL : taxonomy_get_term($tid); + $vocabid = (!is_null($vid)) ? $vid : (is_null($term) ? NULL : $term->vid); + $types = NULL; + if (module_exists('token')) { + $objs['taxonomy'] = $term; + $types = custom_breadcrumbs_token_types($objs); + } + $trail = array(); + $trail = _custom_breadcrumbs_taxonomy_home_trail(); + + if (!is_null($vocabid)) { + $vocab_trail = _custom_breadcrumbs_taxonomy_vocabulary_trail($vocabid, $is_term_page, $objs, $types, count($trail)); + $trail = array_merge($trail, $vocab_trail); + } + + if (!is_null($tid)) { + $term_trail = _custom_breadcrumbs_taxonomy_term_trail($tid, $is_term_page, $objs, $types, count($trail)); + $trail = array_merge($trail, $term_trail); + // Optionally remove the current TERM from end of breadcrumb trail. + if ((!variable_get('custom_breadcrumbs_taxonomy_show_current_term', TRUE) || ($is_term_page && !variable_get('custom_breadcrumbs_taxonomy_show_current_term_term', FALSE))) && (count($trail) > 1)) { + array_pop($trail); + } + } + return $trail; +} + +/** + * Translates configurable string when i18ntaxonomy module is installed. + */ +function _custom_breadcrumbs_taxonomy_tt($string_id, $default, $language = NULL) { + return function_exists('tt') ? tt($string_id, $default, $language) : $default; +} + +/** + * Generates the home breadcrumb trail. + * + * @return + * The breadcrumb trail. + */ +function _custom_breadcrumbs_taxonomy_home_trail() { + $trail = array(); + if (variable_get('custom_breadcrumbs_taxonomy_append_breadcrumb', FALSE)) { + // Check to see if a breadcrumb has already been defined. + $trail = drupal_get_breadcrumb(); + } + else { + $trail = custom_breadcrumbs_home_crumb(); + } + return $trail; +} + +/** + * Generates the vocabulary trail. + * + * @param $vid + * A taxonomy vocabulary id. + * @param $is_term_page + * TRUE if the breadcrumb is being prepared for the taxonomy term page, FALSE otherwise. + * @param $objs + * An optional array of objects to be used to determine breadcrumb visibility and for token replacement. + * @param $types + * An array of token types to be used in token replacement. + * @param $part + * A postive integer indicating the breadcrumb segment (home crumb = 0). + * + * @return + * The breadcrumb trail. + */ +function _custom_breadcrumbs_taxonomy_vocabulary_trail($vid, $is_term_page = FALSE, $objs = array(), $types = array('global' => NULL), $part = 1) { + // Generate the VOCABULARY breadcrumb. + $trail = array(); + // Check to see if a vocabulary breadcrumb exists. + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_vocabulary', array('vid' => $vid)); + $vocabulary_path = NULL; + $title = NULL; + $bid = NULL; + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + $vocabulary_path = $breadcrumb->paths; + $title = $breadcrumb->titles; + $bid = $breadcrumb->bid; + if (module_exists('token')) { + $vocabulary_path = token_replace_multiple($vocabulary_path, $types); + $title = token_replace_multiple($title, $types); + } + } + if ($title == NULL) { + $vocabulary = taxonomy_vocabulary_load($vid); + $title = _custom_breadcrumbs_taxonomy_tt("taxonomy:vocabulary:$vid:name", $vocabulary->name); + } + if ($vocabulary_path != NULL) { + $options = _custom_breadcrumbs_identifiers_option($part, $bid); + $trail = array(l($title, $vocabulary_path, $options)); + } + elseif (variable_get('custom_breadcrumbs_taxonomy_show_vocabulary', FALSE)) { + $trail = array(check_plain($title)); + } + return $trail; +} + +/** + * Generates the taxonomy term trail. + * + * @param $tid + * A taxonomy term id. + * @param $is_term_page + * TRUE if the breadcrumb is being prepared for the taxonomy term page, FALSE otherwise. + * @param $objs + * An optional array of objects to be used to determine breadcrumb visibility and for token replacement. + * @param $types + * An array of token types to be used in token replacement. + * @param $part + * A postive integer indicating the breadcrumb segment (home crumb = 0). + * + * @return + * The breadcrumb trail. + */ +function _custom_breadcrumbs_taxonomy_term_trail($tid, $is_term_page = FALSE, $objs = array(), $types = array('global' => NULL), $part = 1) { + // Generate the TERM breadcrumb. + $trail = array(); + $parent_terms = array_reverse(taxonomy_get_parents_all($tid)); + foreach ($parent_terms as $parent_term) { + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_term', array('tid' => $parent_term->tid)); + $term_path = NULL; + $title = NULL; + $bid = NULL; + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + $term_path = $breadcrumb->paths; + $title = $breadcrumb->titles; + $bid = $breadcrumb->bid; + if (module_exists('token')) { + $term_path = token_replace_multiple($term_path, $types); + $title = token_replace_multiple($title, $types); + } + } + if ($title == NULL) { + $title = _custom_breadcrumbs_taxonomy_tt("taxonomy:term:$parent_term->tid:name", $parent_term->name); + } + if ($term_path == NULL) { + $term_path = taxonomy_term_path(taxonomy_get_term($parent_term->tid)); + } + + // Do not create links to own self if we are on a taxonomy/term page. + if ($is_term_page && $parent_term->tid == $tid) { + $trail[] = check_plain($title); + } + else { + $options = _custom_breadcrumbs_identifiers_option($part, $bid); + $trail[] = l($title, $term_path, $options); + } + ++$part; + } + return $trail; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.info b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.info new file mode 100644 index 0000000..babfa43 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.info @@ -0,0 +1,14 @@ +; $Id: custom_breadcrumbs_taxonomy.info,v 1.1.2.2 2009/05/30 22:02:07 mgn Exp $ +name = Custom Breadcrumbs for Taxonomy +description = "Enables taxonomy based breadcrumbs and allows for node assosciations with taxonomy terms." +package = Custom Breadcrumbs +dependencies[] = custom_breadcrumbs +dependencies[] = taxonomy +core = 6.x + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.install b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.install new file mode 100644 index 0000000..cc8a0c7 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.install @@ -0,0 +1,216 @@ +path, $breadcrumb->tid); + ++$found; + } + $result = db_query("SELECT * FROM {taxonomy_breadcrumb_vocabulary}"); + + while ($breadcrumb = db_fetch_object($result)) { + db_query("INSERT INTO {custom_breadcrumbs_taxonomy_vocabulary} (paths, vid) VALUES ('%s', %d)", $breadcrumb->path, $breadcrumb->vid); + ++$found; + } + + // use variable definitions already set for taxonomy_breadcrumb + variable_set('custom_breadcrumbs_taxonomy_home', variable_get('taxonomy_breadcrumb_home', t('Home'))); + variable_set('custom_breadcrumbs_taxonomy_show_current_term', variable_get('taxonomy_breadcrumb_show_current_term', TRUE)); + variable_set('custom_breadcrumbs_taxonomy_include_node_title', variable_get('taxonomy_breadcrumb_include_node_title', FALSE)); + variable_set('custom_breadcrumbs_taxonomy_include_nodes', variable_get('taxonomy_breadcrumb_include_nodes', FALSE)); + variable_set('custom_breadcrumbs_taxonomy_node_types', variable_get('taxonomy_breadcrumb_node_types', TAXONOMY_BREADCRUMB_NODE_TYPES_DEFAULT)); + define('CUSTOM_BREADCRUMBS_TAXONOMY_NODE_TYPES_DEFAULT', TAXONOMY_BREADCRUMB_NODE_TYPES_DEFAULT); + + // Set custom_breadcrumbs_taxonomy to use taxonomy hierarchy in constructing breadcrumb. + variable_set('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE); + + if ($found > 0) { + drupal_set_message(format_plural($found, 'Copied 1 breadcrumb.', 'Copied @count breadcrumbs.')); + drupal_set_message(t('You can now disable taxonomy_breadcrumb, and test custom_breadcrumb_taxonomy.')); + } + else { + drupal_set_message(t('No taxonomy_breadcrumbs were found')); + } + } + drupal_set_message('Custom Breadcrumbs for Taxonomy Terms and Vocabularies: Taxonomy based breadcrumbs should now appear on node pages and taxonomy/term pages. For the most common applications this module will work "out of the box" and no further configuration is necessary. If customization is desired settings can be changed on the '. l('administration page', 'admin/settings/custom-breadcrumbs') .'.'); +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbs_taxonomy_uninstall() { + // Remove tables. + drupal_uninstall_schema('custom_breadcrumbs_taxonomy'); + + // Remove persistent variables. + variable_del('custom_breadcrumbs_taxonomy_use_hierarchy'); + variable_del('custom_breadcrumbs_taxonomy_append_breadcrumb'); + variable_del('custom_breadcrumbs_taxonomy_show_vocabulary'); + variable_del('custom_breadcrumbs_taxonomy_show_current_term'); + variable_del('custom_breadcrumbs_taxonomy_show_current_term_term'); + variable_del('custom_breadcrumbs_taxonomy_include_node_title'); + variable_del('custom_breadcrumbs_taxonomy_include_nodes'); + variable_del('custom_breadcrumbs_taxonomy_node_types'); + variable_del('custom_breadcrumbs_taxonomy_views'); + variable_del('custom_breadcrumbs_taxonomy_result_nodes'); +} + +/** + * Implements hook_schema(). + */ +function custom_breadcrumbs_taxonomy_schema() { + $schema['custom_breadcrumbs_taxonomy_term'] = array( + 'description' => 'Stores custom breadcrumbs for taxonomy terms', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbs_taxonomy} breadcrumbs.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A title for the breadcrumb link.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A path for the breadcrumb link.', + ), + 'tid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The taxonomy term id for this breadcrumb.', + ), + + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbs_views} visibility.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'tid_language' => array('tid', 'language'), + ), + 'primary key' => array('bid'), + ); + + $schema['custom_breadcrumbs_taxonomy_vocabulary'] = array( + 'description' => 'Stores custom breadcrumbs for taxonomy vocabularies', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbs_taxonomy} breadcrumbs.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A titles for the breadcrumb link.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A paths for the breadcrumb link.', + ), + 'vid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'The taxonomy vocabulary id for this breadcrumb.', + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbs_views} visibility.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'vid_language' => array('vid', 'language'), + ), + 'primary key' => array('bid'), + ); + + return $schema; +} + +/** + * Sets weight of custom_breadcrumbs_taxonomy to be greater than taxonomy, views, and custom_breadcrumbs. + */ +function custom_breadcrumbs_taxonomy_update_6000() { + $ret = array(); + $tax_weight = (int) db_result(db_query("SELECT weight FROM {system} WHERE name = 'taxonomy'")); + $views_weight = (int) db_result(db_query("SELECT weight FROM {system} WHERE name = 'views'")); + $cb_weight = (int) db_result(db_query("SELECT weight FROM {system} WHERE name = 'custom_breadcrumbs'")); + $cb_tax_weight = max($tax_weight, $views_weight, $cb_weight) + 2; + $ret[] = update_sql("UPDATE {system} SET weight = ". $cb_tax_weight ." WHERE name = 'custom_breadcrumbs_taxonomy'"); + return $ret; +} + +/** + * Adds name field for improved organization of breadcrumbs + * Remove set_active_menu field because it is no longer used. + */ +function custom_breadcrumbs_taxonomy_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumbs_taxonomy_term', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + db_add_field($ret, 'custom_breadcrumbs_taxonomy_vocabulary', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + + return $ret; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.module b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.module new file mode 100644 index 0000000..d8b8502 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_taxonomy/custom_breadcrumbs_taxonomy.module @@ -0,0 +1,529 @@ +> [VOCABULARY] >> TERM >> [TERM] ... + * + * - The HOME breadcrumb (if present) links to the home page. The text + * displayed for HOME is administrator configurable on the custom_breadcrumbs + * settings page. + * - The VOCABULARY breadcrumb (if present) will link to an administrator + * defined page. If the VOCABULARY does not have an administrator + * defined page, it will not appear in the breadcrumb trail. + * - Each TERM breadcrumb will link to either + * (1) taxonomy/term/tid by default, or + * (2) an administrator defined page if one is defined for the term. + * + * Examples: + * home >> term >> term + * mysite >> term >> term + * home >> vocabulary >> term >> term + * vocabulary >> term >> term + * + * If 'Use taxonomy hierarchy' is not checked, then the titles and paths used to + * construct the breadcrumb should be defined at the custom_breadcrumbs administration + * page in the same as other custom breadcrumbs. For a node containing multiple terms + * and vocabularies, the lightest term with a visible, matching custom breadcrumb is + * selected. If a taxonomy_term custom breadcrumb is not found, then taxonomy_vocabulary + * custom breadcrumbs are matched against the node's vocabularies. + */ + +module_load_include('inc', 'custom_breadcrumbs', 'custom_breadcrumbs.admin'); +module_load_include('inc', 'custom_breadcrumbs', 'custom_breadcrumbs_common'); + +// Default value for Advanced Settings, Node Types. +define('CUSTOM_BREADCRUMBS_TAXONOMY_NODE_TYPES_DEFAULT', 'book'); + +/** + * Implements hook_cb_breadcrumb_info(). + * + * @return an array with elements + * 'table' indicating the db_table to load the breadcrumb from, + * 'field' a field of the database table used to identify the breadcrumb, + * 'type' a string used for indicating the breadcrumb type on the admin list, + * 'name_constructor' a function which generates the breadcrumb name from the breadcrumb. + */ +function custom_breadcrumbs_taxonomy_cb_breadcrumb_info() { + $breadcrumb_type_info = array(); + $breadcrumb_type_info['taxonomy_vocabulary'] = array( + 'table' => 'custom_breadcrumbs_taxonomy_vocabulary', + 'field' => 'vid', + 'type' => 'taxonomy_vocabulary', + 'name_constructor' => '_custom_breadcrumbs_taxonomy_vocabulary_breadcrumb_name' + ); + $breadcrumb_type_info['taxonomy_term'] = array( + 'table' => 'custom_breadcrumbs_taxonomy_term', + 'field' => 'tid', + 'type' => 'taxonomy_term', + 'name_constructor' => '_custom_breadcrumbs_taxonomy_term_breadcrumb_name' + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a name to display in the admin screen from the taxonomy term. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbs_taxonomy_term_breadcrumb_name($breadcrumb) { + $names = array(); + $parents = taxonomy_get_parents_all($breadcrumb->tid); + while ($parent = array_shift($parents)) { + $names[] = $parent->name; + } + $term = taxonomy_get_term($breadcrumb->tid); + $vocabulary = taxonomy_vocabulary_load($term->vid); + $names[] = $vocabulary->name; + $names = array_reverse($names); + $output = implode('>', $names); + return $output; +} + +/** + * Constructs a name to display in the admin screen from the taxonomy vocabulary. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbs_taxonomy_vocabulary_breadcrumb_name($breadcrumb) { + $vocabulary = taxonomy_vocabulary_load($breadcrumb->vid); + return $vocabulary->name; +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbs_taxonomy_menu() { + $items = array(); + $items['admin/build/custom_breadcrumbs/taxonomy_term/add'] = array( + 'title' => 'Term', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_taxonomy_term_form', 'taxonomy_term'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs_taxonomy.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 5, + ); + $items['admin/build/custom_breadcrumbs/taxonomy_term/edit'] = array( + 'title' => 'Edit custom breadcrumb for taxonomy term', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_taxonomy_term_form', 'taxonomy_term'), + 'file' => 'custom_breadcrumbs_taxonomy.admin.inc', + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_CALLBACK, + ); + $items['admin/build/custom_breadcrumbs/taxonomy_vocabulary/add'] = array( + 'title' => 'Vocabulary', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_taxonomy_vocabulary_form', 'taxonomy_vocabulary'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs_taxonomy.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 5, + ); + $items['admin/build/custom_breadcrumbs/taxonomy_vocabulary/edit'] = array( + 'title' => 'Edit custom breadcrumb for taxonomy vobaculary', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_taxonomy_vocabulary_form', 'taxonomy_vocabulary'), + 'access arguments' => array('administer custom breadcrumbs'), + 'file' => 'custom_breadcrumbs_taxonomy.admin.inc', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Implements hook_nodeapi(). + */ +function custom_breadcrumbs_taxonomy_nodeapi($node, $op, $teaser, $page) { + if ($op == 'alter' && empty($teaser) && !empty($page)) { + if (_custom_breadcrumbs_taxonomy_allowed_node_type($node->type)) { + module_load_include('inc', 'custom_breadcrumbs_taxonomy'); + // Extract the most recently viewed term or the lightest term from lightest vocabulary assosciated with node. + $term = custom_breadcrumbs_taxonomy_node_get_term($node); + if ($term) { + $terms = taxonomy_node_get_terms($node); + _custom_breadcrumbs_taxonomy_set_breadcrumb($term->tid, $term->vid, FALSE, array('node' => $node), $terms); + } + } + } +} + +/** + * Implements hook_menu_alter(). + */ +function custom_breadcrumbs_taxonomy_menu_alter(&$callbacks) { + // This will not work if views or panels has overriden taxonomy/term/% + if (isset($callbacks['taxonomy/term/%']) && ($callbacks['taxonomy/term/%']['page callback'] != 'views_page')) { + $term_callback = &$callbacks['taxonomy/term/%']; + $callback = $term_callback['page callback']; + $arguments = $term_callback['page arguments']; + $file = $term_callback['file']; + $filepath = isset($term_callback['file path']) ? $term_callback['file path'] : drupal_get_path('module', $term_callback['module']); + $term_callback['page callback'] = 'custom_breadcrumbs_taxonomy_term_page'; + $term_callback['page arguments'] = array_merge(array(2, $callback, $file, $filepath), $arguments); + $term_callback['file'] = 'custom_breadcrumbs_taxonomy.inc'; + $term_callback['file path'] = drupal_get_path('module', 'custom_breadcrumbs_taxonomy'); + } +} + +/** + * Implements hook_views_pre_render(). + */ +function custom_breadcrumbs_taxonomy_views_pre_render(&$view) { + if (variable_get('custom_breadcrumbs_taxonomy_views', FALSE)) { + // Set the taxonomy breadcrumb for the view. + if (isset($view->display) && !empty($view->display)) { + $curpath = drupal_get_normal_path($_GET['q']); + // A taxonomy term page is any page with path a component 'taxonomy' followed by 'term'. + $arg_values = arg(NULL, $curpath); + $is_term_page = FALSE; + if (($key = array_search('taxonomy', $arg_values)) !== FALSE) { + $is_term_page = (isset($arg_values[$key + 1]) && $arg_values[$key + 1] == 'term'); + } + foreach ($view->display as $id => $display) { + // Identify allowed displays for breadcrumb replacement. + if (!_custom_breadcrumbs_allowed_display($display)) continue; + $viewpath = _custom_breadcrumbs_construct_view_path($display); + // Verify the view path matches the current path. + if (_custom_breadcrumbs_match_path($curpath, $viewpath)) { + // Select matching display with the greatest number of explicit arguments. + $num = substr_count($display->display_options['path'], '%'); + if (!isset($max) || (isset($max) && ($num > $max))) { + $max = $num; + $max_id = $id; + } + } + } + if (isset($max_id)) { + $display = $view->display[$max_id]; + $arguments = _custom_breadcrumbs_views_display_arguments($display); + if (isset($arguments) && !empty($arguments)) { + $viewargs = (isset($display->handler->view->args) && is_array($display->handler->view->args)) ? $display->handler->view->args : array(); + $arg_values = _custom_breadcrumbs_views_parse_args($arguments, $viewargs); + foreach ($arg_values['types'] as $key => $type) { + $tid = NULL; + $vid = NULL; + switch ($type) { + case 'tid': + $tid = $arg_values['values'][$key]; + break; + case 'vid': + $vid = $arg_values['values'][$key]; + break; + } + if (!is_null($tid) || !is_null($vid)) { + $terms = array(); + if (!is_null($tid)) { + $term = taxonomy_get_term($tid); + $vid = $term->vid; + if ($term) { + $terms[$term->tid] = $term; + } + } + module_load_include('inc', 'custom_breadcrumbs_taxonomy'); + _custom_breadcrumbs_taxonomy_set_breadcrumb($tid, $vid, $is_term_page, array('view' => $view), $terms); + if (!is_null($tid)) { + _custom_breadcrumbs_taxonomy_recent_term($tid); + } + return; + } + } + } + // Otherwise, optionally use the first result node that is of an allowed node type to generate the breadcrumb. + if (variable_get('custom_breadcrumbs_taxonomy_result_nodes', FALSE) && + _custom_breadcrumbs_allowed_display($display) && !empty($view->result)) { + foreach ($view->result as $result) { + if (isset($result->nid)) { + $node = node_load(array('nid' => $result->nid)); + if (_custom_breadcrumbs_taxonomy_allowed_node_type($node->type)) { + module_load_include('inc', 'custom_breadcrumbs_taxonomy'); + $term = custom_breadcrumbs_taxonomy_node_get_term($node); + if ($term) { + $terms = taxonomy_node_get_terms($node); + _custom_breadcrumbs_taxonomy_set_breadcrumb($term->tid, $term->vid, $is_term_page, array('node' => $node), $terms); + _custom_breadcrumbs_taxonomy_recent_term($term->tid); + return; + } + } + } + } + } + } + } + } +} + +/** + * Implements hook_help(). + */ +function custom_breadcrumbs_taxonomy_help($path, $arg) { + switch ($path) { + case 'admin/help#custom_breadcrumbs_taxonomy': + $output = '

' . t('The custom_breadcrumbs_taxonomy module generates taxonomy-based breadcrumbs on node pages and taxonomy/term pages. The breadcrumb trail takes on the form:') . '

'; + $output .= '

' . t('[HOME] >> [VOCABULARY] >> TERM >> [TERM] ...') . '

'; + + $output .= '
    '; + $output .= '
  • ' . t('The text displayed for HOME is configurable below. The HOME breadcrumb (if present) links to the homepage. The text displayed for HOME is configurable by an administrator. If the HOME breadcrumb is not defined by the administrator, it will not appear in the breadcrumb trail.') . '
  • '; + $output .= '
  • ' . t('The VOCABULARY breadcrumb (if present) will link to an administrator defined page. If the VOCABULARY does not have an administrator defined page, it will not appear in the breadcrumb trail. This can be configured on the add/edit vocabulary pages within administer >> categories (taxonomy).', array('@taxonomy' => url('admin/content/taxonomy'))) . '
  • '; + $output .= '
  • ' . t('Each TERM breadcrumb will link to either (1) taxonomy/term/tid by default, or (2) an administrator defined page if one is defined for the term. This can be configured on the add/edit term pages within administer >> categories (taxonomy).', array('@taxonomy' => url('admin/content/taxonomy'))) . '
  • '; + $output .= '
'; + + $output .= '

' . t('Examples:') . '

'; + $output .= '
    '; + $output .= '
  • ' . t('home >> term >> term') . '
  • '; + $output .= '
  • ' . t('mysite >> term >> term') . '
  • '; + $output .= '
  • ' . t('home >> vocabulary >> term >> term') . '
  • '; + $output .= '
  • ' . t('vocabulary >> term >> term') . '
  • '; + $output .= '
'; + + return $output; + } +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_taxonomy_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'custom_breadcrumbs_admin_settings') { + $form['settings']['custom_breadcrumbs_taxonomy'] = array( + '#type' => 'fieldset', + '#title' => t('Taxonomy structure'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_use_hierarchy'] = array( + '#type' => 'checkbox', + '#title' => t('Use the taxonomy hierarchy to set the breadcrumb trail for nodes and taxonomy pages'), + '#description' => t('If selected, the custom breadcrumb trail will be constructed from the taxonomy vocabulary and terms associated with the node. Breadcrumb titles will be selected from taxonomy term and vocabulary names.'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE), + '#weight' => 10, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_append_breadcrumb'] = array( + '#type' => 'checkbox', + '#title' => t('Append taxonomy breadcrumb trail to current breadcrumb for selected node types'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_append_breadcrumb', FALSE), + '#description' => t('If enabled along with the taxonomy hierarchy option (above), the taxonomy breadcrumb trail will be appended to the current breadcrumb trail.'), + '#weight' => 20, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_show_vocabulary'] = array( + '#type' => 'checkbox', + '#title' => t('Show vocabulary in breadcrumb trail'), + '#description' => t('If enabled, the vocabulary name will be shown in the breadcrumb trail, even when a custom breadcrumb has not been defined for the vocabulary. In this case the crumb will not be linked. If a custom breadcrumb has been defined for the vocabulary, it will be displayed whether or not this option is enabled.'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_show_vocabulary', FALSE), + '#weight' => 25, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_show_current_term'] = array( + '#type' => 'checkbox', + '#title' => t('Show current taxonomy term in breadcrumb trail for node pages'), + '#description' => t('If enabled, the lightest term associated with node is shown as the last breadcrumb in the breadcrumb trail. Otherwise, the only terms shown in the breadcrumb trail are parent terms (if any parents exist). The recommended setting is enabled.'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_show_current_term', TRUE), + '#weight' => 30, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_show_current_term_term'] = array( + '#type' => 'checkbox', + '#title' => t('Show current taxonomy term in breadcrumb trail on the taxonomy term page'), + '#description' => t('If enabled, the taxonomy term name is shown as the last breadcrumb item in the breadcrumb trail on the taxonomy term page.'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_show_current_term_term', FALSE), + '#weight' => 35, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_include_node_title'] = array( + '#type' => 'checkbox', + '#title' => t('Show current node title in taxonomy breadcrumb trail'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_include_node_title', FALSE), + '#description' => t("If enabled along with the taxonomy hierarchy option (above) and if viewing a node, the node's title will be shown as the last item in the breadcrumb trail."), + '#weight' => 40, + ); + + if (module_exists('views')) { + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_views'] = array( + '#type' => 'checkbox', + '#title' => t('Use taxonomy-based breadcrumbs for views'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_views', FALSE), + '#description' => t('If enabled, the view argument and/or the taxonomy structure of the nodes returned by views will be used to form the taxonomy breadcrumb trail.'), + '#weight' => 50, + ); + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_result_nodes'] = array( + '#type' => 'checkbox', + '#title' => t('Use the taxonomy of nodes returned by a view to create the taxonomy breadcrumb'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_result_nodes', FALSE), + '#description' => t('If enabled, when a taxonomy-based view argument is not available the taxonomy from the first node returned by the view will be used. The view results will not be used if the taxonomy can be determined from the view argument.'), + '#weight' => 55, + ); + } + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_include_nodes'] = array( + '#type' => 'radios', + '#title' => t('Include or exclude taxonomy-based breadcrumbs for the following node types'), + '#default_value' => variable_get('custom_breadcrumbs_taxonomy_include_nodes', 0), + '#options' => array(1 => t('Include'), 0 => t('Exclude')), + '#weight' => 60, + ); + + $cb_tax_types = (array) variable_get('custom_breadcrumbs_taxonomy_node_types', CUSTOM_BREADCRUMBS_TAXONOMY_NODE_TYPES_DEFAULT); + $default = array(); + foreach ($cb_tax_types as $index => $value) { + if ($value) { + $default[] = $index; + } + } + + $form['settings']['custom_breadcrumbs_taxonomy']['custom_breadcrumbs_taxonomy_node_types'] = array( + '#type' => 'checkboxes', + '#multiple' => TRUE, + '#title' => t('Node types to include or exclude'), + '#default_value' => $default, + '#options' => array_map('check_plain', node_get_types('names')), + '#description' => t('A list of node types to include or exclude applying taxonomy-based breadcrumbs.'), + '#weight' => 70, + ); + } +} + +/** + * Implements hook_form_alter(). + */ +function custom_breadcrumbs_taxonomy_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) { + // Load all custom breadcrumbs for this vid. + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_vocabulary', array('vid' => $form['vid']['#value'])); + $output = NULL; + if (count($breadcrumbs) > 0) { + $output = '

' . t('Custom breadcrumbs have been created for this vocabulary. Use the !link to add additional breadcrumbs. Or follow the links in the table below to edit or delete existing custom breadcrumbs.', array('!link' => l('Custom Breadcrumbs Administration Page', 'admin/build/custom_breadcrumbs'))) . '

'; + } + // Show a table of custom breadcrumbs with links to the edit form. + $output .= custom_breadcrumbs_simple_breadcrumb_table($breadcrumbs); + $form['custom_breadcrumbs_taxonomy_vocabulary'] = array( + '#type' => 'fieldset', + '#title' => t('Custom Breadcrumbs for Taxonomy'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -50, + ); + $form['custom_breadcrumbs_taxonomy_vocabulary']['breadcrumb_table'] = array('#value' => $output, ); +} + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function custom_breadcrumbs_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) { + if (!(isset($_POST['op']) && $_POST['op'] == t('Delete')) || isset($_POST['confirm'])) { + // Load all custom breadcrumbs for this tid. + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_term', array('tid' => $form['tid']['#value'])); + $output = NULL; + if (count($breadcrumbs) > 0) { + $output = '

' . t('Custom breadcrumbs have been created for this term. Use the Custom Breadcrumbs Administration Page to add additional breadcrumbs. Or follow the links in the table below to edit or delete existing custom breadcrumbs.', array('@link' => url('admin/build/custom_breadcrumbs'))) . '

'; + } + // Show a table of custom breadcrumbs with links to the edit form. + $output .= custom_breadcrumbs_simple_breadcrumb_table($breadcrumbs); + $form['custom_breadcrumbs_taxonomy_term'] = array( + '#type' => 'fieldset', + '#title' => t('Custom Breadcrumbs for Taxonomy'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -50, + ); + $form['custom_breadcrumbs_taxonomy_term']['breadcrumb_table'] = array('#value' => $output, ); + } +} + +/** + * Implements hook_cb_node_form_table. + * + * @param $node + * The node object being edited + * + * @return $breadcrumbs + * an array of breadcrumb objects for taxonomy terms and vocabs matching the node + * to be used in the custom_breadcrumbs fieldset on the node edit page + */ +function custom_breadcrumbs_taxonomy_cb_node_form_table($node) { + $breadcrumbs = array(); + if (_custom_breadcrumbs_taxonomy_allowed_node_type($node->type)) { + if (!variable_get('custom_breadcrumbs_taxonomy_use_hierarchy', TRUE)) { + // Check each term to see if it has a custom breadcrumb. + $terms = taxonomy_node_get_terms($node); + foreach ($terms as $term) { + $more = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_term', array('tid' => $term->tid)); + if (!empty($more)) { + $breadcrumbs = array_merge($breadcrumbs, $more); + } + } + // Also look for a match on the taxonomy vocabulary. + $vocabularies = taxonomy_get_vocabularies($node->type); + foreach ($vocabularies as $vocabulary) { + $more = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_taxonomy', 'custom_breadcrumbs_taxonomy_vocabulary', array('vid' => $vocabulary->vid)); + if (!empty($more)) { + $breadcrumbs = array_merge($breadcrumbs, $more); + } + } + } + } + return $breadcrumbs; +} + +/** + * Determines if the current node is one of the types listed on the advanced settings page. + * + * @param $nodetype + * The node type being considered for a custom breadcrumb. + * + * @return + * TRUE if the node type is selected for a custom breadcrumbs taxonomy breadcrumb, + * FALSE otherwise. + */ +function _custom_breadcrumbs_taxonomy_allowed_node_type($nodetype) { + // If the node type IS IN the node types list and the list IS inclusive OR + // If the node type IS NOT IN the node types list and the list IS NOT inclusive (e.g. exclusive) + // THEN modify the breadcrumb trail. + + $array_of_types = array_filter((array)variable_get('custom_breadcrumbs_taxonomy_node_types', CUSTOM_BREADCRUMBS_TAXONOMY_NODE_TYPES_DEFAULT)); + $in_list = in_array($nodetype, $array_of_types); + $allowed = ($in_list == variable_get('custom_breadcrumbs_taxonomy_include_nodes', 0)); + return $allowed; +} + +/** + * Implements hook_enable(). + */ +function custom_breadcrumbs_taxonomy_enable() { + // Sets weight of custom_breadcrumbs_taxonomy to be greater than taxonomy, views, and custom_breadcrumbs. + $max_weight = custom_breadcrumbs_taxonomy_minimum_module_weight(); + $current_weight = _custom_breadcrumbs_get_module_weight(array('custom_breadcrumbs_taxonomy')); + if ($current_weight < $max_weight) { + drupal_set_message(t('Increasing the weight of custom_breadcrumbs_taxonomy for use with other modules.')); + db_query("UPDATE {system} SET weight = %d WHERE name = 'custom_breadcrumbs_taxonomy'", $max_weight); + variable_set('menu_rebuild_needed', TRUE); + } +} + +/** + * Implements hook_minimum_module_weight(). + * + * @see custom_breadcrumbs_admin_settings_submit(). + */ +function custom_breadcrumbs_taxonomy_minimum_module_weight() { + $modules = array('taxonomy', 'views', 'page_manager', 'custom_breadcrumbs', 'i18ntaxonomy'); + $weights = _custom_breadcrumbs_get_module_weight($modules); + $max_weight = array_pop($weights); + return $max_weight + 1; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.info b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.info new file mode 100644 index 0000000..73edf8f --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.info @@ -0,0 +1,17 @@ +; $Id: custom_breadcrumbs_views.info,v 1.1.2.1 2009/03/29 04:36:45 mgn Exp $ +name = Custom Breadcrumbs for Views +package = Custom Breadcrumbs +dependencies[] = custom_breadcrumbs +dependencies[] = views +description = Provides Custom Breadcrumbs for views pages. +core = 6.x + + + + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.install b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.install new file mode 100644 index 0000000..eb8f883 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.install @@ -0,0 +1,123 @@ +paths, "\n"); + $views_path = drupal_substr($breadcrumb->paths, 0, $start); + $title = drupal_substr($breadcrumb->titles, strpos($breadcrumb->titles, "\n")+1); + $newpath = drupal_substr($breadcrumb->paths, strpos($breadcrumb->paths, "\n")+1); + db_query("INSERT INTO {custom_breadcrumbs_views} (titles, paths, visibility_php, views_path, set_active_menu, language) VALUES ('%s', '%s', '%s', '%s', %d, '%s' )", $title, $newpath, $breadcrumb->visibility_php, $views_path, $breadcrumb->set_active_menu, $breadcrumb->language); + drupal_set_message('copied path '. $views_path); + ++$found; + } + if ( $found > 0) { + drupal_set_message(format_plural($found, 'Copied 1 breadcrumb.', 'Copied @count breadcrumbs.')); + drupal_set_message(t('You can now delete the old views breadcrumb from admin/build/custom_breadcrumbs. They will be listed with title Views Page and page type node.', array('@link' => url('admin/build/custom_breadcrumbs')))); + } + else { + drupal_set_message(t('No views breadcrumbs were found in {custom_breadcrumbs}')); + } +} + +/** + * Implements hook_schema(). + */ +function custom_breadcrumbs_views_schema() { + $schema['custom_breadcrumbs_views'] = array( + 'description' => 'Stores custom breadcrumb trail overrides for views pages.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbs_views}.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A return-delimited list of titles for the breadcrumb links.', + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => 'A return-delimited list of url paths for the breadcrumb links.', + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbs_views} visibility.', + ), + 'views_path' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Path to the view for this custom breadcrumb.', + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'vpath_language' => array('views_path', 'language'), + ), + 'primary key' => array('bid'), + ); + return $schema; +} + +/** + * Adds indices to custom_breadcrumb table to improve performance. + */ +function custom_breadcrumbs_views_update_6000() { + $ret = array(); + db_add_index($ret, 'custom_breadcrumbs_views', 'language', array('language')); + db_add_index($ret, 'custom_breadcrumbs_views', 'vpath_language', array('views_path', 'language')); + return $ret; +} + +/** + * Adds name field for improved organization of breadcrumbs + * Remove set_active_menu field because it is no longer used. + */ +function custom_breadcrumbs_views_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumbs_views', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + db_drop_field($ret, 'custom_breadcrumbs_views', 'set_active_menu'); + return $ret; +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbs_views_uninstall() { + drupal_uninstall_schema('custom_breadcrumbs_views'); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.module b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.module new file mode 100644 index 0000000..037abe2 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbs_views/custom_breadcrumbs_views.module @@ -0,0 +1,170 @@ + 'custom_breadcrumbs_views', + 'field' => 'views_path', + 'type' => 'views', + 'name_constructor' => '_custom_breadcrumbs_views_breadcrumb_name', + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a default name to display in the admin screen. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbs_views_breadcrumb_name($breadcrumb) { + if (isset($breadcrumb->views_path)) { + return $breadcrumb->views_path; + } +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbs_views_menu() { + $items = array(); + + $items['admin/build/custom_breadcrumbs/views/add'] = array( + 'title' => 'Views', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_views_form', 'views'), + 'access callback' => 'user_access', + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 5, + ); + + $items['admin/build/custom_breadcrumbs/views/edit'] = array( + 'title' => 'Edit custom breadcrumb for views', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbs_views_form', 'views'), + 'access callback' => 'user_access', + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Implements hook_views_pre_render(). + */ +function custom_breadcrumbs_views_views_pre_render(&$view) { + // Don't really do anything with the view. This is just a pretense to insert a breadcrumb. + global $language; + $languages = array('language' => $language->language, 'all' => ''); + + $curpath = drupal_get_normal_path($_GET['q']); + // Check to see if the view path matches the current path. + foreach ($view->display as $id => $display) { + // Identify allowed displays for breadcrumb replacement. + if (!_custom_breadcrumbs_allowed_display($display)) continue; + $viewpath = _custom_breadcrumbs_construct_view_path($display); + if (_custom_breadcrumbs_match_path($curpath, $viewpath)) { + $loadpath = $display->display_options['path']; + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_views', NULL, array('views_path' => $loadpath), $languages); + if (!empty($breadcrumbs)) { + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, array('view' => $view))) { + // Select breadcrumb for the matching display with the greatest number of explicit arguments. + $num = substr_count($breadcrumb->views_path, '%'); + if (!isset($max) || (isset($max) && ($num > $max))) { + $max = $num; + $candidate = $breadcrumb; + $max_id = $id; + } + } + } + } + } + if (isset($candidate)) { + $objs = _custom_breadcrumbs_views_token_types($view->display[$max_id]); + $objs['view'] = $view; + if (!isset($objs['node']) && isset($view->result[0]->nid)) { + $node = node_load(array('nid' => $view->result[0]->nid)); + $objs['node'] = $node; + } + custom_breadcrumbs_set_breadcrumb($candidate, $objs); + return; + } + if (variable_get('custom_breadcrumbs_set_menu_breadcrumb', FALSE) && (!custom_breadcrumbs_exclude_path())) { + // If a views breadcrumb has not been defined for this view, then use the default menu structure. + custom_breadcrumbs_set_menu_breadcrumb(); + } +} + +/** + * Form builder; Displays an edit form for a views breadcrumb. + * + * @param $type + * The type of custom breadcrumb to edit. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbs_views_form(&$form_state, $type) { + $form = array(); + $breadcrumb = NULL; + $bid = arg(5); + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for View')); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbs_views', NULL, array('bid' => $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for View')); + } + $options = array(); + $views = views_get_all_views(); + foreach ($views as $view) { + if (!isset($view->disabled) || (isset($view->disabled) && !$view->disabled)) { + foreach ($view->display as $display) { + if (_custom_breadcrumbs_allowed_display($display)) { + $name = $display->display_options['path']; + $options[$name] = $name; + } + } + } + } + $form['views_path'] = array( + '#type' => 'select', + '#title' => t('Views Path'), + '#required' => TRUE, + '#options' => $options, + '#description' => t('The path to the view that this custom breadcrumb trail will apply to.'), + '#default_value' => isset($breadcrumb->views_path) ? $breadcrumb->views_path : NULL, + '#weight' => -10, + ); + // Store information needed to save this breadcrumb. + $form['#module'] = 'custom_breadcrumbs_views'; + $form['#infokey'] = 'views'; + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + $form['visibility_php']['#description'] = t('Determine whether this breadcrumb should be displayed by using a PHP snippet to return TRUE or FALSE.'); + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + + return $form; +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.info b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.info new file mode 100644 index 0000000..37689a1 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.info @@ -0,0 +1,16 @@ +; $Id: custom_breadcrumbsapi.info,v 1.1.2.1 2009/09/08 01:31:48 mgn Exp $ +name = Custom Breadcrumbs API +package = Custom Breadcrumbs +dependencies[] = custom_breadcrumbs +description = Provides a simple API to set breadcrumbs on module pages not served by other custom breadcrumbs submodules. +core = 6.x + + + + +; Information added by drupal.org packaging script on 2011-01-08 +version = "6.x-2.0-rc1" +core = "6.x" +project = "custom_breadcrumbs" +datestamp = "1294462254" + diff --git a/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.install b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.install new file mode 100644 index 0000000..12f6aaa --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.install @@ -0,0 +1,91 @@ + 'Stores custom breadcrumb for module pages.', + 'fields' => array( + 'bid' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Unique identifier for the {custom_breadcrumbsapi}.', + ), + 'name' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => FALSE, + 'description' => 'An optional name for the custom breadcrumb.', + ), + 'titles' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => "A return-delimited list of titles for the breadcrumb links.", + ), + 'paths' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + 'description' => "A return-delimited list of url paths for the breadcrumb links.", + ), + 'visibility_php' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'medium', + 'description' => 'An optional PHP snippet to control the {custom_breadcrumbsapi} visibility.', + ), + 'module_page' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => "Name of module page implementing custom breadcrumbs.", + ), + 'language' => array( + 'type' => 'varchar', + 'length' => 12, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The language this breadcrumb is for; if blank, the breadcrumb will be used for unknown languages.', + ), + ), + 'indexes' => array( + 'language' => array('language'), + 'module_language' => array('module_page', 'language'), + ), + 'primary key' => array('bid'), + ); + return $schema; +} + +/** + * Adds name field for improved organization of breadcrumbs + * Remove set_active_menu field because it is no longer used. + */ +function custom_breadcrumbsapi_update_6200() { + $ret = array(); + db_add_field($ret, 'custom_breadcrumbsapi', 'name', array('type' => 'varchar', 'length' => 128, 'NOT NULL' => FALSE, 'description' => 'An optional name for the custom breadcrumb.')); + return $ret; +} + +/** + * Implements hook_uninstall(). + */ +function custom_breadcrumbsapi_uninstall() { + drupal_uninstall_schema('custom_breadcrumbsapi'); +} diff --git a/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.module b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.module new file mode 100644 index 0000000..78762c6 --- /dev/null +++ b/modules/custom_breadcrumbs/custom_breadcrumbsapi/custom_breadcrumbsapi.module @@ -0,0 +1,190 @@ + 'custom_breadcrumbsapi', + 'field' => 'module_page', + 'type' => 'module', + 'name_constructor' => '_custom_breadcrumbsapi_breadcrumb_name', + ); + return $breadcrumb_type_info; +} + +/** + * Constructs a default name to display in the admin screen. + * + * @param $breadcrumb + * The breadcrumb object. + * + * @return + * A text string that will be used as the breadcrumb name. + */ +function _custom_breadcrumbsapi_breadcrumb_name($breadcrumb) { + if (isset($breadcrumb->module_page)) { + return $breadcrumb->module_page; + } +} + +/** + * Implements hook_help(). + */ +function custom_breadcrumbsapi_help($path, $arg) { + switch ($path) { + case 'admin/help#custom_breadcrumbsapi': + $output = '

' . t("Custom Breadcrumbsapi provides a simple api that allows custom breadcrumbs to be defined for module pages that use a theme template or a custom_breadcrumbsapi hook. For the latter, module developers need to provide a moduleName_custom_breadcrumbsapi() function that returns an array containing the names of the module pages for which custom breadcrumbs may be defined. Then, in the callback functions for each of those pages, the following lines needs to be inserted:") . '

'; + $output .= '

' . t("drupal_alter('breadcrumb', \$breadcrumb, 'module_page_name');") . '

'; + $output .= t('within the function, preferably after defining $breadcrumb but before setting the breadcrumb.'); + return $output; + } +} + +/** + * Implements hook_menu(). + */ +function custom_breadcrumbsapi_menu() { + $items = array(); + $items['admin/build/custom_breadcrumbs/module/add'] = array( + 'title' => 'Module Pages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbsapi_form', 'module'), + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_LOCAL_TASK, + ); + $items['admin/build/custom_breadcrumbs/module/edit'] = array( + 'title' => 'Edit custom breadcrumb for module page', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('custom_breadcrumbsapi_form', 'module'), + 'access arguments' => array('administer custom breadcrumbs'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Implements hook_breadcrumb_alter(). + * + * Provide a function for module developers to provide custom breadcrumbs for module pages. + * Modules wishing to provide custom breadcrumbs access to specific pages should include + * drupal_alter('breadcrumb', $breadcrumb, $modulepage, $objs); + * at the end of the function providing the page. + * + * @param $breadcrumb + * The breadcrumb array to be altered + * @param $modulepage + * a string identifying the module page. This name will be displayed on the customb breadcrumb list. + * @param $objs + * an array of objects that can be used in token replacement with array keys indicating the type of object. + */ +function custom_breadcrumbsapi_breadcrumb_alter($breadcrumb, $modulepage, $objs = array()) { + global $language; + $languages = array('language' => $language->language, 'all' => ''); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbsapi', NULL, array('module_page' => $modulepage), $languages); + if (!empty($breadcrumbs)) { + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs); + // Return the modified breadcrumb which has now been set. + $breadcrumb = drupal_get_breadcrumb(); + } + } +} + +/** + * Form builder; Displays an edit form for a module page breadcrumb. + * + * @ingroup forms + * @see custom_breadcrumbs_form_validate() + * @see custom_breadcrumbs_form_submit() + */ +function custom_breadcrumbsapi_form(&$form_state, $type) { + $form = array(); + $breadcrumb = NULL; + $bid = arg(5); + if (isset($bid)) { + drupal_set_title(t('Edit Custom Breadcrumb for Module Page')); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbsapi', NULL, array('bid' => $bid)); + $breadcrumb = array_pop($breadcrumbs); + } + else { + drupal_set_title(t('Add Custom Breadcrumb for Module Page')); + } + $options = array(); + // Include any templates in the theme registry. + init_theme(); + $hooks = theme_get_registry(); + if (is_array($hooks)) { + foreach ($hooks as $name => $hook) { + if (isset($hook['template'])) { + $options[$name] = $hook['template']; + } + } + } + // Include all module functions specified in hook_custom_breadcrumbsapi. + $modules = module_implements('custom_breadcrumbsapi'); + foreach ($modules as $module) { + $func = $module . '_custom_breadcrumbsapi'; + $names = $func(); + foreach ($names as $name) { + $options[$name] = $name; + } + } + if (empty($options)) { + $form['notice'] = array('#value' => t('No module pages using theme templates or currently implementing the custom breadcrumbs api. Read the documentation to learn how to do this.', array('@help' => url('admin/help/custom_breadcrumbsapi')))); + } + else { + $form['module_page'] = array( + '#type' => 'select', + '#title' => t('Module Page'), + '#required' => TRUE, + '#options' => $options, + '#description' => t('The module page that this custom breadcrumb trail will apply to.'), + '#default_value' => isset($breadcrumb->module_page) ? $breadcrumb->module_page : NULL, + '#weight' => -10, + ); + // Store information needed to save this breadcrumb. + $form['#module'] = 'custom_breadcrumbsapi'; + $form['#infokey'] = 'module'; + $form += custom_breadcrumbs_common_form_elements($bid, $breadcrumb); + $form['visibility_php']['#description'] = t('Determine whether this breadcrumb should be displayed by using a PHP snippet to return TRUE or FALSE. For module pages in the theme registry, you will have access to the %var array. Consult the individual template files for variable names that are used as the arguments of this array.', array('%var' => '$variables')); + $form['#submit'][] = 'custom_breadcrumbs_form_submit'; + $form['#validate'][] = 'custom_breadcrumbs_form_validate'; + } + return $form; +} + +/** + * Implements hook_preprocess(). + */ +function custom_breadcrumbsapi_preprocess(&$variables, $hook) { + // Check to see if hook has a defined custom breadcrumb. + static $tried = array(); + // Only respond to the first call for this hook. + if (!isset($tried[$hook])) { + $tried[$hook] = TRUE; + global $language; + $languages = array('language' => $language->language, 'all' => ''); + $breadcrumbs = custom_breadcrumbs_load_breadcrumbs('custom_breadcrumbsapi', NULL, array('module_page' => $hook), $languages); + if (!empty($breadcrumbs)) { + $objs = (isset($variables) && is_array($variables)) ? $variables : array(); + if ($breadcrumb = custom_breadcrumbs_select_breadcrumb($breadcrumbs, $objs)) { + custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs); + $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb()); + } + } + } +}