Play images and video from Synology PhotoStation server

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. <?php
  2. require_once('album.inc.php');
  3. require_once('albumutil.php');
  4. require_once '../include/SYNOPhotoEA.php';
  5. define("PHOTO_ACTION_TMP", '/tmp/photostation_');
  6. define("PHOTO_ACTION_ALBUM_MOVE_KEY", 'SYNOPS_ALBUM_MOVE');
  7. define("PHOTO_ACTION_ALBUM_DEL_KEY", 'SYNOPS_ALBUM_DEL');
  8. class AlbumAPI extends WebAPI {
  9. function __construct() {
  10. parent::__construct(SZ_WEBAPI_API_DESCRIPTION_PATH);
  11. }
  12. protected function Process() {
  13. if (!strcasecmp($this->method, "list")) {
  14. $force = "true" === $_REQUEST['force_update'] ? true : false;
  15. csSYNOPhotoDB::GetDBInstance()->SetSessionCache($force);
  16. session_write_close();
  17. $this->AlbumList();
  18. } elseif (!strcasecmp($this->method, "getinfo")) {
  19. csSYNOPhotoDB::GetDBInstance()->SetSessionCache();
  20. session_write_close();
  21. $this->GetInfo();
  22. } else {
  23. csSYNOPhotoDB::GetDBInstance()->SetSessionCache(true);
  24. csSYNOPhotoMisc::CheckSessionTimeOut(true);
  25. if (!strcasecmp($this->method, "create")) {
  26. $this->Create();
  27. } elseif (!strcasecmp($this->method, "edit")) {
  28. $this->Edit();
  29. } elseif (!strcasecmp($this->method, "delete")) {
  30. csSYNOPhotoDB::GetDBInstance()->ExpireSessionCache();
  31. session_write_close();
  32. $this->Delete();
  33. } elseif (!strcasecmp($this->method, "move")) {
  34. csSYNOPhotoDB::GetDBInstance()->ExpireSessionCache();
  35. session_write_close();
  36. $this->Move();
  37. } elseif (!strcasecmp($this->method, "arrangeitem")) {
  38. $this->ArrangeItem();
  39. } elseif (!strcasecmp($this->method, "cleararrangeitem")) {
  40. $this->ClearArrangeItem();
  41. } elseif (!strcasecmp($this->method, "cancel")) {
  42. session_write_close();
  43. $this->Cancel();
  44. }
  45. }
  46. }
  47. private function GetParams_Info() {
  48. $params = array();
  49. if (!isset($_REQUEST['id']) || '' === $_REQUEST['id']) {
  50. $params['id'] = null; // null means shared root folder
  51. } else {
  52. $split = explode(',', $_REQUEST['id']);
  53. $params['id'] = array();
  54. foreach ($split as $albumID) {
  55. $id = $this->DecodeItemId($albumID, 'album_');
  56. if (false === $id) {
  57. return false;
  58. }
  59. $params['id'][$albumID] = $id;
  60. }
  61. }
  62. $params['password'] = (isset($_REQUEST['password'])) ? $_REQUEST['password'] : null;
  63. $params['additional'] = explode(',', $_REQUEST['additional']);
  64. $params['ignore'] = explode(',', $_REQUEST['ignore']);
  65. return $params;
  66. }
  67. private function GetParams_Create_Edit() {
  68. $params = array();
  69. $params['name'] = $_REQUEST['name'];
  70. $params['id'] = null; // null means shared root folder
  71. if (isset($_REQUEST['id']) && '' !== $_REQUEST['id']) {
  72. $id = $this->DecodeItemId($_REQUEST['id'], 'album_');
  73. if (false === $id) {
  74. return false;
  75. }
  76. $params['id'] = $id;
  77. }
  78. $params['title'] = (isset($_REQUEST['title'])) ? $_REQUEST['title'] : null;
  79. $params['description'] = (isset($_REQUEST['description'])) ? $_REQUEST['description'] : null;
  80. $params['inheritParent'] = (isset($_REQUEST['inheritParent']) && 'true' === $_REQUEST['inheritParent']) ? true : false;
  81. $params['sort_by'] = (isset($_REQUEST['sort_by'])) ? $_REQUEST['sort_by'] : null;
  82. if ((null !== $params['sort_by']) && !in_array($params['sort_by'], array('filename', 'takendate', 'createdate', 'preference', 'default'))) {
  83. return false;
  84. }
  85. $params['sort_direction'] = (isset($_REQUEST['sort_direction'])) ? $_REQUEST['sort_direction'] : null;
  86. if ((null !== $params['sort_direction']) && !in_array($params['sort_direction'], array('asc', 'desc'))) {
  87. return false;
  88. }
  89. $params['allow_comment'] = (isset($_REQUEST['allow_comment'])) ? $_REQUEST['allow_comment'] : null;
  90. if ((null !== $params['allow_comment']) && !in_array($params['allow_comment'], array('true', 'false'))) {
  91. return false;
  92. }
  93. $params['type'] = (isset($_REQUEST['type'])) ? $_REQUEST['type'] : null;
  94. if ((null !== $params['type']) && !in_array($params['type'], array('public', 'private', 'password'))) {
  95. return false;
  96. }
  97. $params['password'] = (isset($_REQUEST['password'])) ? $_REQUEST['password'] : null;
  98. $params['conversion'] = (isset($_REQUEST['conversion'])) ? $_REQUEST['conversion'] : null;
  99. if ((null !== $params['conversion']) && !in_array($params['conversion'], array('true', 'false'))) {
  100. return false;
  101. }
  102. return $params;
  103. }
  104. private function GetTagIdString($tags)
  105. {
  106. $arr = explode(',', $tags);
  107. $idArr = array();
  108. foreach ($arr as $tag) {
  109. $split = explode('tag_', $tag);
  110. if (2 !== count($split)) {
  111. return Error;
  112. }
  113. array_push($idArr, $split[1]);
  114. }
  115. $idString = implode(',', $idArr);
  116. Error:
  117. return $idString;
  118. }
  119. private function GetAlbumString($albums) {
  120. $params = array();
  121. $idArr = array();
  122. $idString = '';
  123. $items = explode(',', $albums);
  124. foreach($items as $item) {
  125. $arr = explode('_', $item);
  126. if (2 !== count($arr) || 'album' !== $arr[0]) {
  127. return $idString;
  128. }
  129. $shareName = trim($arr[1]);
  130. if ('' !== $shareName) {
  131. $shareName = pack('H*', $arr[1]);
  132. array_push($params, $shareName);
  133. }
  134. }
  135. $albums = Album::GetBySharename($params);
  136. foreach ($albums as $album) {
  137. $id = $album['shareid'];
  138. array_push($idArr, $id);
  139. }
  140. $idString = implode(',', $idArr);
  141. return $idString;
  142. }
  143. private function GetExifString($values, $prefix) {
  144. $arr = explode(',', $values);
  145. $idString = '';
  146. $idArr = array();
  147. foreach($arr as $value) {
  148. $split = explode($prefix, $value);
  149. if (2 !== count($split)) {
  150. goto Error;
  151. }
  152. array_push($idArr, $split[1]);
  153. }
  154. $idString = implode(',', $idArr);
  155. Error:
  156. return $idString;
  157. }
  158. private function GetParams_list() {
  159. $params = array();
  160. if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['type'])) {
  161. // FIXME - filter type
  162. return false;
  163. }
  164. $params['offset'] = $_REQUEST['offset'];
  165. $params['limit'] = $_REQUEST['limit'];
  166. $params['type'] = array();
  167. $arr = explode(',', $_REQUEST['type']);
  168. foreach ($arr as $item) {
  169. if (!in_array($item, array('album', 'photo', 'video'))) {
  170. return false;
  171. }
  172. array_push($params['type'], $item);
  173. }
  174. $params['id'] = (isset($_REQUEST['id'])) ? $_REQUEST['id'] : null; // null means shared root folder
  175. $params['albumName'] = '/';
  176. if (isset($_REQUEST['id'])) {
  177. $split = explode('_', $_REQUEST['id']); // ex: album_id
  178. $params['id'] = $split[1];
  179. $params['albumName'] = @pack('H*', $split[1]);
  180. }
  181. $params['password'] = (isset($_REQUEST['password'])) ? $_REQUEST['password'] : null;
  182. $params['ignore'] = explode(',', $_REQUEST['ignore']);
  183. // additional operator
  184. $params['additional'] = array();
  185. $params['additional'] = explode(',', $_REQUEST['additional']);
  186. // sort operator
  187. $params['sort_by'] = isset($_REQUEST['sort_by']) ? $_REQUEST['sort_by'] : 'preference';
  188. if (!in_array($params['sort_by'], array('filename', 'takendate', 'createdate', 'preference', 'mtime'))) {
  189. return false;
  190. }
  191. if (!isset($_REQUEST['sort_direction'])) {
  192. $value = csSYNOPhotoMisc::GetConfigDB("album", "thumb_sort_order", "photo_config");
  193. $params['sort_direction'] = AlbumAPIUtil::ConvertToSortDirection($value);
  194. } else {
  195. $params['sort_direction'] = $_REQUEST['sort_direction'];
  196. }
  197. if (!in_array($params['sort_direction'], array('asc', 'desc'))) {
  198. return false;
  199. }
  200. // filter operator
  201. $params['keyword'] = (isset($_REQUEST['keyword'])) ? $_REQUEST['keyword'] : null;
  202. if (isset($_REQUEST['keyword'])) {
  203. $default = 'all';
  204. $params['keyword_op'] = (isset($_REQUEST['keyword_op'])) ? $_REQUEST['keyword_op'] : $default;
  205. if (isset($params['keyword_op']) && !in_array($params['keyword_op'], array('any', 'all', 'exact'))) {
  206. return false;
  207. }
  208. }
  209. $params['date'] = (isset($_REQUEST['date'])) ? $_REQUEST['date'] : null;
  210. if (isset($_REQUEST['date'])) {
  211. $default = 'taken';
  212. $params['date_op'] = (isset($_REQUEST['date_op'])) ? $_REQUEST['date_op'] : $default;
  213. if (isset($params['date_op']) && !in_array($params['date_op'], array('taken', 'upload', 'recently_add', 'recently_comment'))) {
  214. return false;
  215. }
  216. }
  217. $params['people_tag'] = (isset($_REQUEST['people_tag'])) ? $_REQUEST['people_tag'] : null;
  218. if (isset($_REQUEST['people_tag'])) {
  219. $params['people_tag'] = $this->GetTagIdString($_REQUEST['people_tag']);
  220. $default = 'all';
  221. $params['people_tag_op'] = (isset($_REQUEST['people_tag_op'])) ? $_REQUEST['people_tag_op'] : $default;
  222. if (isset($params['people_tag_op']) && !in_array($params['people_tag_op'], array('all', 'any'))) {
  223. return false;
  224. }
  225. }
  226. $params['geo_tag'] = (isset($_REQUEST['geo_tag'])) ? $_REQUEST['geo_tag'] : null;
  227. if (isset($_REQUEST['geo_tag'])) {
  228. $params['geo_tag'] = $this->GetTagIdString($_REQUEST['geo_tag']);
  229. $default = 'all';
  230. $params['geo_tag_op'] = (isset($_REQUEST['geo_tag_op'])) ? $_REQUEST['geo_tag_op'] : $default;
  231. if (isset($params['geo_tag_op']) && !in_array($params['geo_tag_op'], array('all', 'any'))) {
  232. return false;
  233. }
  234. }
  235. $params['desc_tag'] = (isset($_REQUEST['desc_tag'])) ? $_REQUEST['desc_tag'] : null;
  236. if (isset($_REQUEST['desc_tag'])) {
  237. $params['desc_tag'] = $this->GetTagIdString($_REQUEST['desc_tag']);
  238. $default = 'all';
  239. $params['desc_tag_op'] = (isset($_REQUEST['desc_tag_op'])) ? $_REQUEST['desc_tag_op'] : $default;
  240. if (isset($params['desc_tag_op']) && !in_array($params['desc_tag_op'], array('all', 'any'))) {
  241. return false;
  242. }
  243. }
  244. $params['albums'] = (isset($_REQUEST['albums'])) ? $_REQUEST['albums'] : null;
  245. if (isset($_REQUEST['albums'])) {
  246. $params['albums'] = $this->GetAlbumString($_REQUEST['albums']);
  247. }
  248. foreach(SmartAlbum::$exif2IdPrefix as $label => $value) {
  249. if (isset($_REQUEST[$label])) {
  250. $op = $label.'_op';
  251. $params[$label] = $this->GetExifString($_REQUEST[$label], $value.'_');
  252. $defaultOp = 'any';
  253. $params[$op] = (isset($_REQUEST[$op])) ? $_REQUEST[$op] : $default;
  254. if (isset($params[$op]) && 'any' !== $params[$op]) {
  255. return false;
  256. }
  257. }
  258. }
  259. return $params;
  260. }
  261. private function GetParams_ArrangeItem()
  262. {
  263. if (!isset($_REQUEST['offset']) || !isset($_REQUEST['limit']) || !isset($_REQUEST['item_id'])) {
  264. return false;
  265. }
  266. $params = array();
  267. $params['id'] = (isset($_REQUEST['id'])) ? $_REQUEST['id'] : null; // null means shared root folder
  268. if (null !== $params['id'] && '' !== $params['id']) {
  269. $split = explode('_', $params['id']); // ex: 'album_id'
  270. if (2 !== count($split)) {
  271. return false;
  272. }
  273. if ('album' !== $split[0]) {
  274. return false;
  275. }
  276. $params['albumName'] = @pack('H*', $split[1]);
  277. } else {
  278. $params['albumName'] = '/';
  279. }
  280. $params['offset'] = $_REQUEST['offset'];
  281. $params['limit'] = $_REQUEST['limit'];
  282. $params['item_id'] = array();
  283. $params['item_name'] = array();
  284. $items = explode(',', $_REQUEST['item_id']);
  285. // item must be photo or video, and make sure album must be ahead of photo and video
  286. foreach ($items as $item) {
  287. $arr = explode('_', $item);
  288. if (!in_array($arr[0], array('album', 'photo', 'video'))) {
  289. return false;
  290. }
  291. if ('photo' === $arr[0] || 'video' === $arr[0]) {
  292. $filename = @pack('H*', $arr[2]);
  293. array_push($params['item_name'], $filename);
  294. } elseif ('album' === $arr[0]) {
  295. $foldername = @pack('H*', $arr[1]);
  296. array_push($params['item_name'], basename($foldername).SYNOPHOTO_USER_SORT_ALBUM_SYMBOL);
  297. }
  298. }
  299. $params['item_id'] = $items;
  300. return $params;
  301. }
  302. private function GetParams_ClearArrangeItem()
  303. {
  304. $params = array();
  305. $params['id'] = (isset($_REQUEST['id'])) ? $_REQUEST['id'] : null; // null means shared root folder
  306. if (null !== $params['id'] && '' !== $params['id']) {
  307. $split = explode('_', $params['id']); // ex: 'album_id'
  308. if (2 !== count($split)) {
  309. return false;
  310. }
  311. if ('album' !== $split[0]) {
  312. return false;
  313. }
  314. $params['albumName'] = @pack('H*', $split[1]);
  315. } else {
  316. $params['albumName'] = '/';
  317. }
  318. return $params;
  319. }
  320. private function ConvertSmartCondition($params) {
  321. $matchPattern = array('id', 'keyword', 'keyword_op', 'date', 'date_op', 'people_tag', 'people_tag_op', 'geo_tag', 'geo_tag_op', 'desc_tag', 'desc_tag_op', 'albums');
  322. $formatPattern = array('dir', 'k', 'k_op', 'd', 'd_op', 'pt', 'pt_op', 'gt', 'gt_op', 'dt', 'dt_op', 'albums');
  323. $condition = array();
  324. foreach($matchPattern as $field) {
  325. if (isset($params[$field])) {
  326. $index = array_keys($matchPattern, $field);
  327. $condition[$formatPattern[$index[0]]] = $params[$field];
  328. }
  329. }
  330. foreach(SmartAlbum::$exif2IdPrefix as $label => $value) {
  331. if (isset($params[$label])) {
  332. $condition[$label] = $params[$label];
  333. $op = $label.'_op';
  334. $condition[$op] = $params[$op];
  335. }
  336. }
  337. return $condition;
  338. }
  339. private function CheckRootSharedFolder($sharePath) {
  340. if ('' === $sharePath) {
  341. return true;
  342. }
  343. return false;
  344. }
  345. private function CheckAlbumAccessRight($sharePath, $password) {
  346. $ret = WEBAPI_ERR_NONE;
  347. // admin
  348. if (isset($_SESSION[SYNOPHOTO_ADMIN_USER]['admin_syno_user'])) {
  349. return WEBAPI_ERR_NONE;
  350. }
  351. // not root share folder
  352. if (!$this->CheckRootSharedFolder($sharePath)) {
  353. $albumInfo = csSYNOPhotoDB::GetDBInstance()->GetAlbum($sharePath);
  354. if (empty($albumInfo['password'])) {
  355. // Check accessible
  356. if (!csSYNOPhotoMisc::CheckAlbumAccessible($sharePath)) {
  357. $ret = PHOTOSTATION_ALBUM_NO_ACCESS_RIGHT;
  358. }
  359. } else {
  360. if (!isset($_SESSION[SYNOPHOTO_ADMIN_USER]['password_pass_album'][$sharePath])) {
  361. if ((md5($password) !== $albumInfo['password'])) {
  362. $ret = PHOTOSTATION_ALBUM_PASSWORD_ERROR;
  363. } else {
  364. csSYNOPhotoMisc::PhotoSessionStart();
  365. $_SESSION[SYNOPHOTO_ADMIN_USER]['password_pass_album'][$sharePath] = md5($password);
  366. session_write_close();
  367. }
  368. }
  369. }
  370. }
  371. return $ret;
  372. }
  373. private function FormItems($itemList, $params) {
  374. $items = array();
  375. $allowComment = AlbumAPIUtil::IsAlbumCommentalbGlobal();
  376. $showAlbumHit = AlbumAPIUtil::IsShowAlbumHit();
  377. $needThumbSize = in_array('thumb_size', $params['additional']);
  378. $types = array(0 => 'album', 1 => 'photo', 2 => 'video');
  379. $conversionDirs = array();
  380. $decorator = array(
  381. 'album' => 'Decorator::AlbumFormula',
  382. 'photo' => 'Decorator::PhotoFormula',
  383. 'video' => 'Decorator::VideoFormula'
  384. );
  385. foreach($itemList['items'] as $item_node) {
  386. $type = $types[$item_node['itemType']];
  387. $func = $decorator[$type];
  388. $item = array();
  389. if ($func) {
  390. $item = call_user_func($func, $item_node, array(
  391. 'allowComment' => $allowComment,
  392. 'showAlbumHit' => $showAlbumHit,
  393. 'param' => $params,
  394. 'needThumbSize' => $needThumbSize
  395. ));
  396. }
  397. $items[] = $item;
  398. }
  399. return $items;
  400. }
  401. private function GetAlbumShareName($value) {
  402. if (empty($value)) {
  403. return false;
  404. }
  405. $shareNameList = array();
  406. $albumIDs = explode(',', $value);
  407. foreach ($albumIDs as $albumID) {
  408. $arr = explode('album_', $albumID);
  409. if (2 !== count($arr)) {
  410. return false;
  411. }
  412. $albumName = trim(@pack('H*', $arr[1]));
  413. if (!$albumName || '/' === $albumName) {
  414. return false;
  415. }
  416. array_push($shareNameList, $albumName);
  417. }
  418. return $shareNameList;
  419. }
  420. private function GetInfo() {
  421. // Get and check params
  422. $params = array();
  423. $params = $this->GetParams_Info();
  424. if (!$params) {
  425. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  426. goto End;
  427. }
  428. $needThumbSize = in_array('thumb_size', $params['additional']);
  429. $items = array();
  430. if (null === $params['id']) {
  431. $item = AlbumAPIUtil::GetRootAlbumInfo($params);
  432. array_push($items, $item);
  433. } else {
  434. $allowComment = AlbumAPIUtil::IsAlbumCommentalbGlobal();
  435. $showHits = AlbumAPIUtil::IsShowAlbumHit();
  436. $albums = Album::GetBySharename(array_values($params['id']));
  437. foreach ($params['id'] as $id => $shareName) {
  438. $item = Decorator::AlbumFormula($albums[$shareName], array(
  439. 'allowComment' => $allowComment,
  440. 'showAlbumHit' => $showHits,
  441. 'needThumbSize' => $needThumbSize,
  442. 'param' => $params
  443. ));
  444. array_push($items, $item);
  445. }
  446. }
  447. $resp['items'] = $items;
  448. $this->SetResponse($resp);
  449. End:
  450. return;
  451. }
  452. private function AlbumList() {
  453. // Get and check params
  454. $params = array();
  455. $params = $this->GetParams_list();
  456. if (!$params) {
  457. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  458. goto End;
  459. }
  460. $sharePath = @pack('H*', $params['id']);
  461. // Check access right
  462. if (WEBAPI_ERR_NONE != ($error_code = $this->CheckAlbumAccessRight($sharePath, $params['password']))) {
  463. $this->SetError($error_code);
  464. goto End;
  465. }
  466. $hasExifCond = false;
  467. foreach(SmartAlbum::$exif2IdPrefix as $label => $value) {
  468. if (isset($params[$label])) {
  469. $hasExifCond = true;
  470. break;
  471. }
  472. }
  473. // search operator
  474. $itemsSort = array();
  475. if (isset($params['keyword']) || isset($params['date']) || isset($params['people_tag']) || isset($params['geo_tag']) || isset($params['desc_tag'])
  476. || isset($params['albums']) || $hasExifCond) {
  477. $smart_condition = $this->ConvertSmartCondition($params);
  478. $itemList = csSYNOPhotoBrowse::GetBrowseInstance()->GetSearchThumbList_new($smart_condition, $params['offset'], $params['limit']);
  479. // filter type
  480. $filterItems = AlbumAPIUtil::FilterByType($itemList['items'], $params['type']);
  481. $total = $itemList['itemCount'];
  482. // FIXME - sorting for search operator
  483. $itemsSort['items'] = $filterItems;
  484. } else {
  485. // album operator
  486. csSYNOPhotoMisc::PhotoSessionStart();
  487. $_SESSION[SYNOPHOTO_ADMIN_USER]['album_thumb_sort_by'] = $params['sort_by'];
  488. $_SESSION[SYNOPHOTO_ADMIN_USER]['album_thumb_sort_order'] = $params['sort_direction'];
  489. if (isset($params['id'])) {
  490. csSYNOPhotoAlbum::GetAlbumInstance()->AddHitTimes($sharePath);
  491. }
  492. session_write_close();
  493. $itemsSort = array();
  494. $itemsSort = csSYNOPhotoBrowse::GetBrowseInstance()->GetThumbList_New(isset($params['id']) ? $sharePath : '/', (int)$params['offset'], (int)$params['limit'], $params['sort_by'], $params['sort_direction'], !in_array('album', $params['type']), !in_array('video', $params['type']), !in_array('photo', $params['type']));
  495. $total = $itemsSort['itemCount'];
  496. }
  497. $resp = array();
  498. $resp['total'] = $total;
  499. $offset = (0 > (int)$params['limit']) ? $resp['total'] : $params['offset'] + $params['limit'];
  500. $resp['offset'] = ($offset > $resp['total']) ? $resp['total'] : $offset;
  501. $resp['items'] = $this->FormItems($itemsSort, $params);
  502. $this->SetResponse($resp);
  503. End:
  504. return;
  505. }
  506. private function Create() {
  507. if (!isset($_REQUEST['name'])) {
  508. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  509. goto End;
  510. }
  511. // name can't include [.] start or [\/:*?<>|]
  512. if (preg_match('/[\\\\\/:\*\?<>\|]/', $_REQUEST['name'], $matches) || preg_match('/^\./', $_REQUEST['name'], $matches)) {
  513. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  514. goto End;
  515. }
  516. // Get and check params
  517. $params = array();
  518. $params = $this->GetParams_Create_Edit();
  519. if (!$params) {
  520. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  521. goto End;
  522. }
  523. // get parent share name
  524. $parentShareName = null === $params['id'] ? '' : $params['id'];
  525. $ret = AlbumAPIUtil::SYNOPHOTO_ADMIN_AddAlbum($parentShareName, $params);
  526. $username = GetLoginUsername();
  527. $parentShareName = $parentShareName === '' ? '/' : $parentShareName;
  528. if (is_numeric($ret) && $ret < 0) {
  529. PhotoLog::Add("Failed to create album [".$_REQUEST['name']."] in [".$parentShareName."].", false, strtolower($username));
  530. } else {
  531. PhotoLog::Add("Album [".$_REQUEST['name']."] was created in [".$parentShareName."].", true, strtolower($username));
  532. }
  533. if (-1 === $ret) {
  534. $this->SetError(PHOTOSTATION_ALBUM_CREATE_FAIL);
  535. goto End;
  536. } elseif (-2 === $ret) {
  537. $this->SetError(PHOTOSTATION_ALBUM_NO_UPLOAD_RIGHT);
  538. goto End;
  539. } elseif (-3 === $ret) {
  540. $this->SetError(PHOTOSTATION_ALBUM_NOT_ADMIN);
  541. goto End;
  542. } elseif (-4 === $ret) {
  543. $this->SetError(PHOTOSTATION_ALBUM_HAS_EXIST);
  544. goto End;
  545. }
  546. $row = csSYNOPhotoDB::GetDBInstance()->GetFieldByKeyValue('sharename', 'photo_share', 'shareid', $ret);
  547. $resp['id'] = $this->EncodeItemId($row['sharename'], "album_");
  548. $this->SetResponse($resp);
  549. End:
  550. return;
  551. }
  552. private function Delete() {
  553. if (!isset($_REQUEST['id'])) {
  554. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  555. goto End;
  556. }
  557. if (false === ($shareNameList = $this->GetAlbumShareName($_REQUEST['id']))) {
  558. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  559. goto End;
  560. }
  561. $cancelPath = PHOTO_ACTION_TMP.md5(PHOTO_ACTION_ALBUM_DEL_KEY.$_REQUEST['id']);
  562. $failCount = 0;
  563. foreach ($shareNameList as $shareName) {
  564. self::OutputSingleSpace();
  565. if (file_exists($cancelPath)) {
  566. @unlink($cancelPath);
  567. break;
  568. }
  569. $dirName = dirname($shareName);
  570. $dirName = ('.' === $dirName) ? '/' : $dirName;
  571. if (!csSynoPhotoMisc::CheckAlbumManageable($dirName)) {
  572. $failCount++;
  573. continue;
  574. }
  575. $result = Album::DeleteOneBySharename($shareName);
  576. if (!$result[0]) {
  577. continue;
  578. }
  579. PhotoLog::Add("Album [".$shareName."] was deleted.", true, strtolower(GetLoginUsername()));
  580. }
  581. if ($failCount === count($shareNameList)) {
  582. $this->SetError(PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT);
  583. goto End;
  584. }
  585. SYNOPHOTO_LABEL_UTIL_Check_Photo_Label();
  586. End:
  587. return;
  588. }
  589. private function Edit() {
  590. // Get and check params
  591. $params = array();
  592. $params = $this->GetParams_Create_Edit();
  593. if (!$params || null === $params['id']) {
  594. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  595. goto End;
  596. }
  597. $shareName = $params['id'];
  598. $ret = AlbumAPIUtil::SYNOPHOTO_ADMIN_UpdateAlbum($shareName, $params);
  599. if (-1 === $ret) {
  600. $this->SetError(PHOTOSTATION_ALBUM_EDIT_FAIL);
  601. goto End;
  602. } elseif (-2 === $ret) {
  603. $this->SetError(PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT);
  604. goto End;
  605. } elseif (-3 === $ret) {
  606. $this->SetError(PHOTOSTATION_ALBUM_NOT_ADMIN);
  607. goto End;
  608. }
  609. End:
  610. return;
  611. }
  612. private function GetParams_Move() {
  613. $param = array();
  614. /* return when lack of params */
  615. if (!isset($_REQUEST['id']) || !isset($_REQUEST['sharepath']) || !isset($_REQUEST['duplicate'])) {
  616. return array(false, WEBAPI_ERR_BAD_REQUEST);
  617. }
  618. /* set params */
  619. if (preg_match('/^album_/', $_REQUEST['sharepath'])) {
  620. $sharepath = trim($this->DecodeItemId($_REQUEST['sharepath'], "album_"));
  621. } else {
  622. $sharepath = trim(@pack('H*', $_REQUEST['sharepath']));
  623. }
  624. // $sharepath could be empty
  625. $dirpath = pathinfo($sharepath, PATHINFO_DIRNAME);
  626. if (!$dirpath || '.' === $dirpath) {
  627. $dirpath = '';
  628. }
  629. $destDirs = array();
  630. $checkDestDir = $sharepath;
  631. while('.' !== $checkDestDir && '' !== $checkDestDir) {
  632. $destDirs[] = $checkDestDir;
  633. $checkDestDir = pathinfo($checkDestDir, PATHINFO_DIRNAME);
  634. }
  635. $requestIds = explode(',', $_REQUEST['id']);
  636. $ids = array();
  637. foreach($requestIds as $idStr) {
  638. $id_arr = explode('_', $idStr);
  639. $albumName = trim(@pack('H*', $id_arr[1]));
  640. if (!$albumName || '/' === $albumName) {
  641. return array(false, WEBAPI_ERR_BAD_REQUEST);
  642. }
  643. $sourceDir = pathinfo($albumName, PATHINFO_DIRNAME);
  644. if ('.' === $sourceDir) {
  645. $sourceDir = '';
  646. }
  647. // dir(source) === dest
  648. if ($sharepath === $sourceDir) {
  649. return array(false, PHOTOSTATION_ALBUM_SELECT_CONFLICT);
  650. }
  651. // dest's parent !== dest
  652. if (in_array($albumName, $destDirs)) {
  653. return array(false, PHOTOSTATION_ALBUM_SELECT_CONFLICT);
  654. }
  655. $ids[] = $albumName;
  656. }
  657. $param['destShareName'] = $sharepath;
  658. $param['isOverwrite'] = 'overwrite' === $_REQUEST['duplicate'] ? true : false;
  659. $param['id'] = $ids;
  660. return array(true, $param);
  661. }
  662. private function Move() {
  663. $ret = false;
  664. $param_res = $this->GetParams_Move();
  665. if (!$param_res[0]) {
  666. $this->SetError($param_res[1]);
  667. goto End;
  668. }
  669. $param = $param_res[1];
  670. if ('' !== $param['destShareName']) {
  671. $destPath = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $param['destShareName'];
  672. } else {
  673. $destPath = SYNOPHOTO_SERVICE_REAL_DIR . "/";
  674. }
  675. if (!csSynoPhotoMisc::CheckPathValid($destPath)) {
  676. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  677. goto End;
  678. }
  679. if (!csSynoPhotoMisc::CheckAlbumUploadable('' == $param['destShareName'] ? '/' : $param['destShareName'])) {
  680. $this->SetError(PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT);
  681. goto End;
  682. }
  683. $destShareData = array();
  684. $destShareData['shareid'] = -1;
  685. if ($_REQUEST['sharepath'] == '') {
  686. $destShareData['sharename'] = '';
  687. $albums = Album::GetBySharename(array('/'), true);
  688. $row = $albums['/'];
  689. } else {
  690. $destShareData['sharename'] = $param['destShareName'];
  691. $albums = Album::GetBySharename(array($destShareData['sharename']), true);
  692. $row = $albums[$destShareData['sharename']];
  693. }
  694. if($row) {
  695. $destShareData['shareid'] = $row['shareid'];
  696. $destShareData['public'] = $row['public'];
  697. $destShareData['password'] = $row['password'];
  698. $destShareData['comment'] = $row['comment'];
  699. }
  700. $cancelPath = PHOTO_ACTION_TMP.md5(PHOTO_ACTION_ALBUM_MOVE_KEY.$_REQUEST['id']);
  701. $skip = array();
  702. foreach ($param['id'] as $albumName) {
  703. self::OutputSingleSpace();
  704. if (file_exists($cancelPath)) {
  705. @unlink($cancelPath);
  706. break;
  707. }
  708. $path = SYNOPHOTO_SERVICE_REAL_DIR . "/" . $albumName;
  709. if (!csSynoPhotoMisc::CheckPathValid($path)) {
  710. continue;
  711. }
  712. $ret = SYNOPHOTO_ADMIN_MoveOneAlbum($destShareData, $albumName, $param['isOverwrite']);
  713. // skip this album
  714. if (1 === $ret) {
  715. $skip[] = $this->EncodeItemId($albumName, "album_");
  716. }
  717. }
  718. $ret = true;
  719. $resp = array(
  720. 'skip' => $skip
  721. );
  722. $this->SetResponse($resp);
  723. End:
  724. return $ret;
  725. }
  726. private function ArrangeItem()
  727. {
  728. if (false === ($params = $this->GetParams_ArrangeItem())) {
  729. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  730. goto End;
  731. }
  732. // chack permission
  733. if (!csSynoPhotoMisc::CheckAlbumManageable($params['albumName'])) {
  734. $this->SetError(PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT);
  735. goto End;
  736. }
  737. // get sort_by and sort_direction
  738. $sort_by = $_SESSION[SYNOPHOTO_ADMIN_USER]['album_thumb_sort_by'];
  739. if (empty($sort_by)) {
  740. $value = csSYNOPhotoMisc::GetConfigDB("album", "thumb_sort_type", "photo_config");
  741. $sort_by = AlbumAPIUtil::ConvertToSortName($value);
  742. }
  743. $sort_direction = $_SESSION[SYNOPHOTO_ADMIN_USER]['album_thumb_sort_order'];
  744. if (empty($sort_direction)) {
  745. $value = csSYNOPhotoMisc::GetConfigDB("album", "thumb_sort_order", "photo_config");
  746. $sort_direction = AlbumAPIUtil::ConvertToSortDirection($value);
  747. }
  748. // get original path list
  749. $pathList = csSYNOPhotoBrowse::GetBrowseInstance()->GetList_new($params['albumName'], 0, -1, $sort_by, $sort_direction, false, false, false);
  750. $totalCount = $pathList['totalCount'];
  751. // adjust offset and limit
  752. $offset = $params['offset'];
  753. $limit = (-1 === (int)$params['limit']) ? (int)$totalCount : (int)$params['limit'];
  754. if ($offset >= $totalCount || $limit != count($params['item_name'])) {
  755. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  756. goto End;
  757. }
  758. // get original name list
  759. $nameList = array();
  760. foreach ($pathList['list'] as $path => $itemType) {
  761. $name = (SYNOPHOTO_ITEM_TYPE_ALBUM === $itemType) ? basename($path).SYNOPHOTO_USER_SORT_ALBUM_SYMBOL : basename($path);
  762. $nameList[] = $name;
  763. }
  764. // get sort name list
  765. array_splice($nameList, $offset, $limit, $params['item_name']);
  766. $sortNameList = $nameList;
  767. // make sure album must be ahead of photo and video
  768. $blHavePhotoVideoItem = false;
  769. $blPhotoVideoAheadAlbum = false;
  770. foreach ($sortNameList as $name) {
  771. if ('/' === $name[strlen($name)-1]) {
  772. if ($blHavePhotoVideoItem) {
  773. $blPhotoVideoAheadAlbum = true;
  774. break;
  775. }
  776. } else {
  777. $blHavePhotoVideoItem = true;
  778. }
  779. }
  780. if ($blPhotoVideoAheadAlbum) {
  781. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  782. goto End;
  783. }
  784. // seve to conf
  785. $data['type'] = AlbumAPIUtil::ConvertToSortCode('preference');
  786. $data['list'] = $sortNameList;
  787. csSYNOPhotoAlbum::GetAlbumInstance()->SaveAlbumThumbSortType($params['albumName'], $data);
  788. End:
  789. return;
  790. }
  791. private function ClearArrangeItem()
  792. {
  793. if (false === ($params = $this->GetParams_ClearArrangeItem())) {
  794. $this->SetError(WEBAPI_ERR_BAD_REQUEST);
  795. goto End;
  796. }
  797. $albumName = $params['albumName'];
  798. // chack permission
  799. if (!csSynoPhotoMisc::CheckAlbumManageable($albumName)) {
  800. $this->SetError(PHOTOSTATION_ALBUM_NO_MANAGE_RIGHT);
  801. goto End;
  802. }
  803. //remove sort file directly in PS6
  804. if ('/' === $albumName) {
  805. $fullDirPath = SYNOPHOTO_SERVICE_REAL_DIR;
  806. $sortFile = $fullDirPath . '/' . SYNOPhotoEA::SYNO_EA_DIR . '/' . SYNOPhotoEA::getFilename(SYNOPhotoEA::FILE_ALBUM_SORT);
  807. } else {
  808. $fullDirPath = sprintf("%s/%s", SYNOPHOTO_SERVICE_REAL_DIR, $albumName);
  809. if (false === SYNOPhotoEA::checkFilePath($fullDirPath, SYNOPhotoEA::FILE_ALBUM_SORT, $sortFile)) {
  810. goto End;
  811. }
  812. }
  813. @unlink($sortFile);
  814. End:
  815. return;
  816. }
  817. private function Cancel()
  818. {
  819. $ret = false;
  820. $resp = array();
  821. /* return when lack of params */
  822. if (!isset($_REQUEST['id']) || !isset($_REQUEST['action'])) {
  823. $this->SetError(PHOTOSTATION_PHOTO_BAD_PARAMS);
  824. goto End;
  825. }
  826. if ('mvcp' === $_REQUEST['action']) {
  827. $key = PHOTO_ACTION_ALBUM_MOVE_KEY;
  828. } else if ('delete' === $_REQUEST['action']) {
  829. $key = PHOTO_ACTION_ALBUM_DEL_KEY;
  830. }
  831. $filePath = PHOTO_ACTION_TMP.md5($key.$_REQUEST['id']);
  832. @file_put_contents($filePath, "");
  833. $ret = true;
  834. End:
  835. return $ret;
  836. }
  837. }
  838. $api = new AlbumAPI();
  839. $api->Run();
  840. ?>