1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8use Tiki\Lib\Image\Image; 9 10class Search_Action_FileGalleryImageOverlay implements Search_Action_Action 11{ 12 13 protected $replaceKeys = [ 14 '%file_name%' => 'file.filename', 15 '%file_id%' => 'file.fileId', 16 '%parts_filename%' => 'parts.filename', 17 '%parts_extension%' => 'parts.extension', 18 '%gallery_name%' => 'gallery.name', 19 '%gallery_id%' => 'gallery.galleryId', 20 '%tracker_id%' => 'item.trackerId', 21 '%item_id%' => 'item.itemId', 22 '%field_id%' => 'field.fieldId', 23 '%field_perm_name%' => 'field.permName', 24 '%field_name%' => 'field.name', 25 '%exif_date%' => 'exif.datetime', 26 '%exif_gps%' => 'exif.gps', 27 '%exif_gps_lat%' => 'exif.gps_lat', 28 '%exif_gps_lon%' => 'exif.gps_lon', 29 '%exif_gps_dms%' => 'exif.gps_dms', 30 '%exif_gps_dms_lat%' => 'exif.gps_dms_lat', 31 '%exif_gps_dms_lon%' => 'exif.gps_dms_lon', 32 ]; 33 34 function getValues() 35 { 36 return [ 37 'object_type' => true, 38 'object_id' => true, 39 'field' => true, 40 'value' => true, 41 'error_if_missing' => false, 42 ]; 43 } 44 45 function validate(JitFilter $data) 46 { 47 48 $object_type = $data->object_type->text(); 49 $object_id = $data->object_id->int(); 50 $field = $data->field->word(); 51 $value = $data->value->text(); 52 53 if ('tracker_field_' === substr($field, 0, 14)) { 54 $field = substr($field, 14); 55 } 56 57 if ($object_type != 'trackeritem') { 58 throw new Search_Action_Exception(tr('Cannot apply filegal_image_overlay action to an object type %0.', $object_type)); 59 } 60 61 $trklib = TikiLib::lib('trk'); 62 $info = $trklib->get_item_info($object_id); 63 64 if (! $info) { 65 throw new Search_Action_Exception(tr('Tracker item %0 not found.', $object_id)); 66 } 67 68 $definition = Tracker_Definition::get($info['trackerId']); 69 70 $fieldDefinition = $definition->getFieldFromPermName($field); 71 if (! $fieldDefinition) { 72 throw new Search_Action_Exception(tr('Tracker field %0 not found for tracker %1.', $field, $info['trackerId'])); 73 } 74 75 if ($fieldDefinition['type'] != 'FG') { 76 throw new Search_Action_Exception(tr('Tracker field %0 is not a Files field type.', $field)); 77 } 78 79 if (empty($value)) { 80 throw new Search_Action_Exception(tr('filegal_image_overlay action missing value parameter.')); 81 } 82 83 return true; 84 } 85 86 function execute(JitFilter $data) 87 { 88 89 global $user, $prefs; 90 91 $object_type = $data->object_type->text(); 92 $object_id = $data->object_id->int(); 93 $field = $data->field->word(); 94 $value = $data->value->text(); 95 $error_if_missing = $data->error_if_missing->text(); 96 97 if ('tracker_field_' === substr($field, 0, 14)) { 98 $field = substr($field, 14); 99 } 100 101 $trklib = TikiLib::lib('trk'); 102 $info = $trklib->get_tracker_item($object_id); 103 104 /** @var Tracker_Definition $definition */ 105 $definition = Tracker_Definition::get($info['trackerId']); 106 $fieldDefinition = $definition->getFieldFromPermName($field); 107 108 /** @var FileGalLib $fileGal */ 109 $fileGal = TikiLib::lib('filegal'); 110 111 $fileList = $info[$fieldDefinition['fieldId']]; 112 113 if (empty($fileList)) { 114 return true; 115 } 116 117 if ($error_if_missing == 'y') { 118 $error_if_missing = true; 119 } else { 120 $error_if_missing = false; 121 } 122 123 $newFileList = []; 124 foreach (explode(',', $fileList) as $fileId) { 125 $file = $fileGal->get_file($fileId); 126 if (substr($file['filetype'], 0, 6) != 'image/') { 127 $newFileList[] = $fileId; 128 continue; 129 } 130 $galInfo = $fileGal->get_file_gallery_info($file['galleryId']); 131 $newUser = $user ?: $file['user']; 132 $overlayString = $this->generateString( 133 $value, 134 $file, 135 $galInfo, 136 $info, 137 $fieldDefinition, 138 $error_if_missing, 139 $missingKeys 140 ); 141 if ($overlayString === false) { 142 throw new Search_Action_Exception(tr( 143 'filegal_image_overlay: Problem processing image "%0", the following values form the template are empty: %1', 144 $file['filename'], 145 implode(', ', $missingKeys) 146 )); 147 } 148 $newImage = $this->addTextToImage($file['data'], $overlayString); 149 if ($newImage) { 150 $newFileList[] = $fileGal->update_single_file( 151 $galInfo, 152 $file['filename'], 153 $file['filesize'], 154 $file['filetype'], 155 $newImage, 156 $fileId, 157 $newUser 158 ); 159 } else { 160 $newFileList[] = $fileId; 161 } 162 } 163 164 if ($prefs['fgal_keep_fileId'] == 'n') { 165 // new IDs are generated to for the last version we updated 166 $utilities = new Services_Tracker_Utilities; 167 $utilities->updateItem( 168 $definition, 169 [ 170 'itemId' => $object_id, 171 'status' => $info['status'], 172 'fields' => [ 173 $field => implode(',', $newFileList), 174 ], 175 ] 176 ); 177 } 178 179 return true; 180 } 181 182 function requiresInput(JitFilter $data) 183 { 184 return false; 185 } 186 187 /** 188 * Generate a string based on the template provided 189 * 190 * @param string $template The template (see $replaceKeys) 191 * @param array $fileData File Details 192 * @param array $galleryData Gallery Details 193 * @param array $itemData Item Details 194 * @param array $fieldData Field Details 195 * @param boolean $checkMissing If enable function will return false if some element of the template has a empty value 196 * @param array $missingTemplateKeys The keys missing 197 * 198 * @return string|false 199 */ 200 protected function generateString( 201 $template, 202 $fileData, 203 $galleryData, 204 $itemData, 205 $fieldData, 206 $checkMissing = false, 207 &$missingTemplateKeys = [] 208 ) { 209 $dataValues = [ 210 'file' => $fileData, 211 'gallery' => $galleryData, 212 'item' => $itemData, 213 'field' => $fieldData, 214 'parts' => pathinfo($fileData['filename']), 215 'exif' => $this->getExifArray($fileData), 216 ]; 217 218 $values = []; 219 foreach ($this->replaceKeys as $search => $dataKey) { 220 list($data, $key) = explode('.', $dataKey); 221 $values[$search] = (isset($dataValues[$data]) && isset($dataValues[$data][$key])) ? $dataValues[$data][$key] : ''; 222 } 223 224 if ($checkMissing) { 225 foreach ($values as $key => $value) { 226 if (strpos($template, $key) !== false) { 227 if (empty($value)) { 228 $missingTemplateKeys[] = $key; 229 } 230 } 231 } 232 if (count($missingTemplateKeys)) { 233 return false; 234 } 235 } 236 237 return str_replace(array_keys($values), array_values($values), $template); 238 } 239 240 /** 241 * Allow adding text as overlay to a image 242 * @param $imageString 243 * @param $text 244 * @return string 245 */ 246 public function addTextToImage($imageString, $text) 247 { 248 $image = Image::create($imageString); 249 250 $image->addTextToImage($text); 251 252 return $image->display(); 253 } 254 255 /** 256 * Get some selected Exif information from a image 257 * @param $fileData 258 * @return array 259 */ 260 function getExifArray($fileData) 261 { 262 $exif = []; 263 if ($fileData['filetype'] != 'image/jpeg' || ! function_exists('exif_read_data')) { 264 return $exif; 265 } 266 $exifData = exif_read_data('data://image/jpeg;base64,' . base64_encode($fileData['data'])); 267 268 $exif['datetime'] = isset($exifData['DateTimeOriginal']) ? $exifData['DateTimeOriginal'] : ''; 269 270 if (isset($exifData['GPSLongitude']) && isset($exifData['GPSLatitude'])) { 271 $latitude = $this->gpsCoordinates($exifData["GPSLatitude"], $exifData['GPSLatitudeRef']); 272 $longitude = $this->gpsCoordinates($exifData["GPSLongitude"], $exifData['GPSLongitudeRef']); 273 $exif['gps'] = $latitude['dd'] . ', ' . $longitude['dd']; 274 $exif['gps_lat'] = $latitude['dd']; 275 $exif['gps_lon'] = $longitude['dd']; 276 $exif['gps_dms'] = $latitude['dms'] . ' ' . $longitude['dms']; 277 $exif['gps_dms_lat'] = $latitude['dms']; 278 $exif['gps_dms_lon'] = $longitude['dms']; 279 } else { 280 $exif['gps'] = ''; 281 $exif['gps_lat'] = ''; 282 $exif['gps_lon'] = ''; 283 $exif['gps_dms'] = ''; 284 $exif['gps_dms_lat'] = ''; 285 $exif['gps_dms_lon'] = ''; 286 } 287 288 return ($exif); 289 } 290 291 /** 292 * Conver Exif coordinate information into DD and DMS GPS coordinates 293 * @param $coordinate 294 * @param $hemisphere 295 * @return array 296 */ 297 protected function gpsCoordinates($coordinate, $hemisphere) 298 { 299 for ($i = 0; $i < 3; $i++) { 300 $part = explode('/', $coordinate[$i]); 301 if (count($part) == 1) { 302 $coordinate[$i] = $part[0]; 303 } else { 304 if (count($part) == 2) { 305 $coordinate[$i] = (float)$part[0] / (float)$part[1]; 306 } else { 307 $coordinate[$i] = 0; 308 } 309 } 310 } 311 list($degrees, $minutes, $seconds) = $coordinate; 312 313 $sign = ($hemisphere == 'W' || $hemisphere == 'S') ? -1 : 1; 314 $coordinateDD = sprintf("%.4f", $sign * ($degrees + $minutes / 60 + $seconds / 3600)); 315 316 //normalize 317 $minutes += 60 * ($degrees - floor($degrees)); 318 $degrees = floor($degrees); 319 $seconds += 60 * ($minutes - floor($minutes)); 320 $minutes = floor($minutes); 321 322 //extra normalization, probably not necessary unless you get weird data 323 if ($seconds >= 60) { 324 $minutes += floor($seconds / 60.0); 325 $seconds -= 60 * floor($seconds / 60.0); 326 } 327 if ($minutes >= 60) { 328 $degrees += floor($minutes / 60.0); 329 $minutes -= 60 * floor($minutes / 60.0); 330 } 331 332 $coordinateDMS = sprintf("%d° %d' %.3f'' %s", $degrees, $minutes, $seconds, $hemisphere); 333 334 return [ 335 'dd' => $coordinateDD, 336 'dms' => $coordinateDMS, 337 ]; 338 } 339} 340