Play images and video from Synology PhotoStation server

photo.php 36KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. <?PHP
  2. require_once('photo.inc.php');
  3. require_once('albumutil.php');
  4. require_once('photoutil.php');
  5. define("PHOTO_ACTION_TMP", '/tmp/photostation_');
  6. define("PHOTO_ACTION_MVCP_KEY", 'SYNOPS_MVCP');
  7. define("PHOTO_ACTION_DEL_KEY", 'SYNOPS_DEL');
  8. class PhotoAPI extends WebAPI
  9. {
  10. function __construct()
  11. {
  12. parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
  13. }
  14. protected function Process()
  15. {
  16. if (isset($_REQUEST['public_share_id'])) {
  17. csSYNOPhotoDB::GetDBInstance()->SetPublicShareSessionCache($_REQUEST['public_share_id']);
  18. } else if (isset($_REQUEST['filter_public_share'])) {
  19. csSYNOPhotoDB::GetDBInstance()->SetPublicShareSessionCache($_REQUEST['filter_public_share']);
  20. }
  21. if (!strcasecmp($this->method, "list")) {
  22. $force = "true" === $_REQUEST['force_update'] ? true : false;
  23. csSYNOPhotoDB::GetDBInstance()->SetSessionCache($force);
  24. session_write_close();
  25. $this->ListItem();
  26. } else if (!strcasecmp($this->method, "listexif")) {
  27. session_write_close();
  28. $this->ListExif();
  29. } else if (!strcasecmp($this->method, "listfeatureditem")) {
  30. $force = "true" === $_REQUEST['force_update'] ? true : false;
  31. csSYNOPhotoDB::GetDBInstance()->SetSessionCache($force);
  32. session_write_close();
  33. $this->ListFeaturedItem();
  34. } else if (!strcasecmp($this->method, "listgpsgroup")) {
  35. session_write_close();
  36. $this->ListGPSGroup();
  37. } else if (!strcasecmp($this->method, "listgpsgroupeditem")) {
  38. session_write_close();
  39. $this->ListGPSGroupedItem();
  40. } else if (!strcasecmp($this->method, "getinfo")) {
  41. csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
  42. session_write_close();
  43. $this->GetInfo();
  44. } else if (!strcasecmp($this->method, "getexif")) {
  45. session_write_close();
  46. $this->GetExif();
  47. } else {
  48. csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
  49. csSYNOPhotoMisc::CheckSessionTimeOut(true);
  50. if (!strcasecmp($this->method, "edit")) {
  51. $this->Edit();
  52. }
  53. if (!strcasecmp($this->method, "delete")) {
  54. session_write_close();
  55. $this->DeleteItem();
  56. }
  57. if (!strcasecmp($this->method, "copy")) {
  58. csSYNOPhotoDB::GetDBInstance()->ExpireSessionCache();
  59. session_write_close();
  60. $this->CopyItem();
  61. }
  62. if (!strcasecmp($this->method, "cancel")) {
  63. session_write_close();
  64. $this->Cancel();
  65. }
  66. }
  67. }
  68. private function GetInfo()
  69. {
  70. $ret = false;
  71. $resp = array();
  72. /* return when lack of params */
  73. if (!isset($_REQUEST['id'])) {
  74. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  75. goto End;
  76. }
  77. $publicShareId = NULL;
  78. if (isset($_REQUEST['public_share_id'])) {
  79. $publicShareId = $_REQUEST['public_share_id'];
  80. if (!SharedAlbum::CheckPublicSharePermission($publicShareId)) {
  81. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  82. goto End;
  83. }
  84. $publicShareInfo = SharedAlbum::GetInfoByPublicShare($publicShareId);
  85. if (NULL === $publicShareInfo || 'valid' !== $publicShareInfo['share_status']) {
  86. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  87. goto End;
  88. }
  89. }
  90. $additional = isset($_REQUEST['additional']) ? explode(',', $_REQUEST['additional']) : array();
  91. $ids = explode(',', $_REQUEST['id']);
  92. foreach ($ids as $id_str) {
  93. $id_arr = explode('_', $id_str);
  94. if (3 !== count($id_arr) || ('photo' !== $id_arr[0] && 'video' !== $id_arr[0])) {
  95. continue;
  96. }
  97. $albumName = @pack('H*', $id_arr[1]);
  98. $fileName = @pack('H*', $id_arr[2]);
  99. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName . "/") . $fileName;
  100. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  101. continue;
  102. }
  103. if (!csSYNOPhotoMisc::CheckAlbumAccessible($albumName, false, $publicShareId)) {
  104. continue;
  105. }
  106. $filePath = ('/' === $albumName ? "" : $albumName . "/") . $fileName;
  107. if (NULL !== $publicShareId && !SharedAlbum::CheckSharedAlbumItemValid($filePath, $id_arr[0], $publicShareId)) {
  108. continue;
  109. }
  110. if (@file_exists($path)) {
  111. if (false !== ($item = PhotoAPIUtil::getItemByPath($path, $additional, $id_arr[0], false))) {
  112. $resp[] = $item;
  113. }
  114. }
  115. }
  116. $ret = true;
  117. $this->SetResponse($resp);
  118. End:
  119. return $ret;
  120. }
  121. private function GetParams_ListItem()
  122. {
  123. global $SYNOPHOTO_ALLOW_SORT_TYPE;
  124. if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['type'])) {
  125. return false;
  126. }
  127. $params = array(
  128. 'offset' => $_REQUEST['offset'] + 0,
  129. 'limit' => $_REQUEST['limit'] + 0,
  130. 'type' => explode(',', $_REQUEST['type']),
  131. 'labelIds' => array()
  132. );
  133. foreach ($params['type'] as $_) {
  134. if (!in_array($_, array('photo', 'video'))) {
  135. return false;
  136. }
  137. }
  138. $options_params = array(
  139. // key => default value
  140. 'sort_by' => 'filename',
  141. 'sort_direction' => 'asc',
  142. 'filter_smart' => '',
  143. 'filter_tag' => '',
  144. 'filter_album' => '',
  145. 'filter_shared_album' => '',
  146. 'filter_public_share' => '',
  147. 'item_id' => '',
  148. 'additional' => array(),
  149. 'gps' => false
  150. );
  151. foreach ($options_params as $k => $v) {
  152. if (!isset($_REQUEST[$k])) {
  153. $params[$k] = $v;
  154. continue;
  155. }
  156. if ('sort_by' === $k) {
  157. if (in_array($_REQUEST[$k], $SYNOPHOTO_ALLOW_SORT_TYPE)) {
  158. $params[$k] = $_REQUEST[$k];
  159. } else {
  160. $params[$k] = 'filename';
  161. }
  162. } else if ('sort_direction' === $k) {
  163. $params[$k] = 'desc' === $_REQUEST[$k] ? 'desc' : 'asc';
  164. } else if ('additional' === $k) {
  165. $params[$k] = explode(',', $_REQUEST[$k]);
  166. } else if ('gps' === $k) {
  167. $params[$k] = 'true' === $_REQUEST[$k];
  168. } else if ('filter_tag' === $k) {
  169. $temps = explode(",", $_REQUEST['filter_tag']);
  170. $ids = array();
  171. foreach ($temps as $tagId) {
  172. if (SYNOPHOTO_UNCONFIRM_TAG_ID === $tagId) {
  173. $ids[] = SYNOPHOTO_UNCONFIRM_TAG_ID;
  174. } else {
  175. $arrs = explode("tag_", $tagId);
  176. $ids[] = $arrs[1] + 0;
  177. }
  178. }
  179. $params['labelIds'] = $ids;
  180. } else {
  181. $params[$k] = $_REQUEST[$k];
  182. }
  183. }
  184. // compatible with [time] [time1,time2] [time1,] [,time2]
  185. if (isset($_REQUEST['taken_date'])) {
  186. $taken_dates = explode(',' , trim($_REQUEST['taken_date']));
  187. if (1 === count($taken_dates)) {
  188. if ($taken_dates[0]) {
  189. $taken_date = date_create($taken_dates[0]);
  190. if ($taken_date) {
  191. $date_begin = $taken_date;
  192. }
  193. }
  194. $params['date_end'] = $params['date_begin'] = $date_begin->format('Y-m-d');
  195. } else {
  196. if ($taken_dates[0]) {
  197. $taken_date = date_create($taken_dates[0]);
  198. if ($taken_date) {
  199. $date_begin = $taken_date;
  200. }
  201. }
  202. if ($taken_dates[1]) {
  203. $taken_date_end = date_create($taken_dates[1]);
  204. if ($taken_date_end) {
  205. $date_end = $taken_date_end;
  206. }
  207. }
  208. if ($date_begin && $date_end) {
  209. if ($date_begin > $date_end) {
  210. list($params['date_begin'], $params['date_end']) = array($date_end->format('Y-m-d'), $date_begin->format('Y-m-d'));
  211. } else {
  212. list($params['date_begin'], $params['date_end']) = array($date_begin->format('Y-m-d'), $date_end->format('Y-m-d'));
  213. }
  214. } else if ($date_begin) {
  215. $params['date_begin'] = $date_begin->format('Y-m-d');
  216. } else if ($date_end) {
  217. $params['date_end'] = $date_end->format('Y-m-d');
  218. } else {
  219. return false;
  220. }
  221. }
  222. }
  223. return $params;
  224. }
  225. private function ListItem()
  226. {
  227. $ret = false;
  228. $resp = array();
  229. $items = array();
  230. /* return when lack of params */
  231. $params = $this->GetParams_ListItem();
  232. if (!$params) {
  233. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  234. goto End;
  235. }
  236. $hasPhoto = in_array('photo', $params['type']);
  237. $hasVideo = in_array('video', $params['type']);
  238. // create the variable which name is the same as the key of params
  239. foreach ($params as $k => $v) {
  240. ${$k} = $v;
  241. }
  242. if ('' !== $filter_album) {
  243. $arr = explode('_', $filter_album);
  244. if ('album' !== $arr[0]) {
  245. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  246. goto End;
  247. }
  248. $albumName = empty($arr[1]) ? '/' : @pack('H*', $arr[1]);
  249. $sharename = $albumName;
  250. /* users may have no access right to root album but can access layer one album
  251. * (Setting: Set photos and videos stored at the root folder of Photo Station as public)*/
  252. if ($albumName !== "/" && !csSYNOPhotoMisc::CheckAlbumAccessible($albumName)) {
  253. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  254. goto End;
  255. }
  256. $albumPath = SYNOPHOTO_SERVICE_REAL_DIR . ("/" === $albumName ? "" : "/" . $albumName);
  257. if (false === csSynoPhotoMisc::CheckPathValid($albumPath)) {
  258. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  259. goto End;
  260. }
  261. $personalAlbumPath = @pack('H*', $arr[1]);
  262. } else {
  263. $albumPath = SYNOPHOTO_SERVICE_REAL_DIR;
  264. $sharename = '/';
  265. $personalAlbumPath = '';
  266. }
  267. if ('root' !== SYNOPHOTO_ADMIN_USER) { //personal photo station
  268. if ('' !== $filter_album && '' !== $personalAlbumPath) {
  269. $escPath = "$personalAlbumPath/%";
  270. } else {
  271. $escPath = "$personalAlbumPath%";
  272. }
  273. } else {
  274. $escPath = "$albumPath/%";
  275. }
  276. $getRealPath = ('root' === SYNOPHOTO_ADMIN_USER) ? false : true;
  277. $total = 0;
  278. /* unconfirmed people tag */
  279. if ($hasPhoto && in_array(SYNOPHOTO_UNCONFIRM_TAG_ID, $params['labelIds'])) {
  280. $list = AlbumAPIUtil::GetUnConfirmItemList($limit, $offset);
  281. foreach ($list as $key => $value) {
  282. if (false !== ($item = PhotoAPIUtil::getItemByPath($key, $additional, 'photo', false))) {
  283. $items[] = $item;
  284. }
  285. }
  286. $total = intval($list['total']);
  287. } elseif ('' !== $filter_shared_album || '' !== $filter_public_share) {
  288. // check permission
  289. $publicShareId = '' !== $filter_public_share ? $filter_public_share : NULL;
  290. if (!SharedAlbum::CheckPublicSharePermission($publicShareId)) {
  291. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  292. goto End;
  293. }
  294. $dbPath = NULL;
  295. $blSingleItem = false;
  296. if ('' !== $filter_shared_album) {
  297. // filter by shared album id, check userid in session own this shared album
  298. $id_arr = explode("_", $filter_shared_album);
  299. if (2 !== count($id_arr) || 'sharedalbum' != $id_arr[0]) {
  300. $this->SetError(PHOTOSTATION_SHARED_ALBUM_BAD_PARAMS);
  301. goto End;
  302. }
  303. if ($id_arr[1] === 'single') {
  304. $sharedAlbum = SharedAlbum::GetHiddenAlbumInfo();
  305. $blSingleItem = true;
  306. $id_arr[1] = explode("_", $sharedAlbum['id'])[1];
  307. } else {
  308. $sharedAlbum = SharedAlbum::GetInfoById($id_arr[1], array('public_share'));
  309. }
  310. if ($sharedAlbum === NULL) {
  311. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  312. goto End;
  313. }
  314. $userid = isset($_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid']) ? $_SESSION[SYNOPHOTO_ADMIN_USER]['reg_syno_userid'] : 0;
  315. $sharedAlbumId = $id_arr[1];
  316. $shareStatus = $sharedAlbum['additional']['public_share']['share_status'];
  317. // for generating public share url
  318. // equal to publicShareId, avoid GetAccessibleAlbumQueryConditionWithExcludeFormat use wrong session data
  319. $publicShareLink = $sharedAlbum['additional']['public_share']['shareid'];
  320. } else if ('' !== $filter_public_share) {
  321. // filter by public share link, check public share is existent and valid
  322. $publicShareInfo = SharedAlbum::GetInfoByPublicShare($publicShareId);
  323. if (NULL === $publicShareInfo || 'valid' !== $publicShareInfo['share_status']) {
  324. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  325. goto End;
  326. }
  327. $sharedAlbumId = (int)$publicShareInfo['id'];
  328. $userid = (int)$publicShareInfo['userid'];
  329. $publicShareLink = $publicShareId;
  330. $shareStatus = $publicShareInfo['share_status'];
  331. if ('' !== $item_id) {
  332. $id_arr = explode("_", $item_id);
  333. $type = $id_arr[0];
  334. if (count($id_arr) !== 3 || ($type !== 'photo' && $type !== 'video')) {
  335. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  336. goto End;
  337. }
  338. $albumName = @pack('H*', $id_arr[1]);
  339. $fileName = @pack('H*', $id_arr[2]);
  340. $filePath = ('/' === $albumName ? "" : $albumName . "/") . $fileName;
  341. $dbPath = SYNOPHOTO_SERVICE_REAL_DIR_PATH . $filePath;
  342. }
  343. }
  344. $albumCondition = csSYNOPhotoMisc::GetAccessibleAlbumQueryConditionWithExcludeFormat('A', $publicShareId);
  345. if (!$albumCondition['albumCond']) {
  346. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  347. goto End;
  348. }
  349. $PublicSharePhotoTable = SHARED_ALBUM_PHOTO_TABLE_NAME;
  350. $PublicShareVideoTable = SHARED_ALBUM_VIDEO_TABLE_NAME;
  351. if (0 === $userid) {
  352. $PublicSharePhotoTable = SHARED_ALBUM_PHOTO_ADMIN_TABLE_NAME;
  353. $PublicShareVideoTable = SHARED_ALBUM_VIDEO_ADMIN_TABLE_NAME;
  354. }
  355. $sqlParam = array();
  356. $photoQuery = '';
  357. if ($hasPhoto) {
  358. $photoQuery = "SELECT A.path as path, A.name as filename, A.timetaken as takendate, A.create_time as createdate, 'photo' as type FROM photo_image A, $PublicSharePhotoTable B WHERE A.id = B.photoid AND B.collectionid = ?";
  359. $sqlParam[] = $sharedAlbumId;
  360. if ($dbPath !== NULL) {
  361. $photoQuery .= " AND A.path = ?";
  362. $sqlParam[] = $dbPath;
  363. }
  364. $photoQuery .= " AND {$albumCondition['albumCond']} ";
  365. $sqlParam = array_merge($sqlParam, $albumCondition['sqlParam']);
  366. }
  367. $videoQuery = '';
  368. if ($hasVideo) {
  369. $videoQuery = "SELECT A.path as path, A.title as filename, A.mdate as takendate, A.date as createdate, 'video' as type FROM video A, $PublicShareVideoTable B WHERE A.id = B.videoid AND B.collectionid = ?";
  370. $sqlParam[] = $sharedAlbumId;
  371. if ($dbPath !== NULL) {
  372. $videoQuery .= " AND A.path = ?";
  373. $sqlParam[] = $dbPath;
  374. }
  375. $videoQuery .= " AND {$albumCondition['albumCond']} ";
  376. $sqlParam = array_merge($sqlParam, $albumCondition['sqlParam']);
  377. }
  378. $limitOffsetString = PHOTO_DB_GetLimitOffsetString($limit, $offset);
  379. if ('' !== $photoQuery && '' !== $videoQuery) {
  380. $query = "$photoQuery UNION $videoQuery ORDER BY $sort_by $sort_direction $limitOffsetString";
  381. $queryCount = "SELECT COUNT(*) FROM ($photoQuery UNION $videoQuery) AS totalCount";
  382. } elseif ('' === $photoQuery) {
  383. $query = "$videoQuery ORDER BY $sort_by $sort_direction $limitOffsetString";
  384. $queryCount = "SELECT COUNT(*) FROM ($videoQuery) AS totalCount";
  385. } else {
  386. $query = "$photoQuery ORDER BY $sort_by $sort_direction $limitOffsetString";
  387. $queryCount = "SELECT COUNT(*) FROM ($photoQuery) AS totalCount";
  388. }
  389. $db_result = PHOTO_DB_Query($query, $sqlParam);
  390. $serverHost = csSYNOPhotoMisc::GetServerHost(true);
  391. while ($row = PHOTO_DB_FetchRow($db_result)) {
  392. if (false !== ($item = PhotoAPIUtil::getItemByPath($row['path'], $additional, $row['type'], $getRealPath))) {
  393. if ($shareStatus === 'valid') {
  394. $item['public_share_url'] = $serverHost . SYNOPHOTO_URL_PREFIX . '/photo/share/' . $publicShareLink . ($blSingleItem ? '/' : '#!List/') . $item['id'];
  395. }
  396. $items[] = File::FormatDBData($item);
  397. }
  398. }
  399. $db_result = PHOTO_DB_Query($queryCount, $sqlParam);
  400. $row = PHOTO_DB_FetchRow($db_result);
  401. $total = intval($row[0]);
  402. } elseif ('' === $filter_smart) {
  403. /* filter by albumname and tag */
  404. $itemObjs = File::ListItems($hasPhoto, $hasVideo, array(
  405. 'sharename' => $sharename,
  406. 'labels' => $params['labelIds'],
  407. 'date_begin' => $params['date_begin'],
  408. 'date_end' => $params['date_end'],
  409. 'gps' => $params['gps']
  410. ), $sort_by, $sort_direction, 0 > $limit ? NULL : $limit, $offset >= 0 ? $offset : NULL);
  411. $formularExtraParam = array(
  412. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  413. 'param' => $params
  414. );
  415. $total = $itemObjs['total'];
  416. $items = array();
  417. foreach ($itemObjs['items'] as $obj) {
  418. if ('photo' === $obj['type']) {
  419. $items[] = Decorator::PhotoFormula($obj, $formularExtraParam);
  420. } else if ('video' === $obj['type']) {
  421. $items[] = Decorator::VideoFormula($obj, $formularExtraParam);
  422. }
  423. }
  424. } else {
  425. /* for smart album */
  426. $arr = explode('_', $filter_smart);
  427. if ('smart' !== $arr[0]) {
  428. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  429. goto End;
  430. }
  431. $albumName = @pack('H*', $arr[1]);
  432. $data = array(
  433. "name" => $albumName,
  434. "offset" => $offset,
  435. "limit" => $limit,
  436. 'date_begin' => $params['date_begin'],
  437. 'date_end' => $params['date_end']
  438. );
  439. $smart = json_decode(SmartAlbum::GetSmartAlbumInstance()->ListItem(json_encode($data), $sort_by, $sort_direction), true);
  440. if (!$smart['success']) {
  441. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  442. goto End;
  443. }
  444. $itemLists = $smart['data']['itemList'];
  445. $gpsTotal = 0;
  446. $idx = $offset;
  447. foreach ($itemLists as $row) {
  448. if (!in_array($row['type'], $params['type'])) {
  449. continue;
  450. }
  451. if (false !== ($item = PhotoAPIUtil::getItemByPath($row['path'], $additional, $row['type'], false))) {
  452. $item['pos'] = $idx++;
  453. if ($gps) {
  454. // special filter for map view
  455. if (null !== $item['additional']['photo_exif']['gps']) {
  456. $gpsTotal++;
  457. $items[] = $item;
  458. }
  459. } else {
  460. $items[] = $item;
  461. }
  462. }
  463. }
  464. $total = $gps ? $gpsTotal : $smart['data']['total'];
  465. }
  466. /* set return data */
  467. $resp['total'] = $total;
  468. $resp['offset'] = (-1 == $limit || $offset + $limit > $total) ? $total : $offset + $limit;
  469. $resp['items'] = $items;
  470. $this->SetResponse($resp);
  471. $ret = true;
  472. End:
  473. return $ret;
  474. }
  475. private function GetParams_ListFeaturedItem ()
  476. {
  477. $params = array();
  478. if (!isset($_REQUEST['taken_date']) || !isset($_REQUEST['count'])) {
  479. return false;
  480. }
  481. if (!isset($_REQUEST['filter_smart']) && !isset($_REQUEST['filter_album']) && !isset($_REQUEST['filter_tag'])) {
  482. return false;
  483. }
  484. $listType = false;
  485. if (isset($_REQUEST['filter_album'])) {
  486. if ($_REQUEST['filter_album']) {
  487. $sharename = $this->DecodeItemId($_REQUEST['filter_album'], "album_");
  488. } else {
  489. $sharename = "/";
  490. }
  491. if (!$sharename) {
  492. return false;
  493. }
  494. $params['sharename'] = $sharename;
  495. $listType = "album";
  496. } else if (isset($_REQUEST['filter_tag'])) {
  497. $temps = explode(",", $_REQUEST['filter_tag']);
  498. $ids = array();
  499. foreach ($temps as $tagId) {
  500. if (SYNOPHOTO_UNCONFIRM_TAG_ID === $tagId) {
  501. $ids[] = SYNOPHOTO_UNCONFIRM_TAG_ID;
  502. } else {
  503. $arrs = explode("tag_", $tagId);
  504. $ids[] = $arrs[1] + 0;
  505. }
  506. }
  507. $params['labelIds'] = $ids;
  508. $listType = "label";
  509. } else if (isset($_REQUEST['filter_smart'])) {
  510. $smart = $this->DecodeItemId($_REQUEST['filter_smart'], "smart_");
  511. $params['smart'] = $smart;
  512. $listType = "smart";
  513. }
  514. if (false === $listType) {
  515. return false;
  516. }
  517. $params['listType'] = $listType;
  518. // compatible with [time] [time1,time2] [time1,] [,time2]
  519. $taken_dates = explode(',' , trim($_REQUEST['taken_date']));
  520. if (1 === count($taken_dates)) {
  521. if ($taken_dates[0]) {
  522. $taken_date = date_create($taken_dates[0]);
  523. if ($taken_date) {
  524. $date_begin = $taken_date;
  525. }
  526. }
  527. $params['date_end'] = $params['date_begin'] = $date_begin->format('Y-m-d');
  528. } else {
  529. if ($taken_dates[0]) {
  530. $taken_date = date_create($taken_dates[0]);
  531. if ($taken_date) {
  532. $date_begin = $taken_date;
  533. }
  534. }
  535. if ($taken_dates[1]) {
  536. $taken_date_end = date_create($taken_dates[1]);
  537. if ($taken_date_end) {
  538. $date_end = $taken_date_end;
  539. }
  540. }
  541. if ($date_begin && $date_end) {
  542. if ($date_begin > $date_end) {
  543. list($params['date_begin'], $params['date_end']) = array($date_end->format('Y-m-d'), $date_begin->format('Y-m-d'));
  544. } else {
  545. list($params['date_begin'], $params['date_end']) = array($date_begin->format('Y-m-d'), $date_end->format('Y-m-d'));
  546. }
  547. } else if ($date_begin) {
  548. $params['date_begin'] = $date_begin->format('Y-m-d');
  549. } else if ($date_end) {
  550. $params['date_end'] = $date_end->format('Y-m-d');
  551. } else {
  552. return false;
  553. }
  554. }
  555. // count = -1 means all
  556. $params['count'] = $_REQUEST['count'] + 0;
  557. $params['type'] = explode(',', $_REQUEST['type']);
  558. $params['additional'] = explode(',', $_REQUEST['additional']);
  559. return $params;
  560. }
  561. private function ListFeaturedItem ()
  562. {
  563. $resp = array();
  564. $items = array();
  565. /* return when lack of params */
  566. $params = $this->GetParams_ListFeaturedItem();
  567. if (!$params) {
  568. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  569. goto End;
  570. }
  571. if ("album" === $params['listType']) {
  572. $itemObjs = File::ListFeatured($params['date_begin'], $params['date_end'], $params['sharename'], $params['count'], array(),
  573. in_array('photo', $params['type']), in_array('video', $params['type']));
  574. } else if ("label" === $params["listType"]) {
  575. $itemObjs = File::ListFeatured($params['date_begin'], $params['date_end'], '/', $params['count'], $params['labelIds'],
  576. in_array('photo', $params['type']), in_array('video', $params['type']));
  577. } else if ("smart" === $params["listType"]) {
  578. $smart = json_decode(SmartAlbum::GetSmartAlbumInstance()->ListItem(json_encode(array(
  579. 'name' => $params['smart'],
  580. 'offset' => 0,
  581. 'limit' => -1,
  582. 'date_begin' => $params['date_begin'],
  583. 'date_end' => $params['date_end']
  584. )), 'takendate', 'asc'), true);
  585. if (!$smart['success']) {
  586. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  587. goto End;
  588. }
  589. $data = SmartAlbum::FilterFeatured($smart['data']['itemList'], $smart['data']['total'], $params['count']);
  590. $itemObjs = array(
  591. 'items' => $data,
  592. 'total' => $smart['data']['total']
  593. );
  594. }
  595. $formularExtraParam = array(
  596. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  597. 'param' => $params
  598. );
  599. foreach ($itemObjs['items'] as $obj) {
  600. if ('photo' === $obj['type']) {
  601. $items[] = Decorator::PhotoFormula($obj, $formularExtraParam);
  602. } else if ('video' === $obj['type']) {
  603. $items[] = Decorator::VideoFormula($obj, $formularExtraParam);
  604. }
  605. }
  606. $resp['total'] = $itemObjs['total'];
  607. $resp['items'] = $items;
  608. $this->SetResponse($resp);
  609. End:
  610. return;
  611. }
  612. private function GetParams_ListGPSGroup()
  613. {
  614. $params = array();
  615. if (!isset($_REQUEST['filter_smart']) && !isset($_REQUEST['filter_album']) && !isset($_REQUEST['filter_tag'])) {
  616. return false;
  617. }
  618. $listType = false;
  619. if (isset($_REQUEST['filter_album'])) {
  620. if ($_REQUEST['filter_album']) {
  621. $sharename = $this->DecodeItemId($_REQUEST['filter_album'], "album_");
  622. } else {
  623. $sharename = "/";
  624. }
  625. if (!$sharename) {
  626. return false;
  627. }
  628. $params['sharename'] = $sharename;
  629. $listType = "album";
  630. } else if (isset($_REQUEST['filter_tag'])) {
  631. $temps = explode(",", $_REQUEST['filter_tag']);
  632. $ids = array();
  633. foreach ($temps as $tagId) {
  634. if (SYNOPHOTO_UNCONFIRM_TAG_ID === $tagId) {
  635. $ids[] = SYNOPHOTO_UNCONFIRM_TAG_ID;
  636. } else {
  637. $arrs = explode("tag_", $tagId);
  638. $ids[] = $arrs[1] + 0;
  639. }
  640. }
  641. $params['labelIds'] = $ids;
  642. $listType = "label";
  643. } else if (isset($_REQUEST['filter_smart'])) {
  644. $smart = $this->DecodeItemId($_REQUEST['filter_smart'], "smart_");
  645. $params['smart'] = $smart;
  646. $listType = "smart";
  647. }
  648. $params['listType'] = $listType;
  649. if ($_REQUEST['bounds']) {
  650. $bounds = json_decode($_REQUEST['bounds'], true);
  651. $params['bounds'] = array(
  652. 'sw' => array(
  653. 'lat' => $bounds['sw']['lat'] + 0,
  654. 'lng' => $bounds['sw']['lng'] + 0
  655. ),
  656. 'ne' => array(
  657. 'lat' => $bounds['ne']['lat'] + 0,
  658. 'lng' => $bounds['ne']['lng'] + 0
  659. )
  660. );
  661. }
  662. if ($_REQUEST['gps_idx']) {
  663. $params['gps_idx'] = json_decode($_REQUEST['gps_idx'], true);
  664. if (null === $params['gps_idx']['lat'] || null === $params['gps_idx']['lng']) {
  665. return false;
  666. }
  667. }
  668. if ($_REQUEST['limit']) {
  669. $params['limit'] = $_REQUEST['limit'] + 0;
  670. }
  671. if ($_REQUEST['offset']) {
  672. $params['offset'] = $_REQUEST['offset'] + 0;
  673. }
  674. $params['zoom'] = $_REQUEST['zoom'] ? (int)$_REQUEST['zoom'] : 5;
  675. $params['type'] = explode(',', $_REQUEST['type']);
  676. $params['additional'] = explode(',', $_REQUEST['additional']);
  677. return $params;
  678. }
  679. private function ListGPSGroup ()
  680. {
  681. $resp = array();
  682. $items = array();
  683. /* return when lack of params */
  684. $params = $this->GetParams_ListGPSGroup();
  685. if (!$params) {
  686. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  687. goto End;
  688. }
  689. if ("album" === $params['listType']) {
  690. $itemObjs = File::ListGPSGroup($params['sharename'], array(), $params['bounds'], $params['zoom'], in_array('photo', $params['type']), false);
  691. } else if ("label" === $params["listType"]) {
  692. $itemObjs = File::ListGPSGroup('/', $params['labelIds'], $params['bounds'], $params['zoom'], in_array('photo', $params['type']), false);
  693. } else if ("smart" === $params["listType"]) {
  694. $itemObjs = SmartAlbum::GetSmartAlbumInstance()->ListGPSGroup($params['smart'], $params['bounds'], $params['zoom']);
  695. }
  696. $formularExtraParam = array(
  697. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  698. 'param' => $params
  699. );
  700. $output = array();
  701. foreach ($itemObjs['groups'] as $group) {
  702. $item = Decorator::PhotoFormula($group['item'], $formularExtraParam);
  703. $groupItem = array(
  704. 'count' => $group['count'],
  705. 'item' => $item,
  706. 'gps_idx' => $group['gps_idx']
  707. );
  708. $output[] = $groupItem;
  709. }
  710. $resp['total'] = $itemObjs['total'];
  711. $resp['items'] = $output;
  712. $this->SetResponse($resp);
  713. End:
  714. return;
  715. }
  716. private function ListGPSGroupedItem ()
  717. {
  718. $params = $this->GetParams_ListGPSGroup();
  719. if (!$params) {
  720. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  721. goto End;
  722. }
  723. if ("album" === $params['listType']) {
  724. $itemObjs = File::ListGPSGroupedItems($params['sharename'], array(), $params['gps_idx'], $params['zoom'], $params['limit'], $params['offset']);
  725. } else if ("label" === $params["listType"]) {
  726. $itemObjs = File::ListGPSGroupedItems('/', $params['labelIds'], $params['gps_idx'], $params['zoom'], $params['limit'], $params['offset']);
  727. } else if ("smart" === $params["listType"]) {
  728. $itemObjs = SmartAlbum::GetSmartAlbumInstance()->ListGPSGroupedItem($params['smart'], $params['gps_idx'], $params['zoom'], $params['limit'], $params['offset']);
  729. }
  730. $formularExtraParam = array(
  731. 'needThumbSize' => in_array('thumb_size', $params['additional']),
  732. 'param' => $params
  733. );
  734. $output = array();
  735. foreach ($itemObjs['items'] as $item) {
  736. $item = Decorator::PhotoFormula($item, $formularExtraParam);
  737. $output[] = $item;
  738. }
  739. $resp['total'] = $itemObjs['total'];
  740. $resp['items'] = $output;
  741. $this->SetResponse($resp);
  742. End:
  743. return;
  744. }
  745. private function ListExif()
  746. {
  747. $resp = array();
  748. if (!isset($_REQUEST['type'])) {
  749. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  750. goto End;
  751. }
  752. $map = array(
  753. "focal_length" => "focal_length_v2",
  754. "flash" => "flash_v2",
  755. "lens" => "lens_v2"
  756. );
  757. $column = $type = $_REQUEST['type'];
  758. if (array_key_exists($type, $map)) {
  759. $column = $map[$type];
  760. }
  761. $results = csSYNOPhotoDB::GetDBInstance()->getExifList($column);
  762. if (false === $results) {
  763. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  764. goto End;
  765. }
  766. $items = array();
  767. foreach ($results as $result) {
  768. if ('flash' === $type) {
  769. $item['id'] = $type.'_'.str_replace(',', '#', $result);
  770. } else {
  771. $item['id'] = $type.'_'.$result;
  772. }
  773. $item['name'] = $result;
  774. $item['type'] = 'exif';
  775. $item['tag_type'] = $type;
  776. $items[] = $item;
  777. }
  778. $resp['total'] = count($results);
  779. $resp['tags'] = $items;
  780. $this->SetResponse($resp);
  781. End:
  782. return;
  783. }
  784. private function GetExif()
  785. {
  786. $resp = array();
  787. if (!isset($_REQUEST['id'])) {
  788. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  789. goto End;
  790. }
  791. $id_arr = explode('_', $_REQUEST['id']);
  792. if ('photo' !== $id_arr[0]) {
  793. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  794. goto End;
  795. }
  796. $albumName = @pack('H*', $id_arr[1]);
  797. $fileName = @pack('H*', $id_arr[2]);
  798. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName . "/") . $fileName;
  799. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  800. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  801. goto End;
  802. }
  803. $command = "/usr/syno/bin/exiv2 -pa " . escapeshellarg($path);
  804. $commandTagLabel = "/usr/syno/bin/exiv2 -Pl " . escapeshellarg($path);
  805. @exec($command, $outputs, $retval);
  806. @exec($commandTagLabel, $outputsTagLabel, $retvalTagLabel);
  807. $tagLableCount = count($outputsTagLabel);
  808. $items = array();
  809. foreach ($outputs as $i => $output) {
  810. unset($item);
  811. $nonEmptyString = 0;
  812. $data = explode(" ", $output);
  813. $item['label'] = ($tagLableCount > $i) ? $outputsTagLabel[$i] : 'Unknown';
  814. foreach ($data as $datum) {
  815. if("" !== $datum) {
  816. if (3 <= $nonEmptyString) {
  817. //Last is the value of the tag
  818. $item['value'] = ($item['value']) ? $item['value'] . " " . $datum : $datum;
  819. }
  820. $nonEmptyString ++;
  821. }
  822. //check utf-8
  823. $value = iconv('UTF-8', 'UTF-8//IGNORE', $item['value']);
  824. $item['value'] = ($value) ? $value : '';
  825. }
  826. $items[] = $item;
  827. }
  828. $resp = array();
  829. $resp['total'] = count($items);
  830. $resp['exifs'] = $items;
  831. $this->SetResponse($resp);
  832. End:
  833. return;
  834. }
  835. private function Edit()
  836. {
  837. $ret = false;
  838. $resp = array();
  839. /* return when lack of params */
  840. if (!isset($_REQUEST['id'])) {
  841. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  842. goto End;
  843. }
  844. /* set optional params */
  845. $title = isset($_REQUEST['title']) ? $_REQUEST['title'] : '';
  846. $description = isset($_REQUEST['description']) ? $_REQUEST['description'] : '';
  847. /* update records */
  848. $ids = explode(',', $_REQUEST['id']);
  849. foreach ($ids as $idStr) {
  850. self::OutputSingleSpace();
  851. $id_arr = explode('_', $idStr);
  852. $albumName = @pack('H*', $id_arr[1]);
  853. $fileName = @pack('H*', $id_arr[2]);
  854. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName . "/") . $fileName;
  855. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  856. continue;
  857. }
  858. if (!csSYNOPhotoMisc::CheckAlbumManageable($albumName)) {
  859. continue;
  860. }
  861. if (isset($_REQUEST['title']) || isset($_REQUEST['description'])) {
  862. $dbPath = $this->GetDbPath($path);
  863. if ('photo' === $id_arr[0]) {
  864. $require_update = false;
  865. $query = "UPDATE photo_image SET ";
  866. $queryParam = array();
  867. if (isset($_REQUEST['title'])) {
  868. $query .= "title = ? ";
  869. $queryParam[] = $_REQUEST['title'];
  870. $require_update = true;
  871. }
  872. if (isset($_REQUEST['description'])) {
  873. if ($require_update) {
  874. $query .= ", ";
  875. }
  876. $query .= "description = ? ";
  877. $queryParam[] = $_REQUEST['description'];
  878. $require_update = true;
  879. $description = str_replace('"', '\"', $_REQUEST['description']);
  880. File::UpdateDescriptionMetadata($path, $description);
  881. }
  882. $query .= "WHERE path = ?";
  883. $queryParam[] = $dbPath;
  884. PHOTO_DB_Query($query, $queryParam);
  885. } elseif ('video' === $id_arr[0]) {
  886. $require_update = false;
  887. $query = "UPDATE video_desc SET ";
  888. $queryParam = array();
  889. if (isset($_REQUEST['title'])) {
  890. $query .= "title = ? ";
  891. $queryParam[] = $_REQUEST['title'];
  892. $require_update = true;
  893. }
  894. if (isset($_REQUEST['description'])) {
  895. if ($require_update) {
  896. $query .= ", ";
  897. }
  898. $query .= "description = ? ";
  899. $queryParam[] = $_REQUEST['description'];
  900. }
  901. $query .= "WHERE path = ?";
  902. $queryParam[] = $dbPath;
  903. $db_result = PHOTO_DB_Query($query, $queryParam);
  904. if (false === ($row = PHOTO_DB_FetchRow($db_result))) {
  905. $query = "INSERT INTO video_desc (path, title, description) VALUES (?, ?, ?)";
  906. $sqlParam = array($dbPath, $title, $description);
  907. PHOTO_DB_Query($query, $sqlParam);
  908. }
  909. }
  910. }
  911. if (isset($_REQUEST['gps_lat']) && isset($_REQUEST['gps_lng'])) {
  912. if (is_numeric($_REQUEST['gps_lat']) && is_numeric($_REQUEST['gps_lng'])) {
  913. SYNOPHOTO_GPS_UTIL_SetGPSLatLng($path, $_REQUEST['gps_lat'], $_REQUEST['gps_lng']);
  914. }
  915. }
  916. }
  917. $ret = true;
  918. End:
  919. return $ret;
  920. }
  921. private function DeleteItem()
  922. {
  923. $ret = false;
  924. /* return when lack of params */
  925. if (!isset($_REQUEST['id'])) {
  926. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  927. goto End;
  928. }
  929. $cancelPath = PHOTO_ACTION_TMP.md5(PHOTO_ACTION_DEL_KEY.$_REQUEST['id']);
  930. /* delete records */
  931. $ids = explode(',', $_REQUEST['id']);
  932. foreach ($ids as $idStr) {
  933. self::OutputSingleSpace();
  934. if (file_exists($cancelPath)) {
  935. @unlink($cancelPath);
  936. break;
  937. }
  938. $id_arr = explode('_', $idStr);
  939. $albumName = trim(@pack('H*', $id_arr[1]));
  940. $fileName = trim(@pack('H*', $id_arr[2]));
  941. if (!$albumName || !$fileName || $fileName == '/') {
  942. continue;
  943. }
  944. if (!csSYNOPhotoMisc::CheckAlbumManageable($albumName)) {
  945. continue;
  946. }
  947. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $albumName ? "" : $albumName . "/") . $fileName;
  948. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  949. continue;
  950. }
  951. $isPhoto = 'photo' === $id_arr[0] ? true : false;
  952. SYNOPHOTO_ADMIN_DeleteItemByPath($path, $isPhoto);
  953. SYNOPHOTO_LABEL_UTIL_Check_Photo_Label();
  954. PhotoLog::Add("File [".$fileName."] was deleted from album [".$albumName."].", true, strtolower(GetLoginUsername()));
  955. }
  956. $ret = true;
  957. End:
  958. return $ret;
  959. }
  960. private function CopyItem()
  961. {
  962. $ret = false;
  963. $resp = array();
  964. /* return when lack of params */
  965. if (!isset($_REQUEST['id']) || !isset($_REQUEST['sharepath']) || !isset($_REQUEST['mode']) || !isset($_REQUEST['duplicate'])) {
  966. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  967. goto End;
  968. }
  969. /* set params */
  970. if (preg_match('/^album_/', $_REQUEST['sharepath'])) {
  971. $ids = explode('_', $_REQUEST['sharepath']);
  972. $destShareName = @pack('H*', $ids[1]);
  973. } else {
  974. $destShareName = @pack('H*', $_REQUEST['sharepath']);
  975. }
  976. $destPath = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $destShareName;
  977. if (!csSynoPhotoMisc::CheckPathValid($destPath)) {
  978. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  979. goto End;
  980. }
  981. $mode = $_REQUEST['mode'];
  982. if ('copy' !== $mode && 'move' !== $mode) {
  983. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  984. goto End;
  985. }
  986. if (!csSYNOPhotoMisc::CheckAlbumUploadable('' == $destShareName ? '/' : $destShareName)) {
  987. $this->SetError(PHOTOSTATION_PHOTO_ACCESS_DENY);
  988. goto End;
  989. }
  990. $dirpath = $destShareName ? $destShareName : '/';
  991. $ids = explode(',', $_REQUEST['id']);
  992. $items = array();
  993. foreach ($ids as $idStr) {
  994. $id_arr = explode('_', $idStr);
  995. $albumName = @pack('H*', $id_arr[1]);
  996. $fileName = @pack('H*', $id_arr[2]);
  997. if ($albumName === $dirpath) {
  998. $this->SetError(PHOTOSTATION_PHOTO_SELECT_CONFLICT);
  999. goto End;
  1000. }
  1001. $item = array(
  1002. 'albumName' => $albumName,
  1003. 'fileName' => $fileName,
  1004. 'type' => $id_arr[0]
  1005. );
  1006. $items[] = $item;
  1007. }
  1008. $dup = 'rename' === $_REQUEST['duplicate'] ? 2 : ('ignore' === $_REQUEST['duplicate'] ? 0 : 1);
  1009. $cancelPath = PHOTO_ACTION_TMP.md5(PHOTO_ACTION_MVCP_KEY.$_REQUEST['id']);
  1010. foreach($items as $item) {
  1011. self::OutputSingleSpace();
  1012. if (file_exists($cancelPath)) {
  1013. @unlink($cancelPath);
  1014. break;
  1015. }
  1016. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . ("/" === $item['albumName'] ? "" : $item['albumName'] . "/") . $item['fileName'];
  1017. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  1018. continue;
  1019. }
  1020. if (!csSYNOPhotoMisc::CheckAlbumManageable($item['albumName'])) {
  1021. continue;
  1022. }
  1023. $dbPath = $this->GetDbPath($path);
  1024. /* get item id first */
  1025. if ('photo' === $item['type']) {
  1026. $query = "SELECT id FROM photo_image WHERE path = ?";
  1027. } elseif ('video' === $item['type']) {
  1028. $query = "SELECT id FROM video WHERE path = ?";
  1029. }
  1030. $db_result = PHOTO_DB_Query($query, array($dbPath));
  1031. if ($row = PHOTO_DB_FetchRow($db_result)) {
  1032. $id = $row['id'];
  1033. if ('copy' === $mode) {
  1034. if ('photo' === $item['type']) {
  1035. SYNOPHOTO_ADMIN_CopyOnePhoto($destShareName, $id, $dup);
  1036. @exec("/usr/syno/bin/synomkthumb -a " . escapeshellarg($destPath));
  1037. } elseif ('video' === $item['type']) {
  1038. SYNOPHOTO_ADMIN_CopyOneVideo($destShareName, $id, $dup);
  1039. }
  1040. } elseif ('move' === $mode) {
  1041. if ('photo' === $item['type']) {
  1042. SYNOPHOTO_ADMIN_MoveOnePhoto($destShareName, $id, $dup);
  1043. @exec("/usr/syno/bin/synomkthumb -a " . escapeshellarg($destPath));
  1044. } elseif ('video' === $item['type']) {
  1045. SYNOPHOTO_ADMIN_MoveOneVideo($destShareName, $id, $dup);
  1046. }
  1047. }
  1048. }
  1049. }
  1050. $ret = true;
  1051. End:
  1052. return $ret;
  1053. }
  1054. private function Cancel()
  1055. {
  1056. $ret = false;
  1057. $resp = array();
  1058. /* return when lack of params */
  1059. if (!isset($_REQUEST['id']) || !isset($_REQUEST['action'])) {
  1060. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  1061. goto End;
  1062. }
  1063. if ('mvcp' === $_REQUEST['action']) {
  1064. $key = PHOTO_ACTION_MVCP_KEY;
  1065. } else if ('delete' === $_REQUEST['action']) {
  1066. $key = PHOTO_ACTION_DEL_KEY;
  1067. }
  1068. $filePath = PHOTO_ACTION_TMP.md5($key.$_REQUEST['id']);
  1069. @file_put_contents($filePath, "");
  1070. $ret = true;
  1071. End:
  1072. return $ret;
  1073. }
  1074. private function GetDbPath($path)
  1075. {
  1076. return substr($path, strlen(SYNOPHOTO_SERVICE_REAL_DIR_PREFIX));
  1077. }
  1078. }
  1079. $api = new PhotoAPI();
  1080. $api->Run();