protected function save(waRequestFile $file)
 {
     // check image
     if (!($image = $file->waImage())) {
         throw new waException(_w('Incorrect image'));
     }
     $exif_data = photosExif::getInfo($file->tmp_name);
     $image_changed = false;
     if (!empty($exif_data['Orientation'])) {
         $image_changed = $this->correctOrientation($exif_data['Orientation'], $image);
     }
     /**
      * Extend upload proccess
      * Make extra workup
      * @event photo_upload
      */
     $event = wa()->event('photo_upload', $image);
     if ($event && !$image_changed) {
         foreach ($event as $plugin_id => $result) {
             if ($result) {
                 $image_changed = true;
                 break;
             }
         }
     }
     $data = array('name' => preg_replace('/\\.[^\\.]+$/', '', basename($file->name)), 'ext' => $file->extension, 'size' => $file->size, 'type' => $image->type, 'width' => $image->width, 'height' => $image->height, 'contact_id' => $this->getUser()->getId(), 'status' => $this->status, 'upload_datetime' => date('Y-m-d H:i:s'));
     if ($this->status <= 0) {
         $data['hash'] = md5(uniqid(time(), true));
     }
     $photo_id = $data['id'] = $this->model->insert($data);
     if (!$photo_id) {
         throw new waException(_w('Database error'));
     }
     // update url
     $url = $this->generateUrl($data['name'], $photo_id);
     $this->model->updateById($photo_id, array('url' => $url));
     // check rigths to upload folder
     $photo_path = photosPhoto::getPhotoPath($data);
     if (file_exists($photo_path) && !is_writable($photo_path) || !file_exists($photo_path) && !waFiles::create($photo_path)) {
         $this->model->deleteById($photo_id);
         throw new waException(sprintf(_w("The insufficient file write permissions for the %s folder."), substr($photo_path, strlen($this->getConfig()->getRootPath()))));
     }
     if ($image_changed) {
         $image->save($photo_path);
         // save original
         if ($this->getConfig()->getOption('save_original')) {
             $original_file = photosPhoto::getOriginalPhotoPath($photo_path);
             $file->moveTo($original_file);
         }
     } else {
         $file->moveTo($photo_path);
     }
     unset($image);
     // free variable
     // add to album
     if ($photo_id && $this->album_id) {
         $album_photos_model = new photosAlbumPhotosModel();
         // update note if album is empty and note is yet null
         $r = $album_photos_model->getByField('album_id', $this->album_id);
         if (!$r) {
             $album_model = new photosAlbumModel();
             $sql = "UPDATE " . $album_model->getTableName() . " SET note = IFNULL(note, s:note) WHERE id = i:album_id";
             $time = !empty($exif_data['DateTimeOriginal']) ? strtotime($exif_data['DateTimeOriginal']) : time();
             $album_model->query($sql, array('note' => mb_strtolower(_ws(date('F', $time))) . ' ' . _ws(date('Y', $time)), 'album_id' => $this->album_id));
         }
         // add to album iteself
         $sort = (int) $album_photos_model->query("SELECT sort + 1 AS sort FROM " . $album_photos_model->getTableName() . " WHERE album_id = i:album_id ORDER BY sort DESC LIMIT 1", array('album_id' => $this->album_id))->fetchField('sort');
         $album_photos_model->insert(array('photo_id' => $photo_id, 'album_id' => $this->album_id, 'sort' => $sort));
     }
     // save rights for groups
     if ($this->groups) {
         $rights_model = new photosPhotoRightsModel();
         $rights_model->multiInsert(array('photo_id' => $photo_id, 'group_id' => $this->groups));
     }
     // save exif data
     if (!empty($exif_data)) {
         $exif_model = new photosPhotoExifModel();
         $exif_model->save($photo_id, $exif_data);
     }
     $sizes = $this->getConfig()->getSizes();
     photosPhoto::generateThumbs($data, $sizes);
     return array('name' => $file->name, 'type' => $file->type, 'size' => $file->size, 'thumbnail_url' => photosPhoto::getPhotoUrl($data, photosPhoto::getThumbPhotoSize()), 'url' => '#/photo/' . $photo_id . '/');
 }
 private function renderExifInfo()
 {
     // exif info
     $exif_model = new photosPhotoExifModel();
     $exif = $exif_model->getByPhoto($this->photo['id']);
     return $this->renderer->getExifInfo($exif);
 }
 public function execute()
 {
     $id = waRequest::post('id', 0, waRequest::TYPE_INT);
     $in_stack = waRequest::post('in_stack', 0, waRequest::TYPE_INT);
     $hash = waRequest::post('hash', null, waRequest::TYPE_STRING_TRIM);
     $hash = urldecode($hash);
     // get photo
     $this->photo_model = new photosPhotoModel();
     $this->photo = $this->photo_model->getById($id);
     if (!$this->photo) {
         throw new waException(_w("Photo doesn't exists"), 404);
     }
     $photo_rights_model = new photosPhotoRightsModel();
     if (!$photo_rights_model->checkRights($this->photo)) {
         throw new waRightsException(_w("You don't have sufficient access rights"));
     }
     $this->photo['name_not_escaped'] = $this->photo['name'];
     $this->photo = photosPhoto::escapeFields($this->photo);
     $this->photo['upload_datetime_formatted'] = waDateTime::format('humandate', $this->photo['upload_datetime']);
     $this->photo['upload_timestamp'] = strtotime($this->photo['upload_datetime']);
     $this->photo['edit_rights'] = $photo_rights_model->checkRights($this->photo, true);
     $this->photo['private_url'] = photosPhotoModel::getPrivateUrl($this->photo);
     $this->photo['thumb'] = photosPhoto::getThumbInfo($this->photo, photosPhoto::getThumbPhotoSize());
     $this->photo['thumb_big'] = photosPhoto::getThumbInfo($this->photo, photosPhoto::getBigPhotoSize());
     $this->photo['thumb_middle'] = photosPhoto::getThumbInfo($this->photo, photosPhoto::getMiddlePhotoSize());
     $original_photo_path = photosPhoto::getOriginalPhotoPath($this->photo);
     if (wa('photos')->getConfig()->getOption('save_original') && file_exists($original_photo_path)) {
         $this->photo['original_exists'] = true;
     } else {
         $this->photo['original_exists'] = false;
     }
     $photo_tags_model = new photosPhotoTagsModel();
     $tags = $photo_tags_model->getTags($id);
     $this->photo['tags'] = $tags;
     $this->response['photo'] = $this->photo;
     // get stack if it's possible
     if (!$in_stack && ($stack = $this->photo_model->getStack($id, array('thumb' => true, 'thumb_crop' => true, 'thumb_big' => true, 'thumb_middle' => true)))) {
         $this->response['stack'] = $stack;
     }
     // get albums
     $album_photos_model = new photosAlbumPhotosModel();
     $albums = $album_photos_model->getAlbums($id, array('id', 'name'));
     $this->response['albums'] = isset($albums[$id]) ? array_values($albums[$id]) : array();
     // exif info
     $exif_model = new photosPhotoExifModel();
     $exif = $exif_model->getByPhoto($this->photo['id']);
     if (isset($exif['DateTimeOriginal'])) {
         $exif['DateTimeOriginal'] = waDateTime::format('humandatetime', $exif['DateTimeOriginal'], date_default_timezone_get());
     }
     $this->response['exif'] = $exif;
     // get author
     $contact = new waContact($this->photo['contact_id']);
     $this->response['author'] = array('id' => $contact['id'], 'name' => photosPhoto::escape($contact['name']), 'photo_url' => $contact->getPhoto(photosPhoto::AUTHOR_PHOTO_SIZE), 'backend_url' => $this->getConfig()->getBackendUrl(true) . 'contacts/#/contact/' . $contact['id']);
     // for making inline-editable widget
     $this->response['frontend_link_template'] = photosFrontendPhoto::getLink(array('url' => '%url%'));
     $hooks = array();
     $parent_id = $this->photo_model->getStackParentId($this->photo);
     $photo_id = $parent_id ? $parent_id : $id;
     /**
      * Extend photo page
      * Add extra widget(s)
      * @event backend_photo
      * @return array[string][string]string $return[%plugin_id%]['bottom'] In bottom, under photo any widget
      */
     $hooks['backend_photo'] = wa()->event('backend_photo', $photo_id);
     $this->response['hooks'] = $hooks;
     if ($hash !== null) {
         $collection = new photosCollection($hash);
         if (strstr($hash, 'rate>0') !== false) {
             $collection->orderBy('p.rate DESC, p.id');
         }
         $this->response['photo_stream'] = $this->getPhotoStream($collection);
         if ($collection->getAlbum()) {
             $this->response['album'] = $collection->getAlbum();
         }
     }
 }