/modules/custom-post-types/portfolios.php

  1. <?php 
  2.  
  3. class Jetpack_Portfolio { 
  4. const CUSTOM_POST_TYPE = 'jetpack-portfolio'; 
  5. const CUSTOM_TAXONOMY_TYPE = 'jetpack-portfolio-type'; 
  6. const CUSTOM_TAXONOMY_TAG = 'jetpack-portfolio-tag'; 
  7. const OPTION_NAME = 'jetpack_portfolio'; 
  8. const OPTION_READING_SETTING = 'jetpack_portfolio_posts_per_page'; 
  9.  
  10. public $version = '0.1'; 
  11.  
  12. static function init() { 
  13. static $instance = false; 
  14.  
  15. if ( ! $instance ) { 
  16. $instance = new Jetpack_Portfolio; 
  17.  
  18. return $instance; 
  19.  
  20. /** 
  21. * Conditionally hook into WordPress. 
  22. * 
  23. * Setup user option for enabling CPT 
  24. * If user has CPT enabled, show in admin 
  25. */ 
  26. function __construct() { 
  27. // Add an option to enable the CPT 
  28. add_action( 'admin_init', array( $this, 'settings_api_init' ) ); 
  29.  
  30. // Check on theme switch if theme supports CPT and setting is disabled 
  31. add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) ); 
  32.  
  33. // Make sure the post types are loaded for imports 
  34. add_action( 'import_start', array( $this, 'register_post_types' ) ); 
  35.  
  36. $setting = get_option( self::OPTION_NAME, '0' ); 
  37.  
  38. // Bail early if Portfolio option is not set and the theme doesn't declare support 
  39. if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) { 
  40. return; 
  41.  
  42. // Enable Omnisearch for Portfolio Items. 
  43. if ( class_exists( 'Jetpack_Omnisearch_Posts' ) ) 
  44. new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE ); 
  45.  
  46. // CPT magic 
  47. $this->register_post_types(); 
  48. add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 ); 
  49. add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 ); 
  50. add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'flush_rules_on_first_project' ) ); 
  51. add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) ); 
  52.  
  53. // Admin Customization 
  54. add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) ); 
  55. add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_admin_columns' ) ); 
  56. add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column' ), 10, 2 ); 
  57.  
  58. add_image_size( 'jetpack-portfolio-admin-thumb', 50, 50, true ); 
  59. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) ); 
  60.  
  61. // register jetpack_portfolio shortcode and portfolio shortcode (legacy) 
  62. add_shortcode( 'portfolio', array( $this, 'portfolio_shortcode' ) ); 
  63. add_shortcode( 'jetpack_portfolio', array( $this, 'portfolio_shortcode' ) ); 
  64.  
  65. // Adjust CPT archive and custom taxonomies to obey CPT reading setting 
  66. add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ) ); 
  67.  
  68. // If CPT was enabled programatically and no CPT items exist when user switches away, disable 
  69. if ( $setting && $this->site_supports_custom_post_type() ) { 
  70. add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) ); 
  71.  
  72. /** 
  73. * Add a checkbox field in 'Settings' > 'Writing' 
  74. * for enabling CPT functionality. 
  75. * 
  76. * @return null 
  77. */ 
  78. function settings_api_init() { 
  79. add_settings_field( 
  80. self::OPTION_NAME,  
  81. '<span class="cpt-options">' . __( 'Portfolio Projects', 'jetpack' ) . '</span>',  
  82. array( $this, 'setting_html' ),  
  83. 'writing',  
  84. 'jetpack_cpt_section' 
  85. ); 
  86. register_setting( 
  87. 'writing',  
  88. self::OPTION_NAME,  
  89. 'intval' 
  90. ); 
  91.  
  92. // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering 
  93. if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  94. register_setting( 
  95. 'writing',  
  96. self::OPTION_READING_SETTING,  
  97. 'intval' 
  98. ); 
  99.  
  100. /** 
  101. * HTML code to display a checkbox true/false option 
  102. * for the Portfolio CPT setting. 
  103. * 
  104. * @return html 
  105. */ 
  106. function setting_html() { 
  107. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?> 
  108. <p><?php printf( __( 'Your theme supports <strong>%s</strong>', 'jetpack' ), self::CUSTOM_POST_TYPE ); ?></p> 
  109. <?php else : ?> 
  110. <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>"> 
  111. <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" /> 
  112. <?php esc_html_e( 'Enable Portfolio Projects for this site.', 'jetpack' ); ?> 
  113. <a target="_blank" href="http://en.support.wordpress.com/portfolios/"><?php esc_html_e( 'Learn More', 'jetpack' ); ?></a> 
  114. </label> 
  115. <?php endif; 
  116. if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) : 
  117. printf( '<p><label for="%1$s">%2$s</label></p>',  
  118. esc_attr( self::OPTION_READING_SETTING ),  
  119. sprintf( __( 'Portfolio pages display at most %1$s projects', 'jetpack' ),  
  120. sprintf( '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',  
  121. esc_attr( self::OPTION_READING_SETTING ),  
  122. esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) ) 
  123. ); 
  124. endif; 
  125.  
  126. /** 
  127. * Should this Custom Post Type be made available? 
  128. */ 
  129. function site_supports_custom_post_type() { 
  130. // If the current theme requests it. 
  131. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) { 
  132. return true; 
  133.  
  134. // Otherwise, say no unless something wants to filter us to say yes. 
  135. /** This action is documented in modules/custom-post-types/nova.php */ 
  136. return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE ); 
  137.  
  138. /** 
  139. * Flush permalinks when CPT option is turned on/off 
  140. */ 
  141. function flush_rules_on_enable() { 
  142. flush_rewrite_rules(); 
  143.  
  144. /** 
  145. * Count published projects and flush permalinks when first projects is published 
  146. */ 
  147. function flush_rules_on_first_project() { 
  148. $projects = get_transient( 'jetpack-portfolio-count-cache' ); 
  149.  
  150. if ( false === $projects ) { 
  151. flush_rewrite_rules(); 
  152. $projects = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish; 
  153.  
  154. if ( ! empty( $projects ) ) { 
  155. set_transient( 'jetpack-portfolio-count-cache', $projects, HOUR_IN_SECONDS * 12 ); 
  156.  
  157. /** 
  158. * Flush permalinks when CPT supported theme is activated 
  159. */ 
  160. function flush_rules_on_switch() { 
  161. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  162. flush_rewrite_rules(); 
  163.  
  164. /** 
  165. * On plugin/theme activation, check if current theme supports CPT 
  166. */ 
  167. static function activation_post_type_support() { 
  168. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  169. update_option( self::OPTION_NAME, '1' ); 
  170.  
  171. /** 
  172. * On theme switch, check if CPT item exists and disable if not 
  173. */ 
  174. function deactivation_post_type_support() { 
  175. $portfolios = get_posts( array( 
  176. 'fields' => 'ids',  
  177. 'posts_per_page' => 1,  
  178. 'post_type' => self::CUSTOM_POST_TYPE,  
  179. 'suppress_filters' => false 
  180. ) ); 
  181.  
  182. if ( empty( $portfolios ) ) { 
  183. update_option( self::OPTION_NAME, '0' ); 
  184.  
  185. /** 
  186. * Register Post Type 
  187. */ 
  188. function register_post_types() { 
  189. if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) { 
  190. return; 
  191.  
  192. register_post_type( self::CUSTOM_POST_TYPE, array( 
  193. 'description' => __( 'Portfolio Items', 'jetpack' ),  
  194. 'labels' => array( 
  195. 'name' => esc_html__( 'Projects', 'jetpack' ),  
  196. 'singular_name' => esc_html__( 'Project', 'jetpack' ),  
  197. 'menu_name' => esc_html__( 'Portfolio', 'jetpack' ),  
  198. 'all_items' => esc_html__( 'All Projects', 'jetpack' ),  
  199. 'add_new' => esc_html__( 'Add New', 'jetpack' ),  
  200. 'add_new_item' => esc_html__( 'Add New Project', 'jetpack' ),  
  201. 'edit_item' => esc_html__( 'Edit Project', 'jetpack' ),  
  202. 'new_item' => esc_html__( 'New Project', 'jetpack' ),  
  203. 'view_item' => esc_html__( 'View Project', 'jetpack' ),  
  204. 'search_items' => esc_html__( 'Search Projects', 'jetpack' ),  
  205. 'not_found' => esc_html__( 'No Projects found', 'jetpack' ),  
  206. 'not_found_in_trash' => esc_html__( 'No Projects found in Trash', 'jetpack' ),  
  207. 'filter_items_list' => esc_html__( 'Filter projects list', 'jetpack' ),  
  208. 'items_list_navigation' => esc_html__( 'Project list navigation', 'jetpack' ),  
  209. 'items_list' => esc_html__( 'Projects list', 'jetpack' ),  
  210. ),  
  211. 'supports' => array( 
  212. 'title',  
  213. 'editor',  
  214. 'thumbnail',  
  215. 'comments',  
  216. 'publicize',  
  217. 'wpcom-markdown',  
  218. ),  
  219. 'rewrite' => array( 
  220. 'slug' => 'portfolio',  
  221. 'with_front' => false,  
  222. 'feeds' => true,  
  223. 'pages' => true,  
  224. ),  
  225. 'public' => true,  
  226. 'show_ui' => true,  
  227. 'menu_position' => 20, // below Pages 
  228. 'menu_icon' => 'dashicons-portfolio', // 3.8+ dashicon option 
  229. 'capability_type' => 'page',  
  230. 'map_meta_cap' => true,  
  231. 'taxonomies' => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),  
  232. 'has_archive' => true,  
  233. 'query_var' => 'portfolio',  
  234. ) ); 
  235.  
  236. register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array( 
  237. 'hierarchical' => true,  
  238. 'labels' => array( 
  239. 'name' => esc_html__( 'Project Types', 'jetpack' ),  
  240. 'singular_name' => esc_html__( 'Project Type', 'jetpack' ),  
  241. 'menu_name' => esc_html__( 'Project Types', 'jetpack' ),  
  242. 'all_items' => esc_html__( 'All Project Types', 'jetpack' ),  
  243. 'edit_item' => esc_html__( 'Edit Project Type', 'jetpack' ),  
  244. 'view_item' => esc_html__( 'View Project Type', 'jetpack' ),  
  245. 'update_item' => esc_html__( 'Update Project Type', 'jetpack' ),  
  246. 'add_new_item' => esc_html__( 'Add New Project Type', 'jetpack' ),  
  247. 'new_item_name' => esc_html__( 'New Project Type Name', 'jetpack' ),  
  248. 'parent_item' => esc_html__( 'Parent Project Type', 'jetpack' ),  
  249. 'parent_item_colon' => esc_html__( 'Parent Project Type:', 'jetpack' ),  
  250. 'search_items' => esc_html__( 'Search Project Types', 'jetpack' ),  
  251. 'items_list_navigation' => esc_html__( 'Project type list navigation', 'jetpack' ),  
  252. 'items_list' => esc_html__( 'Project type list', 'jetpack' ),  
  253. ),  
  254. 'public' => true,  
  255. 'show_ui' => true,  
  256. 'show_in_nav_menus' => true,  
  257. 'show_admin_column' => true,  
  258. 'query_var' => true,  
  259. 'rewrite' => array( 'slug' => 'project-type' ),  
  260. ) ); 
  261.  
  262. register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array( 
  263. 'hierarchical' => false,  
  264. 'labels' => array( 
  265. 'name' => esc_html__( 'Project Tags', 'jetpack' ),  
  266. 'singular_name' => esc_html__( 'Project Tag', 'jetpack' ),  
  267. 'menu_name' => esc_html__( 'Project Tags', 'jetpack' ),  
  268. 'all_items' => esc_html__( 'All Project Tags', 'jetpack' ),  
  269. 'edit_item' => esc_html__( 'Edit Project Tag', 'jetpack' ),  
  270. 'view_item' => esc_html__( 'View Project Tag', 'jetpack' ),  
  271. 'update_item' => esc_html__( 'Update Project Tag', 'jetpack' ),  
  272. 'add_new_item' => esc_html__( 'Add New Project Tag', 'jetpack' ),  
  273. 'new_item_name' => esc_html__( 'New Project Tag Name', 'jetpack' ),  
  274. 'search_items' => esc_html__( 'Search Project Tags', 'jetpack' ),  
  275. 'popular_items' => esc_html__( 'Popular Project Tags', 'jetpack' ),  
  276. 'separate_items_with_commas' => esc_html__( 'Separate tags with commas', 'jetpack' ),  
  277. 'add_or_remove_items' => esc_html__( 'Add or remove tags', 'jetpack' ),  
  278. 'choose_from_most_used' => esc_html__( 'Choose from the most used tags', 'jetpack' ),  
  279. 'not_found' => esc_html__( 'No tags found.', 'jetpack' ),  
  280. 'items_list_navigation' => esc_html__( 'Project tag list navigation', 'jetpack' ),  
  281. 'items_list' => esc_html__( 'Project tag list', 'jetpack' ),  
  282. ),  
  283. 'public' => true,  
  284. 'show_ui' => true,  
  285. 'show_in_nav_menus' => true,  
  286. 'show_admin_column' => true,  
  287. 'query_var' => true,  
  288. 'rewrite' => array( 'slug' => 'project-tag' ),  
  289. ) ); 
  290.  
  291. /** 
  292. * Update messages for the Portfolio admin. 
  293. */ 
  294. function updated_messages( $messages ) { 
  295. global $post; 
  296.  
  297. $messages[self::CUSTOM_POST_TYPE] = array( 
  298. 0 => '', // Unused. Messages start at index 1. 
  299. 1 => sprintf( __( 'Project updated. <a href="%s">View item</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),  
  300. 2 => esc_html__( 'Custom field updated.', 'jetpack' ),  
  301. 3 => esc_html__( 'Custom field deleted.', 'jetpack' ),  
  302. 4 => esc_html__( 'Project updated.', 'jetpack' ),  
  303. /** translators: %s: date and time of the revision */ 
  304. 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Project restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  305. 6 => sprintf( __( 'Project published. <a href="%s">View project</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),  
  306. 7 => esc_html__( 'Project saved.', 'jetpack' ),  
  307. 8 => sprintf( __( 'Project submitted. <a target="_blank" href="%s">Preview project</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),  
  308. 9 => sprintf( __( 'Project scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview project</a>', 'jetpack' ),  
  309. // translators: Publish box date format, see http://php.net/date 
  310. date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),  
  311. 10 => sprintf( __( 'Project item draft updated. <a target="_blank" href="%s">Preview project</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),  
  312. ); 
  313.  
  314. return $messages; 
  315.  
  316. /** 
  317. * Change *Title* column label 
  318. * Add Featured Image column 
  319. */ 
  320. function edit_admin_columns( $columns ) { 
  321. // change 'Title' to 'Project' 
  322. $columns['title'] = __( 'Project', 'jetpack' ); 
  323. if ( current_theme_supports( 'post-thumbnails' ) ) { 
  324. // add featured image before 'Project' 
  325. $columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true ); 
  326.  
  327. return $columns; 
  328.  
  329. /** 
  330. * Add featured image to column 
  331. */ 
  332. function image_column( $column, $post_id ) { 
  333. global $post; 
  334. switch ( $column ) { 
  335. case 'thumbnail': 
  336. echo get_the_post_thumbnail( $post_id, 'jetpack-portfolio-admin-thumb' ); 
  337. break; 
  338.  
  339. /** 
  340. * Adjust image column width 
  341. */ 
  342. function enqueue_admin_styles( $hook ) { 
  343. $screen = get_current_screen(); 
  344.  
  345. if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) { 
  346. wp_add_inline_style( 'wp-admin', '.manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' ); 
  347.  
  348. /** 
  349. * Follow CPT reading setting on CPT archive and taxonomy pages 
  350. */ 
  351. function query_reading_setting( $query ) { 
  352. if ( ! is_admin() && 
  353. $query->is_main_query() && 
  354. ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TYPE ) || $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) ) 
  355. ) { 
  356. $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, '10' ) ); 
  357.  
  358. /** 
  359. * Our [portfolio] shortcode. 
  360. * Prints Portfolio data styled to look good on *any* theme. 
  361. * 
  362. * @return portfolio_shortcode_html 
  363. */ 
  364. static function portfolio_shortcode( $atts ) { 
  365. // Default attributes 
  366. $atts = shortcode_atts( array( 
  367. 'display_types' => true,  
  368. 'display_tags' => true,  
  369. 'display_content' => true,  
  370. 'show_filter' => false,  
  371. 'include_type' => false,  
  372. 'include_tag' => false,  
  373. 'columns' => 2,  
  374. 'showposts' => -1,  
  375. 'order' => 'asc',  
  376. 'orderby' => 'date',  
  377. ), $atts, 'portfolio' ); 
  378.  
  379. // A little sanitization 
  380. if ( $atts['display_types'] && 'true' != $atts['display_types'] ) { 
  381. $atts['display_types'] = false; 
  382.  
  383. if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) { 
  384. $atts['display_tags'] = false; 
  385.  
  386. if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) { 
  387. $atts['display_content'] = false; 
  388.  
  389. if ( $atts['include_type'] ) { 
  390. $atts['include_type'] = explode( ', ', str_replace( ' ', '', $atts['include_type'] ) ); 
  391.  
  392. if ( $atts['include_tag'] ) { 
  393. $atts['include_tag'] = explode( ', ', str_replace( ' ', '', $atts['include_tag'] ) ); 
  394.  
  395. $atts['columns'] = absint( $atts['columns'] ); 
  396.  
  397. $atts['showposts'] = intval( $atts['showposts'] ); 
  398.  
  399.  
  400. if ( $atts['order'] ) { 
  401. $atts['order'] = urldecode( $atts['order'] ); 
  402. $atts['order'] = strtoupper( $atts['order'] ); 
  403. if ( 'DESC' != $atts['order'] ) { 
  404. $atts['order'] = 'ASC'; 
  405.  
  406. if ( $atts['orderby'] ) { 
  407. $atts['orderby'] = urldecode( $atts['orderby'] ); 
  408. $atts['orderby'] = strtolower( $atts['orderby'] ); 
  409. $allowed_keys = array( 'author', 'date', 'title', 'rand' ); 
  410.  
  411. $parsed = array(); 
  412. foreach ( explode( ', ', $atts['orderby'] ) as $portfolio_index_number => $orderby ) { 
  413. if ( ! in_array( $orderby, $allowed_keys ) ) { 
  414. continue; 
  415. $parsed[] = $orderby; 
  416.  
  417. if ( empty( $parsed ) ) { 
  418. unset( $atts['orderby'] ); 
  419. } else { 
  420. $atts['orderby'] = implode( ' ', $parsed ); 
  421.  
  422. // enqueue shortcode styles when shortcode is used 
  423. wp_enqueue_style( 'jetpack-portfolio-style', plugins_url( 'css/portfolio-shortcode.css', __FILE__ ), array(), '20140326' ); 
  424.  
  425. return self::portfolio_shortcode_html( $atts ); 
  426.  
  427. /** 
  428. * Query to retrieve entries from the Portfolio post_type. 
  429. * 
  430. * @return object 
  431. */ 
  432. static function portfolio_query( $atts ) { 
  433. // Default query arguments 
  434. $default = array( 
  435. 'order' => $atts['order'],  
  436. 'orderby' => $atts['orderby'],  
  437. 'posts_per_page' => $atts['showposts'],  
  438. ); 
  439.  
  440. $args = wp_parse_args( $atts, $default ); 
  441. $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type 
  442.  
  443. if ( false != $atts['include_type'] || false != $atts['include_tag'] ) { 
  444. $args['tax_query'] = array(); 
  445.  
  446. // If 'include_type' has been set use it on the main query 
  447. if ( false != $atts['include_type'] ) { 
  448. array_push( $args['tax_query'], array( 
  449. 'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,  
  450. 'field' => 'slug',  
  451. 'terms' => $atts['include_type'],  
  452. ) ); 
  453.  
  454. // If 'include_tag' has been set use it on the main query 
  455. if ( false != $atts['include_tag'] ) { 
  456. array_push( $args['tax_query'], array( 
  457. 'taxonomy' => self::CUSTOM_TAXONOMY_TAG,  
  458. 'field' => 'slug',  
  459. 'terms' => $atts['include_tag'],  
  460. ) ); 
  461.  
  462. if ( false != $atts['include_type'] && false != $atts['include_tag'] ) { 
  463. $args['tax_query']['relation'] = 'AND'; 
  464.  
  465. // Run the query and return 
  466. $query = new WP_Query( $args ); 
  467. return $query; 
  468.  
  469. /** 
  470. * The Portfolio shortcode loop. 
  471. * 
  472. * @todo add theme color styles 
  473. * @return html 
  474. */ 
  475. static function portfolio_shortcode_html( $atts ) { 
  476.  
  477. $query = self::portfolio_query( $atts ); 
  478. $portfolio_index_number = 0; 
  479.  
  480. ob_start(); 
  481.  
  482. // If we have posts, create the html 
  483. // with hportfolio markup 
  484. if ( $query->have_posts() ) { 
  485.  
  486. // Render styles 
  487. //self::themecolor_styles(); 
  488.  
  489. ?> 
  490. <div class="jetpack-portfolio-shortcode column-<?php echo esc_attr( $atts['columns'] ); ?>"> 
  491. <?php // open .jetpack-portfolio 
  492.  
  493. // Construct the loop... 
  494. while ( $query->have_posts() ) { 
  495. $query->the_post(); 
  496. $post_id = get_the_ID(); 
  497. ?> 
  498. <div class="portfolio-entry <?php echo esc_attr( self::get_project_class( $portfolio_index_number, $atts['columns'] ) ); ?>"> 
  499. <header class="portfolio-entry-header"> 
  500. <?php 
  501. // Featured image 
  502. echo self::get_portfolio_thumbnail_link( $post_id ); 
  503. ?> 
  504.  
  505. <h2 class="portfolio-entry-title"><a href="<?php echo esc_url( get_permalink() ); ?>" title="<?php echo esc_attr( the_title_attribute( ) ); ?>"><?php the_title(); ?></a></h2> 
  506.  
  507. <div class="portfolio-entry-meta"> 
  508. <?php 
  509. if ( false != $atts['display_types'] ) { 
  510. echo self::get_project_type( $post_id ); 
  511.  
  512. if ( false != $atts['display_tags'] ) { 
  513. echo self::get_project_tags( $post_id ); 
  514. ?> 
  515. </div> 
  516.  
  517. </header> 
  518.  
  519. <?php 
  520. // The content 
  521. if ( false !== $atts['display_content'] ) { 
  522. if ( 'full' === $atts['display_content'] ) { 
  523. echo '<div class="portfolio-entry-content">' . the_content() . '</div>'; 
  524. } else { 
  525. echo '<div class="portfolio-entry-content">' . the_excerpt() . '</div>'; 
  526. } ?> 
  527. </div><!-- close .portfolio-entry --> 
  528. <?php $portfolio_index_number++; 
  529. } // end of while loop 
  530.  
  531. wp_reset_postdata(); 
  532. ?> 
  533. </div><!-- close .jetpack-portfolio --> 
  534. <?php 
  535. } else { ?> 
  536. <p><em><?php _e( 'Your Portfolio Archive currently has no entries. You can start creating them on your dashboard.', 'jetpack' ); ?></p></em> 
  537. <?php 
  538. $html = ob_get_clean(); 
  539.  
  540. // If there is a [portfolio] within a [portfolio], remove the shortcode 
  541. if ( has_shortcode( $html, 'portfolio' ) ) { 
  542. remove_shortcode( 'portfolio' ); 
  543.  
  544. // Return the HTML block 
  545. return $html; 
  546.  
  547. /** 
  548. * Individual project class 
  549. * 
  550. * @return string 
  551. */ 
  552. static function get_project_class( $portfolio_index_number, $columns ) { 
  553. $project_types = wp_get_object_terms( get_the_ID(), self::CUSTOM_TAXONOMY_TYPE, array( 'fields' => 'slugs' ) ); 
  554. $class = array(); 
  555.  
  556. $class[] = 'portfolio-entry-column-'.$columns; 
  557. // add a type- class for each project type 
  558. foreach ( $project_types as $project_type ) { 
  559. $class[] = 'type-' . esc_html( $project_type ); 
  560. if( $columns > 1) { 
  561. if ( ( $portfolio_index_number % 2 ) == 0 ) { 
  562. $class[] = 'portfolio-entry-mobile-first-item-row'; 
  563. } else { 
  564. $class[] = 'portfolio-entry-mobile-last-item-row'; 
  565.  
  566. // add first and last classes to first and last items in a row 
  567. if ( ( $portfolio_index_number % $columns ) == 0 ) { 
  568. $class[] = 'portfolio-entry-first-item-row'; 
  569. } elseif ( ( $portfolio_index_number % $columns ) == ( $columns - 1 ) ) { 
  570. $class[] = 'portfolio-entry-last-item-row'; 
  571.  
  572.  
  573. /** 
  574. * Filter the class applied to project div in the portfolio 
  575. * 
  576. * @module custom-content-types 
  577. * 
  578. * @since 3.1.0 
  579. * 
  580. * @param string $class class name of the div. 
  581. * @param int $portfolio_index_number iterator count the number of columns up starting from 0. 
  582. * @param int $columns number of columns to display the content in. 
  583. * 
  584. */ 
  585. return apply_filters( 'portfolio-project-post-class', implode( " ", $class ) , $portfolio_index_number, $columns ); 
  586.  
  587. /** 
  588. * Displays the project type that a project belongs to. 
  589. * 
  590. * @return html 
  591. */ 
  592. static function get_project_type( $post_id ) { 
  593. $project_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE ); 
  594.  
  595. // If no types, return empty string 
  596. if ( empty( $project_types ) || is_wp_error( $project_types ) ) { 
  597. return; 
  598.  
  599. $html = '<div class="project-types"><span>' . __( 'Types', 'jetpack' ) . ':</span>'; 
  600. $types = array(); 
  601. // Loop thorugh all the types 
  602. foreach ( $project_types as $project_type ) { 
  603. $project_type_link = get_term_link( $project_type, self::CUSTOM_TAXONOMY_TYPE ); 
  604.  
  605. if ( is_wp_error( $project_type_link ) ) { 
  606. return $project_type_link; 
  607.  
  608. $types[] = '<a href="' . esc_url( $project_type_link ) . '" rel="tag">' . esc_html( $project_type->name ) . '</a>'; 
  609. $html .= ' '.implode( ', ', $types ); 
  610. $html .= '</div>'; 
  611.  
  612. return $html; 
  613.  
  614. /** 
  615. * Displays the project tags that a project belongs to. 
  616. * 
  617. * @return html 
  618. */ 
  619. static function get_project_tags( $post_id ) { 
  620. $project_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG ); 
  621.  
  622. // If no tags, return empty string 
  623. if ( empty( $project_tags ) || is_wp_error( $project_tags ) ) { 
  624. return false; 
  625.  
  626. $html = '<div class="project-tags"><span>' . __( 'Tags', 'jetpack' ) . ':</span>'; 
  627. $tags = array(); 
  628. // Loop thorugh all the tags 
  629. foreach ( $project_tags as $project_tag ) { 
  630. $project_tag_link = get_term_link( $project_tag, self::CUSTOM_TAXONOMY_TYPE ); 
  631.  
  632. if ( is_wp_error( $project_tag_link ) ) { 
  633. return $project_tag_link; 
  634.  
  635. $tags[] = '<a href="' . esc_url( $project_tag_link ) . '" rel="tag">' . esc_html( $project_tag->name ) . '</a>'; 
  636. $html .= ' '. implode( ', ', $tags ); 
  637. $html .= '</div>'; 
  638.  
  639. return $html; 
  640.  
  641. /** 
  642. * Display the featured image if it's available 
  643. * 
  644. * @return html 
  645. */ 
  646. static function get_portfolio_thumbnail_link( $post_id ) { 
  647. if ( has_post_thumbnail( $post_id ) ) { 
  648. /** 
  649. * Change the Portfolio thumbnail size. 
  650. * 
  651. * @module custom-content-types 
  652. * 
  653. * @since 3.4.0 
  654. * 
  655. * @param string|array $var Either a registered size keyword or size array. 
  656. */ 
  657. return '<a class="portfolio-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'jetpack_portfolio_thumbnail_size', 'large' ) ) . '</a>'; 
  658.  
  659. add_action( 'init', array( 'Jetpack_Portfolio', 'init' ) ); 
  660.  
  661. // Check on plugin activation if theme supports CPT 
  662. register_activation_hook( __FILE__, array( 'Jetpack_Portfolio', 'activation_post_type_support' ) ); 
  663. add_action( 'jetpack_activate_module_custom-content-types', array( 'Jetpack_Portfolio', 'activation_post_type_support' ) ); 
.