Changeset 2714132
- Timestamp:
- 04/25/2022 03:50:06 AM (4 years ago)
- Location:
- bw-portfolio
- Files:
-
- 4 deleted
- 9 edited
- 4 copied
-
tags/1.2.0 (copied) (copied from bw-portfolio/trunk)
-
tags/1.2.0/assets/css/bw-portfolio-style.css (modified) (12 diffs)
-
tags/1.2.0/assets/js/bw-portfolio-script.js (modified) (8 diffs)
-
tags/1.2.0/bw-portfolio.php (copied) (copied from bw-portfolio/trunk/bw-portfolio.php) (4 diffs)
-
tags/1.2.0/inc/portfolio-filtered-loop.php (deleted)
-
tags/1.2.0/inc/portfolio-item-full.php (deleted)
-
tags/1.2.0/inc/portfolio-loop.php (copied) (copied from bw-portfolio/trunk/inc/portfolio-loop.php) (4 diffs)
-
tags/1.2.0/inc/portfolio-query.php (modified) (1 diff)
-
tags/1.2.0/readme.txt (copied) (copied from bw-portfolio/trunk/readme.txt) (2 diffs)
-
trunk/assets/css/bw-portfolio-style.css (modified) (12 diffs)
-
trunk/assets/js/bw-portfolio-script.js (modified) (8 diffs)
-
trunk/bw-portfolio.php (modified) (4 diffs)
-
trunk/inc/portfolio-filtered-loop.php (deleted)
-
trunk/inc/portfolio-item-full.php (deleted)
-
trunk/inc/portfolio-loop.php (modified) (4 diffs)
-
trunk/inc/portfolio-query.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
bw-portfolio/tags/1.2.0/assets/css/bw-portfolio-style.css
r2668990 r2714132 1 1 /** 2 * BW Portfolio Stylesheet2 * BW Portfolio Plugin Stylesheet 3 3 */ 4 4 … … 12 12 13 13 section.portfolio_filter_tags { 14 display: flex; 15 margin: 0 0 1vh; 16 } 17 section.portfolio_filter_tags > a.portfolio_filter_tag { 18 padding: 0.5em 1em; 19 text-decoration: none; 20 background: #efefef; 21 border: 1px solid #ccc; 22 border-radius: 50px; 23 margin: 0 0.5vw 0 0; 24 color: #444; 25 } 26 section.portfolio_filter_tags > a.portfolio_filter_tag:focus { 14 display: flex; 15 margin: 0.5em 0 1em; 16 } 17 18 section.portfolio_filter_tags>a.portfolio_filter_tag { 19 margin: 0 0.5vw 0 0; 20 padding: 0.5em 1em; 21 font-size: 0.8em; 22 text-decoration: none; 23 background: #efefef; 24 border: 1px solid #ccc; 25 border-radius: 5px; 26 color: #444; 27 } 28 29 section.portfolio_filter_tags>a.portfolio_filter_tag:focus { 27 30 border: none; 28 31 outline: none; 29 } 30 section.portfolio_filter_tags > a.portfolio_filter_tag.bwbh_active_tag { 32 } 33 section.portfolio_filter_tags>a.portfolio_filter_tag:hover { 34 background: #fff; 35 } 36 37 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag, 38 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:link, 39 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:visited, 40 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:focus, 41 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:active { 31 42 background: #02b; 32 43 color: #fff; 33 34 } 35 36 .bw_portfolio_heading { 37 38 } 39 40 .bw_portfolio_content { 44 } 45 46 .bw_portfolio_heading {} 47 48 .bw_portfolio_content_area { 41 49 display: grid; 42 50 grid-gap: 1rem; 43 51 align-items: top; 44 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /* fit as many 300px columns as possible in */ 52 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); 53 /* fit as many 300px columns as possible in */ 45 54 grid-auto-rows: minmax(min-content, auto); 46 55 } … … 49 58 * one column layout 50 59 */ 51 .bw_portfolio_content.col-1-layout { 52 grid-template-columns: repeat(1, 1fr); /* repeat column once */ 53 } 60 .bw_portfolio_content_area.col-1-layout { 61 grid-template-columns: repeat(1, 1fr); 62 /* repeat column once */ 63 } 64 54 65 /** 55 66 * two column layout 56 67 */ 57 .bw_portfolio_content.col-2-layout { 58 grid-template-columns: repeat(2, 1fr); /* repeat twice */ 59 } 68 .bw_portfolio_content_area.col-2-layout { 69 grid-template-columns: repeat(2, 1fr); 70 /* repeat twice */ 71 } 72 60 73 /** 61 74 * three column layout 62 75 */ 63 .bw_portfolio_content.col-3-layout { 64 grid-template-columns: repeat(3, 1fr); /* repeat three times */ 65 } 76 .bw_portfolio_content_area.col-3-layout { 77 grid-template-columns: repeat(3, 1fr); 78 /* repeat three times */ 79 } 80 66 81 /** 67 82 * four column layout 68 83 */ 69 .bw_portfolio_content.col-4-layout { 70 grid-template-columns: repeat(4, 1fr); /* repeat four times */ 84 .bw_portfolio_content_area.col-4-layout { 85 grid-template-columns: repeat(4, 1fr); 86 /* repeat four times */ 71 87 } 72 88 … … 86 102 } 87 103 88 .bw_portfolio_content > .bw_portfolio_item > a,89 .bw_portfolio_content > .bw_portfolio_item > a:link,90 .bw_portfolio_content > .bw_portfolio_item > a:visited,91 .bw_portfolio_content > .bw_portfolio_item > a:focus,92 .bw_portfolio_content > .bw_portfolio_item >a:hover {93 text-decoration: none;94 color: initial;104 .bw_portfolio_content_area>.bw_portfolio_item>a, 105 .bw_portfolio_content_area>.bw_portfolio_item>a:link, 106 .bw_portfolio_content_area>.bw_portfolio_item>a:visited, 107 .bw_portfolio_content_area>.bw_portfolio_item>a:focus, 108 .bw_portfolio_content_area>.bw_portfolio_item>a:hover { 109 text-decoration: none; 110 color: initial; 95 111 } 96 112 … … 102 118 .bw_portfolio_item_text { 103 119 padding: 1em; 104 } 105 106 .bw_portfolio_item_title { 107 108 } 109 110 .bw_portfolio_item_content { 111 112 } 120 } 121 122 .bw_portfolio_item_title {} 123 124 .bw_portfolio_item_content {} 113 125 114 126 .bw_portfolio_tags { … … 116 128 font-weight: lighter; 117 129 font-style: italic; 118 } 119 120 .bw_portfolio_item > a, .bw_portfolio_item > a:link, .bw_portfolio_item > a:visited, .bw_portfolio_item > a:active { 130 font-size: 0.8em; 131 } 132 133 .bw_portfolio_tags span::after { 134 content: " "; 135 } 136 137 .bw_portfolio_item>a, 138 .bw_portfolio_item>a:link, 139 .bw_portfolio_item>a:visited, 140 .bw_portfolio_item>a:focus, 141 .bw_portfolio_item>a:active { 121 142 display: inline-block; 122 143 height: 100%; 123 144 text-decoration: none; 124 145 } 146 125 147 .bw_portfolio_item a.bw_tag { 126 148 font-size: 0.9em; … … 139 161 justify-content: center; 140 162 } 163 141 164 .bw_portfolio_modal { 142 165 display: none; … … 153 176 } 154 177 155 .bw_portfolio_container .loader, .bw_portfolio_modal .loader { 178 .bw_portfolio_container .loader, 179 .bw_portfolio_modal .loader { 180 display: block; 156 181 position: relative; 157 182 max-width: 100px; … … 161 186 162 187 .bw_portfolio_modal_close { 163 position: absolute; 164 top: 0; 165 right: 0; 166 display: block; 167 padding: 0 0.25em; 188 position: relative; 189 top: -15px; 190 right: -25px; 191 padding: 0; 168 192 margin: 0; 169 font-size: 2.5em;170 193 text-decoration: none; 171 194 line-height: 1; 172 } 195 align-self: flex-end; 196 } 197 173 198 .bw_portfolio_item_full { 174 199 position: relative; 175 display: block; 200 display: flex; 201 flex-direction: column; 176 202 margin: 0 auto; 177 203 padding: 2em; … … 187 213 box-shadow: 4px 4px 6px 0px #222; 188 214 } 215 189 216 .bw_portfolio_item_full h3 { 190 217 margin: 2vh 0 0; … … 194 221 * Media Queries 195 222 */ 196 /* Extra small devices (phones, 600px and down) */223 /* Extra small devices (phones, 600px and down) */ 197 224 @media only screen and (max-width: 600px) { 198 225 .bw_portfolio_item_full { … … 209 236 210 237 /* Medium devices (landscape tablets, 768px and up) */ 211 @media only screen and (min-width: 768px) { 212 213 } 238 @media only screen and (min-width: 768px) {} 214 239 215 240 /* Large devices (laptops, 992px and up) */ 216 @media only screen and (min-width: 992px) { 217 218 } 241 @media only screen and (min-width: 992px) {} 219 242 220 243 /* Even Larger devices (laptops and desktops, 1200px and up) */ -
bw-portfolio/tags/1.2.0/assets/js/bw-portfolio-script.js
r2668990 r2714132 2 2 3 3 if ( document.querySelector('.bw_portfolio_container') !== null ) { 4 5 // helper function that closes/hides/resets the full portfolio item modal 6 function closeModal(bwbh_modal_selector = bwbh_modal_el) { 7 bwbh_modal_selector.style.display = "none"; 8 bwbh_modal_selector.innerHTML = "<img class='loader' src='" + bwbh_portfolio_js_vars.loader_gif_url + "'>"; 9 } 10 11 // create modal 12 const bwbh_modal_el = document.createElement("div"); 13 bwbh_modal_el.classList.add("bw_portfolio_modal"); 14 15 // add loader gif to modal 16 const bwbh_loader_gif = document.createElement('img'); 17 bwbh_loader_gif.setAttribute('class', 'loader'); 18 bwbh_loader_gif.setAttribute('src', bwbh_portfolio_js_vars.loader_gif_url); 19 bwbh_modal_el.appendChild(bwbh_loader_gif); 20 21 22 // append modal to body 23 document.body.appendChild(bwbh_modal_el); 4 24 5 // helper function that closes/hides/resets the full portfolio item modal 6 function closeModal(bw_modal_selector = bw_modal_el) { 7 bw_modal_selector.style.display = "none"; 8 bw_modal_selector.innerHTML = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>"; 9 } 10 11 // flag var and function to prevent multiple clicks of relevant links 25 // flag variable used to prevent multiple clicks of relevant links 12 26 let clicked; 13 27 28 // detecting all clicks on document because dealing with dynamically added elements 14 29 document.addEventListener('click', function(e){ 15 30 16 // prevents multiple clicks, set clicked to false in callback31 // try to prevent multiple clicks, set clicked to false at end of click functionality sections below 17 32 if (clicked) { 18 33 return false; … … 22 37 setTimeout(function(){ 23 38 clicked = false; 24 }, 2000); 25 26 // if clicking on portfolio item, or anyting that has portfolio item as parent, and modal_off attribute is NOT set(adds .modal_off class to .bw_portfolio_content) 39 }, 750); 40 41 42 43 /** 44 * if clicking on portfolio item, or anyting that has portfolio item as parent, and modal_off attribute is NOT set(adds .modal_off class to .bw_portfolio_content_area) 45 */ 27 46 if ( (e.target.classList.contains('bw_portfolio_item') || e.target.closest('.bw_portfolio_item') !== null) && 28 !e.target.closest('.bw_portfolio_content ').classList.contains('modal_off') ) {47 !e.target.closest('.bw_portfolio_content_area').classList.contains('modal_off') ) { 29 48 e.preventDefault(); 30 49 … … 37 56 } 38 57 39 // boolean used to handle showing tags on cards of filtered portfolio items40 let bwbh_show_tags = portfolio_item.closest('.bw_portfolio_content ').classList.contains('show_tags');58 // boolean used to handle showing tags on cards of filtered and full modal view portfolio items 59 let bwbh_show_tags = portfolio_item.closest('.bw_portfolio_content_area').classList.contains('show_tags'); 41 60 42 61 // display modal on portfolio item click 43 bw_modal_el.style.display = "grid"; 44 45 const bwbh_data = new FormData(); 46 bwbh_data.append( 'action', 'bwbh_fetch_portfolio_item' ); 47 bwbh_data.append( 'no_nonce_sense', bw_portfolio_js_vars.nonce ); 48 bwbh_data.append( 'post_id', portfolio_item.dataset.post_id); 49 bwbh_data.append( 'bwbh_show_tags', bwbh_show_tags); 50 51 fetch(bw_portfolio_js_vars.ajax_url, { 52 method: "POST", 62 bwbh_modal_el.style.display = "grid"; 63 64 const bwbh_form_data = new FormData(); 65 bwbh_form_data.append( 'bw_portfolio_item_id', portfolio_item.dataset.post_id); 66 bwbh_form_data.append( 'bw_show_tags', bwbh_show_tags); 67 68 // format form data as plain, then into JSON so fetch can send data as JSON 69 const form_data_json = JSON.stringify( Object.fromEntries( bwbh_form_data.entries() ) ); 70 71 fetch(wpApiSettings.root + 'bw-portfolio/v1/view-portfolio-item', { 72 method: 'POST', 53 73 credentials: 'same-origin', 54 body: bwbh_data 74 headers: { 75 "Content-Type": "application/json", 76 "Accept": "application/json", 77 "X-WP-Nonce": bwbh_portfolio_js_vars.nonce, 78 }, 79 body: form_data_json, 55 80 }) 56 .then((response) => response.text()) 57 .then((data) => { 58 bw_modal_el.innerHTML = data; 81 .then( response => response.json() ) 82 .then( data => { 83 84 // remove loader gif from our modal 85 if( bwbh_modal_el.querySelector('.loader') !== null ) { 86 bwbh_modal_el.querySelector('.loader').remove(); 87 } 88 89 /** 90 * dynamically create full view of portfolio item and then insert into modal 91 */ 92 const bwbh_portfolio_item_full = document.createElement("article"); 93 bwbh_portfolio_item_full.setAttribute('class', 'bw_portfolio_item_full'); 94 95 // add close modal link 96 const bwbh_close_link = document.createElement('a'); 97 bwbh_close_link.setAttribute('href', ''); 98 bwbh_close_link.setAttribute('class', 'bw_portfolio_modal_close dashicons dashicons-dismiss'); 99 bwbh_portfolio_item_full.appendChild(bwbh_close_link); 100 101 // portfolio item featured image 102 const bwbh_feat_image = document.createElement('img'); 103 bwbh_feat_image.classList.add('bw_portfolio_item_full_image'); 104 bwbh_feat_image.setAttribute('src', data.feat_image_url); 105 bwbh_portfolio_item_full.appendChild(bwbh_feat_image); 106 107 // add portfolio item text div for holding title, content, and tags 108 const bwbh_portfolio_text = document.createElement('div'); 109 bwbh_portfolio_text.setAttribute('class', 'bw_portfolio_item_full_text'); 110 111 // add heading for title to text area 112 const bwbh_portfolio_heading = document.createElement('h3'); 113 bwbh_portfolio_heading.setAttribute('class', 'bw_portfolio_item_full_title'); 114 bwbh_portfolio_heading.textContent = data.title; 115 bwbh_portfolio_text.appendChild(bwbh_portfolio_heading); 116 117 // add content div to text area 118 const bwbh_portfolio_item_content = document.createElement('div'); 119 bwbh_portfolio_item_content.setAttribute('class', 'bw_portfolio_item_full_content'); 120 bwbh_portfolio_item_content.innerHTML = data.content; 121 bwbh_portfolio_text.appendChild(bwbh_portfolio_item_content); 122 123 // add portfolio item tags if shortcode attribute show_tags is present or true, and a tag(s) have been sent in json response 124 if( data.bw_show_tags === true && Object.keys(data.bw_portfolio_tags).length > 0 ) { 125 126 const bwbh_portfolio_tags = document.createElement('section'); 127 bwbh_portfolio_tags.setAttribute('class', 'bw_portfolio_tags'); 128 129 const bwbh_span = document.createElement('span'); 130 bwbh_span.innerHTML = "Tags:"; 131 bwbh_portfolio_tags.appendChild(bwbh_span); 132 133 for (let tag in data.bw_portfolio_tags) { 134 const bwbh_portfolio_tag = document.createElement('span'); 135 bwbh_portfolio_tag.innerHTML = data.bw_portfolio_tags[tag]; 136 bwbh_portfolio_tags.append( bwbh_portfolio_tag ); 137 } 138 bwbh_portfolio_text.appendChild(bwbh_portfolio_tags); 139 } 140 141 // add text div with title, content, and tags to full portfolio item 142 bwbh_portfolio_item_full.appendChild(bwbh_portfolio_text); 143 144 // add full portfolio item to modal 145 bwbh_modal_el.appendChild(bwbh_portfolio_item_full); 146 147 // reset clicked flag variable 59 148 clicked = false; 149 60 150 }) 61 151 .catch((error) => { … … 65 155 } 66 156 67 // if clicking on portfolio tag filter link, display portfolio items with that tag 157 158 159 /** 160 * if clicking on portfolio tag filter link, display portfolio items with that tag 161 */ 68 162 if (e.target.classList.contains('portfolio_filter_tag')) { 69 163 e.preventDefault(); … … 74 168 let bwbh_filter_tags = bwbh_portfolio_container.querySelectorAll('.portfolio_filter_tag'); 75 169 let bwbh_portfolio_container_height = bwbh_portfolio_container.clientHeight; 76 let bwbh_portfolio_content = bwbh_portfolio_container.querySelector('.bw_portfolio_content');170 let bwbh_portfolio_content_area = bwbh_portfolio_container.querySelector('.bw_portfolio_content_area'); 77 171 let bwbh_term = bwbh_clicked_tag.dataset.term; 78 let bwbh_num_of_words = bwbh_portfolio_content .dataset.num_of_words;172 let bwbh_num_of_words = bwbh_portfolio_content_area.dataset.num_of_words; 79 173 80 174 // remove .bwbh_active_tag class from any active filter tags … … 91 185 92 186 // create loader gif element 93 let bwbh_loader_gif = document.createElement('img'); // = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>";187 let bwbh_loader_gif = document.createElement('img'); 94 188 bwbh_loader_gif.classList.add('loader'); 95 bwbh_loader_gif.setAttribute('src', bw _portfolio_js_vars.loader_gif_url);189 bwbh_loader_gif.setAttribute('src', bwbh_portfolio_js_vars.loader_gif_url); 96 190 97 191 if (bwbh_portfolio_container !== null && bwbh_portfolio_container_height !== null) { … … 101 195 bwbh_portfolio_container.style.height = bwbh_portfolio_container_height + "px"; 102 196 } 103 197 104 198 // clear the old set of portfolio items 105 if (bwbh_portfolio_content !== null) {106 bwbh_portfolio_content .innerHTML = "";199 if (bwbh_portfolio_content_area !== null) { 200 bwbh_portfolio_content_area.innerHTML = ""; 107 201 } 108 202 109 203 // boolean used to handle showing tags on cards of filtered portfolio items 110 let bwbh_show_tags = bwbh_portfolio_content .classList.contains('show_tags');204 let bwbh_show_tags = bwbh_portfolio_content_area.classList.contains('show_tags'); 111 205 // boolean used to handle whether modal is on or off for filtered portfolio items 112 let bwbh_modal_off = bwbh_portfolio_content.classList.contains('modal_off'); 113 206 let bwbh_modal_off = bwbh_portfolio_content_area.classList.contains('modal_off'); 207 208 // setup form data to send with feth request 114 209 const bwbh_data1 = new FormData(); 115 bwbh_data1.append( 'action', 'bwbh_filter_portfolio_items' );116 bwbh_data1.append( 'no_nonce_sense', bw_portfolio_js_vars.nonce );117 210 bwbh_data1.append( 'bwbh_term', bwbh_term); 118 211 bwbh_data1.append( 'bwbh_show_tags', bwbh_show_tags); 119 212 bwbh_data1.append( 'bwbh_modal_off', bwbh_modal_off); 120 213 bwbh_data1.append( 'bwbh_num_of_words', bwbh_num_of_words); 121 122 fetch(bw_portfolio_js_vars.ajax_url, { 123 method: "POST", 214 215 // format form data as plain, then into JSON so fetch can send data as JSON 216 const form1_data_json = JSON.stringify( Object.fromEntries( bwbh_data1.entries() ) ); 217 218 /** 219 * Make fetch request 220 */ 221 fetch( wpApiSettings.root + 'bw-portfolio/v1/filter-portfolio-items', { 222 method: 'POST', 124 223 credentials: 'same-origin', 125 body: bwbh_data1 224 headers: { 225 "Content-Type": "application/json", 226 "Accept": "application/json", 227 "X-WP-Nonce": bwbh_portfolio_js_vars.nonce, 228 }, 229 body: form1_data_json 126 230 }) 127 .then((response) => response.text()) 128 .then((data) => { 231 .then( response => response.json() ) 232 .then( data => { 233 234 console.log(data); 235 129 236 bwbh_loader_gif.remove(); 130 bwbh_portfolio_content.innerHTML = data; 131 bwbh_portfolio_container.style.height = "auto"; 132 clicked = false; 237 238 for ( let bwbh_portfolio_item_id in data.portfolio_items ) { 239 /** 240 * dynamically create card view of each portfolio item and then insert into portfolio container content area .bw_portfolio_content_area 241 */ 242 const bwbh_portfolio_item_card = document.createElement("article"); 243 bwbh_portfolio_item_card.setAttribute('class', 'bw_portfolio_item'); 244 bwbh_portfolio_item_card.setAttribute('data-post_id', bwbh_portfolio_item_id); 245 bwbh_portfolio_item_card.setAttribute('data-permalink', data.portfolio_items[bwbh_portfolio_item_id].permalink); 246 247 // portfolio item featured image 248 const bwbh_feat_image = document.createElement('img'); 249 bwbh_feat_image.classList.add('bw_portfolio_item_image'); 250 bwbh_feat_image.setAttribute('src', data.portfolio_items[bwbh_portfolio_item_id].feat_image_url); 251 252 // add portfolio item text div for holding title, content, and tags 253 const bwbh_portfolio_text = document.createElement('div'); 254 bwbh_portfolio_text.setAttribute('class', 'bw_portfolio_item_text'); 255 256 // add heading for title 257 const bwbh_portfolio_heading = document.createElement('h3'); 258 bwbh_portfolio_heading.setAttribute('class', 'bw_portfolio_item_title'); 259 bwbh_portfolio_heading.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].title; 260 bwbh_portfolio_text.appendChild(bwbh_portfolio_heading); 261 262 // add content div 263 const bwbh_portfolio_content = document.createElement('div'); 264 bwbh_portfolio_content.setAttribute('class', 'bw_portfolio_item_content'); 265 bwbh_portfolio_content.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].content; 266 bwbh_portfolio_text.appendChild(bwbh_portfolio_content); 267 268 269 // add portfolio item tags if shortcode attribute show_tags is present or true, and a tag(s) have been sent in json response 270 if( data.show_tags === true && Object.keys(data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags).length > 0 ) { 271 272 const bwbh_portfolio_tags = document.createElement('section'); 273 bwbh_portfolio_tags.setAttribute('class', 'bw_portfolio_tags'); 274 275 const bwbh_default_span = document.createElement('span'); 276 bwbh_default_span.innerHTML = "Tags:"; 277 bwbh_portfolio_tags.appendChild(bwbh_default_span); 278 279 for (let tag in data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags) { 280 const bwbh_portfolio_tag = document.createElement('span'); 281 bwbh_portfolio_tag.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags[tag]; 282 bwbh_portfolio_tags.append( bwbh_portfolio_tag ); 283 } 284 bwbh_portfolio_text.appendChild(bwbh_portfolio_tags); 285 } 286 287 // add text div with title, content, and portfolio feaured image to the portfolio item link, or to the portfolio item 288 if( data.modal_off === true ) { 289 const bwbh_regular_link = document.createElement('a'); 290 bwbh_regular_link.setAttribute('href', data.portfolio_items[bwbh_portfolio_item_id].permalink); 291 bwbh_portfolio_item_card.appendChild(bwbh_regular_link); 292 293 bwbh_regular_link.appendChild(bwbh_feat_image); 294 bwbh_regular_link.appendChild(bwbh_portfolio_text); 295 } 296 else { 297 bwbh_portfolio_item_card.appendChild(bwbh_feat_image); 298 bwbh_portfolio_item_card.appendChild(bwbh_portfolio_text); 299 } 300 301 302 // add portfolio item card to portfolio content area 303 bwbh_portfolio_content_area.appendChild(bwbh_portfolio_item_card); 304 } 305 306 // reset clicked flag variable 307 clicked = false; 308 133 309 }) 134 310 .catch((error) => { … … 154 330 } 155 331 }); 156 157 // create modal and append to body158 const bw_modal = document.createElement("div");159 bw_modal.classList.add("bw_portfolio_modal");160 bw_modal.innerHTML = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>";161 document.body.appendChild(bw_modal);162 // select and store modal for later use163 bw_modal_el = document.body.querySelector('.bw_portfolio_modal');164 332 165 333 } -
bw-portfolio/tags/1.2.0/bw-portfolio.php
r2668996 r2714132 3 3 * Plugin Name: BW Portfolio 4 4 * Description: The BW Portfolio plugin is powerful yet lightweight and fast. It allows you to easily add portfolio items in your WordPress Dashboard, and organize them with portfolio tags as well. Then by using a handy shortcode you can display your portfolio items just about anywhere in a nice, responsive css grid that is compatible on many different devices. Has tag filtering of portfolio items built in. 5 * Version: 1. 1.75 * Version: 1.2.0 6 6 * Requires at least: 5.2 7 7 * Requires PHP: 7.0 … … 31 31 * Enqueue Scripts and Stylesheets 32 32 */ 33 add_action( 'wp_enqueue_scripts', 'bwbh_portfolio_scripts_styles' ); 34 function bwbh_portfolio_scripts_styles() { 35 wp_enqueue_style( 'bw-portfolio-style', plugins_url('/assets/css/bw-portfolio-style.css', __FILE__), [], filemtime( plugin_dir_path(__FILE__).'assets/css/bw-portfolio-style.css' ), 'screen' ); 33 add_action( 'wp_enqueue_scripts', function() { 36 34 37 wp_enqueue_script( 'bw-portfolio-script', plugins_url('/assets/js/bw-portfolio-script.js', __FILE__), array(), '1.1.0', true ); 38 wp_localize_script( 'bw-portfolio-script', 'bw_portfolio_js_vars', [ 39 'ajax_url' => admin_url( 'admin-ajax.php' ), 40 'nonce' => wp_create_nonce( 'bw_portfolio_secret_nonce_sauce' ), 41 'loader_gif_url' => esc_url( plugins_url( 'bw-portfolio/assets/images/loading.gif', dirname(__FILE__) ) ), 42 ]); 43 } 44 45 46 /** 47 * handle fetch requests to load portfolio items content in modal 48 */ 49 add_action('wp_ajax_bwbh_fetch_portfolio_item', 'bwbh_fetch_portfolio_item'); 50 add_action('wp_ajax_nopriv_bwbh_fetch_portfolio_item', 'bwbh_fetch_portfolio_item'); 51 function bwbh_fetch_portfolio_item() { 52 53 check_ajax_referer( 'bw_portfolio_secret_nonce_sauce', 'no_nonce_sense' ); 54 55 56 if($_POST['post_id']) { 57 $bw_post_id = (int)$_POST['post_id']; 58 $bw_post = get_post($bw_post_id); 59 60 // need to get the shortcode attribute settings that might affect full portfolio item view here 61 $bwbh_show_tags = filter_var($_POST['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN); 62 63 $bw_output = include plugin_dir_path( __FILE__ ) . 'inc/portfolio-item-full.php'; 64 65 echo wp_kses_post($bw_output); 35 if( !is_admin() ) { 36 wp_enqueue_style( 'bw-portfolio-style', plugins_url('/assets/css/bw-portfolio-style.css', __FILE__), ['dashicons'], filemtime( plugin_dir_path(__FILE__).'assets/css/bw-portfolio-style.css' ), 'screen' ); 37 wp_enqueue_script( 'bw-portfolio-script', plugins_url('/assets/js/bw-portfolio-script.js', __FILE__), ['wp-api-request'], '1.2.0', true ); 38 39 wp_localize_script( 'bw-portfolio-script', 'bwbh_portfolio_js_vars', [ 40 'ajax_url' => admin_url( 'admin-ajax.php' ), 41 'nonce' => wp_create_nonce( 'wp_rest' ), 42 'loader_gif_url' => esc_url( plugins_url( 'bw-portfolio/assets/images/loading.gif', dirname(__FILE__) ) ), 43 ]); 44 } 45 46 }); 47 48 49 50 add_action('rest_api_init', function () { 51 52 // custom route to view full portfolio item 53 register_rest_route('bw-portfolio/v1', '/view-portfolio-item', [ 54 'methods' => 'POST', 55 'callback' => 'bw_view_portfolio_item', 56 'permission_callback' => '__return_true', 57 ]); 58 59 // custom route to filter portfolio items 60 register_rest_route('bw-portfolio/v1', '/filter-portfolio-items', [ 61 'methods' => 'POST', 62 'callback' => 'bw_filter_portfolio_items', 63 'permission_callback' => '__return_true', 64 ]); 65 }); 66 67 /** 68 * handle fetch requests to load full view of portfolio item in modal via rest api 69 */ 70 function bw_view_portfolio_item( $request ) { 71 // if $request includes the portfolio item id... 72 if( $request['bw_portfolio_item_id'] ) { 73 74 $bw_portfolio_item_id = (int)$request['bw_portfolio_item_id']; 75 $bw_post = get_post($bw_portfolio_item_id, OBJECT, 'display'); 76 77 // if portfolio item was found... 78 if($bw_post !== null) { 79 80 // store the needed post data to send back in rest response 81 $bw_response_data = [ 82 'title' => esc_html__($bw_post->post_title), 83 'content' => apply_filters('the_content', $bw_post->post_content ), 84 ]; 85 86 // get and add tags to $bw_post_data 87 if( $bw_show_tags !== false ) { 88 $bw_portfolio_tags = get_the_terms( $bw_post->ID, 'portfolio_tag' ); 89 if( !is_wp_error($bw_portfolio_tags) && $bw_portfolio_tags !== false ) { 90 foreach( $bw_portfolio_tags as $bw_tag ) { 91 $bw_response_data['bw_portfolio_tags'][$bw_tag->term_id] = $bw_tag->name; 92 } 93 } 94 } 95 96 // add featured image url to array 97 $post_thumbnail_id = get_post_thumbnail_id( $bw_post->ID ); 98 if(!empty($post_thumbnail_id)) { 99 $feat_image_urls = wp_get_attachment_image_src( $post_thumbnail_id, 'full' ); 100 $bw_response_data['feat_image_url'] = $feat_image_urls[0]; 101 } 102 103 // store the shortcode attribute settings that affect full portfolio item view here 104 $bw_show_tags = filter_var($request['bw_show_tags'], FILTER_VALIDATE_BOOLEAN); 105 $bw_response_data['bw_show_tags'] = $bw_show_tags; 106 107 return new WP_REST_Response( $bw_response_data, 200 ); 108 } 109 else { 110 // Return a WP_Error because the request product was not found. In this case we return a 404 because the main resource was not found. 111 return new WP_Error( 'rest_portfolio_item_invalid', esc_html__( 'The portfolio item does not exist.', 'bw_portfolio' ), array( 'status' => 404 ) ); 112 } 113 } 114 } 115 116 117 /** 118 * handle fetch requests to filter portfolio items by portfolio tag 119 */ 120 function bw_filter_portfolio_items( $request ) { 121 122 if( $request['bwbh_term'] ) { 123 124 $bwbh_term = $request['bwbh_term']; 125 126 // need to get the shortcode attribute settings that might affect filtered results here 127 $bwbh_show_tags = filter_var($request['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN); 128 $bwbh_modal_off = filter_var($request['bwbh_modal_off'], FILTER_VALIDATE_BOOLEAN); 129 $bwbh_num_of_words = (int)$request['bwbh_num_of_words']; 130 131 $bw_response_data = [ 132 'term' => $bwbh_term, 133 'show_tags' => $bwbh_show_tags, 134 'modal_off' => $bwbh_modal_off, 135 'num_of_words' => $bwbh_num_of_words, 136 ]; 137 138 // include file that builds custom WP_Query for filtered portfolio items 139 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-query.php'; 140 141 // loop through query results, and only add relevant pieces of portfolio item data to the $bw_response_data array 142 if ( $bw_portfolio_query->have_posts() ) : 143 while ( $bw_portfolio_query->have_posts() ) : $bw_portfolio_query->the_post(); 144 $bw_response_data['portfolio_items'][get_the_ID()] = [ 145 'title' => get_the_title(), 146 'content' => wp_kses_post( bwbh_limit_words( strip_tags( get_the_content() ), $bwbh_num_of_words ) ) . '...', 147 ]; 148 149 // if modal_off is true, then send permalink for opening portfolio item manually 150 if( $bwbh_modal_off ) { 151 $bw_response_data['portfolio_items'][get_the_ID()]['permalink'] = get_permalink( get_the_ID() ); 152 } 153 154 // get and add portfolio item tags to $bw_response_data if show_tags shortcode attribute was true 155 if( $bwbh_show_tags !== false ) { 156 $bw_portfolio_tags = get_the_terms( get_the_ID(), 'portfolio_tag' ); 157 if( !is_wp_error($bw_portfolio_tags) && $bw_portfolio_tags !== false ) { 158 foreach($bw_portfolio_tags as $bw_tag) { 159 $bw_response_data['portfolio_items'][get_the_ID()]['bw_portfolio_tags'][$bw_tag->term_id] = $bw_tag->name; 160 } 161 } 162 } 163 164 // add featured image url to array if it exists 165 $post_thumbnail_id = get_post_thumbnail_id( $bw_post->ID ); 166 if(!empty($post_thumbnail_id)) { 167 $feat_image_urls = wp_get_attachment_image_src( $post_thumbnail_id, 'full' ); 168 $bw_response_data['portfolio_items'][get_the_ID()]['feat_image_url'] = $feat_image_urls[0]; 169 } 170 171 172 endwhile; 173 174 wp_reset_postdata(); 175 176 endif; // else if no portfolio items in tag are found then return error?(don't think this can happen though?) 177 178 return new WP_REST_Response( $bw_response_data, 200 ); 179 66 180 } 67 181 … … 69 183 } 70 184 71 72 /**73 * handle fetch requests to filter portfolio items by portfolio tag74 */75 add_action('wp_ajax_bwbh_filter_portfolio_items', 'bwbh_filter_portfolio_items');76 add_action('wp_ajax_nopriv_bwbh_filter_portfolio_items', 'bwbh_filter_portfolio_items');77 function bwbh_filter_portfolio_items() {78 79 check_ajax_referer( 'bw_portfolio_secret_nonce_sauce', 'no_nonce_sense' );80 81 if($_POST['bwbh_term']) {82 83 $bwbh_term = $_POST['bwbh_term'];84 85 // need to get the shortcode attribute settings that might affect filtered results here86 $bwbh_show_tags = filter_var($_POST['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN);87 $bwbh_modal_off = filter_var($_POST['bwbh_modal_off'], FILTER_VALIDATE_BOOLEAN);88 $bwbh_num_of_words = (int)$_POST['bwbh_num_of_words'];89 90 // include file that builds custom WP_Query for filtered portfolio items91 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-query.php';92 93 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-filtered-loop.php';94 95 }96 97 wp_die();98 }99 100 185 /** 101 186 * Register bw_portfolio post type 102 187 * Register portfolio_tag taxonomy 103 188 */ 104 add_action( 'init', 'bwbh_portfolio_cpt_init' ); 105 function bwbh_portfolio_cpt_init() { 189 add_action( 'init', function() { 106 190 // 107 191 register_post_type('bw_portfolio', … … 151 235 unset( $bw_tax_labels ); 152 236 unset( $bw_tax_args ); 153 } 154 155 /** 156 * Helper functions for the [bw_portfolio] Shortcode 157 */ 237 }); 238 239 /** 240 * Helper functions for the [bw_portfolio] Shortcode attributes 241 */ 242 243 // num_of_words_on_cards shortcode attribute 158 244 if (!function_exists('bwbh_limit_words')) { 159 245 function bwbh_limit_words($string, $word_limit) { 160 246 $words = explode(" ", trim($string)); 161 return implode(" ", array_splice($words, 0, $word_limit));247 return implode(" ", array_splice($words, 0, $word_limit)); 162 248 } 163 249 } 164 250 251 // shortcode attributes with no value set are normally sent through as "indexed key => attribute name" pairs, where normal shortcode attributes with a value set are sent as "attribute name => att value" 252 // a number of the shortcode attributes I have added are simply flag variables, so to make it easier for users they can simply enter the attribute name, instead of attribute name=true 165 253 if (!function_exists('bwbh_normalize_empty_atts')) { 166 254 function bwbh_normalize_empty_atts($atts) { 167 255 foreach ($atts as $attribute => $value) { 168 // empty value atts are sent through as "indexed key => attribute name" pairs, where normal atts with value set are sent as "attribute name => att value" 169 // so, if key is an integer then that was an attribute set with no value, and we want to unset that, and add attr name into the atts array with value true 256 // if $attribute/key is an integer then that was an attribute set with no value, and we want to unset that, and re-add the attr name into the $atts array with boolean value true 170 257 if (is_int($attribute)) { 171 258 $atts[strtolower($value)] = true; -
bw-portfolio/tags/1.2.0/inc/portfolio-loop.php
r2668996 r2714132 6 6 if ( $bw_portfolio_query->have_posts() ) : 7 7 8 $portfolio_output .= "<section class='bw_portfolio_container '>\n";8 $portfolio_output .= "<section class='bw_portfolio_container alignwide'>\n"; 9 9 10 10 $portfolio_output .= "<header class='bw_portfolio_header'>\n"; … … 13 13 $portfolio_output .= "<h2 class='bw_portfolio_heading'>" . esc_html__( $bw_atts['portfolio_title'] ) . "</h2>\n"; 14 14 } 15 // portfolio tag filter links 15 /** 16 * portfolio tag filter links 17 */ 16 18 $bw_portfolio_tags = get_terms([ 17 19 'taxonomy' => 'portfolio_tag', 18 //'hide_empty' => true, // default is already true20 'orderby' => 'id' 19 21 ]); 20 22 if( !is_wp_error($bw_portfolio_tags) && count($bw_portfolio_tags) > 0 ) { 21 23 22 24 $portfolio_output .= "<section class='portfolio_filter_tags'>\n"; 23 24 $portfolio_output .= "<a href='#' class='portfolio_filter_tag' data-term='show_all'>Show All</a>\n";25 25 26 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='show_all' title='Show all portfolio items'>Show All</a>\n"; 27 26 28 foreach($bw_portfolio_tags as $bw_tag) { 27 $portfolio_output .= "<a href='#' class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "'>" . $bw_tag->name . "</a>\n"; 29 if( isset($bw_tags_array) ) { 30 if( in_array($bw_tag->slug, $bw_tags_array) ) { 31 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "' title='Show portfolio items tagged with " . $bw_tag->name . "'>" . $bw_tag->name . "</a>\n"; 32 } 33 } 34 else { 35 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "' title='Show portfolio items tagged with " . $bw_tag->name . "'>" . $bw_tag->name . "</a>\n"; 36 } 37 28 38 } 29 39 … … 76 86 $data_num_of_words = "data-num_of_words='" . $num_of_words . "'"; 77 87 78 $portfolio_output .= "<div class='bw_portfolio_content " . $bw_content_classes . "' " . $data_num_of_words . ">\n";88 $portfolio_output .= "<div class='bw_portfolio_content_area" . $bw_content_classes . "' " . $data_num_of_words . ">\n"; 79 89 80 90 while ( $bw_portfolio_query->have_posts() ) : $bw_portfolio_query->the_post(); 81 91 // add portfolio item html to output variable 82 $portfolio_output .= "<article class='bw_portfolio_item' data-post_id='" . get_the_ID() . "' >\n";92 $portfolio_output .= "<article class='bw_portfolio_item' data-post_id='" . get_the_ID() . "' data-permalink='" . get_the_permalink() . "' >\n"; 83 93 84 94 // if user adds modal_off attribute, then add html anchor link to handle normal anchor link click to the full portfolio item being displayed by theme template … … 87 97 } 88 98 89 $portfolio_output .= "<img src='" . esc_url( get_the_post_thumbnail_url(get_the_ID(), ' post-thumbnail') ) . "' class='bw_portfolio_item_image'>";99 $portfolio_output .= "<img src='" . esc_url( get_the_post_thumbnail_url(get_the_ID(), 'large') ) . "' class='bw_portfolio_item_image'>"; 90 100 91 101 $portfolio_output .= "<div class='bw_portfolio_item_text'>\n"; -
bw-portfolio/tags/1.2.0/inc/portfolio-query.php
r2668990 r2714132 16 16 $bw_tags_array = explode(',', $bw_tags_str); 17 17 // for each array value make spaces between words into dashes, and make words lowercase 18 array_map('sanitize_title_with_dashes', $bw_tags_array);18 $bw_tags_array = array_map('sanitize_title_with_dashes', $bw_tags_array); 19 19 // trim whitespace off ends of tags 20 array_map('trim', $bw_tags_array);20 $bw_tags_array = array_map('trim', $bw_tags_array); 21 21 22 22 // if user clicked on portfolio tag filter link, empty array of filter terms from shortcode, and insert only the term user clicked on 23 23 if(!empty($bwbh_term)) { 24 24 $bw_tags_array = []; 25 $bw_tags_array[] = strip_tags( $bwbh_term);25 $bw_tags_array[] = strip_tags(trim($bwbh_term)); 26 26 } 27 27 -
bw-portfolio/tags/1.2.0/readme.txt
r2668996 r2714132 6 6 Tested up to: 5.9 7 7 Requires PHP: 7.0 8 Stable tag: 1. 1.78 Stable tag: 1.2.0 9 9 License: GPLv3 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 33 33 34 34 == Changelog == 35 36 = 1.2.0 = 37 * Major update where I stopped using admin-ajax and switched over to using the WordPress REST API for viewing a portfolio item, and filtering portfolio items by portfolio tag 38 * added .alignwide class to portfolio container so wordpress themes(like twentytwentone, etc) default styling allows it to be full width of its parent 39 * fixed some minor display bugs 35 40 36 41 = 1.1.7 = -
bw-portfolio/trunk/assets/css/bw-portfolio-style.css
r2668990 r2714132 1 1 /** 2 * BW Portfolio Stylesheet2 * BW Portfolio Plugin Stylesheet 3 3 */ 4 4 … … 12 12 13 13 section.portfolio_filter_tags { 14 display: flex; 15 margin: 0 0 1vh; 16 } 17 section.portfolio_filter_tags > a.portfolio_filter_tag { 18 padding: 0.5em 1em; 19 text-decoration: none; 20 background: #efefef; 21 border: 1px solid #ccc; 22 border-radius: 50px; 23 margin: 0 0.5vw 0 0; 24 color: #444; 25 } 26 section.portfolio_filter_tags > a.portfolio_filter_tag:focus { 14 display: flex; 15 margin: 0.5em 0 1em; 16 } 17 18 section.portfolio_filter_tags>a.portfolio_filter_tag { 19 margin: 0 0.5vw 0 0; 20 padding: 0.5em 1em; 21 font-size: 0.8em; 22 text-decoration: none; 23 background: #efefef; 24 border: 1px solid #ccc; 25 border-radius: 5px; 26 color: #444; 27 } 28 29 section.portfolio_filter_tags>a.portfolio_filter_tag:focus { 27 30 border: none; 28 31 outline: none; 29 } 30 section.portfolio_filter_tags > a.portfolio_filter_tag.bwbh_active_tag { 32 } 33 section.portfolio_filter_tags>a.portfolio_filter_tag:hover { 34 background: #fff; 35 } 36 37 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag, 38 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:link, 39 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:visited, 40 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:focus, 41 section.portfolio_filter_tags>a.portfolio_filter_tag.bwbh_active_tag:active { 31 42 background: #02b; 32 43 color: #fff; 33 34 } 35 36 .bw_portfolio_heading { 37 38 } 39 40 .bw_portfolio_content { 44 } 45 46 .bw_portfolio_heading {} 47 48 .bw_portfolio_content_area { 41 49 display: grid; 42 50 grid-gap: 1rem; 43 51 align-items: top; 44 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /* fit as many 300px columns as possible in */ 52 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); 53 /* fit as many 300px columns as possible in */ 45 54 grid-auto-rows: minmax(min-content, auto); 46 55 } … … 49 58 * one column layout 50 59 */ 51 .bw_portfolio_content.col-1-layout { 52 grid-template-columns: repeat(1, 1fr); /* repeat column once */ 53 } 60 .bw_portfolio_content_area.col-1-layout { 61 grid-template-columns: repeat(1, 1fr); 62 /* repeat column once */ 63 } 64 54 65 /** 55 66 * two column layout 56 67 */ 57 .bw_portfolio_content.col-2-layout { 58 grid-template-columns: repeat(2, 1fr); /* repeat twice */ 59 } 68 .bw_portfolio_content_area.col-2-layout { 69 grid-template-columns: repeat(2, 1fr); 70 /* repeat twice */ 71 } 72 60 73 /** 61 74 * three column layout 62 75 */ 63 .bw_portfolio_content.col-3-layout { 64 grid-template-columns: repeat(3, 1fr); /* repeat three times */ 65 } 76 .bw_portfolio_content_area.col-3-layout { 77 grid-template-columns: repeat(3, 1fr); 78 /* repeat three times */ 79 } 80 66 81 /** 67 82 * four column layout 68 83 */ 69 .bw_portfolio_content.col-4-layout { 70 grid-template-columns: repeat(4, 1fr); /* repeat four times */ 84 .bw_portfolio_content_area.col-4-layout { 85 grid-template-columns: repeat(4, 1fr); 86 /* repeat four times */ 71 87 } 72 88 … … 86 102 } 87 103 88 .bw_portfolio_content > .bw_portfolio_item > a,89 .bw_portfolio_content > .bw_portfolio_item > a:link,90 .bw_portfolio_content > .bw_portfolio_item > a:visited,91 .bw_portfolio_content > .bw_portfolio_item > a:focus,92 .bw_portfolio_content > .bw_portfolio_item >a:hover {93 text-decoration: none;94 color: initial;104 .bw_portfolio_content_area>.bw_portfolio_item>a, 105 .bw_portfolio_content_area>.bw_portfolio_item>a:link, 106 .bw_portfolio_content_area>.bw_portfolio_item>a:visited, 107 .bw_portfolio_content_area>.bw_portfolio_item>a:focus, 108 .bw_portfolio_content_area>.bw_portfolio_item>a:hover { 109 text-decoration: none; 110 color: initial; 95 111 } 96 112 … … 102 118 .bw_portfolio_item_text { 103 119 padding: 1em; 104 } 105 106 .bw_portfolio_item_title { 107 108 } 109 110 .bw_portfolio_item_content { 111 112 } 120 } 121 122 .bw_portfolio_item_title {} 123 124 .bw_portfolio_item_content {} 113 125 114 126 .bw_portfolio_tags { … … 116 128 font-weight: lighter; 117 129 font-style: italic; 118 } 119 120 .bw_portfolio_item > a, .bw_portfolio_item > a:link, .bw_portfolio_item > a:visited, .bw_portfolio_item > a:active { 130 font-size: 0.8em; 131 } 132 133 .bw_portfolio_tags span::after { 134 content: " "; 135 } 136 137 .bw_portfolio_item>a, 138 .bw_portfolio_item>a:link, 139 .bw_portfolio_item>a:visited, 140 .bw_portfolio_item>a:focus, 141 .bw_portfolio_item>a:active { 121 142 display: inline-block; 122 143 height: 100%; 123 144 text-decoration: none; 124 145 } 146 125 147 .bw_portfolio_item a.bw_tag { 126 148 font-size: 0.9em; … … 139 161 justify-content: center; 140 162 } 163 141 164 .bw_portfolio_modal { 142 165 display: none; … … 153 176 } 154 177 155 .bw_portfolio_container .loader, .bw_portfolio_modal .loader { 178 .bw_portfolio_container .loader, 179 .bw_portfolio_modal .loader { 180 display: block; 156 181 position: relative; 157 182 max-width: 100px; … … 161 186 162 187 .bw_portfolio_modal_close { 163 position: absolute; 164 top: 0; 165 right: 0; 166 display: block; 167 padding: 0 0.25em; 188 position: relative; 189 top: -15px; 190 right: -25px; 191 padding: 0; 168 192 margin: 0; 169 font-size: 2.5em;170 193 text-decoration: none; 171 194 line-height: 1; 172 } 195 align-self: flex-end; 196 } 197 173 198 .bw_portfolio_item_full { 174 199 position: relative; 175 display: block; 200 display: flex; 201 flex-direction: column; 176 202 margin: 0 auto; 177 203 padding: 2em; … … 187 213 box-shadow: 4px 4px 6px 0px #222; 188 214 } 215 189 216 .bw_portfolio_item_full h3 { 190 217 margin: 2vh 0 0; … … 194 221 * Media Queries 195 222 */ 196 /* Extra small devices (phones, 600px and down) */223 /* Extra small devices (phones, 600px and down) */ 197 224 @media only screen and (max-width: 600px) { 198 225 .bw_portfolio_item_full { … … 209 236 210 237 /* Medium devices (landscape tablets, 768px and up) */ 211 @media only screen and (min-width: 768px) { 212 213 } 238 @media only screen and (min-width: 768px) {} 214 239 215 240 /* Large devices (laptops, 992px and up) */ 216 @media only screen and (min-width: 992px) { 217 218 } 241 @media only screen and (min-width: 992px) {} 219 242 220 243 /* Even Larger devices (laptops and desktops, 1200px and up) */ -
bw-portfolio/trunk/assets/js/bw-portfolio-script.js
r2668990 r2714132 2 2 3 3 if ( document.querySelector('.bw_portfolio_container') !== null ) { 4 5 // helper function that closes/hides/resets the full portfolio item modal 6 function closeModal(bwbh_modal_selector = bwbh_modal_el) { 7 bwbh_modal_selector.style.display = "none"; 8 bwbh_modal_selector.innerHTML = "<img class='loader' src='" + bwbh_portfolio_js_vars.loader_gif_url + "'>"; 9 } 10 11 // create modal 12 const bwbh_modal_el = document.createElement("div"); 13 bwbh_modal_el.classList.add("bw_portfolio_modal"); 14 15 // add loader gif to modal 16 const bwbh_loader_gif = document.createElement('img'); 17 bwbh_loader_gif.setAttribute('class', 'loader'); 18 bwbh_loader_gif.setAttribute('src', bwbh_portfolio_js_vars.loader_gif_url); 19 bwbh_modal_el.appendChild(bwbh_loader_gif); 20 21 22 // append modal to body 23 document.body.appendChild(bwbh_modal_el); 4 24 5 // helper function that closes/hides/resets the full portfolio item modal 6 function closeModal(bw_modal_selector = bw_modal_el) { 7 bw_modal_selector.style.display = "none"; 8 bw_modal_selector.innerHTML = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>"; 9 } 10 11 // flag var and function to prevent multiple clicks of relevant links 25 // flag variable used to prevent multiple clicks of relevant links 12 26 let clicked; 13 27 28 // detecting all clicks on document because dealing with dynamically added elements 14 29 document.addEventListener('click', function(e){ 15 30 16 // prevents multiple clicks, set clicked to false in callback31 // try to prevent multiple clicks, set clicked to false at end of click functionality sections below 17 32 if (clicked) { 18 33 return false; … … 22 37 setTimeout(function(){ 23 38 clicked = false; 24 }, 2000); 25 26 // if clicking on portfolio item, or anyting that has portfolio item as parent, and modal_off attribute is NOT set(adds .modal_off class to .bw_portfolio_content) 39 }, 750); 40 41 42 43 /** 44 * if clicking on portfolio item, or anyting that has portfolio item as parent, and modal_off attribute is NOT set(adds .modal_off class to .bw_portfolio_content_area) 45 */ 27 46 if ( (e.target.classList.contains('bw_portfolio_item') || e.target.closest('.bw_portfolio_item') !== null) && 28 !e.target.closest('.bw_portfolio_content ').classList.contains('modal_off') ) {47 !e.target.closest('.bw_portfolio_content_area').classList.contains('modal_off') ) { 29 48 e.preventDefault(); 30 49 … … 37 56 } 38 57 39 // boolean used to handle showing tags on cards of filtered portfolio items40 let bwbh_show_tags = portfolio_item.closest('.bw_portfolio_content ').classList.contains('show_tags');58 // boolean used to handle showing tags on cards of filtered and full modal view portfolio items 59 let bwbh_show_tags = portfolio_item.closest('.bw_portfolio_content_area').classList.contains('show_tags'); 41 60 42 61 // display modal on portfolio item click 43 bw_modal_el.style.display = "grid"; 44 45 const bwbh_data = new FormData(); 46 bwbh_data.append( 'action', 'bwbh_fetch_portfolio_item' ); 47 bwbh_data.append( 'no_nonce_sense', bw_portfolio_js_vars.nonce ); 48 bwbh_data.append( 'post_id', portfolio_item.dataset.post_id); 49 bwbh_data.append( 'bwbh_show_tags', bwbh_show_tags); 50 51 fetch(bw_portfolio_js_vars.ajax_url, { 52 method: "POST", 62 bwbh_modal_el.style.display = "grid"; 63 64 const bwbh_form_data = new FormData(); 65 bwbh_form_data.append( 'bw_portfolio_item_id', portfolio_item.dataset.post_id); 66 bwbh_form_data.append( 'bw_show_tags', bwbh_show_tags); 67 68 // format form data as plain, then into JSON so fetch can send data as JSON 69 const form_data_json = JSON.stringify( Object.fromEntries( bwbh_form_data.entries() ) ); 70 71 fetch(wpApiSettings.root + 'bw-portfolio/v1/view-portfolio-item', { 72 method: 'POST', 53 73 credentials: 'same-origin', 54 body: bwbh_data 74 headers: { 75 "Content-Type": "application/json", 76 "Accept": "application/json", 77 "X-WP-Nonce": bwbh_portfolio_js_vars.nonce, 78 }, 79 body: form_data_json, 55 80 }) 56 .then((response) => response.text()) 57 .then((data) => { 58 bw_modal_el.innerHTML = data; 81 .then( response => response.json() ) 82 .then( data => { 83 84 // remove loader gif from our modal 85 if( bwbh_modal_el.querySelector('.loader') !== null ) { 86 bwbh_modal_el.querySelector('.loader').remove(); 87 } 88 89 /** 90 * dynamically create full view of portfolio item and then insert into modal 91 */ 92 const bwbh_portfolio_item_full = document.createElement("article"); 93 bwbh_portfolio_item_full.setAttribute('class', 'bw_portfolio_item_full'); 94 95 // add close modal link 96 const bwbh_close_link = document.createElement('a'); 97 bwbh_close_link.setAttribute('href', ''); 98 bwbh_close_link.setAttribute('class', 'bw_portfolio_modal_close dashicons dashicons-dismiss'); 99 bwbh_portfolio_item_full.appendChild(bwbh_close_link); 100 101 // portfolio item featured image 102 const bwbh_feat_image = document.createElement('img'); 103 bwbh_feat_image.classList.add('bw_portfolio_item_full_image'); 104 bwbh_feat_image.setAttribute('src', data.feat_image_url); 105 bwbh_portfolio_item_full.appendChild(bwbh_feat_image); 106 107 // add portfolio item text div for holding title, content, and tags 108 const bwbh_portfolio_text = document.createElement('div'); 109 bwbh_portfolio_text.setAttribute('class', 'bw_portfolio_item_full_text'); 110 111 // add heading for title to text area 112 const bwbh_portfolio_heading = document.createElement('h3'); 113 bwbh_portfolio_heading.setAttribute('class', 'bw_portfolio_item_full_title'); 114 bwbh_portfolio_heading.textContent = data.title; 115 bwbh_portfolio_text.appendChild(bwbh_portfolio_heading); 116 117 // add content div to text area 118 const bwbh_portfolio_item_content = document.createElement('div'); 119 bwbh_portfolio_item_content.setAttribute('class', 'bw_portfolio_item_full_content'); 120 bwbh_portfolio_item_content.innerHTML = data.content; 121 bwbh_portfolio_text.appendChild(bwbh_portfolio_item_content); 122 123 // add portfolio item tags if shortcode attribute show_tags is present or true, and a tag(s) have been sent in json response 124 if( data.bw_show_tags === true && Object.keys(data.bw_portfolio_tags).length > 0 ) { 125 126 const bwbh_portfolio_tags = document.createElement('section'); 127 bwbh_portfolio_tags.setAttribute('class', 'bw_portfolio_tags'); 128 129 const bwbh_span = document.createElement('span'); 130 bwbh_span.innerHTML = "Tags:"; 131 bwbh_portfolio_tags.appendChild(bwbh_span); 132 133 for (let tag in data.bw_portfolio_tags) { 134 const bwbh_portfolio_tag = document.createElement('span'); 135 bwbh_portfolio_tag.innerHTML = data.bw_portfolio_tags[tag]; 136 bwbh_portfolio_tags.append( bwbh_portfolio_tag ); 137 } 138 bwbh_portfolio_text.appendChild(bwbh_portfolio_tags); 139 } 140 141 // add text div with title, content, and tags to full portfolio item 142 bwbh_portfolio_item_full.appendChild(bwbh_portfolio_text); 143 144 // add full portfolio item to modal 145 bwbh_modal_el.appendChild(bwbh_portfolio_item_full); 146 147 // reset clicked flag variable 59 148 clicked = false; 149 60 150 }) 61 151 .catch((error) => { … … 65 155 } 66 156 67 // if clicking on portfolio tag filter link, display portfolio items with that tag 157 158 159 /** 160 * if clicking on portfolio tag filter link, display portfolio items with that tag 161 */ 68 162 if (e.target.classList.contains('portfolio_filter_tag')) { 69 163 e.preventDefault(); … … 74 168 let bwbh_filter_tags = bwbh_portfolio_container.querySelectorAll('.portfolio_filter_tag'); 75 169 let bwbh_portfolio_container_height = bwbh_portfolio_container.clientHeight; 76 let bwbh_portfolio_content = bwbh_portfolio_container.querySelector('.bw_portfolio_content');170 let bwbh_portfolio_content_area = bwbh_portfolio_container.querySelector('.bw_portfolio_content_area'); 77 171 let bwbh_term = bwbh_clicked_tag.dataset.term; 78 let bwbh_num_of_words = bwbh_portfolio_content .dataset.num_of_words;172 let bwbh_num_of_words = bwbh_portfolio_content_area.dataset.num_of_words; 79 173 80 174 // remove .bwbh_active_tag class from any active filter tags … … 91 185 92 186 // create loader gif element 93 let bwbh_loader_gif = document.createElement('img'); // = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>";187 let bwbh_loader_gif = document.createElement('img'); 94 188 bwbh_loader_gif.classList.add('loader'); 95 bwbh_loader_gif.setAttribute('src', bw _portfolio_js_vars.loader_gif_url);189 bwbh_loader_gif.setAttribute('src', bwbh_portfolio_js_vars.loader_gif_url); 96 190 97 191 if (bwbh_portfolio_container !== null && bwbh_portfolio_container_height !== null) { … … 101 195 bwbh_portfolio_container.style.height = bwbh_portfolio_container_height + "px"; 102 196 } 103 197 104 198 // clear the old set of portfolio items 105 if (bwbh_portfolio_content !== null) {106 bwbh_portfolio_content .innerHTML = "";199 if (bwbh_portfolio_content_area !== null) { 200 bwbh_portfolio_content_area.innerHTML = ""; 107 201 } 108 202 109 203 // boolean used to handle showing tags on cards of filtered portfolio items 110 let bwbh_show_tags = bwbh_portfolio_content .classList.contains('show_tags');204 let bwbh_show_tags = bwbh_portfolio_content_area.classList.contains('show_tags'); 111 205 // boolean used to handle whether modal is on or off for filtered portfolio items 112 let bwbh_modal_off = bwbh_portfolio_content.classList.contains('modal_off'); 113 206 let bwbh_modal_off = bwbh_portfolio_content_area.classList.contains('modal_off'); 207 208 // setup form data to send with feth request 114 209 const bwbh_data1 = new FormData(); 115 bwbh_data1.append( 'action', 'bwbh_filter_portfolio_items' );116 bwbh_data1.append( 'no_nonce_sense', bw_portfolio_js_vars.nonce );117 210 bwbh_data1.append( 'bwbh_term', bwbh_term); 118 211 bwbh_data1.append( 'bwbh_show_tags', bwbh_show_tags); 119 212 bwbh_data1.append( 'bwbh_modal_off', bwbh_modal_off); 120 213 bwbh_data1.append( 'bwbh_num_of_words', bwbh_num_of_words); 121 122 fetch(bw_portfolio_js_vars.ajax_url, { 123 method: "POST", 214 215 // format form data as plain, then into JSON so fetch can send data as JSON 216 const form1_data_json = JSON.stringify( Object.fromEntries( bwbh_data1.entries() ) ); 217 218 /** 219 * Make fetch request 220 */ 221 fetch( wpApiSettings.root + 'bw-portfolio/v1/filter-portfolio-items', { 222 method: 'POST', 124 223 credentials: 'same-origin', 125 body: bwbh_data1 224 headers: { 225 "Content-Type": "application/json", 226 "Accept": "application/json", 227 "X-WP-Nonce": bwbh_portfolio_js_vars.nonce, 228 }, 229 body: form1_data_json 126 230 }) 127 .then((response) => response.text()) 128 .then((data) => { 231 .then( response => response.json() ) 232 .then( data => { 233 234 console.log(data); 235 129 236 bwbh_loader_gif.remove(); 130 bwbh_portfolio_content.innerHTML = data; 131 bwbh_portfolio_container.style.height = "auto"; 132 clicked = false; 237 238 for ( let bwbh_portfolio_item_id in data.portfolio_items ) { 239 /** 240 * dynamically create card view of each portfolio item and then insert into portfolio container content area .bw_portfolio_content_area 241 */ 242 const bwbh_portfolio_item_card = document.createElement("article"); 243 bwbh_portfolio_item_card.setAttribute('class', 'bw_portfolio_item'); 244 bwbh_portfolio_item_card.setAttribute('data-post_id', bwbh_portfolio_item_id); 245 bwbh_portfolio_item_card.setAttribute('data-permalink', data.portfolio_items[bwbh_portfolio_item_id].permalink); 246 247 // portfolio item featured image 248 const bwbh_feat_image = document.createElement('img'); 249 bwbh_feat_image.classList.add('bw_portfolio_item_image'); 250 bwbh_feat_image.setAttribute('src', data.portfolio_items[bwbh_portfolio_item_id].feat_image_url); 251 252 // add portfolio item text div for holding title, content, and tags 253 const bwbh_portfolio_text = document.createElement('div'); 254 bwbh_portfolio_text.setAttribute('class', 'bw_portfolio_item_text'); 255 256 // add heading for title 257 const bwbh_portfolio_heading = document.createElement('h3'); 258 bwbh_portfolio_heading.setAttribute('class', 'bw_portfolio_item_title'); 259 bwbh_portfolio_heading.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].title; 260 bwbh_portfolio_text.appendChild(bwbh_portfolio_heading); 261 262 // add content div 263 const bwbh_portfolio_content = document.createElement('div'); 264 bwbh_portfolio_content.setAttribute('class', 'bw_portfolio_item_content'); 265 bwbh_portfolio_content.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].content; 266 bwbh_portfolio_text.appendChild(bwbh_portfolio_content); 267 268 269 // add portfolio item tags if shortcode attribute show_tags is present or true, and a tag(s) have been sent in json response 270 if( data.show_tags === true && Object.keys(data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags).length > 0 ) { 271 272 const bwbh_portfolio_tags = document.createElement('section'); 273 bwbh_portfolio_tags.setAttribute('class', 'bw_portfolio_tags'); 274 275 const bwbh_default_span = document.createElement('span'); 276 bwbh_default_span.innerHTML = "Tags:"; 277 bwbh_portfolio_tags.appendChild(bwbh_default_span); 278 279 for (let tag in data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags) { 280 const bwbh_portfolio_tag = document.createElement('span'); 281 bwbh_portfolio_tag.innerHTML = data.portfolio_items[bwbh_portfolio_item_id].bw_portfolio_tags[tag]; 282 bwbh_portfolio_tags.append( bwbh_portfolio_tag ); 283 } 284 bwbh_portfolio_text.appendChild(bwbh_portfolio_tags); 285 } 286 287 // add text div with title, content, and portfolio feaured image to the portfolio item link, or to the portfolio item 288 if( data.modal_off === true ) { 289 const bwbh_regular_link = document.createElement('a'); 290 bwbh_regular_link.setAttribute('href', data.portfolio_items[bwbh_portfolio_item_id].permalink); 291 bwbh_portfolio_item_card.appendChild(bwbh_regular_link); 292 293 bwbh_regular_link.appendChild(bwbh_feat_image); 294 bwbh_regular_link.appendChild(bwbh_portfolio_text); 295 } 296 else { 297 bwbh_portfolio_item_card.appendChild(bwbh_feat_image); 298 bwbh_portfolio_item_card.appendChild(bwbh_portfolio_text); 299 } 300 301 302 // add portfolio item card to portfolio content area 303 bwbh_portfolio_content_area.appendChild(bwbh_portfolio_item_card); 304 } 305 306 // reset clicked flag variable 307 clicked = false; 308 133 309 }) 134 310 .catch((error) => { … … 154 330 } 155 331 }); 156 157 // create modal and append to body158 const bw_modal = document.createElement("div");159 bw_modal.classList.add("bw_portfolio_modal");160 bw_modal.innerHTML = "<img class='loader' src='" + bw_portfolio_js_vars.loader_gif_url + "'>";161 document.body.appendChild(bw_modal);162 // select and store modal for later use163 bw_modal_el = document.body.querySelector('.bw_portfolio_modal');164 332 165 333 } -
bw-portfolio/trunk/bw-portfolio.php
r2668996 r2714132 3 3 * Plugin Name: BW Portfolio 4 4 * Description: The BW Portfolio plugin is powerful yet lightweight and fast. It allows you to easily add portfolio items in your WordPress Dashboard, and organize them with portfolio tags as well. Then by using a handy shortcode you can display your portfolio items just about anywhere in a nice, responsive css grid that is compatible on many different devices. Has tag filtering of portfolio items built in. 5 * Version: 1. 1.75 * Version: 1.2.0 6 6 * Requires at least: 5.2 7 7 * Requires PHP: 7.0 … … 31 31 * Enqueue Scripts and Stylesheets 32 32 */ 33 add_action( 'wp_enqueue_scripts', 'bwbh_portfolio_scripts_styles' ); 34 function bwbh_portfolio_scripts_styles() { 35 wp_enqueue_style( 'bw-portfolio-style', plugins_url('/assets/css/bw-portfolio-style.css', __FILE__), [], filemtime( plugin_dir_path(__FILE__).'assets/css/bw-portfolio-style.css' ), 'screen' ); 33 add_action( 'wp_enqueue_scripts', function() { 36 34 37 wp_enqueue_script( 'bw-portfolio-script', plugins_url('/assets/js/bw-portfolio-script.js', __FILE__), array(), '1.1.0', true ); 38 wp_localize_script( 'bw-portfolio-script', 'bw_portfolio_js_vars', [ 39 'ajax_url' => admin_url( 'admin-ajax.php' ), 40 'nonce' => wp_create_nonce( 'bw_portfolio_secret_nonce_sauce' ), 41 'loader_gif_url' => esc_url( plugins_url( 'bw-portfolio/assets/images/loading.gif', dirname(__FILE__) ) ), 42 ]); 43 } 44 45 46 /** 47 * handle fetch requests to load portfolio items content in modal 48 */ 49 add_action('wp_ajax_bwbh_fetch_portfolio_item', 'bwbh_fetch_portfolio_item'); 50 add_action('wp_ajax_nopriv_bwbh_fetch_portfolio_item', 'bwbh_fetch_portfolio_item'); 51 function bwbh_fetch_portfolio_item() { 52 53 check_ajax_referer( 'bw_portfolio_secret_nonce_sauce', 'no_nonce_sense' ); 54 55 56 if($_POST['post_id']) { 57 $bw_post_id = (int)$_POST['post_id']; 58 $bw_post = get_post($bw_post_id); 59 60 // need to get the shortcode attribute settings that might affect full portfolio item view here 61 $bwbh_show_tags = filter_var($_POST['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN); 62 63 $bw_output = include plugin_dir_path( __FILE__ ) . 'inc/portfolio-item-full.php'; 64 65 echo wp_kses_post($bw_output); 35 if( !is_admin() ) { 36 wp_enqueue_style( 'bw-portfolio-style', plugins_url('/assets/css/bw-portfolio-style.css', __FILE__), ['dashicons'], filemtime( plugin_dir_path(__FILE__).'assets/css/bw-portfolio-style.css' ), 'screen' ); 37 wp_enqueue_script( 'bw-portfolio-script', plugins_url('/assets/js/bw-portfolio-script.js', __FILE__), ['wp-api-request'], '1.2.0', true ); 38 39 wp_localize_script( 'bw-portfolio-script', 'bwbh_portfolio_js_vars', [ 40 'ajax_url' => admin_url( 'admin-ajax.php' ), 41 'nonce' => wp_create_nonce( 'wp_rest' ), 42 'loader_gif_url' => esc_url( plugins_url( 'bw-portfolio/assets/images/loading.gif', dirname(__FILE__) ) ), 43 ]); 44 } 45 46 }); 47 48 49 50 add_action('rest_api_init', function () { 51 52 // custom route to view full portfolio item 53 register_rest_route('bw-portfolio/v1', '/view-portfolio-item', [ 54 'methods' => 'POST', 55 'callback' => 'bw_view_portfolio_item', 56 'permission_callback' => '__return_true', 57 ]); 58 59 // custom route to filter portfolio items 60 register_rest_route('bw-portfolio/v1', '/filter-portfolio-items', [ 61 'methods' => 'POST', 62 'callback' => 'bw_filter_portfolio_items', 63 'permission_callback' => '__return_true', 64 ]); 65 }); 66 67 /** 68 * handle fetch requests to load full view of portfolio item in modal via rest api 69 */ 70 function bw_view_portfolio_item( $request ) { 71 // if $request includes the portfolio item id... 72 if( $request['bw_portfolio_item_id'] ) { 73 74 $bw_portfolio_item_id = (int)$request['bw_portfolio_item_id']; 75 $bw_post = get_post($bw_portfolio_item_id, OBJECT, 'display'); 76 77 // if portfolio item was found... 78 if($bw_post !== null) { 79 80 // store the needed post data to send back in rest response 81 $bw_response_data = [ 82 'title' => esc_html__($bw_post->post_title), 83 'content' => apply_filters('the_content', $bw_post->post_content ), 84 ]; 85 86 // get and add tags to $bw_post_data 87 if( $bw_show_tags !== false ) { 88 $bw_portfolio_tags = get_the_terms( $bw_post->ID, 'portfolio_tag' ); 89 if( !is_wp_error($bw_portfolio_tags) && $bw_portfolio_tags !== false ) { 90 foreach( $bw_portfolio_tags as $bw_tag ) { 91 $bw_response_data['bw_portfolio_tags'][$bw_tag->term_id] = $bw_tag->name; 92 } 93 } 94 } 95 96 // add featured image url to array 97 $post_thumbnail_id = get_post_thumbnail_id( $bw_post->ID ); 98 if(!empty($post_thumbnail_id)) { 99 $feat_image_urls = wp_get_attachment_image_src( $post_thumbnail_id, 'full' ); 100 $bw_response_data['feat_image_url'] = $feat_image_urls[0]; 101 } 102 103 // store the shortcode attribute settings that affect full portfolio item view here 104 $bw_show_tags = filter_var($request['bw_show_tags'], FILTER_VALIDATE_BOOLEAN); 105 $bw_response_data['bw_show_tags'] = $bw_show_tags; 106 107 return new WP_REST_Response( $bw_response_data, 200 ); 108 } 109 else { 110 // Return a WP_Error because the request product was not found. In this case we return a 404 because the main resource was not found. 111 return new WP_Error( 'rest_portfolio_item_invalid', esc_html__( 'The portfolio item does not exist.', 'bw_portfolio' ), array( 'status' => 404 ) ); 112 } 113 } 114 } 115 116 117 /** 118 * handle fetch requests to filter portfolio items by portfolio tag 119 */ 120 function bw_filter_portfolio_items( $request ) { 121 122 if( $request['bwbh_term'] ) { 123 124 $bwbh_term = $request['bwbh_term']; 125 126 // need to get the shortcode attribute settings that might affect filtered results here 127 $bwbh_show_tags = filter_var($request['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN); 128 $bwbh_modal_off = filter_var($request['bwbh_modal_off'], FILTER_VALIDATE_BOOLEAN); 129 $bwbh_num_of_words = (int)$request['bwbh_num_of_words']; 130 131 $bw_response_data = [ 132 'term' => $bwbh_term, 133 'show_tags' => $bwbh_show_tags, 134 'modal_off' => $bwbh_modal_off, 135 'num_of_words' => $bwbh_num_of_words, 136 ]; 137 138 // include file that builds custom WP_Query for filtered portfolio items 139 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-query.php'; 140 141 // loop through query results, and only add relevant pieces of portfolio item data to the $bw_response_data array 142 if ( $bw_portfolio_query->have_posts() ) : 143 while ( $bw_portfolio_query->have_posts() ) : $bw_portfolio_query->the_post(); 144 $bw_response_data['portfolio_items'][get_the_ID()] = [ 145 'title' => get_the_title(), 146 'content' => wp_kses_post( bwbh_limit_words( strip_tags( get_the_content() ), $bwbh_num_of_words ) ) . '...', 147 ]; 148 149 // if modal_off is true, then send permalink for opening portfolio item manually 150 if( $bwbh_modal_off ) { 151 $bw_response_data['portfolio_items'][get_the_ID()]['permalink'] = get_permalink( get_the_ID() ); 152 } 153 154 // get and add portfolio item tags to $bw_response_data if show_tags shortcode attribute was true 155 if( $bwbh_show_tags !== false ) { 156 $bw_portfolio_tags = get_the_terms( get_the_ID(), 'portfolio_tag' ); 157 if( !is_wp_error($bw_portfolio_tags) && $bw_portfolio_tags !== false ) { 158 foreach($bw_portfolio_tags as $bw_tag) { 159 $bw_response_data['portfolio_items'][get_the_ID()]['bw_portfolio_tags'][$bw_tag->term_id] = $bw_tag->name; 160 } 161 } 162 } 163 164 // add featured image url to array if it exists 165 $post_thumbnail_id = get_post_thumbnail_id( $bw_post->ID ); 166 if(!empty($post_thumbnail_id)) { 167 $feat_image_urls = wp_get_attachment_image_src( $post_thumbnail_id, 'full' ); 168 $bw_response_data['portfolio_items'][get_the_ID()]['feat_image_url'] = $feat_image_urls[0]; 169 } 170 171 172 endwhile; 173 174 wp_reset_postdata(); 175 176 endif; // else if no portfolio items in tag are found then return error?(don't think this can happen though?) 177 178 return new WP_REST_Response( $bw_response_data, 200 ); 179 66 180 } 67 181 … … 69 183 } 70 184 71 72 /**73 * handle fetch requests to filter portfolio items by portfolio tag74 */75 add_action('wp_ajax_bwbh_filter_portfolio_items', 'bwbh_filter_portfolio_items');76 add_action('wp_ajax_nopriv_bwbh_filter_portfolio_items', 'bwbh_filter_portfolio_items');77 function bwbh_filter_portfolio_items() {78 79 check_ajax_referer( 'bw_portfolio_secret_nonce_sauce', 'no_nonce_sense' );80 81 if($_POST['bwbh_term']) {82 83 $bwbh_term = $_POST['bwbh_term'];84 85 // need to get the shortcode attribute settings that might affect filtered results here86 $bwbh_show_tags = filter_var($_POST['bwbh_show_tags'], FILTER_VALIDATE_BOOLEAN);87 $bwbh_modal_off = filter_var($_POST['bwbh_modal_off'], FILTER_VALIDATE_BOOLEAN);88 $bwbh_num_of_words = (int)$_POST['bwbh_num_of_words'];89 90 // include file that builds custom WP_Query for filtered portfolio items91 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-query.php';92 93 include plugin_dir_path( __FILE__ ) . 'inc/portfolio-filtered-loop.php';94 95 }96 97 wp_die();98 }99 100 185 /** 101 186 * Register bw_portfolio post type 102 187 * Register portfolio_tag taxonomy 103 188 */ 104 add_action( 'init', 'bwbh_portfolio_cpt_init' ); 105 function bwbh_portfolio_cpt_init() { 189 add_action( 'init', function() { 106 190 // 107 191 register_post_type('bw_portfolio', … … 151 235 unset( $bw_tax_labels ); 152 236 unset( $bw_tax_args ); 153 } 154 155 /** 156 * Helper functions for the [bw_portfolio] Shortcode 157 */ 237 }); 238 239 /** 240 * Helper functions for the [bw_portfolio] Shortcode attributes 241 */ 242 243 // num_of_words_on_cards shortcode attribute 158 244 if (!function_exists('bwbh_limit_words')) { 159 245 function bwbh_limit_words($string, $word_limit) { 160 246 $words = explode(" ", trim($string)); 161 return implode(" ", array_splice($words, 0, $word_limit));247 return implode(" ", array_splice($words, 0, $word_limit)); 162 248 } 163 249 } 164 250 251 // shortcode attributes with no value set are normally sent through as "indexed key => attribute name" pairs, where normal shortcode attributes with a value set are sent as "attribute name => att value" 252 // a number of the shortcode attributes I have added are simply flag variables, so to make it easier for users they can simply enter the attribute name, instead of attribute name=true 165 253 if (!function_exists('bwbh_normalize_empty_atts')) { 166 254 function bwbh_normalize_empty_atts($atts) { 167 255 foreach ($atts as $attribute => $value) { 168 // empty value atts are sent through as "indexed key => attribute name" pairs, where normal atts with value set are sent as "attribute name => att value" 169 // so, if key is an integer then that was an attribute set with no value, and we want to unset that, and add attr name into the atts array with value true 256 // if $attribute/key is an integer then that was an attribute set with no value, and we want to unset that, and re-add the attr name into the $atts array with boolean value true 170 257 if (is_int($attribute)) { 171 258 $atts[strtolower($value)] = true; -
bw-portfolio/trunk/inc/portfolio-loop.php
r2668996 r2714132 6 6 if ( $bw_portfolio_query->have_posts() ) : 7 7 8 $portfolio_output .= "<section class='bw_portfolio_container '>\n";8 $portfolio_output .= "<section class='bw_portfolio_container alignwide'>\n"; 9 9 10 10 $portfolio_output .= "<header class='bw_portfolio_header'>\n"; … … 13 13 $portfolio_output .= "<h2 class='bw_portfolio_heading'>" . esc_html__( $bw_atts['portfolio_title'] ) . "</h2>\n"; 14 14 } 15 // portfolio tag filter links 15 /** 16 * portfolio tag filter links 17 */ 16 18 $bw_portfolio_tags = get_terms([ 17 19 'taxonomy' => 'portfolio_tag', 18 //'hide_empty' => true, // default is already true20 'orderby' => 'id' 19 21 ]); 20 22 if( !is_wp_error($bw_portfolio_tags) && count($bw_portfolio_tags) > 0 ) { 21 23 22 24 $portfolio_output .= "<section class='portfolio_filter_tags'>\n"; 23 24 $portfolio_output .= "<a href='#' class='portfolio_filter_tag' data-term='show_all'>Show All</a>\n";25 25 26 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='show_all' title='Show all portfolio items'>Show All</a>\n"; 27 26 28 foreach($bw_portfolio_tags as $bw_tag) { 27 $portfolio_output .= "<a href='#' class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "'>" . $bw_tag->name . "</a>\n"; 29 if( isset($bw_tags_array) ) { 30 if( in_array($bw_tag->slug, $bw_tags_array) ) { 31 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "' title='Show portfolio items tagged with " . $bw_tag->name . "'>" . $bw_tag->name . "</a>\n"; 32 } 33 } 34 else { 35 $portfolio_output .= "<a class='portfolio_filter_tag' data-term='" . $bw_tag->slug . "' title='Show portfolio items tagged with " . $bw_tag->name . "'>" . $bw_tag->name . "</a>\n"; 36 } 37 28 38 } 29 39 … … 76 86 $data_num_of_words = "data-num_of_words='" . $num_of_words . "'"; 77 87 78 $portfolio_output .= "<div class='bw_portfolio_content " . $bw_content_classes . "' " . $data_num_of_words . ">\n";88 $portfolio_output .= "<div class='bw_portfolio_content_area" . $bw_content_classes . "' " . $data_num_of_words . ">\n"; 79 89 80 90 while ( $bw_portfolio_query->have_posts() ) : $bw_portfolio_query->the_post(); 81 91 // add portfolio item html to output variable 82 $portfolio_output .= "<article class='bw_portfolio_item' data-post_id='" . get_the_ID() . "' >\n";92 $portfolio_output .= "<article class='bw_portfolio_item' data-post_id='" . get_the_ID() . "' data-permalink='" . get_the_permalink() . "' >\n"; 83 93 84 94 // if user adds modal_off attribute, then add html anchor link to handle normal anchor link click to the full portfolio item being displayed by theme template … … 87 97 } 88 98 89 $portfolio_output .= "<img src='" . esc_url( get_the_post_thumbnail_url(get_the_ID(), ' post-thumbnail') ) . "' class='bw_portfolio_item_image'>";99 $portfolio_output .= "<img src='" . esc_url( get_the_post_thumbnail_url(get_the_ID(), 'large') ) . "' class='bw_portfolio_item_image'>"; 90 100 91 101 $portfolio_output .= "<div class='bw_portfolio_item_text'>\n"; -
bw-portfolio/trunk/inc/portfolio-query.php
r2668990 r2714132 16 16 $bw_tags_array = explode(',', $bw_tags_str); 17 17 // for each array value make spaces between words into dashes, and make words lowercase 18 array_map('sanitize_title_with_dashes', $bw_tags_array);18 $bw_tags_array = array_map('sanitize_title_with_dashes', $bw_tags_array); 19 19 // trim whitespace off ends of tags 20 array_map('trim', $bw_tags_array);20 $bw_tags_array = array_map('trim', $bw_tags_array); 21 21 22 22 // if user clicked on portfolio tag filter link, empty array of filter terms from shortcode, and insert only the term user clicked on 23 23 if(!empty($bwbh_term)) { 24 24 $bw_tags_array = []; 25 $bw_tags_array[] = strip_tags( $bwbh_term);25 $bw_tags_array[] = strip_tags(trim($bwbh_term)); 26 26 } 27 27 -
bw-portfolio/trunk/readme.txt
r2668996 r2714132 6 6 Tested up to: 5.9 7 7 Requires PHP: 7.0 8 Stable tag: 1. 1.78 Stable tag: 1.2.0 9 9 License: GPLv3 10 10 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 33 33 34 34 == Changelog == 35 36 = 1.2.0 = 37 * Major update where I stopped using admin-ajax and switched over to using the WordPress REST API for viewing a portfolio item, and filtering portfolio items by portfolio tag 38 * added .alignwide class to portfolio container so wordpress themes(like twentytwentone, etc) default styling allows it to be full width of its parent 39 * fixed some minor display bugs 35 40 36 41 = 1.1.7 =
Note: See TracChangeset
for help on using the changeset viewer.