Changeset 4326 for sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-badge-generator/includes/indesign-badges.php
- Timestamp:
- 11/03/2016 07:14:06 PM (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-badge-generator/includes/indesign-badges.php
r3015 r4326 4 4 use \CampTix\Badge_Generator; 5 5 use \CampTix\Badge_Generator\HTML; 6 use \WordCamp\Logger; 6 7 7 8 defined( 'WPINC' ) or die(); … … 21 22 require_once( dirname( __DIR__ ) . '/views/indesign-badges/page-indesign-badges.php' ); 22 23 } 24 25 /** 26 * Build the badge assets for InDesign 27 */ 28 function build_assets() { 29 try { 30 // Security: assets are intentionally saved to a folder outside the web root. See serve_zip_file() for details. 31 $assets_folder = sprintf( '%scamptix-badges-%d-%d', get_temp_dir(), get_current_blog_id(), time() ); 32 $gravatar_folder = $assets_folder . '/gravatars'; 33 $csv_filename = $assets_folder . '/attendees.csv'; 34 $zip_filename = get_zip_filename( $assets_folder ); 35 $zip_local_folder = pathinfo( $zip_filename, PATHINFO_FILENAME ); 36 $attendees = Badge_Generator\get_attendees(); 37 38 wp_mkdir_p( $gravatar_folder ); 39 download_gravatars( $attendees, $gravatar_folder ); 40 generate_csv( $csv_filename, $zip_local_folder, $attendees, $gravatar_folder ); 41 create_zip_file( $zip_filename, $zip_local_folder, $csv_filename, $gravatar_folder ); 42 } finally { 43 // todo Delete contents of $assets_folder, then rmdir( $assets_folder ); 44 } 45 } 46 47 /** 48 * Download each attendee's Gravatar 49 * 50 * @todo Remove set_time_limit() if end up running via a cron job 51 * 52 * @param array $attendees 53 * @param string $gravatar_folder 54 * 55 * @throws \Exception 56 */ 57 function download_gravatars( $attendees, $gravatar_folder ) { 58 set_time_limit( 0 ); 59 60 foreach ( $attendees as $attendee ) { 61 if ( ! is_email( $attendee->tix_email ) ) { 62 continue; 63 } 64 65 $request_url = str_replace( '=blank', '=404', $attendee->avatar_url ); 66 $response = wp_remote_get( $request_url ); 67 $image = wp_remote_retrieve_body( $response ); 68 $status_code = wp_remote_retrieve_response_code( $response ); 69 70 if ( 404 == $status_code ) { 71 continue; 72 } 73 74 if ( ! $image || 200 != $status_code ) { 75 Logger\log( 'request_failed', compact( 'attendee', 'request_url', 'response' ) ); 76 throw new \Exception( __( "Couldn't download all Gravatars.", 'wordcamporg' ) ); 77 } 78 79 $filename = get_gravatar_filename( $attendee ); 80 $gravatar_file = fopen( $gravatar_folder . '/' . $filename, 'w' ); 81 82 fwrite( $gravatar_file, $image ); 83 fclose( $gravatar_file ); 84 } 85 } 86 87 /** 88 * Get the filename of the saved Gravatar for the given attendee 89 * 90 * @todo Returned value is false for input symbols like ♥, and maybe also for emoji 91 * 92 * @param \WP_Post $attendee 93 * 94 * @return string 95 */ 96 function get_gravatar_filename( $attendee ) { 97 return sanitize_file_name( strtolower( sprintf( 98 '%d-%s-%s.jpg', 99 $attendee->ID, 100 remove_accents( $attendee->tix_first_name ), 101 remove_accents( $attendee->tix_last_name ) 102 ) ) ); 103 } 104 105 /** 106 * Get the filename for the Zip file 107 * 108 * @param $assets_folder 109 * 110 * @return string 111 */ 112 function get_zip_filename( $assets_folder ) { 113 return $zip_filename = sprintf( 114 '%s/%s-badges.zip', 115 $assets_folder, 116 sanitize_file_name( sanitize_title( get_wordcamp_name() ) ) 117 ); 118 } 119 120 /** 121 * Generate the CSV that InDesign will merge 122 * 123 * @todo Accept $destination_directory, $empty_twitter, and arbitrary tix_question fields from form input 124 * 125 * @todo Twitter username gets prefixed with ' by wcorg_esc_csv. Spreadsheet programs will ignore that, but 126 * InDesign might not. If it doesn't, need to do something else to prevent the user having to manually remove 127 * them. 128 * 129 * @param string $csv_filename 130 * @param string $zip_local_folder 131 * @param array $attendees 132 * @param string $gravatar_folder 133 * 134 * @throws \Exception 135 */ 136 function generate_csv( $csv_filename, $zip_local_folder, $attendees, $gravatar_folder ) { 137 $csv_handle = fopen( $csv_filename, 'w' ); 138 $destination_directory = "Macintosh HD:Users:your_username:Desktop:$zip_local_folder:gravatars:"; 139 $empty_twitter = 'replace'; 140 141 $header_row = array( 142 'First Name', 'Last Name', 'Email Address', 'Ticket', 'Coupon', 'Twitter', 143 '@Gravatar' // Prefixed with an @ to let InDesign know that it contains an image 144 ); 145 146 if ( ! $csv_handle ) { 147 Logger\log( 'open_csv_failed' ); 148 throw new \Exception( __( "Couldn't open CSV file.", 'wordcamporg' ) ); 149 } 150 151 /* 152 * Intentionally not escaping the header, because we need to preserve the `@` for InDesign. The values are all 153 * hardcoded strings, so they're safe. 154 */ 155 fputcsv( $csv_handle, $header_row ); 156 157 foreach ( $attendees as $attendee ) { 158 $row = get_attendee_csv_row( $attendee, $gravatar_folder, $destination_directory, $empty_twitter ); 159 160 if ( empty( $row ) ) { 161 continue; 162 } 163 164 fputcsv( $csv_handle, wcorg_esc_csv( $row ) ); 165 } 166 167 fclose( $csv_handle ); 168 } 169 170 /** 171 * Get the CSV row for the given attendee 172 * 173 * @param \WP_Post $attendee 174 * @param string $gravatar_folder 175 * @param string $destination_directory 176 * @param string $empty_twitter 177 * 178 * @return array 179 */ 180 function get_attendee_csv_row( $attendee, $gravatar_folder, $destination_directory, $empty_twitter ) { 181 $row = array(); 182 183 if ( 'unknown.attendee@example.org' == $attendee->tix_email ) { 184 return $row; 185 } 186 187 $gravatar_path = ''; 188 $first_name = ucwords( $attendee->tix_first_name ); 189 $gravatar_filename = get_gravatar_filename( $attendee ); 190 191 if ( file_exists( $gravatar_folder .'/'. $gravatar_filename ) ) { 192 $gravatar_path = $destination_directory . $gravatar_filename; 193 } 194 195 $row = array( 196 'first-name' => $first_name, 197 'last-name' => ucwords( $attendee->tix_last_name ), 198 'email-address' => $attendee->tix_email, 199 'ticket-name' => $attendee->ticket, 200 'coupon-name' => $attendee->coupon, 201 'twitter-username' => format_twitter_username( get_twitter_username( $attendee ), $first_name, $empty_twitter ), 202 'gravatar-path' => $gravatar_path, 203 ); 204 205 return $row; 206 } 207 208 /** 209 * Get an attendee's Twitter username 210 * 211 * @todo For DRY-ness, make this a public static method in CampTix_Addon_Twitter_Field and refactor 212 * attendees_shortcode_item() to use it. 213 * 214 * @param \WP_Post $attendee 215 * 216 * @return string 217 */ 218 function get_twitter_username( $attendee ) { 219 /** @global \CampTix_Plugin $camptix */ 220 global $camptix; 221 222 $username = ''; 223 224 foreach ( $camptix->get_all_questions() as $question ) { 225 if ( 'twitter' !== $question->tix_type ) { 226 continue; 227 } 228 229 if ( ! isset( $attendee->tix_questions[ $question->ID ] ) ) { 230 continue; 231 } 232 233 $username = trim( $attendee->tix_questions[ $question->ID ] ); 234 break; 235 } 236 237 return $username; 238 } 239 240 /** 241 * Format a Twitter username 242 * 243 * @param string $username 244 * @param string $first_name 245 * @param string $empty_mode 'replace' to replace empty usernames with first names 246 * 247 * @return string 248 */ 249 function format_twitter_username( $username, $first_name, $empty_mode = 'replace' ) { 250 if ( empty ( $username ) ) { 251 if ( 'replace' === $empty_mode ) { 252 $username = $first_name; 253 } 254 } else { 255 // Strip out everything but the username, and prefix a @ 256 $username = '@' . preg_replace( 257 '/ 258 (https?:\/\/)? 259 (twitter\.com\/)? 260 (@)? 261 /ix', 262 '', 263 $username 264 ); 265 } 266 267 return $username; 268 } 269 270 /** 271 * Create a Zip file with all of the assets 272 * 273 * @param string $zip_filename 274 * @param string $zip_local_folder 275 * @param string $csv_filename 276 * @param string $gravatar_folder 277 * 278 * @throws \Exception 279 */ 280 function create_zip_file( $zip_filename, $zip_local_folder, $csv_filename, $gravatar_folder ) { 281 if ( ! class_exists( 'ZipArchive') ) { 282 Logger\log( 'zip_ext_not_installed' ); 283 throw new \Exception( __( 'The Zip extension for PHP is not installed.', 'wordcamporg' ) ); 284 } 285 286 $zip_file = new \ZipArchive(); 287 $open_status = $zip_file->open( $zip_filename, \ZipArchive::OVERWRITE ); 288 289 if ( true !== $open_status ) { 290 Logger\log( 'zip_open_failed', compact( 'zip_filename', 'open_status' ) ); 291 throw new \Exception( __( 'The Zip file could not be created.', 'wordcamporg' ) ); 292 } 293 294 $zip_file->addFile( 295 $csv_filename, 296 trailingslashit( $zip_local_folder ) . basename( $csv_filename ) 297 ); 298 299 $zip_file->addGlob( 300 $gravatar_folder . '/*', 301 0, 302 array( 303 'add_path' => $zip_local_folder . '/gravatars/', 304 'remove_all_path' => true 305 ) 306 ); 307 308 $zip_file->close(); 309 } 310 311 /** 312 * Serve the Zip file for downloading 313 * 314 * Security: This is intentionally served through PHP instead of making it accessible directly through wp-content, 315 * because the CSV file contains email addresses that we don't want to risk exposing to anyone scraping public 316 * folders. 317 * 318 * @todo If run into problems, maybe look into disabling gzip and/or adding support for range requests. 319 * See http://www.media-division.com/the-right-way-to-handle-file-downloads-in-php/ 320 * 321 * @param string $filename 322 * 323 * @throws \Exception 324 */ 325 function serve_zip_file( $filename ) { 326 if ( ! current_user_can( Badge_Generator\REQUIRED_CAPABILITY ) ) { 327 Logger\log( 'access_denied' ); 328 throw new \Exception( __( "You don't have authorization to perform this action.", 'wordcamporg' ) ); 329 } 330 331 set_time_limit( 0 ); 332 333 $headers = array( 334 'Content-Type' => 'application/octet-stream', 335 'Content-Length' => filesize( $filename ), 336 'Content-Disposition' => sprintf( 'attachment; filename="%s"', basename( $filename ) ), 337 ); 338 339 foreach ( $headers as $header => $value ) { 340 header( sprintf( '%s: %s', $header, $value ) ); 341 } 342 343 ob_clean(); 344 flush(); 345 readfile( $filename ); 346 die(); 347 }
Note: See TracChangeset
for help on using the changeset viewer.