├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── lang ├── hptal-textdomain-de_DE.mo ├── hptal-textdomain-it_IT.mo ├── hptal-textdomain-it_IT.po └── hptal-textdomain-de_DE.po ├── .gitignore ├── package.json ├── composer.json ├── metabox.js ├── Gruntfile.js ├── readme.txt ├── readme.md └── post-type-archive-links.php /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenharris/WordPress-Post-Type-Archive-Links/HEAD/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenharris/WordPress-Post-Type-Archive-Links/HEAD/screenshot-2.png -------------------------------------------------------------------------------- /screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenharris/WordPress-Post-Type-Archive-Links/HEAD/screenshot-3.png -------------------------------------------------------------------------------- /lang/hptal-textdomain-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenharris/WordPress-Post-Type-Archive-Links/HEAD/lang/hptal-textdomain-de_DE.mo -------------------------------------------------------------------------------- /lang/hptal-textdomain-it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephenharris/WordPress-Post-Type-Archive-Links/HEAD/lang/hptal-textdomain-it_IT.mo -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .project 3 | node_modules 4 | build 5 | .settings 6 | .buildpath 7 | .metadata 8 | compiler.jar 9 | dist 10 | externs.js 11 | Makefile 12 | Thumbs.db 13 | ehthumbs.db 14 | Desktop.ini 15 | $RECYCLE.BIN/ 16 | .DS_Store 17 | .coverage 18 | .tox 19 | vendor/ 20 | composer.lock 21 | *.min.js 22 | *.mo -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "post-type-archive-links", 3 | "version": "1.3.1", 4 | "description": "Adds a MetaBox to the Appearance > Menu page to add post type archive links", 5 | "main": "Gruntfile.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/stephenharris/WordPress-Post-Type-Archive-Links" 9 | }, 10 | "author": "Stephen Harris", 11 | "license": "GPL", 12 | "bugs": { 13 | "url": "https://github.com/stephenharris/WordPress-Post-Type-Archive-Links/issues" 14 | }, 15 | "devDependencies": { 16 | "grunt": "~0.4.1", 17 | "grunt-checkrepo": "~0.1.0", 18 | "grunt-checkbranch": "~0.2.2", 19 | "grunt-contrib-clean": "~0.5.0", 20 | "grunt-contrib-copy": "~0.4.1", 21 | "grunt-contrib-jshint": "~0.6.3", 22 | "grunt-contrib-uglify": "~0.5.0", 23 | "grunt-po2mo": "~0.1.0", 24 | "grunt-wp-deploy": "~1.0.3", 25 | "grunt-wp-readme-to-markdown": "~0.8.0", 26 | "jshint-stylish": "~0.1.3", 27 | "load-grunt-tasks": "~0.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stephenharris/post_type_archive_link", 3 | "description": "WordPress plugin that creates a metabox to the Appearance > Menu page to add custom post type archive link.", 4 | "keywords": ["wordpress"], 5 | "type": "wordpress-plugin", 6 | "homepage": "https://github.com/stephenharris/WordPress-Post-Type-Archive-Links", 7 | "license": "GPL", 8 | "authors": [ 9 | { 10 | "name": "Stephen Harris", 11 | "email": "contact@stephenharris.info", 12 | "homepage": "http://stephenharris.info", 13 | "role": "Lead Developer" 14 | }, 15 | { 16 | "name": "F J Kaiser", 17 | "email": "wecodemore@gmail.com", 18 | "role": "Developer" 19 | }, 20 | { 21 | "name": "Jörn Lund", 22 | "role": "Developer" 23 | }, 24 | { 25 | "name": "Giuseppe Mazzapica", 26 | "email": "giuseppe.mazzapica@gmail.com", 27 | "role": "Developer" 28 | } 29 | ], 30 | "require": { 31 | "composer/installers": "~1.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /metabox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handle the custom post type nav menu meta box 3 | */ 4 | jQuery( document ).ready( function($) { 5 | $( '#submit-post-type-archives' ).click( function( event ) { 6 | event.preventDefault(); 7 | 8 | var $hptal_list_items = $( '#' + hptal_obj.metabox_list_id + ' li :checked' ); 9 | var $hptal_submit = $( 'input#submit-post-type-archives' ); 10 | 11 | // Get checked boxes 12 | var postTypes = []; 13 | $hptal_list_items.each( function() { 14 | postTypes.push( $( this ).val() ); 15 | } ); 16 | 17 | // Show spinner 18 | $( '#' + hptal_obj.metabox_id ).find('.spinner').show(); 19 | 20 | // Disable button 21 | $hptal_submit.prop( 'disabled', true ); 22 | 23 | // Send checked post types with our action, and nonce 24 | $.post( hptal_obj.ajaxurl, { 25 | action: hptal_obj.action, 26 | posttypearchive_nonce: hptal_obj.nonce, 27 | post_types: postTypes, 28 | nonce: hptal_obj.nonce 29 | }, 30 | 31 | // AJAX returns html to add to the menu, hide spinner, remove checks 32 | function( response ) { 33 | $( '#menu-to-edit' ).append( response ); 34 | $( '#' + hptal_obj.metabox_id ).find('.spinner').hide(); 35 | $hptal_list_items.prop("checked", false); 36 | $hptal_submit.prop( 'disabled', false ); 37 | } 38 | ); 39 | } ); 40 | } ); 41 | -------------------------------------------------------------------------------- /lang/hptal-textdomain-it_IT.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: WordPress Post Type Archive Links v1.3\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: \n" 6 | "PO-Revision-Date: 2014-04-26 21:16:22+0000\n" 7 | "Last-Translator: admin \n" 8 | "Language-Team: \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 13 | "X-Generator: CSL v1.x\n" 14 | "X-Poedit-Language: Italian\n" 15 | "X-Poedit-Country: ITALY\n" 16 | "X-Poedit-SourceCharset: utf-8\n" 17 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n" 18 | "X-Poedit-Basepath: ../\n" 19 | "X-Poedit-Bookmarks: \n" 20 | "X-Poedit-SearchPath-0: .\n" 21 | "X-Textdomain-Support: yes" 22 | 23 | #. translators: plugin header field 'Name' 24 | #: post-type-archive-links.php:0 25 | #@ hptal-textdomain 26 | msgid "WordPress Post Type Archive Links" 27 | msgstr "" 28 | 29 | #. translators: plugin header field 'PluginURI' 30 | #: post-type-archive-links.php:0 31 | #@ hptal-textdomain 32 | msgid "https://github.com/stephenharris/WordPress-Post-Type-Archive-Links" 33 | msgstr "" 34 | 35 | #. translators: plugin header field 'Description' 36 | #: post-type-archive-links.php:0 37 | #@ hptal-textdomain 38 | msgid "Adds a MetaBox to the Appearance > Menu page to add post type archive links" 39 | msgstr "Aggiunge una MetaBox alla pagina Aspetto > Menu per aggiungere i link agli archivi dei CPT" 40 | 41 | #. translators: plugin header field 'Author' 42 | #: post-type-archive-links.php:0 43 | #@ hptal-textdomain 44 | msgid "Stephen Harris" 45 | msgstr "" 46 | 47 | #. translators: plugin header field 'AuthorURI' 48 | #: post-type-archive-links.php:0 49 | #@ hptal-textdomain 50 | msgid "https://github.com/stephenharris/" 51 | msgstr "" 52 | 53 | #: post-type-archive-links.php:194 54 | #@ hptal-textdomain 55 | msgid "Post Type Archives" 56 | msgstr "Tipi Post Personalizzati" 57 | 58 | #: post-type-archive-links.php:378 59 | #@ hptal-textdomain 60 | msgid "Archive" 61 | msgstr "Archivio" 62 | 63 | #. translators: plugin header field 'Version' 64 | #: post-type-archive-links.php:0 65 | #@ hptal-textdomain 66 | msgid "1.3" 67 | msgstr "" 68 | 69 | -------------------------------------------------------------------------------- /lang/hptal-textdomain-de_DE.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: WordPress Post Type Archive Links v1.3\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: \n" 6 | "PO-Revision-Date: 2014-04-26 21:15:34+0000\n" 7 | "Last-Translator: admin \n" 8 | "Language-Team: \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 13 | "X-Generator: CSL v1.x\n" 14 | "X-Poedit-Language: German\n" 15 | "X-Poedit-Country: GERMANY\n" 16 | "X-Poedit-SourceCharset: utf-8\n" 17 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n" 18 | "X-Poedit-Basepath: ../\n" 19 | "X-Poedit-Bookmarks: \n" 20 | "X-Poedit-SearchPath-0: .\n" 21 | "X-Textdomain-Support: yes" 22 | 23 | #. translators: plugin header field 'Name' 24 | #: post-type-archive-links.php:0 25 | #@ hptal-textdomain 26 | msgid "WordPress Post Type Archive Links" 27 | msgstr "WordPress Post-Type-Archiv Links" 28 | 29 | #. translators: plugin header field 'PluginURI' 30 | #: post-type-archive-links.php:0 31 | #@ hptal-textdomain 32 | msgid "https://github.com/stephenharris/WordPress-Post-Type-Archive-Links" 33 | msgstr "" 34 | 35 | #. translators: plugin header field 'Description' 36 | #: post-type-archive-links.php:0 37 | #@ hptal-textdomain 38 | msgid "Adds a MetaBox to the Appearance > Menu page to add post type archive links" 39 | msgstr "Fügt in eine Auswahl unter Design > Menüs hinzu, mit der Sie Post-Type-Archiv-Links hinzufügen können." 40 | 41 | #. translators: plugin header field 'Author' 42 | #: post-type-archive-links.php:0 43 | #@ hptal-textdomain 44 | msgid "Stephen Harris" 45 | msgstr "" 46 | 47 | #. translators: plugin header field 'AuthorURI' 48 | #: post-type-archive-links.php:0 49 | #@ hptal-textdomain 50 | msgid "https://github.com/stephenharris/" 51 | msgstr "" 52 | 53 | #: post-type-archive-links.php:194 54 | #@ hptal-textdomain 55 | msgid "Post Type Archives" 56 | msgstr "Post-Type Archive" 57 | 58 | #. translators: plugin header field 'Version' 59 | #: post-type-archive-links.php:0 60 | #@ hptal-textdomain 61 | msgid "1.3" 62 | msgstr "" 63 | 64 | #: post-type-archive-links.php:378 65 | #@ hptal-textdomain 66 | msgid "Archive" 67 | msgstr "" 68 | 69 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | //Load all the tasks 4 | require('load-grunt-tasks')(grunt); 5 | 6 | // Project configuration. 7 | grunt.initConfig({ 8 | pkg: grunt.file.readJSON('package.json'), 9 | 10 | jshint: { 11 | options: { 12 | reporter: require('jshint-stylish'), 13 | }, 14 | all: [ '*.js', '!*.min.js' ] 15 | }, 16 | 17 | uglify: { 18 | options: { 19 | compress: { 20 | dead_code: true 21 | }, 22 | banner: '/*! <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd HH:MM") %> */\n' 23 | }, 24 | build: { 25 | files: [{ 26 | expand: true, // Enable dynamic expansion. 27 | src: ['*.js', '!*.min.js', '!Gruntfile.js'], // Actual pattern(s) to match. 28 | ext: '.min.js', // Dest filepaths will have this extension. 29 | }] 30 | } 31 | }, 32 | 33 | copy: { 34 | // Copy the plugin to a versioned release directory 35 | main: { 36 | src: [ 37 | '**', 38 | '!node_modules/**', 39 | '!build/**', 40 | '!.git/**', 41 | '!vendor/**', 42 | '!Gruntfile.js', 43 | '!package.json', 44 | '!.gitignore', 45 | '!.gitmodules', 46 | '!*~', 47 | '!composer.lock', 48 | '!composer.phar', 49 | '!composer.json', 50 | ], 51 | dest: 'build/<%= pkg.name %>/' 52 | } 53 | }, 54 | 55 | wp_readme_to_markdown: { 56 | convert:{ 57 | files: { 58 | 'readme.md': 'readme.txt' 59 | }, 60 | options:{ 61 | screenshot_url: 'http://s.w.org/plugins/post-type-archive-links/{screenshot}.png' 62 | } 63 | }, 64 | }, 65 | 66 | checkrepo: { 67 | deploy: { 68 | tag: { 69 | eq: '<%= pkg.version %>', // Check if highest repo tag is equal to pkg.version 70 | }, 71 | tagged: true, // Check if last repo commit (HEAD) is not tagged 72 | clean: true, // Check if the repo working directory is clean 73 | } 74 | }, 75 | 76 | clean: { 77 | //Clean up build folder 78 | main: ['build/<%= pkg.name %>'] 79 | }, 80 | 81 | po2mo: { 82 | files: { 83 | src: 'lang/*.po', 84 | expand: true, 85 | }, 86 | }, 87 | 88 | wp_deploy: { 89 | deploy:{ 90 | options: { 91 | svn_user: 'stephenharris', 92 | plugin_slug: '<%= pkg.name %>', 93 | build_dir: 'build/<%= pkg.name %>/' 94 | }, 95 | } 96 | } 97 | }); 98 | 99 | 100 | grunt.registerTask( 'test', [ 'jshint' ] ); 101 | 102 | grunt.registerTask( 'compile', [ 'wp_readme_to_markdown', 'uglify' ] ); 103 | 104 | grunt.registerTask( 'build', [ 'test', 'compile', 'clean', 'copy' ] ); 105 | 106 | grunt.registerTask( 'deploy', [ 'checkbranch:master', 'checkrepo:deploy', 'build', 'wp_deploy'] ); //Deploy via svn 107 | 108 | //TODO on pre-commit: test/uglify? 109 | }; 110 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Post Type Archive Link === 2 | Contributors: stephenharris, F J Kaiser, ryancurban, giuseppe.mazzapica 3 | Tags: post type archives, menu link, archives, navigation, metabox, administration user interface 4 | Requires at least: 3.3 5 | Tested up to: 4.1.1 6 | Stable tag: 1.3.1 7 | License: GPLv3 or later 8 | License URI: http://www.gnu.org/licenses/gpl.txt 9 | 10 | Creates a metabox to the Appearance > Menu page to add custom post type archive links 11 | 12 | == Description == 13 | 14 | Post Type Archive Link creates a metabox on the Appearance > Menu admin page. 15 | This lists your custom post types and allows you to add links to each archive page in your WordPress menus. 16 | 17 | The plug-in uses WordPress' default menu classes for current pages, current page parent and current page ancestor. 18 | 19 | By default all post types with archives (and not registered by core) are available for adding to your menu. 20 | You can forcibly revent a particlar post type from appearing using the `show_{$posttype}_archive_in_nav_menus` hook. 21 | 22 | 23 | == Installation == 24 | 25 | Installation is standard and straight forward. 26 | 27 | 1. Upload `WordPress-Post-Type-Archive-Links` folder (and all it's contents!) to the `/wp-content/plugins/` directory 28 | 1. Activate the plugin through the 'Plugins' menu in WordPress 29 | 1. The metabox will appear at the bottom of your Appearance > Menu 30 | 31 | 32 | == Frequently Asked Questions == 33 | 34 | = I can't see in the 'post type' metabox on the Apperance > Menus screen = 35 | 36 | View the "screen options" (top right), and ensure that "Post Type Archives" is checked. 37 | 38 | 39 | = Why are some post types missing? = 40 | 41 | The metabox will only list custom post types registered with non-falsey `has_archive`, `publicly_queryable` or `show_in_vav_menus`. 42 | 43 | CPTs having true `has_archive' but false `publicly_queryable` and/or `show_in_vav_menus` can be shown using `show_{$cpt_slug}_archive_in_nav_menus` filter hook. 44 | 45 | 46 | == Screenshots == 47 | 48 | 1. Custom post types admin menu metabox 49 | 2. Custom post types added to your menu 50 | 3. Custom post type 'Clients' in front-end menu with WordPress menu classes and current item styles 51 | 52 | 53 | == Changelog == 54 | 55 | = 1.3.1 - 12th April 2015 = 56 | * Fixes incomptability with PHP 5.3 and older. 57 | 58 | = 1.3 = 59 | * Make submit button available for translation. Thanks to [@antwortzeit](https://github.com/antwortzeit). 60 | * Removed hooks from constructor, allowed plugin disabling: removing all hooks and text domain 61 | * Introduced "post_type_archive_links" filter hook to get an instance of plugin class 62 | * Hide CPTs having 'has_archive' true, but 'publicly_queryable' and/or 'show_in_vav_menus' set to false 63 | * Introduced "show_{$cpt_slug}_archive_in_nav_menus" filter to force CPTs be added on metabox 64 | * Show "No items." when there are no CPTs available 65 | * Tested up to 4.0 66 | * Added Italian language. Thanks to [@giuseppe.mazzapica](http://gm.zoomlab.it). 67 | * Updated readme 68 | 69 | = 1.2 = 70 | * Use has_archive rather than public. [See #13](https://github.com/stephenharris/WordPress-Post-Type-Archive-Links/issues/13) 71 | * Fixes bug where "disabled" is printed if no menu has been created. 72 | * Tested up to 3.7.1 73 | * Added German language. Thanks to [@mcguffin](https://github.com/mcguffin). 74 | 75 | = 1.1 = 76 | * Fixed a couple of notices that displayed with debug on 77 | * Better maintainability (avoid touching JS files) 78 | * Static init now runs during plugins_loaded hook 79 | * Code cleanup and safer names 80 | 81 | = 1.0.1 = 82 | * Fixed enqueue bug 83 | 84 | = 1.0 = 85 | * Added plug-in 86 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Post Type Archive Link # 2 | **Contributors:** stephenharris, F J Kaiser, ryancurban, giuseppe.mazzapica 3 | **Tags:** post type archives, menu link, archives, navigation, metabox, administration user interface 4 | **Requires at least:** 3.3 5 | **Tested up to:** 4.1.1 6 | **Stable tag:** 1.3.1 7 | **License:** GPLv3 or later 8 | **License URI:** http://www.gnu.org/licenses/gpl.txt 9 | 10 | Creates a metabox to the Appearance > Menu page to add custom post type archive links 11 | 12 | ## Description ## 13 | 14 | Post Type Archive Link creates a metabox on the Appearance > Menu admin page. 15 | This lists your custom post types and allows you to add links to each archive page in your WordPress menus. 16 | 17 | The plug-in uses WordPress' default menu classes for current pages, current page parent and current page ancestor. 18 | 19 | By default all post types with archives (and not registered by core) are available for adding to your menu. 20 | You can forcibly revent a particlar post type from appearing using the `show_{$posttype}_archive_in_nav_menus` hook. 21 | 22 | 23 | ## Installation ## 24 | 25 | Installation is standard and straight forward. 26 | 27 | 1. Upload `WordPress-Post-Type-Archive-Links` folder (and all it's contents!) to the `/wp-content/plugins/` directory 28 | 1. Activate the plugin through the 'Plugins' menu in WordPress 29 | 1. The metabox will appear at the bottom of your Appearance > Menu 30 | 31 | 32 | ## Frequently Asked Questions ## 33 | 34 | ### I can't see in the 'post type' metabox on the Apperance > Menus screen ### 35 | 36 | View the "screen options" (top right), and ensure that "Post Type Archives" is checked. 37 | 38 | 39 | ### Why are some post types missing? ### 40 | 41 | The metabox will only list custom post types registered with non-falsey `has_archive`, `publicly_queryable` or `show_in_vav_menus`. 42 | 43 | CPTs having true `has_archive' but false `publicly_queryable` and/or `show_in_vav_menus` can be shown using `show_{$cpt_slug}_archive_in_nav_menus` filter hook. 44 | 45 | 46 | ## Screenshots ## 47 | 48 | ### 1. Custom post types admin menu metabox ### 49 | ![Custom post types admin menu metabox](http://s.w.org/plugins/post-type-archive-links/screenshot-1.png) 50 | 51 | ### 2. Custom post types added to your menu ### 52 | ![Custom post types added to your menu](http://s.w.org/plugins/post-type-archive-links/screenshot-2.png) 53 | 54 | ### 3. Custom post type 'Clients' in front-end menu with WordPress menu classes and current item styles ### 55 | ![Custom post type 'Clients' in front-end menu with WordPress menu classes and current item styles](http://s.w.org/plugins/post-type-archive-links/screenshot-3.png) 56 | 57 | 58 | 59 | ## Changelog ## 60 | 61 | ### 1.3.1 - 12th April 2015 ### 62 | * Fixes incomptability with PHP 5.3 and older. 63 | 64 | ### 1.3 ### 65 | * Make submit button available for translation. Thanks to [@antwortzeit](https://github.com/antwortzeit). 66 | * Removed hooks from constructor, allowed plugin disabling: removing all hooks and text domain 67 | * Introduced "post_type_archive_links" filter hook to get an instance of plugin class 68 | * Hide CPTs having 'has_archive' true, but 'publicly_queryable' and/or 'show_in_vav_menus' set to false 69 | * Introduced "show_{$cpt_slug}_archive_in_nav_menus" filter to force CPTs be added on metabox 70 | * Show "No items." when there are no CPTs available 71 | * Tested up to 4.0 72 | * Added Italian language. Thanks to [@giuseppe.mazzapica](http://gm.zoomlab.it). 73 | * Updated readme 74 | 75 | ### 1.2 ### 76 | * Use has_archive rather than public. [See #13](https://github.com/stephenharris/WordPress-Post-Type-Archive-Links/issues/13) 77 | * Fixes bug where "disabled" is printed if no menu has been created. 78 | * Tested up to 3.7.1 79 | * Added German language. Thanks to [@mcguffin](https://github.com/mcguffin). 80 | 81 | ### 1.1 ### 82 | * Fixed a couple of notices that displayed with debug on 83 | * Better maintainability (avoid touching JS files) 84 | * Static init now runs during plugins_loaded hook 85 | * Code cleanup and safer names 86 | 87 | ### 1.0.1 ### 88 | * Fixed enqueue bug 89 | 90 | ### 1.0 ### 91 | * Added plug-in 92 | -------------------------------------------------------------------------------- /post-type-archive-links.php: -------------------------------------------------------------------------------- 1 | Menu page to add post type archive links 7 | Version: 1.3.1 8 | Author: Stephen Harris 9 | Author URI: https://github.com/stephenharris/ 10 | Author Email: contact@stephenharris.info 11 | Contributors: Franz Josef Kaiser , Ryan Urban 12 | License: GPLv3 13 | License URI: http://www.gnu.org/licenses/gpl.txt 14 | Text Domain: hptal-textdomain 15 | Domain Path: /lang/ 16 | 17 | Copyright 2013 Stephen Harris (contact@stephenharris.info) 18 | 19 | This program is free software; you can redistribute it and/or modify 20 | it under the terms of the GNU General Public License, version 2, as 21 | published by the Free Software Foundation. 22 | 23 | This program is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU General Public License for more details. 27 | 28 | You should have received a copy of the GNU General Public License 29 | along with this program; if not, write to the Free Software 30 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 31 | 32 | */ 33 | 34 | // Load at the default priority of 10 35 | add_action( 'plugins_loaded', array( new Post_Type_Archive_Links, 'init' ) ); 36 | 37 | class Post_Type_Archive_Links { 38 | /** 39 | * True if class already inited 40 | * @access private 41 | * @var bool 42 | */ 43 | private $ininited; 44 | 45 | /** 46 | * Nonce Value 47 | * @const \Post_Type_Archive_Links::NONCE 48 | */ 49 | const NONCE = 'hptal_nonce'; 50 | 51 | /** 52 | * ID of the custom metabox 53 | * @const \Post_Type_Archive_Links::METABOXID 54 | */ 55 | const METABOXID = 'hptal-metabox'; 56 | 57 | /** 58 | * ID of the custom post type list items 59 | * @const \Post_Type_Archive_Links::METABOXLISTID 60 | */ 61 | const METABOXLISTID = 'post-type-archive-checklist'; 62 | 63 | /** 64 | * CPT objects that plugin should handle: having true 65 | * 'has_archive', 'publicly_queryable' and 'show_in_nav_menu' 66 | * @var array 67 | * @access protected 68 | */ 69 | protected $cpts; 70 | 71 | /** 72 | * Handle backward compatibility for removed object variables 73 | */ 74 | public function __get( $name ) { 75 | switch ( $name ) { 76 | case 'metabox_id' : 77 | return self::METABOXID; 78 | case 'metabox_list_id' : 79 | return self::METABOXLISTID; 80 | case 'nonce' : 81 | return self::NONCE; 82 | case 'instance' : 83 | return $this; 84 | } 85 | } 86 | 87 | /** 88 | * Instantiates the class, add hooks and load domain 89 | * @return \Post_Type_Archive_Links 90 | * @use \Post_Type_Archive_Links::enable() Add hooks, load domain 91 | */ 92 | public function init() { 93 | if ( ! $this->ininited ) { 94 | $this->ininited = true; 95 | $this->enable( dirname( plugin_basename( __FILE__ ) ) ); 96 | 97 | /** 98 | This filter allow to access to current class instance 99 | by calling $hptal = apply_filters( 'post_type_archive_links', NULL ); 100 | No singleton was harmed in the making of this plugin. 101 | */ 102 | add_filter( 'post_type_archive_links', array( $this, __FUNCTION__ ) ); 103 | } 104 | return $this; 105 | } 106 | 107 | 108 | /** 109 | * Add plugin hooks and load domain. 110 | * @return void 111 | * @access private 112 | */ 113 | private function enable( $path ) { 114 | 115 | load_plugin_textdomain( 'hptal-textdomain' , false , $path . '/lang/' ); 116 | 117 | add_action( 'admin_init', array( $this, 'get_cpts' ) ); 118 | 119 | add_action( 'admin_init', array( $this, 'add_meta_box' ), 20 ); 120 | 121 | add_filter( 'wp_setup_nav_menu_item', array( $this, 'setup_archive_item' ) ); 122 | 123 | add_filter( 'wp_nav_menu_objects', array( $this, 'maybe_make_current' ) ); 124 | 125 | add_action( 'admin_enqueue_scripts', array( $this, 'metabox_script' ) ); 126 | 127 | add_action( "wp_ajax_" . self::NONCE, array( $this, 'ajax_add_post_type' ) ); 128 | 129 | } 130 | 131 | /** 132 | * Remove plugin hooks and unset domain if exists. 133 | * @return void 134 | */ 135 | public function disable() { 136 | if ( $this->ininited ) { 137 | 138 | if ( isset( $GLOBALS['l10n']['hptal-textdomain'] ) ) { 139 | unset( $GLOBALS['l10n']['hptal-textdomain'] ); 140 | } 141 | 142 | remove_action( 'admin_init', array( $this, 'get_cpts' ) ); 143 | 144 | remove_action( 'admin_init', array( $this, 'add_meta_box' ), 20 ); 145 | 146 | remove_filter( 'wp_setup_nav_menu_item', array( $this, 'setup_archive_item' ) ); 147 | 148 | remove_filter( 'wp_nav_menu_objects', array( $this, 'maybe_make_current' ) ); 149 | 150 | remove_action( 'admin_enqueue_scripts', array( $this, 'metabox_script' ) ); 151 | 152 | remove_action( "wp_ajax_" . self::NONCE, array( $this, 'ajax_add_post_type' ) ); 153 | 154 | } 155 | } 156 | 157 | /** 158 | * Get CPTs that plugin should handle: having true 159 | * 'has_archive', 'publicly_queryable' and 'show_in_nav_menu' 160 | * @return void 161 | */ 162 | public function get_cpts() { 163 | $cpts = array(); 164 | $has_archive_cps = get_post_types( 165 | array( 166 | 'has_archive' => true, 167 | '_builtin' => false 168 | ), 169 | 'object' 170 | ); 171 | foreach ( $has_archive_cps as $ptid => $pt ) { 172 | $to_show = $pt->show_in_nav_menus && $pt->publicly_queryable; 173 | if ( apply_filters( "show_{$ptid}_archive_in_nav_menus", $to_show, $pt ) ) { 174 | $cpts[] = $pt; 175 | } 176 | } 177 | if ( ! empty( $cpts ) ) { 178 | $this->cpts = $cpts; 179 | } 180 | } 181 | 182 | 183 | /** 184 | * Adds the meta box to the menu page 185 | * @return void 186 | */ 187 | public function add_meta_box() { 188 | 189 | add_meta_box( 190 | self::METABOXID, 191 | __( 'Post Type Archives', 'hptal-textdomain' ), 192 | array( $this, 'metabox' ), 193 | 'nav-menus', 194 | 'side', 195 | 'low' 196 | ); 197 | } 198 | 199 | 200 | /** 201 | * Scripts for AJAX call 202 | * Only loads on nav-menus.php 203 | * @param string $hook Page Name 204 | * @return void 205 | */ 206 | public function metabox_script( $hook ) { 207 | if ( 'nav-menus.php' !== $hook ) 208 | return; 209 | 210 | // Do nothing if no CPTs to handle 211 | if ( empty( $this->cpts ) ) return; 212 | 213 | $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG) ? '' : '.min'; 214 | 215 | wp_register_script( 216 | 'hptal-ajax-script', 217 | plugins_url( "metabox{$suffix}.js", __FILE__ ), 218 | array( 'jquery' ), 219 | filemtime( plugin_dir_path( __FILE__ ) . "metabox{$suffix}.js" ), 220 | true 221 | ); 222 | wp_enqueue_script( 'hptal-ajax-script' ); 223 | 224 | // Add nonce variable 225 | wp_localize_script( 226 | 'hptal-ajax-script', 227 | 'hptal_obj', 228 | array( 229 | 'ajaxurl' => admin_url( 'admin-ajax.php' ), 230 | 'nonce' => wp_create_nonce( self::NONCE ), 231 | 'metabox_id' => self::METABOXID, 232 | 'metabox_list_id' => self::METABOXLISTID, 233 | 'action' => self::NONCE 234 | ) 235 | ); 236 | } 237 | 238 | 239 | /** 240 | * MetaBox Content Callback 241 | * @return string $html 242 | */ 243 | public function metabox() { 244 | 245 | // Inform user no CPTs available to be shown. 246 | if ( empty( $this->cpts ) ) { 247 | echo '

' . __( 'No items.' ) . '

'; 248 | return; 249 | } 250 | 251 | global $nav_menu_selected_id; 252 | 253 | $html = '
    '; 254 | foreach ( $this->cpts as $pt ) { 255 | $html .= sprintf( 256 | '
  • ', 257 | esc_attr( $pt->name ), 258 | esc_attr( $pt->labels->name ) 259 | ); 260 | } 261 | $html .= '
'; 262 | 263 | // 'Add to Menu' button 264 | $html .= '

'; 265 | $html .= ''; 268 | $html .= ''; 269 | $html .= '

'; 270 | 271 | print $html; 272 | } 273 | 274 | /** 275 | * AJAX Callback to create the menu item and add it to menu 276 | * @return string $HTML built with walk_nav_menu_tree() 277 | * use \Post_Type_Archive_Links::is_allowed() Check request and return choosen post types 278 | */ 279 | public function ajax_add_post_type() { 280 | $post_types = $this->is_allowed(); 281 | 282 | // Create menu items and store IDs in array 283 | $item_ids = array(); 284 | foreach ( $post_types as $post_type ) { 285 | $post_type_obj = get_post_type_object( $post_type ); 286 | 287 | if( ! $post_type_obj ) 288 | continue; 289 | 290 | $menu_item_data= array( 291 | 'menu-item-title' => esc_attr( $post_type_obj->labels->name ) 292 | ,'menu-item-type' => 'post_type_archive' 293 | ,'menu-item-object' => esc_attr( $post_type ) 294 | ,'menu-item-url' => get_post_type_archive_link( $post_type ) 295 | ); 296 | 297 | // Collect the items' IDs. 298 | $item_ids[] = wp_update_nav_menu_item( 0, 0, $menu_item_data ); 299 | } 300 | 301 | // If there was an error die here 302 | is_wp_error( $item_ids ) AND die( '-1' ); 303 | 304 | // Set up menu items 305 | foreach ( (array) $item_ids as $menu_item_id ) { 306 | $menu_obj = get_post( $menu_item_id ); 307 | if ( ! empty( $menu_obj->ID ) ) { 308 | $menu_obj = wp_setup_nav_menu_item( $menu_obj ); 309 | // don't show "(pending)" in ajax-added items 310 | $menu_obj->label = $menu_obj->title; 311 | 312 | $menu_items[] = $menu_obj; 313 | } 314 | } 315 | 316 | // Needed to get the Walker up and running 317 | require_once ABSPATH.'wp-admin/includes/nav-menu.php'; 318 | 319 | // This gets the HTML to returns it to the menu 320 | if ( ! empty( $menu_items ) ) { 321 | $args = array( 322 | 'after' => '', 323 | 'before' => '', 324 | 'link_after' => '', 325 | 'link_before' => '', 326 | 'walker' => new Walker_Nav_Menu_Edit 327 | ); 328 | 329 | echo walk_nav_menu_tree( 330 | $menu_items, 331 | 0, 332 | (object) $args 333 | ); 334 | } 335 | 336 | // Finally don't forget to exit 337 | exit; 338 | 339 | } 340 | 341 | 342 | /** 343 | * Is the AJAX request allowed and should be processed? 344 | * @return void 345 | */ 346 | public function is_allowed() { 347 | // Capability Check 348 | ! current_user_can( 'edit_theme_options' ) AND die( '-1' ); 349 | 350 | // Nonce check 351 | check_ajax_referer( self::NONCE, 'nonce' ); 352 | 353 | // Is a post type chosen? 354 | $post_types = filter_input_array( 355 | INPUT_POST, 356 | array( 357 | 'post_types' => array( 358 | 'filter' => FILTER_SANITIZE_STRING, 359 | 'flags' => FILTER_REQUIRE_ARRAY 360 | ) 361 | ) 362 | ); 363 | 364 | empty( $post_types['post_types'] ) AND exit; 365 | // return post types if chosen 366 | return array_values( $post_types['post_types'] ); 367 | } 368 | 369 | 370 | /** 371 | * Assign menu item the appropriate url 372 | * @param object $menu_item 373 | * @return object $menu_item 374 | */ 375 | public function setup_archive_item( $menu_item ) { 376 | if ( $menu_item->type !== 'post_type_archive' ) 377 | return $menu_item; 378 | 379 | $post_type = $menu_item->object; 380 | $menu_item->type_label = __( 'Archive', 'hptal-textdomain' ); 381 | $menu_item->url = get_post_type_archive_link( $post_type ); 382 | 383 | return $menu_item; 384 | } 385 | 386 | 387 | /** 388 | * Make post type archive link 'current' 389 | * @uses Post_Type_Archive_Links :: get_item_ancestors() 390 | * @param array $items 391 | * @return array $items 392 | */ 393 | public function maybe_make_current( $items ) { 394 | foreach ( $items as $item ) { 395 | if ( 'post_type_archive' !== $item->type ) 396 | continue; 397 | 398 | $post_type = $item->object; 399 | if ( 400 | ! is_post_type_archive( $post_type ) 401 | AND ! is_singular( $post_type ) 402 | ) 403 | continue; 404 | 405 | // Make item current 406 | $item->current = true; 407 | $item->classes[] = 'current-menu-item'; 408 | 409 | // Loop through ancestors and give them 'parent' or 'ancestor' class 410 | $active_anc_item_ids = $this->get_item_ancestors( $item ); 411 | foreach ( $items as $key => $parent_item ) { 412 | $classes = (array) $parent_item->classes; 413 | 414 | // If menu item is the parent 415 | if ( $parent_item->db_id == $item->menu_item_parent ) { 416 | $classes[] = 'current-menu-parent'; 417 | $items[ $key ]->current_item_parent = true; 418 | } 419 | 420 | // If menu item is an ancestor 421 | if ( in_array( intval( $parent_item->db_id ), $active_anc_item_ids ) ) { 422 | $classes[] = 'current-menu-ancestor'; 423 | $items[ $key ]->current_item_ancestor = true; 424 | } 425 | 426 | $items[ $key ]->classes = array_unique( $classes ); 427 | } 428 | } 429 | 430 | return $items; 431 | } 432 | 433 | 434 | /** 435 | * Get menu item's ancestors 436 | * @param object $item 437 | * @return array $active_anc_item_ids 438 | */ 439 | public function get_item_ancestors( $item ) { 440 | $anc_id = absint( $item->db_id ); 441 | 442 | $active_anc_item_ids = array(); 443 | while ( 444 | $anc_id = get_post_meta( $anc_id, '_menu_item_menu_item_parent', true ) 445 | AND ! in_array( $anc_id, $active_anc_item_ids ) 446 | ) 447 | $active_anc_item_ids[] = $anc_id; 448 | 449 | return $active_anc_item_ids; 450 | } 451 | } 452 | --------------------------------------------------------------------------------